19172SFei.Feng@Sun.COM /* 29172SFei.Feng@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 39172SFei.Feng@Sun.COM * Use is subject to license terms. 49172SFei.Feng@Sun.COM */ 59172SFei.Feng@Sun.COM 69172SFei.Feng@Sun.COM /* 79172SFei.Feng@Sun.COM * Copyright (c) 2007, 2008 89172SFei.Feng@Sun.COM * Damien Bergamini <damien.bergamini@free.fr> 99172SFei.Feng@Sun.COM * 109172SFei.Feng@Sun.COM * Permission to use, copy, modify, and distribute this software for any 119172SFei.Feng@Sun.COM * purpose with or without fee is hereby granted, provided that the above 129172SFei.Feng@Sun.COM * copyright notice and this permission notice appear in all copies. 139172SFei.Feng@Sun.COM * 149172SFei.Feng@Sun.COM * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 159172SFei.Feng@Sun.COM * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 169172SFei.Feng@Sun.COM * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 179172SFei.Feng@Sun.COM * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 189172SFei.Feng@Sun.COM * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 199172SFei.Feng@Sun.COM * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 209172SFei.Feng@Sun.COM * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 219172SFei.Feng@Sun.COM */ 229172SFei.Feng@Sun.COM 239172SFei.Feng@Sun.COM /* 249172SFei.Feng@Sun.COM * Ralink Technology RT2860 chipset driver 259172SFei.Feng@Sun.COM * http://www.ralinktech.com/ 269172SFei.Feng@Sun.COM */ 279172SFei.Feng@Sun.COM 289172SFei.Feng@Sun.COM #include <sys/types.h> 299172SFei.Feng@Sun.COM #include <sys/byteorder.h> 309172SFei.Feng@Sun.COM #include <sys/conf.h> 319172SFei.Feng@Sun.COM #include <sys/cmn_err.h> 329172SFei.Feng@Sun.COM #include <sys/stat.h> 339172SFei.Feng@Sun.COM #include <sys/ddi.h> 349172SFei.Feng@Sun.COM #include <sys/sunddi.h> 359172SFei.Feng@Sun.COM #include <sys/strsubr.h> 369172SFei.Feng@Sun.COM #include <inet/common.h> 379172SFei.Feng@Sun.COM #include <sys/note.h> 389172SFei.Feng@Sun.COM #include <sys/stream.h> 399172SFei.Feng@Sun.COM #include <sys/strsun.h> 409172SFei.Feng@Sun.COM #include <sys/modctl.h> 419172SFei.Feng@Sun.COM #include <sys/devops.h> 429172SFei.Feng@Sun.COM #include <sys/mac_provider.h> 439172SFei.Feng@Sun.COM #include <sys/mac_wifi.h> 449172SFei.Feng@Sun.COM #include <sys/net80211.h> 459172SFei.Feng@Sun.COM #include <sys/net80211_proto.h> 469172SFei.Feng@Sun.COM #include <sys/varargs.h> 479172SFei.Feng@Sun.COM #include <sys/pci.h> 489172SFei.Feng@Sun.COM #include <sys/crypto/common.h> 499172SFei.Feng@Sun.COM #include <sys/crypto/api.h> 509172SFei.Feng@Sun.COM #include <inet/wifi_ioctl.h> 519172SFei.Feng@Sun.COM 529172SFei.Feng@Sun.COM #include "rt2860_reg.h" 539172SFei.Feng@Sun.COM #include "rt2860_var.h" 549172SFei.Feng@Sun.COM 559172SFei.Feng@Sun.COM #define RT2860_DBG_80211 (1 << 0) 569172SFei.Feng@Sun.COM #define RT2860_DBG_DMA (1 << 1) 579172SFei.Feng@Sun.COM #define RT2860_DBG_EEPROM (1 << 2) 589172SFei.Feng@Sun.COM #define RT2860_DBG_FW (1 << 3) 599172SFei.Feng@Sun.COM #define RT2860_DBG_HW (1 << 4) 609172SFei.Feng@Sun.COM #define RT2860_DBG_INTR (1 << 5) 619172SFei.Feng@Sun.COM #define RT2860_DBG_RX (1 << 6) 629172SFei.Feng@Sun.COM #define RT2860_DBG_SCAN (1 << 7) 639172SFei.Feng@Sun.COM #define RT2860_DBG_TX (1 << 8) 649172SFei.Feng@Sun.COM #define RT2860_DBG_RADIO (1 << 9) 659172SFei.Feng@Sun.COM #define RT2860_DBG_RESUME (1 << 10) 669172SFei.Feng@Sun.COM #define RT2860_DBG_MSG (1 << 11) 679172SFei.Feng@Sun.COM 689172SFei.Feng@Sun.COM uint32_t rt2860_dbg_flags = 0x0; 699172SFei.Feng@Sun.COM 709172SFei.Feng@Sun.COM #ifdef DEBUG 719172SFei.Feng@Sun.COM #define RWN_DEBUG \ 729172SFei.Feng@Sun.COM rt2860_debug 739172SFei.Feng@Sun.COM #else 749172SFei.Feng@Sun.COM #define RWN_DEBUG 759172SFei.Feng@Sun.COM #endif 769172SFei.Feng@Sun.COM 779172SFei.Feng@Sun.COM static void *rt2860_soft_state_p = NULL; 789172SFei.Feng@Sun.COM static uint8_t rt2860_fw_bin [] = { 799172SFei.Feng@Sun.COM #include "fw-rt2860/rt2860.ucode" 809172SFei.Feng@Sun.COM }; 819172SFei.Feng@Sun.COM 82*10108SFei.Feng@Sun.COM static const struct ieee80211_rateset rt2860_rateset_11b = 839172SFei.Feng@Sun.COM { 4, { 2, 4, 11, 22 } }; 849172SFei.Feng@Sun.COM 85*10108SFei.Feng@Sun.COM static const struct ieee80211_rateset rt2860_rateset_11g = 869172SFei.Feng@Sun.COM { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } }; 879172SFei.Feng@Sun.COM 889172SFei.Feng@Sun.COM static const struct { 899172SFei.Feng@Sun.COM uint32_t reg; 909172SFei.Feng@Sun.COM uint32_t val; 919172SFei.Feng@Sun.COM } rt2860_def_mac[] = { 929172SFei.Feng@Sun.COM RT2860_DEF_MAC 939172SFei.Feng@Sun.COM }; 949172SFei.Feng@Sun.COM 959172SFei.Feng@Sun.COM static const struct { 969172SFei.Feng@Sun.COM uint8_t reg; 979172SFei.Feng@Sun.COM uint8_t val; 989172SFei.Feng@Sun.COM } rt2860_def_bbp[] = { 999172SFei.Feng@Sun.COM RT2860_DEF_BBP 1009172SFei.Feng@Sun.COM }; 1019172SFei.Feng@Sun.COM 1029172SFei.Feng@Sun.COM static const struct rfprog { 1039172SFei.Feng@Sun.COM uint8_t chan; 1049172SFei.Feng@Sun.COM uint32_t r1, r2, r3, r4; 1059172SFei.Feng@Sun.COM } rt2860_rf2850[] = { 1069172SFei.Feng@Sun.COM RT2860_RF2850 1079172SFei.Feng@Sun.COM }; 1089172SFei.Feng@Sun.COM 1099172SFei.Feng@Sun.COM /* 1109172SFei.Feng@Sun.COM * PIO access attributes for registers 1119172SFei.Feng@Sun.COM */ 1129172SFei.Feng@Sun.COM static ddi_device_acc_attr_t rwn_csr_accattr = { 1139172SFei.Feng@Sun.COM DDI_DEVICE_ATTR_V0, 1149172SFei.Feng@Sun.COM DDI_STRUCTURE_LE_ACC, 1159172SFei.Feng@Sun.COM DDI_STRICTORDER_ACC 1169172SFei.Feng@Sun.COM }; 1179172SFei.Feng@Sun.COM 1189172SFei.Feng@Sun.COM /* 1199172SFei.Feng@Sun.COM * DMA access attributes for descriptors: NOT to be byte swapped. 1209172SFei.Feng@Sun.COM */ 1219172SFei.Feng@Sun.COM static ddi_device_acc_attr_t rt2860_desc_accattr = { 1229172SFei.Feng@Sun.COM DDI_DEVICE_ATTR_V0, 1239172SFei.Feng@Sun.COM DDI_STRUCTURE_LE_ACC, 1249172SFei.Feng@Sun.COM DDI_STRICTORDER_ACC 1259172SFei.Feng@Sun.COM }; 1269172SFei.Feng@Sun.COM 1279172SFei.Feng@Sun.COM static ddi_device_acc_attr_t rt2860_buf_accattr = { 1289172SFei.Feng@Sun.COM DDI_DEVICE_ATTR_V0, 1299172SFei.Feng@Sun.COM DDI_NEVERSWAP_ACC, 1309172SFei.Feng@Sun.COM DDI_STRICTORDER_ACC, 1319172SFei.Feng@Sun.COM DDI_DEFAULT_ACC 1329172SFei.Feng@Sun.COM }; 1339172SFei.Feng@Sun.COM 1349172SFei.Feng@Sun.COM /* 1359172SFei.Feng@Sun.COM * Describes the chip's DMA engine 1369172SFei.Feng@Sun.COM */ 1379172SFei.Feng@Sun.COM static ddi_dma_attr_t rt2860_dma_attr = { 1389172SFei.Feng@Sun.COM DMA_ATTR_V0, /* dma_attr version */ 1399172SFei.Feng@Sun.COM 0x0, /* dma_attr_addr_lo */ 1409172SFei.Feng@Sun.COM 0xffffffffU, /* dma_attr_addr_hi */ 1419172SFei.Feng@Sun.COM 0xffffffffU, /* dma_attr_count_max */ 1429172SFei.Feng@Sun.COM 16, /* dma_attr_align */ 1439172SFei.Feng@Sun.COM 0x00000fff, /* dma_attr_burstsizes */ 1449172SFei.Feng@Sun.COM 1, /* dma_attr_minxfer */ 1459172SFei.Feng@Sun.COM 0xffffffffU, /* dma_attr_maxxfer */ 1469172SFei.Feng@Sun.COM 0xffffffffU, /* dma_attr_seg */ 1479172SFei.Feng@Sun.COM 1, /* dma_attr_sgllen */ 1489172SFei.Feng@Sun.COM 1, /* dma_attr_granular */ 1499172SFei.Feng@Sun.COM 0 /* dma_attr_flags */ 1509172SFei.Feng@Sun.COM }; 1519172SFei.Feng@Sun.COM 1529172SFei.Feng@Sun.COM static uint16_t rt2860_eeprom_read(struct rt2860_softc *, uint8_t); 1539172SFei.Feng@Sun.COM static int rt2860_read_eeprom(struct rt2860_softc *); 1549172SFei.Feng@Sun.COM const char *rt2860_get_rf(uint8_t); 1559172SFei.Feng@Sun.COM static int rt2860_alloc_dma_mem(dev_info_t *, ddi_dma_attr_t *, size_t, 1569172SFei.Feng@Sun.COM ddi_device_acc_attr_t *, uint_t, uint_t, struct dma_area *); 1579172SFei.Feng@Sun.COM static void rt2860_free_dma_mem(struct dma_area *); 1589172SFei.Feng@Sun.COM static int rt2860_alloc_tx_ring(struct rt2860_softc *, 1599172SFei.Feng@Sun.COM struct rt2860_tx_ring *); 1609172SFei.Feng@Sun.COM static void rt2860_free_tx_ring(struct rt2860_softc *, 1619172SFei.Feng@Sun.COM struct rt2860_tx_ring *); 1629172SFei.Feng@Sun.COM static int rt2860_alloc_rx_ring(struct rt2860_softc *, 1639172SFei.Feng@Sun.COM struct rt2860_rx_ring *); 1649172SFei.Feng@Sun.COM static void rt2860_free_rx_ring(struct rt2860_softc *, 1659172SFei.Feng@Sun.COM struct rt2860_rx_ring *); 1669172SFei.Feng@Sun.COM static int rt2860_alloc_tx_pool(struct rt2860_softc *); 1679172SFei.Feng@Sun.COM static void rt2860_free_tx_pool(struct rt2860_softc *); 1689172SFei.Feng@Sun.COM static uint16_t rt2860_txtime(int, int, uint32_t); 1699172SFei.Feng@Sun.COM static int rt2860_ack_rate(struct ieee80211com *, int); 1709172SFei.Feng@Sun.COM static int rt2860_send(ieee80211com_t *, mblk_t *, uint8_t); 1719172SFei.Feng@Sun.COM static uint8_t rt2860_maxrssi_chain(struct rt2860_softc *, 1729172SFei.Feng@Sun.COM const struct rt2860_rxwi *); 1739172SFei.Feng@Sun.COM static void rt2860_drain_stats_fifo(struct rt2860_softc *); 1749172SFei.Feng@Sun.COM static void rt2860_tx_intr(struct rt2860_softc *, int); 1759172SFei.Feng@Sun.COM static void rt2860_rx_intr(struct rt2860_softc *); 1769172SFei.Feng@Sun.COM static uint_t rt2860_softintr(caddr_t); 1779172SFei.Feng@Sun.COM static uint_t rt2860_intr(caddr_t); 1789172SFei.Feng@Sun.COM static void rt2860_set_region_4(struct rt2860_softc *, 1799172SFei.Feng@Sun.COM uint32_t, uint32_t, int); 1809172SFei.Feng@Sun.COM static int rt2860_load_microcode(struct rt2860_softc *); 1819172SFei.Feng@Sun.COM static void rt2860_set_macaddr(struct rt2860_softc *, const uint8_t *); 1829172SFei.Feng@Sun.COM static int rt2860_bbp_init(struct rt2860_softc *); 1839172SFei.Feng@Sun.COM static uint8_t rt2860_mcu_bbp_read(struct rt2860_softc *, uint8_t); 1849172SFei.Feng@Sun.COM static void rt2860_mcu_bbp_write(struct rt2860_softc *, uint8_t, uint8_t); 1859172SFei.Feng@Sun.COM static int rt2860_mcu_cmd(struct rt2860_softc *, uint8_t, uint16_t); 1869172SFei.Feng@Sun.COM static void rt2860_rf_write(struct rt2860_softc *, uint8_t, uint32_t); 1879172SFei.Feng@Sun.COM static void rt2860_select_chan_group(struct rt2860_softc *, int); 1889172SFei.Feng@Sun.COM static void rt2860_set_chan(struct rt2860_softc *, 1899172SFei.Feng@Sun.COM struct ieee80211_channel *); 1909172SFei.Feng@Sun.COM static void rt2860_updateprot(struct ieee80211com *); 1919172SFei.Feng@Sun.COM static void rt2860_set_leds(struct rt2860_softc *, uint16_t); 1929172SFei.Feng@Sun.COM static void rt2860_next_scan(void *); 1939172SFei.Feng@Sun.COM static void rt2860_iter_func(void *, struct ieee80211_node *); 1949172SFei.Feng@Sun.COM static void rt2860_updateslot(struct rt2860_softc *); 1959172SFei.Feng@Sun.COM static uint8_t rt2860_rate2mcs(uint8_t); 1969172SFei.Feng@Sun.COM static void rt2860_enable_mrr(struct rt2860_softc *); 1979172SFei.Feng@Sun.COM static void rt2860_set_txpreamble(struct rt2860_softc *); 1989172SFei.Feng@Sun.COM static void rt2860_set_basicrates(struct rt2860_softc *); 1999172SFei.Feng@Sun.COM static void rt2860_set_bssid(struct rt2860_softc *, const uint8_t *); 2009172SFei.Feng@Sun.COM static void rt2860_amrr_node_init(const struct rt2860_amrr *, 2019172SFei.Feng@Sun.COM struct rt2860_amrr_node *); 2029172SFei.Feng@Sun.COM static void rt2860_amrr_choose(struct rt2860_amrr *, 2039172SFei.Feng@Sun.COM struct ieee80211_node *, struct rt2860_amrr_node *); 2049172SFei.Feng@Sun.COM static void rt2860_newassoc(struct ieee80211com *, struct ieee80211_node *, 2059172SFei.Feng@Sun.COM int); 2069172SFei.Feng@Sun.COM static void rt2860_enable_tsf_sync(struct rt2860_softc *); 2079172SFei.Feng@Sun.COM static int rt2860_newstate(struct ieee80211com *, 2089172SFei.Feng@Sun.COM enum ieee80211_state, int); 2099172SFei.Feng@Sun.COM static int rt2860_init(struct rt2860_softc *); 2109172SFei.Feng@Sun.COM static void rt2860_stop(struct rt2860_softc *); 2119172SFei.Feng@Sun.COM static int rt2860_quiesce(dev_info_t *t); 2129172SFei.Feng@Sun.COM 2139172SFei.Feng@Sun.COM /* 2149172SFei.Feng@Sun.COM * device operations 2159172SFei.Feng@Sun.COM */ 2169172SFei.Feng@Sun.COM static int rt2860_attach(dev_info_t *, ddi_attach_cmd_t); 2179172SFei.Feng@Sun.COM static int rt2860_detach(dev_info_t *, ddi_detach_cmd_t); 2189172SFei.Feng@Sun.COM 2199172SFei.Feng@Sun.COM /* 2209172SFei.Feng@Sun.COM * Module Loading Data & Entry Points 2219172SFei.Feng@Sun.COM */ 2229172SFei.Feng@Sun.COM DDI_DEFINE_STREAM_OPS(rwn_dev_ops, nulldev, nulldev, rt2860_attach, 2239172SFei.Feng@Sun.COM rt2860_detach, nodev, NULL, D_MP, NULL, rt2860_quiesce); 2249172SFei.Feng@Sun.COM 2259172SFei.Feng@Sun.COM static struct modldrv rwn_modldrv = { 2269172SFei.Feng@Sun.COM &mod_driverops, /* Type of module. This one is a driver */ 227*10108SFei.Feng@Sun.COM "Ralink RT2700/2800 driver v1.2", /* short description */ 2289172SFei.Feng@Sun.COM &rwn_dev_ops /* driver specific ops */ 2299172SFei.Feng@Sun.COM }; 2309172SFei.Feng@Sun.COM 2319172SFei.Feng@Sun.COM static struct modlinkage modlinkage = { 2329172SFei.Feng@Sun.COM MODREV_1, 2339172SFei.Feng@Sun.COM (void *)&rwn_modldrv, 2349172SFei.Feng@Sun.COM NULL 2359172SFei.Feng@Sun.COM }; 2369172SFei.Feng@Sun.COM 2379172SFei.Feng@Sun.COM static int rt2860_m_stat(void *, uint_t, uint64_t *); 2389172SFei.Feng@Sun.COM static int rt2860_m_start(void *); 2399172SFei.Feng@Sun.COM static void rt2860_m_stop(void *); 2409172SFei.Feng@Sun.COM static int rt2860_m_promisc(void *, boolean_t); 2419172SFei.Feng@Sun.COM static int rt2860_m_multicst(void *, boolean_t, const uint8_t *); 2429172SFei.Feng@Sun.COM static int rt2860_m_unicst(void *, const uint8_t *); 2439172SFei.Feng@Sun.COM static mblk_t *rt2860_m_tx(void *, mblk_t *); 2449172SFei.Feng@Sun.COM static void rt2860_m_ioctl(void *, queue_t *, mblk_t *); 2459172SFei.Feng@Sun.COM static int rt2860_m_setprop(void *arg, const char *pr_name, 2469172SFei.Feng@Sun.COM mac_prop_id_t wldp_pr_num, 2479172SFei.Feng@Sun.COM uint_t wldp_length, const void *wldp_buf); 2489172SFei.Feng@Sun.COM static int rt2860_m_getprop(void *arg, const char *pr_name, 2499172SFei.Feng@Sun.COM mac_prop_id_t wldp_pr_num, uint_t pr_flags, 2509172SFei.Feng@Sun.COM uint_t wldp_length, void *wldp_buf, uint_t *); 2519172SFei.Feng@Sun.COM 2529172SFei.Feng@Sun.COM static mac_callbacks_t rt2860_m_callbacks = { 2539172SFei.Feng@Sun.COM MC_IOCTL | MC_SETPROP | MC_GETPROP, 2549172SFei.Feng@Sun.COM rt2860_m_stat, 2559172SFei.Feng@Sun.COM rt2860_m_start, 2569172SFei.Feng@Sun.COM rt2860_m_stop, 2579172SFei.Feng@Sun.COM rt2860_m_promisc, 2589172SFei.Feng@Sun.COM rt2860_m_multicst, 2599172SFei.Feng@Sun.COM rt2860_m_unicst, 2609172SFei.Feng@Sun.COM rt2860_m_tx, 2619172SFei.Feng@Sun.COM rt2860_m_ioctl, 2629172SFei.Feng@Sun.COM NULL, 2639172SFei.Feng@Sun.COM NULL, 2649172SFei.Feng@Sun.COM NULL, 2659172SFei.Feng@Sun.COM rt2860_m_setprop, 2669172SFei.Feng@Sun.COM rt2860_m_getprop 2679172SFei.Feng@Sun.COM }; 2689172SFei.Feng@Sun.COM 2699172SFei.Feng@Sun.COM #ifdef DEBUG 2709172SFei.Feng@Sun.COM void 2719172SFei.Feng@Sun.COM rt2860_debug(uint32_t dbg_flags, const int8_t *fmt, ...) 2729172SFei.Feng@Sun.COM { 2739172SFei.Feng@Sun.COM va_list args; 2749172SFei.Feng@Sun.COM 2759172SFei.Feng@Sun.COM if (dbg_flags & rt2860_dbg_flags) { 2769172SFei.Feng@Sun.COM va_start(args, fmt); 2779172SFei.Feng@Sun.COM vcmn_err(CE_CONT, fmt, args); 2789172SFei.Feng@Sun.COM va_end(args); 2799172SFei.Feng@Sun.COM } 2809172SFei.Feng@Sun.COM } 2819172SFei.Feng@Sun.COM #endif 2829172SFei.Feng@Sun.COM 2839172SFei.Feng@Sun.COM const char * 2849172SFei.Feng@Sun.COM rt2860_get_rf(uint8_t rev) 2859172SFei.Feng@Sun.COM { 2869172SFei.Feng@Sun.COM switch (rev) { 2879172SFei.Feng@Sun.COM case RT2860_RF_2820: return "RT2820"; 2889172SFei.Feng@Sun.COM case RT2860_RF_2850: return "RT2850"; 2899172SFei.Feng@Sun.COM case RT2860_RF_2720: return "RT2720"; 2909172SFei.Feng@Sun.COM case RT2860_RF_2750: return "RT2750"; 2919172SFei.Feng@Sun.COM default: return "unknown"; 2929172SFei.Feng@Sun.COM } 2939172SFei.Feng@Sun.COM } 2949172SFei.Feng@Sun.COM 2959172SFei.Feng@Sun.COM /* 2969172SFei.Feng@Sun.COM * Read 16 bits at address 'addr' from the serial EEPROM (either 93C46, 2979172SFei.Feng@Sun.COM * 93C66 or 93C86). 2989172SFei.Feng@Sun.COM */ 2999172SFei.Feng@Sun.COM static uint16_t 3009172SFei.Feng@Sun.COM rt2860_eeprom_read(struct rt2860_softc *sc, uint8_t addr) 3019172SFei.Feng@Sun.COM { 3029172SFei.Feng@Sun.COM int n; 3039172SFei.Feng@Sun.COM uint16_t val; 3049172SFei.Feng@Sun.COM uint32_t tmp; 3059172SFei.Feng@Sun.COM 3069172SFei.Feng@Sun.COM /* clock C once before the first command */ 3079172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, 0); 3089172SFei.Feng@Sun.COM 3099172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S); 3109172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_C); 3119172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S); 3129172SFei.Feng@Sun.COM 3139172SFei.Feng@Sun.COM /* write start bit (1) */ 3149172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_D); 3159172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_D | RT2860_C); 3169172SFei.Feng@Sun.COM 3179172SFei.Feng@Sun.COM /* write READ opcode (10) */ 3189172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_D); 3199172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_D | RT2860_C); 3209172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S); 3219172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_C); 3229172SFei.Feng@Sun.COM 3239172SFei.Feng@Sun.COM /* write address (A5-A0 or A7-A0) */ 3249172SFei.Feng@Sun.COM n = ((RT2860_READ(sc, RT2860_PCI_EECTRL) & 0x30) == 0) ? 5 : 7; 3259172SFei.Feng@Sun.COM for (; n >= 0; n--) { 3269172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S | 3279172SFei.Feng@Sun.COM (((addr >> n) & 1) << RT2860_SHIFT_D)); 3289172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S | 3299172SFei.Feng@Sun.COM (((addr >> n) & 1) << RT2860_SHIFT_D) | RT2860_C); 3309172SFei.Feng@Sun.COM } 3319172SFei.Feng@Sun.COM 3329172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S); 3339172SFei.Feng@Sun.COM 3349172SFei.Feng@Sun.COM /* read data Q15-Q0 */ 3359172SFei.Feng@Sun.COM val = 0; 3369172SFei.Feng@Sun.COM for (n = 15; n >= 0; n--) { 3379172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_C); 3389172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_PCI_EECTRL); 3399172SFei.Feng@Sun.COM val |= ((tmp & RT2860_Q) >> RT2860_SHIFT_Q) << n; 3409172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S); 3419172SFei.Feng@Sun.COM } 3429172SFei.Feng@Sun.COM 3439172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, 0); 3449172SFei.Feng@Sun.COM 3459172SFei.Feng@Sun.COM /* clear Chip Select and clock C */ 3469172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_S); 3479172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, 0); 3489172SFei.Feng@Sun.COM RT2860_EEPROM_CTL(sc, RT2860_C); 3499172SFei.Feng@Sun.COM 3509172SFei.Feng@Sun.COM return (val); 3519172SFei.Feng@Sun.COM } 3529172SFei.Feng@Sun.COM 3539172SFei.Feng@Sun.COM /* 3549172SFei.Feng@Sun.COM * Add `delta' (signed) to each 4-bit sub-word of a 32-bit word. 3559172SFei.Feng@Sun.COM * Used to adjust per-rate Tx power registers. 3569172SFei.Feng@Sun.COM */ 3579172SFei.Feng@Sun.COM static inline uint32_t 3589172SFei.Feng@Sun.COM b4inc(uint32_t b32, int8_t delta) 3599172SFei.Feng@Sun.COM { 3609172SFei.Feng@Sun.COM int8_t i, b4; 3619172SFei.Feng@Sun.COM 3629172SFei.Feng@Sun.COM for (i = 0; i < 8; i++) { 3639172SFei.Feng@Sun.COM b4 = b32 & 0xf; 3649172SFei.Feng@Sun.COM b4 += delta; 3659172SFei.Feng@Sun.COM if (b4 < 0) 3669172SFei.Feng@Sun.COM b4 = 0; 3679172SFei.Feng@Sun.COM else if (b4 > 0xf) 3689172SFei.Feng@Sun.COM b4 = 0xf; 3699172SFei.Feng@Sun.COM b32 = b32 >> 4 | b4 << 28; 3709172SFei.Feng@Sun.COM } 3719172SFei.Feng@Sun.COM return (b32); 3729172SFei.Feng@Sun.COM } 3739172SFei.Feng@Sun.COM 3749172SFei.Feng@Sun.COM static int 3759172SFei.Feng@Sun.COM rt2860_read_eeprom(struct rt2860_softc *sc) 3769172SFei.Feng@Sun.COM { 3779172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 3789172SFei.Feng@Sun.COM int ridx, ant, i; 3799172SFei.Feng@Sun.COM int8_t delta_2ghz, delta_5ghz; 3809172SFei.Feng@Sun.COM uint16_t val; 3819172SFei.Feng@Sun.COM 3829172SFei.Feng@Sun.COM /* read EEPROM version */ 3839172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_VERSION); 3849172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 3859172SFei.Feng@Sun.COM "EEPROM rev=%d, FAE=%d\n", 3869172SFei.Feng@Sun.COM val & 0xff, val >> 8); 3879172SFei.Feng@Sun.COM 3889172SFei.Feng@Sun.COM /* read MAC address */ 3899172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_MAC01); 3909172SFei.Feng@Sun.COM ic->ic_macaddr[0] = val & 0xff; 3919172SFei.Feng@Sun.COM ic->ic_macaddr[1] = val >> 8; 3929172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_MAC23); 3939172SFei.Feng@Sun.COM ic->ic_macaddr[2] = val & 0xff; 3949172SFei.Feng@Sun.COM ic->ic_macaddr[3] = val >> 8; 3959172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_MAC45); 3969172SFei.Feng@Sun.COM ic->ic_macaddr[4] = val & 0xff; 3979172SFei.Feng@Sun.COM ic->ic_macaddr[5] = val >> 8; 3989172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 3999172SFei.Feng@Sun.COM "MAC address is: %x:%x:%x:%x:%x:%x\n", 4009172SFei.Feng@Sun.COM ic->ic_macaddr[0], ic->ic_macaddr[1], 4019172SFei.Feng@Sun.COM ic->ic_macaddr[2], ic->ic_macaddr[3], 4029172SFei.Feng@Sun.COM ic->ic_macaddr[4], ic->ic_macaddr[5]); 4039172SFei.Feng@Sun.COM 4049172SFei.Feng@Sun.COM /* read country code */ 4059172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_COUNTRY); 4069172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 4079172SFei.Feng@Sun.COM "EEPROM region code=0x%04x\n", val); 4089172SFei.Feng@Sun.COM 4099172SFei.Feng@Sun.COM /* read default BBP settings */ 4109172SFei.Feng@Sun.COM for (i = 0; i < 8; i++) { 4119172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_BBP_BASE + i); 4129172SFei.Feng@Sun.COM sc->bbp[i].val = val & 0xff; 4139172SFei.Feng@Sun.COM sc->bbp[i].reg = val >> 8; 4149172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 4159172SFei.Feng@Sun.COM "BBP%d=0x%02x\n", 4169172SFei.Feng@Sun.COM sc->bbp[i].reg, sc->bbp[i].val); 4179172SFei.Feng@Sun.COM } 4189172SFei.Feng@Sun.COM 4199172SFei.Feng@Sun.COM /* read RF frequency offset from EEPROM */ 4209172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_FREQ_LEDS); 4219172SFei.Feng@Sun.COM sc->freq = ((val & 0xff) != 0xff) ? val & 0xff : 0; 4229172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 4239172SFei.Feng@Sun.COM "EEPROM freq offset %d\n", sc->freq & 0xff); 4249172SFei.Feng@Sun.COM 4259172SFei.Feng@Sun.COM if ((sc->leds = val >> 8) != 0xff) { 4269172SFei.Feng@Sun.COM /* read LEDs operating mode */ 4279172SFei.Feng@Sun.COM sc->led[0] = rt2860_eeprom_read(sc, RT2860_EEPROM_LED1); 4289172SFei.Feng@Sun.COM sc->led[1] = rt2860_eeprom_read(sc, RT2860_EEPROM_LED2); 4299172SFei.Feng@Sun.COM sc->led[2] = rt2860_eeprom_read(sc, RT2860_EEPROM_LED3); 4309172SFei.Feng@Sun.COM } else { 4319172SFei.Feng@Sun.COM /* broken EEPROM, use default settings */ 4329172SFei.Feng@Sun.COM sc->leds = 0x01; 4339172SFei.Feng@Sun.COM sc->led[0] = 0x5555; 4349172SFei.Feng@Sun.COM sc->led[1] = 0x2221; 4359172SFei.Feng@Sun.COM sc->led[2] = 0xa9f8; 4369172SFei.Feng@Sun.COM } 4379172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 4389172SFei.Feng@Sun.COM "EEPROM LED mode=0x%02x, LEDs=0x%04x/0x%04x/0x%04x\n", 4399172SFei.Feng@Sun.COM sc->leds, sc->led[0], sc->led[1], sc->led[2]); 4409172SFei.Feng@Sun.COM 4419172SFei.Feng@Sun.COM /* read RF information */ 4429172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_ANTENNA); 4439172SFei.Feng@Sun.COM if (val == 0xffff) { 4449172SFei.Feng@Sun.COM /* broken EEPROM, default to RF2820 1T2R */ 4459172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 4469172SFei.Feng@Sun.COM "invalid EEPROM antenna info, using default\n"); 4479172SFei.Feng@Sun.COM sc->rf_rev = RT2860_RF_2820; 4489172SFei.Feng@Sun.COM sc->ntxchains = 1; 4499172SFei.Feng@Sun.COM sc->nrxchains = 2; 4509172SFei.Feng@Sun.COM } else { 4519172SFei.Feng@Sun.COM sc->rf_rev = (val >> 8) & 0xf; 4529172SFei.Feng@Sun.COM sc->ntxchains = (val >> 4) & 0xf; 4539172SFei.Feng@Sun.COM sc->nrxchains = val & 0xf; 4549172SFei.Feng@Sun.COM } 4559172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 4569172SFei.Feng@Sun.COM "EEPROM RF rev=0x%02x chains=%dT%dR\n", 4579172SFei.Feng@Sun.COM sc->rf_rev, sc->ntxchains, sc->nrxchains); 4589172SFei.Feng@Sun.COM 4599172SFei.Feng@Sun.COM /* check if RF supports automatic Tx access gain control */ 4609172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_CONFIG); 4619172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 4629172SFei.Feng@Sun.COM "EEPROM CFG 0x%04x\n", val); 4639172SFei.Feng@Sun.COM if ((val & 0xff) != 0xff) 4649172SFei.Feng@Sun.COM sc->calib_2ghz = sc->calib_5ghz = 0; /* XXX (val >> 1) & 1 */; 4659172SFei.Feng@Sun.COM 4669172SFei.Feng@Sun.COM if (sc->sc_flags & RT2860_ADVANCED_PS) { 4679172SFei.Feng@Sun.COM /* read PCIe power save level */ 4689172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_PCIE_PSLEVEL); 4699172SFei.Feng@Sun.COM if ((val & 0xff) != 0xff) { 4709172SFei.Feng@Sun.COM sc->pslevel = val & 0x3; 4719172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_REV); 4729172SFei.Feng@Sun.COM if (val >> 8 != 0x92 || !(val & 0x80)) 4739172SFei.Feng@Sun.COM sc->pslevel = MIN(sc->pslevel, 1); 4749172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, 4759172SFei.Feng@Sun.COM "rwn: rt2860_read_eeprom(): " 4769172SFei.Feng@Sun.COM "EEPROM PCIe PS Level=%d\n", 4779172SFei.Feng@Sun.COM sc->pslevel); 4789172SFei.Feng@Sun.COM } 4799172SFei.Feng@Sun.COM } 4809172SFei.Feng@Sun.COM /* read power settings for 2GHz channels */ 4819172SFei.Feng@Sun.COM for (i = 0; i < 14; i += 2) { 4829172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, 4839172SFei.Feng@Sun.COM RT2860_EEPROM_PWR2GHZ_BASE1 + i / 2); 4849172SFei.Feng@Sun.COM sc->txpow1[i + 0] = (int8_t)(val & 0xff); 4859172SFei.Feng@Sun.COM sc->txpow1[i + 1] = (int8_t)(val >> 8); 4869172SFei.Feng@Sun.COM 4879172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, 4889172SFei.Feng@Sun.COM RT2860_EEPROM_PWR2GHZ_BASE2 + i / 2); 4899172SFei.Feng@Sun.COM sc->txpow2[i + 0] = (int8_t)(val & 0xff); 4909172SFei.Feng@Sun.COM sc->txpow2[i + 1] = (int8_t)(val >> 8); 4919172SFei.Feng@Sun.COM } 4929172SFei.Feng@Sun.COM /* fix broken Tx power entries */ 4939172SFei.Feng@Sun.COM for (i = 0; i < 14; i++) { 4949172SFei.Feng@Sun.COM if (sc->txpow1[i] < 0 || sc->txpow1[i] > 31) 4959172SFei.Feng@Sun.COM sc->txpow1[i] = 5; 4969172SFei.Feng@Sun.COM if (sc->txpow2[i] < 0 || sc->txpow2[i] > 31) 4979172SFei.Feng@Sun.COM sc->txpow2[i] = 5; 4989172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 4999172SFei.Feng@Sun.COM "chan %d: power1=%d, power2=%d\n", 5009172SFei.Feng@Sun.COM rt2860_rf2850[i].chan, sc->txpow1[i], sc->txpow2[i]); 5019172SFei.Feng@Sun.COM } 5029172SFei.Feng@Sun.COM /* read power settings for 5GHz channels */ 5039172SFei.Feng@Sun.COM for (i = 0; i < 36; i += 2) { 5049172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, 5059172SFei.Feng@Sun.COM RT2860_EEPROM_PWR5GHZ_BASE1 + i / 2); 5069172SFei.Feng@Sun.COM sc->txpow1[i + 14] = (int8_t)(val & 0xff); 5079172SFei.Feng@Sun.COM sc->txpow1[i + 15] = (int8_t)(val >> 8); 5089172SFei.Feng@Sun.COM 5099172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, 5109172SFei.Feng@Sun.COM RT2860_EEPROM_PWR5GHZ_BASE2 + i / 2); 5119172SFei.Feng@Sun.COM sc->txpow2[i + 14] = (int8_t)(val & 0xff); 5129172SFei.Feng@Sun.COM sc->txpow2[i + 15] = (int8_t)(val >> 8); 5139172SFei.Feng@Sun.COM } 5149172SFei.Feng@Sun.COM /* fix broken Tx power entries */ 5159172SFei.Feng@Sun.COM for (i = 0; i < 36; i++) { 5169172SFei.Feng@Sun.COM if (sc->txpow1[14 + i] < -7 || sc->txpow1[14 + i] > 15) 5179172SFei.Feng@Sun.COM sc->txpow1[14 + i] = 5; 5189172SFei.Feng@Sun.COM if (sc->txpow2[14 + i] < -7 || sc->txpow2[14 + i] > 15) 5199172SFei.Feng@Sun.COM sc->txpow2[14 + i] = 5; 5209172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 5219172SFei.Feng@Sun.COM "chan %d: power1=%d, power2=%d\n", 5229172SFei.Feng@Sun.COM rt2860_rf2850[14 + i].chan, sc->txpow1[14 + i], 5239172SFei.Feng@Sun.COM sc->txpow2[14 + i]); 5249172SFei.Feng@Sun.COM } 5259172SFei.Feng@Sun.COM 5269172SFei.Feng@Sun.COM /* read Tx power compensation for each Tx rate */ 5279172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_DELTAPWR); 5289172SFei.Feng@Sun.COM delta_2ghz = delta_5ghz = 0; 5299172SFei.Feng@Sun.COM if ((val & 0xff) != 0xff && (val & 0x80)) { 5309172SFei.Feng@Sun.COM delta_2ghz = val & 0xf; 5319172SFei.Feng@Sun.COM if (!(val & 0x40)) /* negative number */ 5329172SFei.Feng@Sun.COM delta_2ghz = -delta_2ghz; 5339172SFei.Feng@Sun.COM } 5349172SFei.Feng@Sun.COM val >>= 8; 5359172SFei.Feng@Sun.COM if ((val & 0xff) != 0xff && (val & 0x80)) { 5369172SFei.Feng@Sun.COM delta_5ghz = val & 0xf; 5379172SFei.Feng@Sun.COM if (!(val & 0x40)) /* negative number */ 5389172SFei.Feng@Sun.COM delta_5ghz = -delta_5ghz; 5399172SFei.Feng@Sun.COM } 5409172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 5419172SFei.Feng@Sun.COM "power compensation=%d (2GHz), %d (5GHz) \n", 5429172SFei.Feng@Sun.COM delta_2ghz, delta_5ghz); 5439172SFei.Feng@Sun.COM 5449172SFei.Feng@Sun.COM for (ridx = 0; ridx < 5; ridx++) { 5459172SFei.Feng@Sun.COM uint32_t reg; 5469172SFei.Feng@Sun.COM 5479172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_RPWR + ridx); 5489172SFei.Feng@Sun.COM reg = (uint32_t)val << 16; 5499172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_RPWR + ridx + 1); 5509172SFei.Feng@Sun.COM reg |= val; 5519172SFei.Feng@Sun.COM 5529172SFei.Feng@Sun.COM sc->txpow20mhz[ridx] = reg; 5539172SFei.Feng@Sun.COM sc->txpow40mhz_2ghz[ridx] = b4inc(reg, delta_2ghz); 5549172SFei.Feng@Sun.COM sc->txpow40mhz_5ghz[ridx] = b4inc(reg, delta_5ghz); 5559172SFei.Feng@Sun.COM 5569172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 5579172SFei.Feng@Sun.COM "ridx %d: power 20MHz=0x%08x, 40MHz/2GHz=0x%08x, " 5589172SFei.Feng@Sun.COM "40MHz/5GHz=0x%08x\n", ridx, sc->txpow20mhz[ridx], 5599172SFei.Feng@Sun.COM sc->txpow40mhz_2ghz[ridx], sc->txpow40mhz_5ghz[ridx]); 5609172SFei.Feng@Sun.COM } 5619172SFei.Feng@Sun.COM 5629172SFei.Feng@Sun.COM /* read factory-calibrated samples for temperature compensation */ 5639172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI1_2GHZ); 5649172SFei.Feng@Sun.COM sc->tssi_2ghz[0] = val & 0xff; /* [-4] */ 5659172SFei.Feng@Sun.COM sc->tssi_2ghz[1] = val >> 8; /* [-3] */ 5669172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI2_2GHZ); 5679172SFei.Feng@Sun.COM sc->tssi_2ghz[2] = val & 0xff; /* [-2] */ 5689172SFei.Feng@Sun.COM sc->tssi_2ghz[3] = val >> 8; /* [-1] */ 5699172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI3_2GHZ); 5709172SFei.Feng@Sun.COM sc->tssi_2ghz[4] = val & 0xff; /* [+0] */ 5719172SFei.Feng@Sun.COM sc->tssi_2ghz[5] = val >> 8; /* [+1] */ 5729172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI4_2GHZ); 5739172SFei.Feng@Sun.COM sc->tssi_2ghz[6] = val & 0xff; /* [+2] */ 5749172SFei.Feng@Sun.COM sc->tssi_2ghz[7] = val >> 8; /* [+3] */ 5759172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI5_2GHZ); 5769172SFei.Feng@Sun.COM sc->tssi_2ghz[8] = val & 0xff; /* [+4] */ 5779172SFei.Feng@Sun.COM sc->step_2ghz = val >> 8; 5789172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 5799172SFei.Feng@Sun.COM "TSSI 2GHz: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x " 5809172SFei.Feng@Sun.COM "0x%02x 0x%02x step=%d\n", sc->tssi_2ghz[0], sc->tssi_2ghz[1], 5819172SFei.Feng@Sun.COM sc->tssi_2ghz[2], sc->tssi_2ghz[3], sc->tssi_2ghz[4], 5829172SFei.Feng@Sun.COM sc->tssi_2ghz[5], sc->tssi_2ghz[6], sc->tssi_2ghz[7], 5839172SFei.Feng@Sun.COM sc->tssi_2ghz[8], sc->step_2ghz); 5849172SFei.Feng@Sun.COM /* check that ref value is correct, otherwise disable calibration */ 5859172SFei.Feng@Sun.COM if (sc->tssi_2ghz[4] == 0xff) 5869172SFei.Feng@Sun.COM sc->calib_2ghz = 0; 5879172SFei.Feng@Sun.COM 5889172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI1_5GHZ); 5899172SFei.Feng@Sun.COM sc->tssi_5ghz[0] = val & 0xff; /* [-4] */ 5909172SFei.Feng@Sun.COM sc->tssi_5ghz[1] = val >> 8; /* [-3] */ 5919172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI2_5GHZ); 5929172SFei.Feng@Sun.COM sc->tssi_5ghz[2] = val & 0xff; /* [-2] */ 5939172SFei.Feng@Sun.COM sc->tssi_5ghz[3] = val >> 8; /* [-1] */ 5949172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI3_5GHZ); 5959172SFei.Feng@Sun.COM sc->tssi_5ghz[4] = val & 0xff; /* [+0] */ 5969172SFei.Feng@Sun.COM sc->tssi_5ghz[5] = val >> 8; /* [+1] */ 5979172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI4_5GHZ); 5989172SFei.Feng@Sun.COM sc->tssi_5ghz[6] = val & 0xff; /* [+2] */ 5999172SFei.Feng@Sun.COM sc->tssi_5ghz[7] = val >> 8; /* [+3] */ 6009172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI5_5GHZ); 6019172SFei.Feng@Sun.COM sc->tssi_5ghz[8] = val & 0xff; /* [+4] */ 6029172SFei.Feng@Sun.COM sc->step_5ghz = val >> 8; 6039172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 6049172SFei.Feng@Sun.COM "TSSI 5GHz: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x " 6059172SFei.Feng@Sun.COM "0x%02x 0x%02x step=%d\n", sc->tssi_5ghz[0], sc->tssi_5ghz[1], 6069172SFei.Feng@Sun.COM sc->tssi_5ghz[2], sc->tssi_5ghz[3], sc->tssi_5ghz[4], 6079172SFei.Feng@Sun.COM sc->tssi_5ghz[5], sc->tssi_5ghz[6], sc->tssi_5ghz[7], 6089172SFei.Feng@Sun.COM sc->tssi_5ghz[8], sc->step_5ghz); 6099172SFei.Feng@Sun.COM /* check that ref value is correct, otherwise disable calibration */ 6109172SFei.Feng@Sun.COM if (sc->tssi_5ghz[4] == 0xff) 6119172SFei.Feng@Sun.COM sc->calib_5ghz = 0; 6129172SFei.Feng@Sun.COM 6139172SFei.Feng@Sun.COM /* read RSSI offsets and LNA gains from EEPROM */ 6149172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_RSSI1_2GHZ); 6159172SFei.Feng@Sun.COM sc->rssi_2ghz[0] = val & 0xff; /* Ant A */ 6169172SFei.Feng@Sun.COM sc->rssi_2ghz[1] = val >> 8; /* Ant B */ 6179172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_RSSI2_2GHZ); 6189172SFei.Feng@Sun.COM sc->rssi_2ghz[2] = val & 0xff; /* Ant C */ 6199172SFei.Feng@Sun.COM sc->lna[2] = val >> 8; /* channel group 2 */ 6209172SFei.Feng@Sun.COM 6219172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_RSSI1_5GHZ); 6229172SFei.Feng@Sun.COM sc->rssi_5ghz[0] = val & 0xff; /* Ant A */ 6239172SFei.Feng@Sun.COM sc->rssi_5ghz[1] = val >> 8; /* Ant B */ 6249172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_RSSI2_5GHZ); 6259172SFei.Feng@Sun.COM sc->rssi_5ghz[2] = val & 0xff; /* Ant C */ 6269172SFei.Feng@Sun.COM sc->lna[3] = val >> 8; /* channel group 3 */ 6279172SFei.Feng@Sun.COM 6289172SFei.Feng@Sun.COM val = rt2860_eeprom_read(sc, RT2860_EEPROM_LNA); 6299172SFei.Feng@Sun.COM sc->lna[0] = val & 0xff; /* channel group 0 */ 6309172SFei.Feng@Sun.COM sc->lna[1] = val >> 8; /* channel group 1 */ 6319172SFei.Feng@Sun.COM 6329172SFei.Feng@Sun.COM /* fix broken 5GHz LNA entries */ 6339172SFei.Feng@Sun.COM if (sc->lna[2] == 0 || sc->lna[2] == 0xff) { 6349172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 6359172SFei.Feng@Sun.COM "invalid LNA for channel group %d\n", 2); 6369172SFei.Feng@Sun.COM sc->lna[2] = sc->lna[1]; 6379172SFei.Feng@Sun.COM } 6389172SFei.Feng@Sun.COM if (sc->lna[3] == 0 || sc->lna[3] == 0xff) { 6399172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, "rwn: rt2860_read_eeprom(): " 6409172SFei.Feng@Sun.COM "invalid LNA for channel group %d\n", 3); 6419172SFei.Feng@Sun.COM sc->lna[3] = sc->lna[1]; 6429172SFei.Feng@Sun.COM } 6439172SFei.Feng@Sun.COM 6449172SFei.Feng@Sun.COM /* fix broken RSSI offset entries */ 6459172SFei.Feng@Sun.COM for (ant = 0; ant < 3; ant++) { 6469172SFei.Feng@Sun.COM if (sc->rssi_2ghz[ant] < -10 || sc->rssi_2ghz[ant] > 10) { 6479172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, 6489172SFei.Feng@Sun.COM "rwn: rt2860_read_eeprom(): " 6499172SFei.Feng@Sun.COM "invalid RSSI%d offset: %d (2GHz)\n", 6509172SFei.Feng@Sun.COM ant + 1, sc->rssi_2ghz[ant]); 6519172SFei.Feng@Sun.COM sc->rssi_2ghz[ant] = 0; 6529172SFei.Feng@Sun.COM } 6539172SFei.Feng@Sun.COM if (sc->rssi_5ghz[ant] < -10 || sc->rssi_5ghz[ant] > 10) { 6549172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_EEPROM, 6559172SFei.Feng@Sun.COM "rwn: rt2860_read_eeprom(): " 6569172SFei.Feng@Sun.COM "invalid RSSI%d offset: %d (2GHz)\n", 6579172SFei.Feng@Sun.COM ant + 1, sc->rssi_5ghz[ant]); 6589172SFei.Feng@Sun.COM sc->rssi_5ghz[ant] = 0; 6599172SFei.Feng@Sun.COM } 6609172SFei.Feng@Sun.COM } 6619172SFei.Feng@Sun.COM 6629172SFei.Feng@Sun.COM return (RT2860_SUCCESS); 6639172SFei.Feng@Sun.COM } 6649172SFei.Feng@Sun.COM 6659172SFei.Feng@Sun.COM /* 6669172SFei.Feng@Sun.COM * Allocate an DMA memory and a DMA handle for accessing it 6679172SFei.Feng@Sun.COM */ 6689172SFei.Feng@Sun.COM static int 6699172SFei.Feng@Sun.COM rt2860_alloc_dma_mem(dev_info_t *devinfo, ddi_dma_attr_t *dma_attr, 6709172SFei.Feng@Sun.COM size_t memsize, ddi_device_acc_attr_t *attr_p, uint_t alloc_flags, 6719172SFei.Feng@Sun.COM uint_t bind_flags, struct dma_area *dma_p) 6729172SFei.Feng@Sun.COM { 6739172SFei.Feng@Sun.COM int err; 6749172SFei.Feng@Sun.COM 6759172SFei.Feng@Sun.COM /* 6769172SFei.Feng@Sun.COM * Allocate handle 6779172SFei.Feng@Sun.COM */ 6789172SFei.Feng@Sun.COM err = ddi_dma_alloc_handle(devinfo, dma_attr, 6799172SFei.Feng@Sun.COM DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl); 6809172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 6819172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, "rwn: rwn_allo_dma_mem(): " 6829172SFei.Feng@Sun.COM "failed to alloc handle\n"); 6839172SFei.Feng@Sun.COM goto fail1; 6849172SFei.Feng@Sun.COM } 6859172SFei.Feng@Sun.COM 6869172SFei.Feng@Sun.COM /* 6879172SFei.Feng@Sun.COM * Allocate memory 6889172SFei.Feng@Sun.COM */ 6899172SFei.Feng@Sun.COM err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize, attr_p, 6909172SFei.Feng@Sun.COM alloc_flags, DDI_DMA_SLEEP, NULL, &dma_p->mem_va, 6919172SFei.Feng@Sun.COM &dma_p->alength, &dma_p->acc_hdl); 6929172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 6939172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, "rwn: rwn_alloc_dma_mem(): " 6949172SFei.Feng@Sun.COM "failed to alloc mem\n"); 6959172SFei.Feng@Sun.COM goto fail2; 6969172SFei.Feng@Sun.COM } 6979172SFei.Feng@Sun.COM 6989172SFei.Feng@Sun.COM /* 6999172SFei.Feng@Sun.COM * Bind the two together 7009172SFei.Feng@Sun.COM */ 7019172SFei.Feng@Sun.COM err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL, 7029172SFei.Feng@Sun.COM dma_p->mem_va, dma_p->alength, bind_flags, 7039172SFei.Feng@Sun.COM DDI_DMA_SLEEP, NULL, &dma_p->cookie, &dma_p->ncookies); 7049172SFei.Feng@Sun.COM if (err != DDI_DMA_MAPPED) { 7059172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, "rwn: rwn_alloc_dma_mem(): " 7069172SFei.Feng@Sun.COM "failed to bind handle\n"); 7079172SFei.Feng@Sun.COM goto fail3; 7089172SFei.Feng@Sun.COM } 7099172SFei.Feng@Sun.COM 7109172SFei.Feng@Sun.COM if (dma_p->ncookies != 1) { 7119172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, "rwn: rwn_alloc_dma_mem(): " 7129172SFei.Feng@Sun.COM "failed to alloc cookies\n"); 7139172SFei.Feng@Sun.COM goto fail4; 7149172SFei.Feng@Sun.COM } 7159172SFei.Feng@Sun.COM 7169172SFei.Feng@Sun.COM dma_p->nslots = ~0U; 7179172SFei.Feng@Sun.COM dma_p->size = ~0U; 7189172SFei.Feng@Sun.COM dma_p->token = ~0U; 7199172SFei.Feng@Sun.COM dma_p->offset = 0; 7209172SFei.Feng@Sun.COM return (DDI_SUCCESS); 7219172SFei.Feng@Sun.COM 7229172SFei.Feng@Sun.COM fail4: 7239172SFei.Feng@Sun.COM (void) ddi_dma_unbind_handle(dma_p->dma_hdl); 7249172SFei.Feng@Sun.COM fail3: 7259172SFei.Feng@Sun.COM ddi_dma_mem_free(&dma_p->acc_hdl); 7269172SFei.Feng@Sun.COM fail2: 7279172SFei.Feng@Sun.COM ddi_dma_free_handle(&dma_p->dma_hdl); 7289172SFei.Feng@Sun.COM fail1: 7299172SFei.Feng@Sun.COM return (err); 7309172SFei.Feng@Sun.COM } 7319172SFei.Feng@Sun.COM 7329172SFei.Feng@Sun.COM static void 7339172SFei.Feng@Sun.COM rt2860_free_dma_mem(struct dma_area *dma_p) 7349172SFei.Feng@Sun.COM { 7359172SFei.Feng@Sun.COM if (dma_p->dma_hdl != NULL) { 7369172SFei.Feng@Sun.COM (void) ddi_dma_unbind_handle(dma_p->dma_hdl); 7379172SFei.Feng@Sun.COM if (dma_p->acc_hdl != NULL) { 7389172SFei.Feng@Sun.COM ddi_dma_mem_free(&dma_p->acc_hdl); 7399172SFei.Feng@Sun.COM dma_p->acc_hdl = NULL; 7409172SFei.Feng@Sun.COM } 7419172SFei.Feng@Sun.COM ddi_dma_free_handle(&dma_p->dma_hdl); 7429172SFei.Feng@Sun.COM dma_p->ncookies = 0; 7439172SFei.Feng@Sun.COM dma_p->dma_hdl = NULL; 7449172SFei.Feng@Sun.COM } 7459172SFei.Feng@Sun.COM } 7469172SFei.Feng@Sun.COM 7479172SFei.Feng@Sun.COM /*ARGSUSED*/ 7489172SFei.Feng@Sun.COM static int 7499172SFei.Feng@Sun.COM rt2860_alloc_tx_ring(struct rt2860_softc *sc, struct rt2860_tx_ring *ring) 7509172SFei.Feng@Sun.COM { 7519172SFei.Feng@Sun.COM int size, err; 7529172SFei.Feng@Sun.COM 7539172SFei.Feng@Sun.COM size = RT2860_TX_RING_COUNT * sizeof (struct rt2860_txd); 7549172SFei.Feng@Sun.COM 7559172SFei.Feng@Sun.COM err = rt2860_alloc_dma_mem(sc->sc_dev, &rt2860_dma_attr, size, 7569172SFei.Feng@Sun.COM &rt2860_desc_accattr, DDI_DMA_CONSISTENT, 7579172SFei.Feng@Sun.COM DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 7589172SFei.Feng@Sun.COM &ring->txdesc_dma); 7599172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 7609172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, "rwn: rt2860_alloc_tx_ring(): " 7619172SFei.Feng@Sun.COM "failed to alloc dma mem\n"); 7629172SFei.Feng@Sun.COM goto fail1; 7639172SFei.Feng@Sun.COM } 7649172SFei.Feng@Sun.COM 7659172SFei.Feng@Sun.COM ring->txd = (struct rt2860_txd *)ring->txdesc_dma.mem_va; 7669172SFei.Feng@Sun.COM ring->paddr = ring->txdesc_dma.cookie.dmac_address; 7679172SFei.Feng@Sun.COM 7689172SFei.Feng@Sun.COM ring->cur = 0; 7699172SFei.Feng@Sun.COM ring->next = 0; 7709172SFei.Feng@Sun.COM ring->queued = 0; 7719172SFei.Feng@Sun.COM 7729172SFei.Feng@Sun.COM (void) bzero(ring->txd, size); 7739172SFei.Feng@Sun.COM RT2860_DMA_SYNC(ring->txdesc_dma, DDI_DMA_SYNC_FORDEV); 7749172SFei.Feng@Sun.COM return (DDI_SUCCESS); 7759172SFei.Feng@Sun.COM fail1: 7769172SFei.Feng@Sun.COM return (err); 7779172SFei.Feng@Sun.COM } 7789172SFei.Feng@Sun.COM 7799172SFei.Feng@Sun.COM void 7809172SFei.Feng@Sun.COM rt2860_reset_tx_ring(struct rt2860_softc *sc, struct rt2860_tx_ring *ring) 7819172SFei.Feng@Sun.COM { 7829172SFei.Feng@Sun.COM struct rt2860_tx_data *data; 7839172SFei.Feng@Sun.COM int i; 7849172SFei.Feng@Sun.COM 7859172SFei.Feng@Sun.COM for (i = 0; i < RT2860_TX_RING_COUNT; i++) { 7869172SFei.Feng@Sun.COM ring->txd[i].sdl0 &= ~LE_16(RT2860_TX_DDONE); 7879172SFei.Feng@Sun.COM 7889172SFei.Feng@Sun.COM if ((data = ring->data[i]) == NULL) 7899172SFei.Feng@Sun.COM continue; /* nothing mapped in this slot */ 7909172SFei.Feng@Sun.COM 7919172SFei.Feng@Sun.COM /* by pass if it's quiesced */ 7929172SFei.Feng@Sun.COM if (!(sc->sc_flags & RT2860_F_QUIESCE)) 7939172SFei.Feng@Sun.COM RT2860_DMA_SYNC(data->txbuf_dma, DDI_DMA_SYNC_FORDEV); 7949172SFei.Feng@Sun.COM 7959172SFei.Feng@Sun.COM if (data->ni != NULL) { 7969172SFei.Feng@Sun.COM ieee80211_free_node(data->ni); 7979172SFei.Feng@Sun.COM data->ni = NULL; /* node already freed */ 7989172SFei.Feng@Sun.COM } 7999172SFei.Feng@Sun.COM 8009172SFei.Feng@Sun.COM SLIST_INSERT_HEAD(&sc->data_pool, data, next); 8019172SFei.Feng@Sun.COM ring->data[i] = NULL; 8029172SFei.Feng@Sun.COM } 8039172SFei.Feng@Sun.COM 8049172SFei.Feng@Sun.COM /* by pass if it's quiesced */ 8059172SFei.Feng@Sun.COM if (!(sc->sc_flags & RT2860_F_QUIESCE)) 8069172SFei.Feng@Sun.COM RT2860_DMA_SYNC(ring->txdesc_dma, DDI_DMA_SYNC_FORDEV); 8079172SFei.Feng@Sun.COM 8089172SFei.Feng@Sun.COM ring->queued = 0; 8099172SFei.Feng@Sun.COM ring->cur = ring->next = 0; 8109172SFei.Feng@Sun.COM } 8119172SFei.Feng@Sun.COM 8129172SFei.Feng@Sun.COM /*ARGSUSED*/ 8139172SFei.Feng@Sun.COM static void 8149172SFei.Feng@Sun.COM rt2860_free_tx_ring(struct rt2860_softc *sc, struct rt2860_tx_ring *ring) 8159172SFei.Feng@Sun.COM { 8169172SFei.Feng@Sun.COM if (ring->txd != NULL) { 8179172SFei.Feng@Sun.COM rt2860_free_dma_mem(&ring->txdesc_dma); 8189172SFei.Feng@Sun.COM } 8199172SFei.Feng@Sun.COM } 8209172SFei.Feng@Sun.COM 8219172SFei.Feng@Sun.COM static int 8229172SFei.Feng@Sun.COM rt2860_alloc_rx_ring(struct rt2860_softc *sc, struct rt2860_rx_ring *ring) 8239172SFei.Feng@Sun.COM { 8249172SFei.Feng@Sun.COM struct rt2860_rx_data *data; 8259172SFei.Feng@Sun.COM struct rt2860_rxd *rxd; 8269172SFei.Feng@Sun.COM int i, err, size, datalen; 8279172SFei.Feng@Sun.COM 8289172SFei.Feng@Sun.COM size = RT2860_RX_RING_COUNT * sizeof (struct rt2860_rxd); 8299172SFei.Feng@Sun.COM 8309172SFei.Feng@Sun.COM err = rt2860_alloc_dma_mem(sc->sc_dev, &rt2860_dma_attr, size, 8319172SFei.Feng@Sun.COM &rt2860_desc_accattr, DDI_DMA_CONSISTENT, 8329172SFei.Feng@Sun.COM DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 8339172SFei.Feng@Sun.COM &ring->rxdesc_dma); 8349172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 8359172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, "rwn: rt2860_alloc_rx_ring(): " 8369172SFei.Feng@Sun.COM "failed to alloc dma mem\n"); 8379172SFei.Feng@Sun.COM goto fail1; 8389172SFei.Feng@Sun.COM } 8399172SFei.Feng@Sun.COM 8409172SFei.Feng@Sun.COM ring->rxd = (struct rt2860_rxd *)ring->rxdesc_dma.mem_va; 8419172SFei.Feng@Sun.COM ring->paddr = ring->rxdesc_dma.cookie.dmac_address; 8429172SFei.Feng@Sun.COM bzero(ring->rxd, size); 8439172SFei.Feng@Sun.COM 8449172SFei.Feng@Sun.COM /* 8459172SFei.Feng@Sun.COM * Pre-allocate Rx buffers and populate Rx ring. 8469172SFei.Feng@Sun.COM */ 8479172SFei.Feng@Sun.COM datalen = RT2860_RX_RING_COUNT * sizeof (struct rt2860_rx_data); 8489172SFei.Feng@Sun.COM bzero(ring->data, datalen); 8499172SFei.Feng@Sun.COM for (i = 0; i < RT2860_RX_RING_COUNT; i++) { 8509172SFei.Feng@Sun.COM rxd = &ring->rxd[i]; 8519172SFei.Feng@Sun.COM data = &ring->data[i]; 8529172SFei.Feng@Sun.COM /* alloc DMA memory */ 8539172SFei.Feng@Sun.COM (void) rt2860_alloc_dma_mem(sc->sc_dev, &rt2860_dma_attr, 8549172SFei.Feng@Sun.COM sc->sc_dmabuf_size, 8559172SFei.Feng@Sun.COM &rt2860_buf_accattr, 8569172SFei.Feng@Sun.COM DDI_DMA_STREAMING, 8579172SFei.Feng@Sun.COM DDI_DMA_READ | DDI_DMA_STREAMING, 8589172SFei.Feng@Sun.COM &data->rxbuf_dma); 8599172SFei.Feng@Sun.COM rxd->sdp0 = LE_32(data->rxbuf_dma.cookie.dmac_address); 8609172SFei.Feng@Sun.COM rxd->sdl0 = LE_16(sc->sc_dmabuf_size); 8619172SFei.Feng@Sun.COM } 8629172SFei.Feng@Sun.COM 8639172SFei.Feng@Sun.COM ring->cur = 0; 8649172SFei.Feng@Sun.COM 8659172SFei.Feng@Sun.COM RT2860_DMA_SYNC(ring->rxdesc_dma, DDI_DMA_SYNC_FORDEV); 8669172SFei.Feng@Sun.COM return (DDI_SUCCESS); 8679172SFei.Feng@Sun.COM fail2: 8689172SFei.Feng@Sun.COM rt2860_free_dma_mem(&ring->rxdesc_dma); 8699172SFei.Feng@Sun.COM fail1: 8709172SFei.Feng@Sun.COM return (err); 8719172SFei.Feng@Sun.COM } 8729172SFei.Feng@Sun.COM 8739172SFei.Feng@Sun.COM /*ARGSUSED*/ 8749172SFei.Feng@Sun.COM void 8759172SFei.Feng@Sun.COM rt2860_reset_rx_ring(struct rt2860_softc *sc, struct rt2860_rx_ring *ring) 8769172SFei.Feng@Sun.COM { 8779172SFei.Feng@Sun.COM int i; 8789172SFei.Feng@Sun.COM 8799172SFei.Feng@Sun.COM for (i = 0; i < RT2860_RX_RING_COUNT; i++) 8809172SFei.Feng@Sun.COM ring->rxd[i].sdl0 &= ~LE_16(RT2860_RX_DDONE); 8819172SFei.Feng@Sun.COM 8829172SFei.Feng@Sun.COM RT2860_DMA_SYNC(ring->rxdesc_dma, DDI_DMA_SYNC_FORDEV); 8839172SFei.Feng@Sun.COM 8849172SFei.Feng@Sun.COM ring->cur = 0; 8859172SFei.Feng@Sun.COM } 8869172SFei.Feng@Sun.COM 8879172SFei.Feng@Sun.COM /*ARGSUSED*/ 8889172SFei.Feng@Sun.COM static void 8899172SFei.Feng@Sun.COM rt2860_free_rx_ring(struct rt2860_softc *sc, struct rt2860_rx_ring *ring) 8909172SFei.Feng@Sun.COM { 8919172SFei.Feng@Sun.COM struct rt2860_rx_data *data; 8929172SFei.Feng@Sun.COM int i, count; 8939172SFei.Feng@Sun.COM 8949172SFei.Feng@Sun.COM if (ring->rxd != NULL) 8959172SFei.Feng@Sun.COM rt2860_free_dma_mem(&ring->rxdesc_dma); 8969172SFei.Feng@Sun.COM 8979172SFei.Feng@Sun.COM count = RT2860_RX_RING_COUNT; 8989172SFei.Feng@Sun.COM if (ring->data != NULL) { 8999172SFei.Feng@Sun.COM for (i = 0; i < count; i++) { 9009172SFei.Feng@Sun.COM data = &ring->data[i]; 9019172SFei.Feng@Sun.COM rt2860_free_dma_mem(&data->rxbuf_dma); 9029172SFei.Feng@Sun.COM } 9039172SFei.Feng@Sun.COM } 9049172SFei.Feng@Sun.COM } 9059172SFei.Feng@Sun.COM 9069172SFei.Feng@Sun.COM static int 9079172SFei.Feng@Sun.COM rt2860_alloc_tx_pool(struct rt2860_softc *sc) 9089172SFei.Feng@Sun.COM { 9099172SFei.Feng@Sun.COM struct rt2860_tx_data *data; 9109172SFei.Feng@Sun.COM int i, err, size; 9119172SFei.Feng@Sun.COM 9129172SFei.Feng@Sun.COM size = RT2860_TX_POOL_COUNT * sizeof (struct rt2860_txwi); 9139172SFei.Feng@Sun.COM 9149172SFei.Feng@Sun.COM /* init data_pool early in case of failure.. */ 9159172SFei.Feng@Sun.COM SLIST_INIT(&sc->data_pool); 9169172SFei.Feng@Sun.COM 9179172SFei.Feng@Sun.COM err = rt2860_alloc_dma_mem(sc->sc_dev, &rt2860_dma_attr, size, 9189172SFei.Feng@Sun.COM &rt2860_desc_accattr, DDI_DMA_CONSISTENT, 9199172SFei.Feng@Sun.COM DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 9209172SFei.Feng@Sun.COM &sc->txpool_dma); 9219172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 9229172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, "rwn: rt2860_alloc_tx_pool(): " 9239172SFei.Feng@Sun.COM "failed to alloc dma mem\n"); 9249172SFei.Feng@Sun.COM goto fail1; 9259172SFei.Feng@Sun.COM } 9269172SFei.Feng@Sun.COM 9279172SFei.Feng@Sun.COM sc->txwi = (struct rt2860_txwi *)sc->txpool_dma.mem_va; 9289172SFei.Feng@Sun.COM (void) bzero(sc->txwi, size); 9299172SFei.Feng@Sun.COM RT2860_DMA_SYNC(sc->txpool_dma, DDI_DMA_SYNC_FORDEV); 9309172SFei.Feng@Sun.COM 9319172SFei.Feng@Sun.COM for (i = 0; i < RT2860_TX_POOL_COUNT; i++) { 9329172SFei.Feng@Sun.COM data = &sc->data[i]; 9339172SFei.Feng@Sun.COM 9349172SFei.Feng@Sun.COM err = rt2860_alloc_dma_mem(sc->sc_dev, &rt2860_dma_attr, 9359172SFei.Feng@Sun.COM sc->sc_dmabuf_size, 9369172SFei.Feng@Sun.COM &rt2860_buf_accattr, DDI_DMA_CONSISTENT, 9379172SFei.Feng@Sun.COM DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 9389172SFei.Feng@Sun.COM &data->txbuf_dma); 9399172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 9409172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, 9419172SFei.Feng@Sun.COM "rwn: rt2860_alloc_tx_pool(): " 9429172SFei.Feng@Sun.COM "failed to alloc dma mem\n"); 9439172SFei.Feng@Sun.COM goto fail2; 9449172SFei.Feng@Sun.COM } 9459172SFei.Feng@Sun.COM data->txwi = &sc->txwi[i]; 9469172SFei.Feng@Sun.COM data->paddr = sc->txpool_dma.cookie.dmac_address + 9479172SFei.Feng@Sun.COM i * sizeof (struct rt2860_txwi); 9489172SFei.Feng@Sun.COM 9499172SFei.Feng@Sun.COM SLIST_INSERT_HEAD(&sc->data_pool, data, next); 9509172SFei.Feng@Sun.COM } 9519172SFei.Feng@Sun.COM return (DDI_SUCCESS); 9529172SFei.Feng@Sun.COM fail2: 9539172SFei.Feng@Sun.COM rt2860_free_dma_mem(&sc->txpool_dma); 9549172SFei.Feng@Sun.COM fail1: 9559172SFei.Feng@Sun.COM return (err); 9569172SFei.Feng@Sun.COM } 9579172SFei.Feng@Sun.COM 9589172SFei.Feng@Sun.COM static void 9599172SFei.Feng@Sun.COM rt2860_free_tx_pool(struct rt2860_softc *sc) 9609172SFei.Feng@Sun.COM { 9619172SFei.Feng@Sun.COM struct rt2860_tx_data *data; 9629172SFei.Feng@Sun.COM int i; 9639172SFei.Feng@Sun.COM 9649172SFei.Feng@Sun.COM if (sc->txwi != NULL) { 9659172SFei.Feng@Sun.COM rt2860_free_dma_mem(&sc->txpool_dma); 9669172SFei.Feng@Sun.COM } 9679172SFei.Feng@Sun.COM 9689172SFei.Feng@Sun.COM for (i = 0; i < RT2860_TX_POOL_COUNT; i++) { 9699172SFei.Feng@Sun.COM data = &sc->data[i]; 9709172SFei.Feng@Sun.COM rt2860_free_dma_mem(&data->txbuf_dma); 9719172SFei.Feng@Sun.COM } 9729172SFei.Feng@Sun.COM } 9739172SFei.Feng@Sun.COM 9749172SFei.Feng@Sun.COM /* quickly determine if a given rate is CCK or OFDM */ 9759172SFei.Feng@Sun.COM #define RT2860_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22) 9769172SFei.Feng@Sun.COM 9779172SFei.Feng@Sun.COM #define RT2860_ACK_SIZE 14 /* 10 + 4(FCS) */ 9789172SFei.Feng@Sun.COM #define RT2860_SIFS_TIME 10 9799172SFei.Feng@Sun.COM 9809172SFei.Feng@Sun.COM static uint8_t 9819172SFei.Feng@Sun.COM rt2860_rate2mcs(uint8_t rate) 9829172SFei.Feng@Sun.COM { 9839172SFei.Feng@Sun.COM switch (rate) { 9849172SFei.Feng@Sun.COM /* CCK rates */ 9859172SFei.Feng@Sun.COM case 2: 9869172SFei.Feng@Sun.COM return (0); 9879172SFei.Feng@Sun.COM case 4: 9889172SFei.Feng@Sun.COM return (1); 9899172SFei.Feng@Sun.COM case 11: 9909172SFei.Feng@Sun.COM return (2); 9919172SFei.Feng@Sun.COM case 22: 9929172SFei.Feng@Sun.COM return (3); 9939172SFei.Feng@Sun.COM /* OFDM rates */ 9949172SFei.Feng@Sun.COM case 12: 9959172SFei.Feng@Sun.COM return (0); 9969172SFei.Feng@Sun.COM case 18: 9979172SFei.Feng@Sun.COM return (1); 9989172SFei.Feng@Sun.COM case 24: 9999172SFei.Feng@Sun.COM return (2); 10009172SFei.Feng@Sun.COM case 36: 10019172SFei.Feng@Sun.COM return (3); 10029172SFei.Feng@Sun.COM case 48: 10039172SFei.Feng@Sun.COM return (4); 10049172SFei.Feng@Sun.COM case 72: 10059172SFei.Feng@Sun.COM return (5); 10069172SFei.Feng@Sun.COM case 96: 10079172SFei.Feng@Sun.COM return (6); 10089172SFei.Feng@Sun.COM case 108: 10099172SFei.Feng@Sun.COM return (7); 10109172SFei.Feng@Sun.COM } 10119172SFei.Feng@Sun.COM 10129172SFei.Feng@Sun.COM return (0); /* shouldn't get there */ 10139172SFei.Feng@Sun.COM } 10149172SFei.Feng@Sun.COM 10159172SFei.Feng@Sun.COM /* 10169172SFei.Feng@Sun.COM * Return the expected ack rate for a frame transmitted at rate `rate'. 10179172SFei.Feng@Sun.COM */ 10189172SFei.Feng@Sun.COM static int 10199172SFei.Feng@Sun.COM rt2860_ack_rate(struct ieee80211com *ic, int rate) 10209172SFei.Feng@Sun.COM { 10219172SFei.Feng@Sun.COM switch (rate) { 10229172SFei.Feng@Sun.COM /* CCK rates */ 10239172SFei.Feng@Sun.COM case 2: 10249172SFei.Feng@Sun.COM return (2); 10259172SFei.Feng@Sun.COM case 4: 10269172SFei.Feng@Sun.COM case 11: 10279172SFei.Feng@Sun.COM case 22: 10289172SFei.Feng@Sun.COM return ((ic->ic_curmode == IEEE80211_MODE_11B) ? 4 : rate); 10299172SFei.Feng@Sun.COM 10309172SFei.Feng@Sun.COM /* OFDM rates */ 10319172SFei.Feng@Sun.COM case 12: 10329172SFei.Feng@Sun.COM case 18: 10339172SFei.Feng@Sun.COM return (12); 10349172SFei.Feng@Sun.COM case 24: 10359172SFei.Feng@Sun.COM case 36: 10369172SFei.Feng@Sun.COM return (24); 10379172SFei.Feng@Sun.COM case 48: 10389172SFei.Feng@Sun.COM case 72: 10399172SFei.Feng@Sun.COM case 96: 10409172SFei.Feng@Sun.COM case 108: 10419172SFei.Feng@Sun.COM return (48); 10429172SFei.Feng@Sun.COM } 10439172SFei.Feng@Sun.COM 10449172SFei.Feng@Sun.COM /* default to 1Mbps */ 10459172SFei.Feng@Sun.COM return (2); 10469172SFei.Feng@Sun.COM } 10479172SFei.Feng@Sun.COM 10489172SFei.Feng@Sun.COM 10499172SFei.Feng@Sun.COM /* 10509172SFei.Feng@Sun.COM * Compute the duration (in us) needed to transmit `len' bytes at rate `rate'. 10519172SFei.Feng@Sun.COM * The function automatically determines the operating mode depending on the 10529172SFei.Feng@Sun.COM * given rate. `flags' indicates whether short preamble is in use or not. 10539172SFei.Feng@Sun.COM */ 10549172SFei.Feng@Sun.COM static uint16_t 10559172SFei.Feng@Sun.COM rt2860_txtime(int len, int rate, uint32_t flags) 10569172SFei.Feng@Sun.COM { 10579172SFei.Feng@Sun.COM uint16_t txtime; 10589172SFei.Feng@Sun.COM 10599172SFei.Feng@Sun.COM if (RT2860_RATE_IS_OFDM(rate)) { 10609172SFei.Feng@Sun.COM /* IEEE Std 802.11g-2003, pp. 44 */ 10619172SFei.Feng@Sun.COM txtime = (8 + 4 * len + 3 + rate - 1) / rate; 10629172SFei.Feng@Sun.COM txtime = 16 + 4 + 4 * txtime + 6; 10639172SFei.Feng@Sun.COM } else { 10649172SFei.Feng@Sun.COM /* IEEE Std 802.11b-1999, pp. 28 */ 10659172SFei.Feng@Sun.COM txtime = (16 * len + rate - 1) / rate; 10669172SFei.Feng@Sun.COM if (rate != 2 && (flags & IEEE80211_F_SHPREAMBLE)) 10679172SFei.Feng@Sun.COM txtime += 72 + 24; 10689172SFei.Feng@Sun.COM else 10699172SFei.Feng@Sun.COM txtime += 144 + 48; 10709172SFei.Feng@Sun.COM } 10719172SFei.Feng@Sun.COM return (txtime); 10729172SFei.Feng@Sun.COM } 10739172SFei.Feng@Sun.COM 10749172SFei.Feng@Sun.COM static int 10759172SFei.Feng@Sun.COM rt2860_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type) 10769172SFei.Feng@Sun.COM { 10779172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)ic; 10789172SFei.Feng@Sun.COM struct rt2860_tx_ring *ring; 10799172SFei.Feng@Sun.COM struct rt2860_tx_data *data; 10809172SFei.Feng@Sun.COM struct rt2860_txd *txd; 10819172SFei.Feng@Sun.COM struct rt2860_txwi *txwi; 10829172SFei.Feng@Sun.COM struct ieee80211_frame *wh; 10839172SFei.Feng@Sun.COM struct ieee80211_node *ni; 10849172SFei.Feng@Sun.COM int qid, off, rate, err; 10859172SFei.Feng@Sun.COM int mblen, pktlen; 10869172SFei.Feng@Sun.COM uint_t hdrlen; 10879172SFei.Feng@Sun.COM uint8_t mcs, pid, qsel; 10889172SFei.Feng@Sun.COM uint16_t dur; 10899172SFei.Feng@Sun.COM mblk_t *m, *m0; 10909172SFei.Feng@Sun.COM 10919172SFei.Feng@Sun.COM err = DDI_SUCCESS; 10929172SFei.Feng@Sun.COM 10939172SFei.Feng@Sun.COM mutex_enter(&sc->sc_txlock); 10949172SFei.Feng@Sun.COM if (RT2860_IS_SUSPEND(sc)) { 10959172SFei.Feng@Sun.COM err = ENXIO; 10969172SFei.Feng@Sun.COM goto fail1; 10979172SFei.Feng@Sun.COM } 10989172SFei.Feng@Sun.COM 10999172SFei.Feng@Sun.COM if ((type & IEEE80211_FC0_TYPE_MASK) != 11009172SFei.Feng@Sun.COM IEEE80211_FC0_TYPE_DATA) 11019172SFei.Feng@Sun.COM qid = sc->mgtqid; 11029172SFei.Feng@Sun.COM else 11039172SFei.Feng@Sun.COM qid = EDCA_AC_BE; 11049172SFei.Feng@Sun.COM ring = &sc->txq[qid]; 11059172SFei.Feng@Sun.COM 11069172SFei.Feng@Sun.COM if (SLIST_EMPTY(&sc->data_pool) || (ring->queued > 15)) { 11079172SFei.Feng@Sun.COM sc->sc_need_sched = 1; 11089172SFei.Feng@Sun.COM sc->sc_tx_nobuf++; 11099172SFei.Feng@Sun.COM err = ENOMEM; 11109172SFei.Feng@Sun.COM goto fail1; 11119172SFei.Feng@Sun.COM } 11129172SFei.Feng@Sun.COM 11139172SFei.Feng@Sun.COM /* the data pool contains at least one element, pick the first */ 11149172SFei.Feng@Sun.COM data = SLIST_FIRST(&sc->data_pool); 11159172SFei.Feng@Sun.COM 11169172SFei.Feng@Sun.COM m = allocb(msgdsize(mp) + 32, BPRI_MED); 11179172SFei.Feng@Sun.COM if (m == NULL) { 11189172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_TX, "rwn: rt2860_send():" 1119*10108SFei.Feng@Sun.COM "rt2860_mgmt_send: can't alloc mblk.\n"); 11209172SFei.Feng@Sun.COM err = DDI_FAILURE; 11219172SFei.Feng@Sun.COM goto fail1; 11229172SFei.Feng@Sun.COM } 11239172SFei.Feng@Sun.COM 11249172SFei.Feng@Sun.COM for (off = 0, m0 = mp; m0 != NULL; m0 = m0->b_cont) { 11259172SFei.Feng@Sun.COM mblen = MBLKL(m0); 11269172SFei.Feng@Sun.COM (void) bcopy(m0->b_rptr, m->b_rptr + off, mblen); 11279172SFei.Feng@Sun.COM off += mblen; 11289172SFei.Feng@Sun.COM } 11299172SFei.Feng@Sun.COM m->b_wptr += off; 11309172SFei.Feng@Sun.COM 11319172SFei.Feng@Sun.COM wh = (struct ieee80211_frame *)m->b_rptr; 11329172SFei.Feng@Sun.COM ni = ieee80211_find_txnode(ic, wh->i_addr1); 11339172SFei.Feng@Sun.COM if (ni == NULL) { 11349172SFei.Feng@Sun.COM err = DDI_FAILURE; 11359172SFei.Feng@Sun.COM sc->sc_tx_err++; 11369172SFei.Feng@Sun.COM goto fail2; 11379172SFei.Feng@Sun.COM } 11389172SFei.Feng@Sun.COM 11399172SFei.Feng@Sun.COM if ((type & IEEE80211_FC0_TYPE_MASK) == 11409172SFei.Feng@Sun.COM IEEE80211_FC0_TYPE_DATA) 11419172SFei.Feng@Sun.COM (void) ieee80211_encap(ic, m, ni); 11429172SFei.Feng@Sun.COM 11439172SFei.Feng@Sun.COM if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 11449172SFei.Feng@Sun.COM struct ieee80211_key *k; 11459172SFei.Feng@Sun.COM k = ieee80211_crypto_encap(ic, m); 11469172SFei.Feng@Sun.COM if (k == NULL) { 11479172SFei.Feng@Sun.COM sc->sc_tx_err++; 11489172SFei.Feng@Sun.COM err = DDI_FAILURE; 11499172SFei.Feng@Sun.COM goto fail3; 11509172SFei.Feng@Sun.COM } 11519172SFei.Feng@Sun.COM /* packet header may have moved, reset our local pointer */ 11529172SFei.Feng@Sun.COM wh = (struct ieee80211_frame *)m->b_rptr; 11539172SFei.Feng@Sun.COM } 11549172SFei.Feng@Sun.COM pktlen = msgdsize(m); 11559172SFei.Feng@Sun.COM hdrlen = sizeof (*wh); 11569172SFei.Feng@Sun.COM 11579172SFei.Feng@Sun.COM /* pickup a rate */ 11589172SFei.Feng@Sun.COM if (IEEE80211_IS_MULTICAST(wh->i_addr1) || 11599172SFei.Feng@Sun.COM ((type & IEEE80211_FC0_TYPE_MASK) != 11609172SFei.Feng@Sun.COM IEEE80211_FC0_TYPE_DATA)) 11619172SFei.Feng@Sun.COM rate = ni->in_rates.ir_rates[0]; 11629172SFei.Feng@Sun.COM else { 11639172SFei.Feng@Sun.COM if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) 11649172SFei.Feng@Sun.COM rate = ic->ic_fixed_rate; 11659172SFei.Feng@Sun.COM else 11669172SFei.Feng@Sun.COM rate = ni->in_rates.ir_rates[ni->in_txrate]; 11679172SFei.Feng@Sun.COM } 11689172SFei.Feng@Sun.COM rate &= IEEE80211_RATE_VAL; 11699172SFei.Feng@Sun.COM 11709172SFei.Feng@Sun.COM /* get MCS code from rate */ 11719172SFei.Feng@Sun.COM mcs = rt2860_rate2mcs(rate); 11729172SFei.Feng@Sun.COM 11739172SFei.Feng@Sun.COM /* setup TX Wireless Information */ 11749172SFei.Feng@Sun.COM txwi = data->txwi; 11759172SFei.Feng@Sun.COM (void) bzero(txwi, sizeof (struct rt2860_txwi)); 11769172SFei.Feng@Sun.COM txwi->wcid = (type == IEEE80211_FC0_TYPE_DATA) ? 11779172SFei.Feng@Sun.COM RT2860_AID2WCID(ni->in_associd) : 0xff; 11789172SFei.Feng@Sun.COM txwi->len = LE_16(pktlen); 11799172SFei.Feng@Sun.COM if (!RT2860_RATE_IS_OFDM(rate)) { 11809172SFei.Feng@Sun.COM txwi->phy = LE_16(RT2860_PHY_CCK); 11819172SFei.Feng@Sun.COM if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) 11829172SFei.Feng@Sun.COM mcs |= RT2860_PHY_SHPRE; 11839172SFei.Feng@Sun.COM } else 11849172SFei.Feng@Sun.COM txwi->phy = LE_16(RT2860_PHY_OFDM); 11859172SFei.Feng@Sun.COM txwi->phy |= LE_16(mcs); 11869172SFei.Feng@Sun.COM 11879172SFei.Feng@Sun.COM /* 11889172SFei.Feng@Sun.COM * We store the MCS code into the driver-private PacketID field. 11899172SFei.Feng@Sun.COM * The PacketID is latched into TX_STAT_FIFO when Tx completes so 11909172SFei.Feng@Sun.COM * that we know at which initial rate the frame was transmitted. 11919172SFei.Feng@Sun.COM * We add 1 to the MCS code because setting the PacketID field to 11929172SFei.Feng@Sun.COM * 0 means that we don't want feedback in TX_STAT_FIFO. 11939172SFei.Feng@Sun.COM */ 11949172SFei.Feng@Sun.COM pid = (mcs + 1) & 0xf; 11959172SFei.Feng@Sun.COM txwi->len |= LE_16(pid << RT2860_TX_PID_SHIFT); 11969172SFei.Feng@Sun.COM 11979172SFei.Feng@Sun.COM /* check if RTS/CTS or CTS-to-self protection is required */ 11989172SFei.Feng@Sun.COM if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && 11999172SFei.Feng@Sun.COM (pktlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold || 12009172SFei.Feng@Sun.COM ((ic->ic_flags & 12019172SFei.Feng@Sun.COM IEEE80211_F_USEPROT) && RT2860_RATE_IS_OFDM(rate)))) 12029172SFei.Feng@Sun.COM txwi->txop = RT2860_TX_TXOP_HT; 12039172SFei.Feng@Sun.COM else 12049172SFei.Feng@Sun.COM txwi->txop = RT2860_TX_TXOP_BACKOFF; 12059172SFei.Feng@Sun.COM 12069172SFei.Feng@Sun.COM if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 12079172SFei.Feng@Sun.COM txwi->xflags |= RT2860_TX_ACK; 12089172SFei.Feng@Sun.COM 12099172SFei.Feng@Sun.COM dur = rt2860_txtime(RT2860_ACK_SIZE, rt2860_ack_rate(ic, rate), 12109172SFei.Feng@Sun.COM ic->ic_flags) + sc->sifs; 12119172SFei.Feng@Sun.COM *(uint16_t *)wh->i_dur = LE_16(dur); 12129172SFei.Feng@Sun.COM } 12139172SFei.Feng@Sun.COM 12149172SFei.Feng@Sun.COM /* copy and trim 802.11 header */ 12159172SFei.Feng@Sun.COM bcopy(wh, &txwi->wh, hdrlen); 12169172SFei.Feng@Sun.COM m->b_rptr += hdrlen; 12179172SFei.Feng@Sun.COM bcopy(m->b_rptr, data->txbuf_dma.mem_va, pktlen - hdrlen); 12189172SFei.Feng@Sun.COM 12199172SFei.Feng@Sun.COM qsel = (qid < EDCA_NUM_AC) ? RT2860_TX_QSEL_EDCA : RT2860_TX_QSEL_MGMT; 12209172SFei.Feng@Sun.COM 12219172SFei.Feng@Sun.COM /* first segment is TXWI + 802.11 header */ 12229172SFei.Feng@Sun.COM txd = &ring->txd[ring->cur]; 12239172SFei.Feng@Sun.COM txd->sdp0 = LE_32(data->paddr); 12249172SFei.Feng@Sun.COM txd->sdl0 = LE_16(16 + hdrlen); 12259172SFei.Feng@Sun.COM txd->flags = qsel; 12269172SFei.Feng@Sun.COM 12279172SFei.Feng@Sun.COM /* finalize last segment */ 12289172SFei.Feng@Sun.COM txd->sdp1 = LE_32(data->txbuf_dma.cookie.dmac_address); 12299172SFei.Feng@Sun.COM txd->sdl1 = LE_16(pktlen - hdrlen | RT2860_TX_LS1); 12309172SFei.Feng@Sun.COM 12319172SFei.Feng@Sun.COM /* remove from the free pool and link it into the SW Tx slot */ 12329172SFei.Feng@Sun.COM SLIST_REMOVE_HEAD(&sc->data_pool, next); 12339172SFei.Feng@Sun.COM data->ni = ieee80211_ref_node(ni); 12349172SFei.Feng@Sun.COM ring->data[ring->cur] = data; 12359172SFei.Feng@Sun.COM 12369172SFei.Feng@Sun.COM (void) ddi_dma_sync(sc->txpool_dma.dma_hdl, 12379172SFei.Feng@Sun.COM _PTRDIFF(txwi, sc->txwi), 12389172SFei.Feng@Sun.COM (hdrlen + 16 + 2), 12399172SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV); 12409172SFei.Feng@Sun.COM RT2860_DMA_SYNC(data->txbuf_dma, DDI_DMA_SYNC_FORDEV); 12419172SFei.Feng@Sun.COM RT2860_DMA_SYNC(ring->txdesc_dma, DDI_DMA_SYNC_FORDEV); 12429172SFei.Feng@Sun.COM 12439172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_TX, "rwn: rt2860_send():" 12449172SFei.Feng@Sun.COM "sending frame qid=%d wcid=%d rate=%d cur = %x\n", 12459172SFei.Feng@Sun.COM qid, txwi->wcid, rate, ring->cur); 12469172SFei.Feng@Sun.COM 12479172SFei.Feng@Sun.COM ring->queued++; 12489172SFei.Feng@Sun.COM ring->cur = (ring->cur + 1) % RT2860_TX_RING_COUNT; 12499172SFei.Feng@Sun.COM 12509172SFei.Feng@Sun.COM /* kick Tx */ 12519172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_TX_CTX_IDX(qid), ring->cur); 12529172SFei.Feng@Sun.COM 12539172SFei.Feng@Sun.COM sc->sc_tx_timer = 5; 12549172SFei.Feng@Sun.COM 12559172SFei.Feng@Sun.COM ic->ic_stats.is_tx_frags++; 12569172SFei.Feng@Sun.COM ic->ic_stats.is_tx_bytes += pktlen; 12579172SFei.Feng@Sun.COM 12589172SFei.Feng@Sun.COM fail3: 12599172SFei.Feng@Sun.COM ieee80211_free_node(ni); 12609172SFei.Feng@Sun.COM fail2: 12619172SFei.Feng@Sun.COM freemsg(m); 12629172SFei.Feng@Sun.COM fail1: 12639172SFei.Feng@Sun.COM if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA || 12649172SFei.Feng@Sun.COM err == DDI_SUCCESS) 12659172SFei.Feng@Sun.COM freemsg(mp); 12669172SFei.Feng@Sun.COM mutex_exit(&sc->sc_txlock); 12679172SFei.Feng@Sun.COM return (err); 12689172SFei.Feng@Sun.COM } 12699172SFei.Feng@Sun.COM 12709172SFei.Feng@Sun.COM /* 12719172SFei.Feng@Sun.COM * This function is called periodically (every 200ms) during scanning to 12729172SFei.Feng@Sun.COM * switch from one channel to another. 12739172SFei.Feng@Sun.COM */ 12749172SFei.Feng@Sun.COM static void 12759172SFei.Feng@Sun.COM rt2860_next_scan(void *arg) 12769172SFei.Feng@Sun.COM { 12779172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 12789172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 12799172SFei.Feng@Sun.COM 12809172SFei.Feng@Sun.COM if (ic->ic_state == IEEE80211_S_SCAN) 12819172SFei.Feng@Sun.COM (void) ieee80211_next_scan(ic); 12829172SFei.Feng@Sun.COM } 12839172SFei.Feng@Sun.COM 12849172SFei.Feng@Sun.COM static void 12859172SFei.Feng@Sun.COM rt2860_updateslot(struct rt2860_softc *sc) 12869172SFei.Feng@Sun.COM { 12879172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 12889172SFei.Feng@Sun.COM uint32_t tmp; 12899172SFei.Feng@Sun.COM 12909172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_BKOFF_SLOT_CFG); 12919172SFei.Feng@Sun.COM tmp &= ~0xff; 12929172SFei.Feng@Sun.COM tmp |= (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; 12939172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_BKOFF_SLOT_CFG, tmp); 12949172SFei.Feng@Sun.COM } 12959172SFei.Feng@Sun.COM 12969172SFei.Feng@Sun.COM static void 12979172SFei.Feng@Sun.COM rt2860_iter_func(void *arg, struct ieee80211_node *ni) 12989172SFei.Feng@Sun.COM { 12999172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 13009172SFei.Feng@Sun.COM uint8_t wcid; 13019172SFei.Feng@Sun.COM 13029172SFei.Feng@Sun.COM wcid = RT2860_AID2WCID(ni->in_associd); 13039172SFei.Feng@Sun.COM rt2860_amrr_choose(&sc->amrr, ni, &sc->amn[wcid]); 13049172SFei.Feng@Sun.COM } 13059172SFei.Feng@Sun.COM 13069172SFei.Feng@Sun.COM static void 13079172SFei.Feng@Sun.COM rt2860_updatestats(void *arg) 13089172SFei.Feng@Sun.COM { 13099172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 13109172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 13119172SFei.Feng@Sun.COM 13129172SFei.Feng@Sun.COM if (ic->ic_opmode == IEEE80211_M_STA) 13139172SFei.Feng@Sun.COM rt2860_iter_func(sc, ic->ic_bss); 13149172SFei.Feng@Sun.COM else 13159172SFei.Feng@Sun.COM ieee80211_iterate_nodes(&ic->ic_sta, rt2860_iter_func, arg); 13169172SFei.Feng@Sun.COM 13179172SFei.Feng@Sun.COM sc->sc_rssadapt_id = timeout(rt2860_updatestats, (void *)sc, 13189172SFei.Feng@Sun.COM drv_usectohz(500 * 1000)); 13199172SFei.Feng@Sun.COM } 13209172SFei.Feng@Sun.COM 13219172SFei.Feng@Sun.COM static void 13229172SFei.Feng@Sun.COM rt2860_enable_mrr(struct rt2860_softc *sc) 13239172SFei.Feng@Sun.COM { 13249172SFei.Feng@Sun.COM #define CCK(mcs) (mcs) 13259172SFei.Feng@Sun.COM #define OFDM(mcs) ((uint32_t)1 << 3 | (mcs)) 13269172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_LG_FBK_CFG0, 13279172SFei.Feng@Sun.COM OFDM(6) << 28 | /* 54->48 */ 13289172SFei.Feng@Sun.COM OFDM(5) << 24 | /* 48->36 */ 13299172SFei.Feng@Sun.COM OFDM(4) << 20 | /* 36->24 */ 13309172SFei.Feng@Sun.COM OFDM(3) << 16 | /* 24->18 */ 13319172SFei.Feng@Sun.COM OFDM(2) << 12 | /* 18->12 */ 13329172SFei.Feng@Sun.COM OFDM(1) << 8 | /* 12-> 9 */ 13339172SFei.Feng@Sun.COM OFDM(0) << 4 | /* 9-> 6 */ 13349172SFei.Feng@Sun.COM OFDM(0)); /* 6-> 6 */ 13359172SFei.Feng@Sun.COM 13369172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_LG_FBK_CFG1, 13379172SFei.Feng@Sun.COM CCK(2) << 12 | /* 11->5.5 */ 13389172SFei.Feng@Sun.COM CCK(1) << 8 | /* 5.5-> 2 */ 13399172SFei.Feng@Sun.COM CCK(0) << 4 | /* 2-> 1 */ 13409172SFei.Feng@Sun.COM CCK(0)); /* 1-> 1 */ 13419172SFei.Feng@Sun.COM #undef OFDM 13429172SFei.Feng@Sun.COM #undef CCK 13439172SFei.Feng@Sun.COM } 13449172SFei.Feng@Sun.COM 13459172SFei.Feng@Sun.COM static void 13469172SFei.Feng@Sun.COM rt2860_set_txpreamble(struct rt2860_softc *sc) 13479172SFei.Feng@Sun.COM { 13489172SFei.Feng@Sun.COM uint32_t tmp; 13499172SFei.Feng@Sun.COM 13509172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_AUTO_RSP_CFG); 13519172SFei.Feng@Sun.COM tmp &= ~RT2860_CCK_SHORT_EN; 13529172SFei.Feng@Sun.COM if (sc->sc_ic.ic_flags & IEEE80211_F_SHPREAMBLE) 13539172SFei.Feng@Sun.COM tmp |= RT2860_CCK_SHORT_EN; 13549172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_AUTO_RSP_CFG, tmp); 13559172SFei.Feng@Sun.COM } 13569172SFei.Feng@Sun.COM 13579172SFei.Feng@Sun.COM static void 13589172SFei.Feng@Sun.COM rt2860_set_bssid(struct rt2860_softc *sc, const uint8_t *bssid) 13599172SFei.Feng@Sun.COM { 13609172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_BSSID_DW0, 13619172SFei.Feng@Sun.COM bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24); 13629172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_BSSID_DW1, 13639172SFei.Feng@Sun.COM bssid[4] | bssid[5] << 8); 13649172SFei.Feng@Sun.COM } 13659172SFei.Feng@Sun.COM 13669172SFei.Feng@Sun.COM static void 13679172SFei.Feng@Sun.COM rt2860_set_basicrates(struct rt2860_softc *sc) 13689172SFei.Feng@Sun.COM { 13699172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 13709172SFei.Feng@Sun.COM 13719172SFei.Feng@Sun.COM /* set basic rates mask */ 13729172SFei.Feng@Sun.COM if (ic->ic_curmode == IEEE80211_MODE_11B) 13739172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_LEGACY_BASIC_RATE, 0x003); 13749172SFei.Feng@Sun.COM else if (ic->ic_curmode == IEEE80211_MODE_11A) 13759172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_LEGACY_BASIC_RATE, 0x150); 13769172SFei.Feng@Sun.COM else /* 11g */ 13779172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_LEGACY_BASIC_RATE, 0x15f); 13789172SFei.Feng@Sun.COM } 13799172SFei.Feng@Sun.COM 13809172SFei.Feng@Sun.COM static void 13819172SFei.Feng@Sun.COM rt2860_amrr_node_init(const struct rt2860_amrr *amrr, 13829172SFei.Feng@Sun.COM struct rt2860_amrr_node *amn) 13839172SFei.Feng@Sun.COM { 13849172SFei.Feng@Sun.COM amn->amn_success = 0; 13859172SFei.Feng@Sun.COM amn->amn_recovery = 0; 13869172SFei.Feng@Sun.COM amn->amn_txcnt = amn->amn_retrycnt = 0; 13879172SFei.Feng@Sun.COM amn->amn_success_threshold = amrr->amrr_min_success_threshold; 13889172SFei.Feng@Sun.COM } 13899172SFei.Feng@Sun.COM 13909172SFei.Feng@Sun.COM static void 13919172SFei.Feng@Sun.COM rt2860_amrr_choose(struct rt2860_amrr *amrr, struct ieee80211_node *ni, 13929172SFei.Feng@Sun.COM struct rt2860_amrr_node *amn) 13939172SFei.Feng@Sun.COM { 13949172SFei.Feng@Sun.COM #define RV(rate) ((rate) & IEEE80211_RATE_VAL) 13959172SFei.Feng@Sun.COM #define is_success(amn) \ 13969172SFei.Feng@Sun.COM ((amn)->amn_retrycnt < (amn)->amn_txcnt / 10) 13979172SFei.Feng@Sun.COM #define is_failure(amn) \ 13989172SFei.Feng@Sun.COM ((amn)->amn_retrycnt > (amn)->amn_txcnt / 3) 13999172SFei.Feng@Sun.COM #define is_enough(amn) \ 14009172SFei.Feng@Sun.COM ((amn)->amn_txcnt > 10) 14019172SFei.Feng@Sun.COM #define is_min_rate(ni) \ 14029172SFei.Feng@Sun.COM ((ni)->in_txrate == 0) 14039172SFei.Feng@Sun.COM #define is_max_rate(ni) \ 14049172SFei.Feng@Sun.COM ((ni)->in_txrate == (ni)->in_rates.ir_nrates - 1) 14059172SFei.Feng@Sun.COM #define increase_rate(ni) \ 14069172SFei.Feng@Sun.COM ((ni)->in_txrate++) 14079172SFei.Feng@Sun.COM #define decrease_rate(ni) \ 14089172SFei.Feng@Sun.COM ((ni)->in_txrate--) 14099172SFei.Feng@Sun.COM #define reset_cnt(amn) \ 14109172SFei.Feng@Sun.COM { (amn)->amn_txcnt = (amn)->amn_retrycnt = 0; } 14119172SFei.Feng@Sun.COM 14129172SFei.Feng@Sun.COM int need_change = 0; 14139172SFei.Feng@Sun.COM 14149172SFei.Feng@Sun.COM if (is_success(amn) && is_enough(amn)) { 14159172SFei.Feng@Sun.COM amn->amn_success++; 14169172SFei.Feng@Sun.COM if (amn->amn_success >= amn->amn_success_threshold && 14179172SFei.Feng@Sun.COM !is_max_rate(ni)) { 14189172SFei.Feng@Sun.COM amn->amn_recovery = 1; 14199172SFei.Feng@Sun.COM amn->amn_success = 0; 14209172SFei.Feng@Sun.COM increase_rate(ni); 14219172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_amrr_choose(): " 14229172SFei.Feng@Sun.COM "increase rate = %d, #tx = %d, #retries = %d\n", 14239172SFei.Feng@Sun.COM RV(ni->in_rates.ir_rates[ni->in_txrate]), 14249172SFei.Feng@Sun.COM amn->amn_txcnt, amn->amn_retrycnt); 14259172SFei.Feng@Sun.COM need_change = 1; 14269172SFei.Feng@Sun.COM } else { 14279172SFei.Feng@Sun.COM amn->amn_recovery = 0; 14289172SFei.Feng@Sun.COM } 14299172SFei.Feng@Sun.COM } else if (is_failure(amn)) { 14309172SFei.Feng@Sun.COM amn->amn_success = 0; 14319172SFei.Feng@Sun.COM if (!is_min_rate(ni)) { 14329172SFei.Feng@Sun.COM if (amn->amn_recovery) { 14339172SFei.Feng@Sun.COM amn->amn_success_threshold *= 2; 14349172SFei.Feng@Sun.COM if (amn->amn_success_threshold > 14359172SFei.Feng@Sun.COM amrr->amrr_max_success_threshold) 14369172SFei.Feng@Sun.COM amn->amn_success_threshold = 14379172SFei.Feng@Sun.COM amrr->amrr_max_success_threshold; 14389172SFei.Feng@Sun.COM } else { 14399172SFei.Feng@Sun.COM amn->amn_success_threshold = 14409172SFei.Feng@Sun.COM amrr->amrr_min_success_threshold; 14419172SFei.Feng@Sun.COM } 14429172SFei.Feng@Sun.COM decrease_rate(ni); 14439172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_amrr_choose(): " 14449172SFei.Feng@Sun.COM "decrease rate = %d, #tx = %d, #retries = %d\n", 14459172SFei.Feng@Sun.COM RV(ni->in_rates.ir_rates[ni->in_txrate]), 14469172SFei.Feng@Sun.COM amn->amn_txcnt, amn->amn_retrycnt); 14479172SFei.Feng@Sun.COM need_change = 1; 14489172SFei.Feng@Sun.COM } 14499172SFei.Feng@Sun.COM amn->amn_recovery = 0; 14509172SFei.Feng@Sun.COM } 14519172SFei.Feng@Sun.COM 14529172SFei.Feng@Sun.COM if (is_enough(amn) || need_change) 14539172SFei.Feng@Sun.COM reset_cnt(amn); 14549172SFei.Feng@Sun.COM #undef RV 14559172SFei.Feng@Sun.COM } 14569172SFei.Feng@Sun.COM 14579172SFei.Feng@Sun.COM static void 14589172SFei.Feng@Sun.COM rt2860_newassoc(struct ieee80211com *ic, struct ieee80211_node *in, int isnew) 14599172SFei.Feng@Sun.COM { 14609172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)ic; 14619172SFei.Feng@Sun.COM uint32_t off; 14629172SFei.Feng@Sun.COM uint8_t *fptr, wcid = 0; 14639172SFei.Feng@Sun.COM int i; 14649172SFei.Feng@Sun.COM 14659172SFei.Feng@Sun.COM if (isnew && in->in_associd != 0) { 14669172SFei.Feng@Sun.COM /* only interested in true associations */ 14679172SFei.Feng@Sun.COM wcid = RT2860_AID2WCID(in->in_associd); 14689172SFei.Feng@Sun.COM 14699172SFei.Feng@Sun.COM /* init WCID table entry */ 14709172SFei.Feng@Sun.COM off = RT2860_WCID_ENTRY(wcid); 14719172SFei.Feng@Sun.COM fptr = in->in_macaddr; 14729172SFei.Feng@Sun.COM for (i = 0; i < IEEE80211_ADDR_LEN; i++) 14739172SFei.Feng@Sun.COM rt2860_mem_write1(sc, off++, *fptr++); 14749172SFei.Feng@Sun.COM } 14759172SFei.Feng@Sun.COM rt2860_amrr_node_init(&sc->amrr, &sc->amn[wcid]); 14769172SFei.Feng@Sun.COM 14779172SFei.Feng@Sun.COM /* set rate to some reasonable initial value */ 14789172SFei.Feng@Sun.COM i = in->in_rates.ir_nrates - 1; 14799172SFei.Feng@Sun.COM for (; i > 0 && (in->in_rates.ir_rates[i] & IEEE80211_RATE_VAL) > 72; ) 14809172SFei.Feng@Sun.COM i--; 14819172SFei.Feng@Sun.COM in->in_txrate = i; 14829172SFei.Feng@Sun.COM 14839172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_80211, "rwn: rt2860_newassoc(): " 14849172SFei.Feng@Sun.COM "new assoc isnew=%d WCID=%d, initial rate=%d\n", 14859172SFei.Feng@Sun.COM isnew, wcid, 14869172SFei.Feng@Sun.COM in->in_rates.ir_rates[i] & IEEE80211_RATE_VAL); 14879172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_80211, "rwn: rt2860_newassoc(): " 14889172SFei.Feng@Sun.COM "addr=%x:%x:%x:%x:%x:%x\n", 14899172SFei.Feng@Sun.COM in->in_macaddr[0], in->in_macaddr[1], in->in_macaddr[2], 14909172SFei.Feng@Sun.COM in->in_macaddr[3], in->in_macaddr[4], in->in_macaddr[5]); 14919172SFei.Feng@Sun.COM } 14929172SFei.Feng@Sun.COM 14939172SFei.Feng@Sun.COM void 14949172SFei.Feng@Sun.COM rt2860_enable_tsf_sync(struct rt2860_softc *sc) 14959172SFei.Feng@Sun.COM { 14969172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 14979172SFei.Feng@Sun.COM uint32_t tmp; 14989172SFei.Feng@Sun.COM 14999172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_BCN_TIME_CFG); 15009172SFei.Feng@Sun.COM 15019172SFei.Feng@Sun.COM tmp &= ~0x1fffff; 15029172SFei.Feng@Sun.COM tmp |= ic->ic_bss->in_intval * 16; 15039172SFei.Feng@Sun.COM tmp |= RT2860_TSF_TIMER_EN | RT2860_TBTT_TIMER_EN; 15049172SFei.Feng@Sun.COM if (ic->ic_opmode == IEEE80211_M_STA) { 15059172SFei.Feng@Sun.COM /* 15069172SFei.Feng@Sun.COM * Local TSF is always updated with remote TSF on beacon 15079172SFei.Feng@Sun.COM * reception. 15089172SFei.Feng@Sun.COM */ 15099172SFei.Feng@Sun.COM tmp |= 1 << RT2860_TSF_SYNC_MODE_SHIFT; 15109172SFei.Feng@Sun.COM } 15119172SFei.Feng@Sun.COM 15129172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_BCN_TIME_CFG, tmp); 15139172SFei.Feng@Sun.COM } 15149172SFei.Feng@Sun.COM 15159172SFei.Feng@Sun.COM static int 15169172SFei.Feng@Sun.COM rt2860_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) 15179172SFei.Feng@Sun.COM { 15189172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)ic; 15199172SFei.Feng@Sun.COM enum ieee80211_state ostate; 15209172SFei.Feng@Sun.COM int err; 15219172SFei.Feng@Sun.COM uint32_t tmp; 15229172SFei.Feng@Sun.COM 15239172SFei.Feng@Sun.COM ostate = ic->ic_state; 15249172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_80211, "rwn: rt2860_newstate(): " 15259172SFei.Feng@Sun.COM "%x -> %x!\n", ostate, nstate); 15269172SFei.Feng@Sun.COM 15279172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 15289172SFei.Feng@Sun.COM if (sc->sc_scan_id != 0) { 15299172SFei.Feng@Sun.COM (void) untimeout(sc->sc_scan_id); 15309172SFei.Feng@Sun.COM sc->sc_scan_id = 0; 15319172SFei.Feng@Sun.COM } 15329172SFei.Feng@Sun.COM if (sc->sc_rssadapt_id != 0) { 15339172SFei.Feng@Sun.COM (void) untimeout(sc->sc_rssadapt_id); 15349172SFei.Feng@Sun.COM sc->sc_rssadapt_id = 0; 15359172SFei.Feng@Sun.COM } 15369172SFei.Feng@Sun.COM if (ostate == IEEE80211_S_RUN) { 15379172SFei.Feng@Sun.COM /* turn link LED off */ 15389172SFei.Feng@Sun.COM rt2860_set_leds(sc, RT2860_LED_RADIO); 15399172SFei.Feng@Sun.COM } 15409172SFei.Feng@Sun.COM 15419172SFei.Feng@Sun.COM switch (nstate) { 15429172SFei.Feng@Sun.COM case IEEE80211_S_INIT: 15439172SFei.Feng@Sun.COM if (ostate == IEEE80211_S_RUN) { 15449172SFei.Feng@Sun.COM /* abort TSF synchronization */ 15459172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_BCN_TIME_CFG); 15469172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_BCN_TIME_CFG, 15479172SFei.Feng@Sun.COM tmp & ~(RT2860_BCN_TX_EN | RT2860_TSF_TIMER_EN | 15489172SFei.Feng@Sun.COM RT2860_TBTT_TIMER_EN)); 15499172SFei.Feng@Sun.COM } 15509172SFei.Feng@Sun.COM break; 15519172SFei.Feng@Sun.COM 15529172SFei.Feng@Sun.COM case IEEE80211_S_SCAN: 15539172SFei.Feng@Sun.COM rt2860_set_chan(sc, ic->ic_curchan); 15549172SFei.Feng@Sun.COM sc->sc_scan_id = timeout(rt2860_next_scan, (void *)sc, 15559172SFei.Feng@Sun.COM drv_usectohz(200000)); 15569172SFei.Feng@Sun.COM break; 15579172SFei.Feng@Sun.COM 15589172SFei.Feng@Sun.COM case IEEE80211_S_AUTH: 15599172SFei.Feng@Sun.COM case IEEE80211_S_ASSOC: 15609172SFei.Feng@Sun.COM rt2860_set_chan(sc, ic->ic_curchan); 15619172SFei.Feng@Sun.COM break; 15629172SFei.Feng@Sun.COM 15639172SFei.Feng@Sun.COM case IEEE80211_S_RUN: 15649172SFei.Feng@Sun.COM rt2860_set_chan(sc, ic->ic_curchan); 15659172SFei.Feng@Sun.COM 15669172SFei.Feng@Sun.COM if (ic->ic_opmode != IEEE80211_M_MONITOR) { 15679172SFei.Feng@Sun.COM rt2860_updateslot(sc); 15689172SFei.Feng@Sun.COM rt2860_enable_mrr(sc); 15699172SFei.Feng@Sun.COM rt2860_set_txpreamble(sc); 15709172SFei.Feng@Sun.COM rt2860_set_basicrates(sc); 15719172SFei.Feng@Sun.COM rt2860_set_bssid(sc, ic->ic_bss->in_bssid); 15729172SFei.Feng@Sun.COM } 15739172SFei.Feng@Sun.COM if (ic->ic_opmode == IEEE80211_M_STA) { 15749172SFei.Feng@Sun.COM /* fake a join to init the tx rate */ 15759172SFei.Feng@Sun.COM rt2860_newassoc(ic, ic->ic_bss, 1); 15769172SFei.Feng@Sun.COM } 15779172SFei.Feng@Sun.COM 15789172SFei.Feng@Sun.COM if (ic->ic_opmode != IEEE80211_M_MONITOR) { 15799172SFei.Feng@Sun.COM rt2860_enable_tsf_sync(sc); 15809172SFei.Feng@Sun.COM sc->sc_rssadapt_id = timeout(rt2860_updatestats, 15819172SFei.Feng@Sun.COM (void *)sc, drv_usectohz(500 * 1000)); 15829172SFei.Feng@Sun.COM } 15839172SFei.Feng@Sun.COM 15849172SFei.Feng@Sun.COM /* turn link LED on */ 15859172SFei.Feng@Sun.COM rt2860_set_leds(sc, RT2860_LED_RADIO | 15869172SFei.Feng@Sun.COM (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan) ? 15879172SFei.Feng@Sun.COM RT2860_LED_LINK_2GHZ : RT2860_LED_LINK_5GHZ)); 15889172SFei.Feng@Sun.COM break; 15899172SFei.Feng@Sun.COM } 15909172SFei.Feng@Sun.COM 15919172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 15929172SFei.Feng@Sun.COM 15939172SFei.Feng@Sun.COM err = sc->sc_newstate(ic, nstate, arg); 15949172SFei.Feng@Sun.COM 15959172SFei.Feng@Sun.COM return (err); 15969172SFei.Feng@Sun.COM } 15979172SFei.Feng@Sun.COM 15989172SFei.Feng@Sun.COM /* 15999172SFei.Feng@Sun.COM * Return the Rx chain with the highest RSSI for a given frame. 16009172SFei.Feng@Sun.COM */ 16019172SFei.Feng@Sun.COM static uint8_t 16029172SFei.Feng@Sun.COM rt2860_maxrssi_chain(struct rt2860_softc *sc, const struct rt2860_rxwi *rxwi) 16039172SFei.Feng@Sun.COM { 16049172SFei.Feng@Sun.COM uint8_t rxchain = 0; 16059172SFei.Feng@Sun.COM 16069172SFei.Feng@Sun.COM if (sc->nrxchains > 1) 16079172SFei.Feng@Sun.COM if (rxwi->rssi[1] > rxwi->rssi[rxchain]) 16089172SFei.Feng@Sun.COM rxchain = 1; 16099172SFei.Feng@Sun.COM if (sc->nrxchains > 2) 16109172SFei.Feng@Sun.COM if (rxwi->rssi[2] > rxwi->rssi[rxchain]) 16119172SFei.Feng@Sun.COM rxchain = 2; 16129172SFei.Feng@Sun.COM 16139172SFei.Feng@Sun.COM return (rxchain); 16149172SFei.Feng@Sun.COM } 16159172SFei.Feng@Sun.COM 16169172SFei.Feng@Sun.COM static void 16179172SFei.Feng@Sun.COM rt2860_drain_stats_fifo(struct rt2860_softc *sc) 16189172SFei.Feng@Sun.COM { 16199172SFei.Feng@Sun.COM struct rt2860_amrr_node *amn; 16209172SFei.Feng@Sun.COM uint32_t stat; 16219172SFei.Feng@Sun.COM uint8_t wcid, mcs, pid; 16229172SFei.Feng@Sun.COM 16239172SFei.Feng@Sun.COM /* drain Tx status FIFO (maxsize = 16) */ 16249172SFei.Feng@Sun.COM while ((stat = RT2860_READ(sc, RT2860_TX_STAT_FIFO)) & RT2860_TXQ_VLD) { 16259172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_TX, "rwn: rt2860_drain_stats_fifo(): " 16269172SFei.Feng@Sun.COM "tx stat 0x%08\n", stat); 16279172SFei.Feng@Sun.COM 16289172SFei.Feng@Sun.COM wcid = (stat >> 8) & 0xff; 16299172SFei.Feng@Sun.COM 16309172SFei.Feng@Sun.COM /* if no ACK was requested, no feedback is available */ 16319172SFei.Feng@Sun.COM if (!(stat & RT2860_TXQ_ACKREQ) || wcid == 0xff) 16329172SFei.Feng@Sun.COM continue; 16339172SFei.Feng@Sun.COM /* update per-STA AMRR stats */ 16349172SFei.Feng@Sun.COM amn = &sc->amn[wcid]; 16359172SFei.Feng@Sun.COM amn->amn_txcnt++; 16369172SFei.Feng@Sun.COM if (stat & RT2860_TXQ_OK) { 16379172SFei.Feng@Sun.COM /* 16389172SFei.Feng@Sun.COM * Check if there were retries, ie if the Tx success 16399172SFei.Feng@Sun.COM * rate is different from the requested rate. Note 16409172SFei.Feng@Sun.COM * that it works only because we do not allow rate 16419172SFei.Feng@Sun.COM * fallback from OFDM to CCK. 16429172SFei.Feng@Sun.COM */ 16439172SFei.Feng@Sun.COM mcs = (stat >> RT2860_TXQ_MCS_SHIFT) & 0x7f; 16449172SFei.Feng@Sun.COM pid = (stat >> RT2860_TXQ_PID_SHIFT) & 0xf; 16459172SFei.Feng@Sun.COM if (mcs + 1 != pid) 16469172SFei.Feng@Sun.COM amn->amn_retrycnt++; 16479172SFei.Feng@Sun.COM } else 16489172SFei.Feng@Sun.COM amn->amn_retrycnt++; 16499172SFei.Feng@Sun.COM } 16509172SFei.Feng@Sun.COM } 16519172SFei.Feng@Sun.COM 16529172SFei.Feng@Sun.COM /*ARGSUSED*/ 16539172SFei.Feng@Sun.COM static void 16549172SFei.Feng@Sun.COM rt2860_tx_intr(struct rt2860_softc *sc, int qid) 16559172SFei.Feng@Sun.COM { 16569172SFei.Feng@Sun.COM struct rt2860_tx_ring *ring = &sc->txq[qid]; 16579172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 16589172SFei.Feng@Sun.COM uint32_t hw; 16599172SFei.Feng@Sun.COM 16609172SFei.Feng@Sun.COM rt2860_drain_stats_fifo(sc); 16619172SFei.Feng@Sun.COM 16629172SFei.Feng@Sun.COM mutex_enter(&sc->sc_txlock); 16639172SFei.Feng@Sun.COM hw = RT2860_READ(sc, RT2860_TX_DTX_IDX(qid)); 16649172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_TX, "rwn: rwn_tx_intr():" 16659172SFei.Feng@Sun.COM "hw = %x, ring->next = %x, queued = %d\n", 16669172SFei.Feng@Sun.COM hw, ring->next, ring->queued); 16679172SFei.Feng@Sun.COM while (ring->next != hw) { 16689172SFei.Feng@Sun.COM struct rt2860_txd *txd = &ring->txd[ring->next]; 16699172SFei.Feng@Sun.COM struct rt2860_tx_data *data = ring->data[ring->next]; 16709172SFei.Feng@Sun.COM 16719172SFei.Feng@Sun.COM if (data != NULL) { 16729172SFei.Feng@Sun.COM RT2860_DMA_SYNC(data->txbuf_dma, DDI_DMA_SYNC_FORDEV); 16739172SFei.Feng@Sun.COM if (data->ni != NULL) { 16749172SFei.Feng@Sun.COM ieee80211_free_node(data->ni); 16759172SFei.Feng@Sun.COM data->ni = NULL; 16769172SFei.Feng@Sun.COM } 16779172SFei.Feng@Sun.COM SLIST_INSERT_HEAD(&sc->data_pool, data, next); 16789172SFei.Feng@Sun.COM ring->data[ring->next] = NULL; 16799172SFei.Feng@Sun.COM } 16809172SFei.Feng@Sun.COM 16819172SFei.Feng@Sun.COM txd->sdl0 &= ~LE_16(RT2860_TX_DDONE); 16829172SFei.Feng@Sun.COM 16839172SFei.Feng@Sun.COM (void) ddi_dma_sync(ring->txdesc_dma.dma_hdl, 16849172SFei.Feng@Sun.COM ring->next * sizeof (struct rt2860_txd), 16859172SFei.Feng@Sun.COM sizeof (struct rt2860_txd), 16869172SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV); 16879172SFei.Feng@Sun.COM 16889172SFei.Feng@Sun.COM ring->queued--; 16899172SFei.Feng@Sun.COM ring->next = (ring->next + 1) % RT2860_TX_RING_COUNT; 16909172SFei.Feng@Sun.COM 16919172SFei.Feng@Sun.COM if (sc->sc_need_sched && 16929172SFei.Feng@Sun.COM (ring->queued < RT2860_TX_RING_COUNT)) { 16939172SFei.Feng@Sun.COM sc->sc_need_sched = 0; 16949172SFei.Feng@Sun.COM mac_tx_update(ic->ic_mach); 16959172SFei.Feng@Sun.COM } 16969172SFei.Feng@Sun.COM } 16979172SFei.Feng@Sun.COM sc->sc_tx_timer = 0; 16989172SFei.Feng@Sun.COM mutex_exit(&sc->sc_txlock); 16999172SFei.Feng@Sun.COM } 1700*10108SFei.Feng@Sun.COM 17019172SFei.Feng@Sun.COM static void 17029172SFei.Feng@Sun.COM rt2860_rx_intr(struct rt2860_softc *sc) 17039172SFei.Feng@Sun.COM { 17049172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 17059172SFei.Feng@Sun.COM struct ieee80211_node *ni; 17069172SFei.Feng@Sun.COM struct ieee80211_frame *wh; 17079172SFei.Feng@Sun.COM int pktlen; 17089172SFei.Feng@Sun.COM uint8_t ant, rssi, *rxbuf; 17099172SFei.Feng@Sun.COM mblk_t *mp0; 17109172SFei.Feng@Sun.COM 17119172SFei.Feng@Sun.COM mutex_enter(&sc->sc_rxlock); 17129172SFei.Feng@Sun.COM for (;;) { 17139172SFei.Feng@Sun.COM struct rt2860_rx_data *data = &sc->rxq.data[sc->rxq.cur]; 17149172SFei.Feng@Sun.COM struct rt2860_rxd *rxd = &sc->rxq.rxd[sc->rxq.cur]; 17159172SFei.Feng@Sun.COM struct rt2860_rxwi *rxwi; 17169172SFei.Feng@Sun.COM 17179172SFei.Feng@Sun.COM (void) ddi_dma_sync(sc->rxq.rxdesc_dma.dma_hdl, 17189172SFei.Feng@Sun.COM sc->rxq.cur * sizeof (struct rt2860_rxd), 17199172SFei.Feng@Sun.COM sizeof (struct rt2860_rxd), 17209172SFei.Feng@Sun.COM DDI_DMA_SYNC_FORKERNEL); 17219172SFei.Feng@Sun.COM 17229172SFei.Feng@Sun.COM if (!(rxd->sdl0 & LE_16(RT2860_RX_DDONE))) { 17239172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_RX, "rwn: rt2860_rx_intr(): " 17249172SFei.Feng@Sun.COM "rx done!\n"); 17259172SFei.Feng@Sun.COM break; 17269172SFei.Feng@Sun.COM } 17279172SFei.Feng@Sun.COM 17289172SFei.Feng@Sun.COM if (rxd->flags & 17299172SFei.Feng@Sun.COM LE_32(RT2860_RX_CRCERR | RT2860_RX_ICVERR)) { 17309172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_RX, "rwn: rt2860_rx_intr(): " 17319172SFei.Feng@Sun.COM "rx crc error & rx icv error!\n"); 17329172SFei.Feng@Sun.COM sc->sc_rx_err++; 17339172SFei.Feng@Sun.COM goto skip; 17349172SFei.Feng@Sun.COM } 17359172SFei.Feng@Sun.COM 17369172SFei.Feng@Sun.COM if (rxd->flags & LE_32(RT2860_RX_MICERR)) { 17379172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_RX, "rwn: rt2860_rx_intr(): " 17389172SFei.Feng@Sun.COM "rx mic error!\n"); 17399172SFei.Feng@Sun.COM sc->sc_rx_err++; 17409172SFei.Feng@Sun.COM goto skip; 17419172SFei.Feng@Sun.COM } 17429172SFei.Feng@Sun.COM 17439172SFei.Feng@Sun.COM (void) ddi_dma_sync(data->rxbuf_dma.dma_hdl, 17449172SFei.Feng@Sun.COM data->rxbuf_dma.offset, 17459172SFei.Feng@Sun.COM data->rxbuf_dma.alength, 17469172SFei.Feng@Sun.COM DDI_DMA_SYNC_FORCPU); 17479172SFei.Feng@Sun.COM 17489172SFei.Feng@Sun.COM rxbuf = (uint8_t *)data->rxbuf_dma.mem_va; 17499172SFei.Feng@Sun.COM rxd->sdp0 = LE_32(data->rxbuf_dma.cookie.dmac_address); 17509172SFei.Feng@Sun.COM rxwi = (struct rt2860_rxwi *)rxbuf; 17519172SFei.Feng@Sun.COM rxbuf = (uint8_t *)(rxwi + 1); 17529172SFei.Feng@Sun.COM pktlen = LE_16(rxwi->len) & 0xfff; 17539172SFei.Feng@Sun.COM 17549172SFei.Feng@Sun.COM mp0 = allocb(sc->sc_dmabuf_size, BPRI_MED); 17559172SFei.Feng@Sun.COM if (mp0 == NULL) { 17569172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_RX, "rwn: rt2860_rx_intr():" 17579172SFei.Feng@Sun.COM "alloc mblk error\n"); 17589172SFei.Feng@Sun.COM sc->sc_rx_nobuf++; 17599172SFei.Feng@Sun.COM goto skip; 17609172SFei.Feng@Sun.COM } 17619172SFei.Feng@Sun.COM bcopy(rxbuf, mp0->b_rptr, pktlen); 17629172SFei.Feng@Sun.COM mp0->b_wptr += pktlen; 17639172SFei.Feng@Sun.COM 17649172SFei.Feng@Sun.COM wh = (struct ieee80211_frame *)mp0->b_rptr; 17659172SFei.Feng@Sun.COM 17669172SFei.Feng@Sun.COM /* HW may insert 2 padding bytes after 802.11 header */ 17679172SFei.Feng@Sun.COM if (rxd->flags & LE_32(RT2860_RX_L2PAD)) { 17689172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_RX, "rwn: rt2860_rx_intr():" 17699172SFei.Feng@Sun.COM "2 padding bytes after 80211 header!\n"); 17709172SFei.Feng@Sun.COM } 17719172SFei.Feng@Sun.COM 17729172SFei.Feng@Sun.COM ant = rt2860_maxrssi_chain(sc, rxwi); 1773*10108SFei.Feng@Sun.COM rssi = RT2860_RSSI_OFFSET - rxwi->rssi[ant]; 17749172SFei.Feng@Sun.COM /* grab a reference to the source node */ 17759172SFei.Feng@Sun.COM ni = ieee80211_find_rxnode(ic, wh); 17769172SFei.Feng@Sun.COM 17779172SFei.Feng@Sun.COM ieee80211_input(ic, mp0, ni, rssi, 0); 17789172SFei.Feng@Sun.COM 17799172SFei.Feng@Sun.COM /* node is no longer needed */ 17809172SFei.Feng@Sun.COM ieee80211_free_node(ni); 17819172SFei.Feng@Sun.COM skip: 17829172SFei.Feng@Sun.COM rxd->sdl0 &= ~LE_16(RT2860_RX_DDONE); 17839172SFei.Feng@Sun.COM 17849172SFei.Feng@Sun.COM (void) ddi_dma_sync(sc->rxq.rxdesc_dma.dma_hdl, 17859172SFei.Feng@Sun.COM sc->rxq.cur * sizeof (struct rt2860_rxd), 17869172SFei.Feng@Sun.COM sizeof (struct rt2860_rxd), 17879172SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV); 17889172SFei.Feng@Sun.COM 17899172SFei.Feng@Sun.COM sc->rxq.cur = (sc->rxq.cur + 1) % RT2860_RX_RING_COUNT; 17909172SFei.Feng@Sun.COM } 17919172SFei.Feng@Sun.COM mutex_exit(&sc->sc_rxlock); 17929172SFei.Feng@Sun.COM 17939172SFei.Feng@Sun.COM /* tell HW what we have processed */ 17949172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_RX_CALC_IDX, 17959172SFei.Feng@Sun.COM (sc->rxq.cur - 1) % RT2860_RX_RING_COUNT); 17969172SFei.Feng@Sun.COM } 17979172SFei.Feng@Sun.COM 17989172SFei.Feng@Sun.COM static uint_t 17999172SFei.Feng@Sun.COM rt2860_softintr(caddr_t data) 18009172SFei.Feng@Sun.COM { 18019172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)data; 18029172SFei.Feng@Sun.COM 18039172SFei.Feng@Sun.COM /* 18049172SFei.Feng@Sun.COM * Check if the soft interrupt is triggered by another 18059172SFei.Feng@Sun.COM * driver at the same level. 18069172SFei.Feng@Sun.COM */ 18079172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 18089172SFei.Feng@Sun.COM if (sc->sc_rx_pend) { 18099172SFei.Feng@Sun.COM sc->sc_rx_pend = 0; 18109172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 18119172SFei.Feng@Sun.COM rt2860_rx_intr(sc); 18129172SFei.Feng@Sun.COM return (DDI_INTR_CLAIMED); 18139172SFei.Feng@Sun.COM } 18149172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 18159172SFei.Feng@Sun.COM 18169172SFei.Feng@Sun.COM return (DDI_INTR_UNCLAIMED); 18179172SFei.Feng@Sun.COM } 18189172SFei.Feng@Sun.COM 18199172SFei.Feng@Sun.COM static uint_t 18209172SFei.Feng@Sun.COM rt2860_intr(caddr_t arg) 18219172SFei.Feng@Sun.COM { 18229172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 18239172SFei.Feng@Sun.COM uint32_t r; 18249172SFei.Feng@Sun.COM 18259172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 18269172SFei.Feng@Sun.COM 18279172SFei.Feng@Sun.COM if ((!RT2860_IS_RUNNING(sc)) || RT2860_IS_SUSPEND(sc)) { 18289172SFei.Feng@Sun.COM /* 18299172SFei.Feng@Sun.COM * The hardware is not ready/present, don't touch anything. 18309172SFei.Feng@Sun.COM * Note this can happen early on if the IRQ is shared. 18319172SFei.Feng@Sun.COM */ 18329172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 18339172SFei.Feng@Sun.COM return (DDI_INTR_UNCLAIMED); 18349172SFei.Feng@Sun.COM } 18359172SFei.Feng@Sun.COM 18369172SFei.Feng@Sun.COM r = RT2860_READ(sc, RT2860_INT_STATUS); 18379172SFei.Feng@Sun.COM if (r == 0xffffffff) { 18389172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 18399172SFei.Feng@Sun.COM return (DDI_INTR_UNCLAIMED); 18409172SFei.Feng@Sun.COM } 18419172SFei.Feng@Sun.COM if (r == 0) { 18429172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 18439172SFei.Feng@Sun.COM return (DDI_INTR_UNCLAIMED); 18449172SFei.Feng@Sun.COM } 18459172SFei.Feng@Sun.COM 18469172SFei.Feng@Sun.COM /* acknowledge interrupts */ 18479172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_INT_STATUS, r); 18489172SFei.Feng@Sun.COM 18499172SFei.Feng@Sun.COM if (r & RT2860_TX_COHERENT) 18509172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr()" 18519172SFei.Feng@Sun.COM "RT2860_TX_COHERENT\n"); 18529172SFei.Feng@Sun.COM 18539172SFei.Feng@Sun.COM if (r & RT2860_RX_COHERENT) 18549172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr()" 18559172SFei.Feng@Sun.COM "RT2860_RX_COHERENT\n"); 18569172SFei.Feng@Sun.COM 18579172SFei.Feng@Sun.COM if (r & RT2860_MAC_INT_2) { 18589172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): " 18599172SFei.Feng@Sun.COM "RT2860_MAC_INT_2\n"); 18609172SFei.Feng@Sun.COM rt2860_drain_stats_fifo(sc); 18619172SFei.Feng@Sun.COM } 18629172SFei.Feng@Sun.COM 18639172SFei.Feng@Sun.COM if (r & RT2860_TX_DONE_INT5) { 18649172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): " 18659172SFei.Feng@Sun.COM "RT2860_TX_DONE_INT5\n"); 18669172SFei.Feng@Sun.COM rt2860_tx_intr(sc, 5); 18679172SFei.Feng@Sun.COM } 18689172SFei.Feng@Sun.COM 18699172SFei.Feng@Sun.COM if (r & RT2860_RX_DONE_INT) { 18709172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr()" 18719172SFei.Feng@Sun.COM "RT2860_RX_INT\n"); 18729172SFei.Feng@Sun.COM sc->sc_rx_pend = 1; 18739172SFei.Feng@Sun.COM ddi_trigger_softintr(sc->sc_softintr_hdl); 18749172SFei.Feng@Sun.COM } 18759172SFei.Feng@Sun.COM 18769172SFei.Feng@Sun.COM if (r & RT2860_TX_DONE_INT4) { 18779172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): " 18789172SFei.Feng@Sun.COM "RT2860_TX_DONE_INT4\n"); 18799172SFei.Feng@Sun.COM rt2860_tx_intr(sc, 4); 18809172SFei.Feng@Sun.COM } 18819172SFei.Feng@Sun.COM 18829172SFei.Feng@Sun.COM if (r & RT2860_TX_DONE_INT3) { 18839172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): " 18849172SFei.Feng@Sun.COM "RT2860_TX_DONE_INT3\n"); 18859172SFei.Feng@Sun.COM rt2860_tx_intr(sc, 3); 18869172SFei.Feng@Sun.COM } 18879172SFei.Feng@Sun.COM 18889172SFei.Feng@Sun.COM if (r & RT2860_TX_DONE_INT2) { 18899172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): " 18909172SFei.Feng@Sun.COM "RT2860_TX_DONE_INT2\n"); 18919172SFei.Feng@Sun.COM rt2860_tx_intr(sc, 2); 18929172SFei.Feng@Sun.COM } 18939172SFei.Feng@Sun.COM 18949172SFei.Feng@Sun.COM if (r & RT2860_TX_DONE_INT1) { 18959172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): " 18969172SFei.Feng@Sun.COM "RT2860_TX_DONE_INT1\n"); 18979172SFei.Feng@Sun.COM rt2860_tx_intr(sc, 1); 18989172SFei.Feng@Sun.COM } 18999172SFei.Feng@Sun.COM 19009172SFei.Feng@Sun.COM if (r & RT2860_TX_DONE_INT0) { 19019172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): " 19029172SFei.Feng@Sun.COM "RT2860_TX_DONE_INT0\n"); 19039172SFei.Feng@Sun.COM rt2860_tx_intr(sc, 0); 19049172SFei.Feng@Sun.COM } 19059172SFei.Feng@Sun.COM 19069172SFei.Feng@Sun.COM if (r & RT2860_MAC_INT_0) { 19079172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): " 19089172SFei.Feng@Sun.COM "RT2860_MAC_INT_0\n"); 19099172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 19109172SFei.Feng@Sun.COM /* check if protection mode has changed */ 19119172SFei.Feng@Sun.COM if ((sc->sc_ic_flags ^ ic->ic_flags) & IEEE80211_F_USEPROT) { 19129172SFei.Feng@Sun.COM rt2860_updateprot(ic); 19139172SFei.Feng@Sun.COM sc->sc_ic_flags = ic->ic_flags; 19149172SFei.Feng@Sun.COM } 19159172SFei.Feng@Sun.COM } 19169172SFei.Feng@Sun.COM 19179172SFei.Feng@Sun.COM if (r & RT2860_MAC_INT_3) 19189172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_INTR, "rwn: rt2860_intr(): " 19199172SFei.Feng@Sun.COM "RT2860_MAC_INT_3\n"); 19209172SFei.Feng@Sun.COM 19219172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 19229172SFei.Feng@Sun.COM 19239172SFei.Feng@Sun.COM return (DDI_INTR_CLAIMED); 19249172SFei.Feng@Sun.COM } 19259172SFei.Feng@Sun.COM 19269172SFei.Feng@Sun.COM static void 19279172SFei.Feng@Sun.COM rt2860_set_region_4(struct rt2860_softc *sc, 19289172SFei.Feng@Sun.COM uint32_t addr, uint32_t data, int size) 19299172SFei.Feng@Sun.COM { 19309172SFei.Feng@Sun.COM for (; size > 0; size--, data++, addr += 4) 19319172SFei.Feng@Sun.COM ddi_put32((sc)->sc_io_handle, 19329172SFei.Feng@Sun.COM (uint32_t *)((uintptr_t)(sc)->sc_io_base + addr), data); 19339172SFei.Feng@Sun.COM } 19349172SFei.Feng@Sun.COM 19359172SFei.Feng@Sun.COM static int 19369172SFei.Feng@Sun.COM rt2860_load_microcode(struct rt2860_softc *sc) 19379172SFei.Feng@Sun.COM { 19389172SFei.Feng@Sun.COM int ntries; 19399172SFei.Feng@Sun.COM size_t size; 19409172SFei.Feng@Sun.COM uint8_t *ucode, *fptr; 19419172SFei.Feng@Sun.COM uint32_t off, i; 19429172SFei.Feng@Sun.COM 19439172SFei.Feng@Sun.COM ucode = rt2860_fw_bin; 19449172SFei.Feng@Sun.COM size = sizeof (rt2860_fw_bin); 19459172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_load_microcode(): " 19469172SFei.Feng@Sun.COM "The size of ucode is: %x\n", size); 19479172SFei.Feng@Sun.COM 19489172SFei.Feng@Sun.COM /* set "host program ram write selection" bit */ 19499172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_SYS_CTRL, RT2860_HST_PM_SEL); 19509172SFei.Feng@Sun.COM /* write microcode image */ 19519172SFei.Feng@Sun.COM fptr = ucode; 19529172SFei.Feng@Sun.COM off = RT2860_FW_BASE; 19539172SFei.Feng@Sun.COM for (i = 0; i < size; i++) { 19549172SFei.Feng@Sun.COM rt2860_mem_write1(sc, off++, *fptr++); 19559172SFei.Feng@Sun.COM } 19569172SFei.Feng@Sun.COM /* kick microcontroller unit */ 19579172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_SYS_CTRL, 0); 19589172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_SYS_CTRL, RT2860_MCU_RESET); 19599172SFei.Feng@Sun.COM 19609172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_H2M_BBPAGENT, 0); 19619172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_H2M_MAILBOX, 0); 19629172SFei.Feng@Sun.COM 19639172SFei.Feng@Sun.COM /* wait until microcontroller is ready */ 19649172SFei.Feng@Sun.COM for (ntries = 0; ntries < 1000; ntries++) { 19659172SFei.Feng@Sun.COM if (RT2860_READ(sc, RT2860_SYS_CTRL) & RT2860_MCU_READY) 19669172SFei.Feng@Sun.COM break; 19679172SFei.Feng@Sun.COM DELAY(1000); 19689172SFei.Feng@Sun.COM } 19699172SFei.Feng@Sun.COM if (ntries == 1000) { 19709172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_load_microcode(): " 19719172SFei.Feng@Sun.COM "timeout waiting for MCU to initialie\n"); 19729172SFei.Feng@Sun.COM return (ETIMEDOUT); 19739172SFei.Feng@Sun.COM } 19749172SFei.Feng@Sun.COM 19759172SFei.Feng@Sun.COM return (0); 19769172SFei.Feng@Sun.COM } 19779172SFei.Feng@Sun.COM 19789172SFei.Feng@Sun.COM static void 19799172SFei.Feng@Sun.COM rt2860_set_macaddr(struct rt2860_softc *sc, const uint8_t *addr) 19809172SFei.Feng@Sun.COM { 19819172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_ADDR_DW0, 19829172SFei.Feng@Sun.COM addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24); 19839172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_ADDR_DW1, 19849172SFei.Feng@Sun.COM addr[4] | addr[5] << 8); 19859172SFei.Feng@Sun.COM } 19869172SFei.Feng@Sun.COM 19879172SFei.Feng@Sun.COM /* 19889172SFei.Feng@Sun.COM * Send a command to the 8051 microcontroller unit. 19899172SFei.Feng@Sun.COM */ 19909172SFei.Feng@Sun.COM static int 19919172SFei.Feng@Sun.COM rt2860_mcu_cmd(struct rt2860_softc *sc, uint8_t cmd, uint16_t arg) 19929172SFei.Feng@Sun.COM { 19939172SFei.Feng@Sun.COM int ntries; 19949172SFei.Feng@Sun.COM 19959172SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) { 19969172SFei.Feng@Sun.COM if (!(RT2860_READ(sc, RT2860_H2M_MAILBOX) & RT2860_H2M_BUSY)) 19979172SFei.Feng@Sun.COM break; 19989172SFei.Feng@Sun.COM DELAY(2); 19999172SFei.Feng@Sun.COM } 20009172SFei.Feng@Sun.COM if (ntries == 100) 20019172SFei.Feng@Sun.COM return (EIO); 20029172SFei.Feng@Sun.COM 20039172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_H2M_MAILBOX, 20049172SFei.Feng@Sun.COM RT2860_H2M_BUSY | RT2860_TOKEN_NO_INTR << 16 | arg); 20059172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_HOST_CMD, cmd); 20069172SFei.Feng@Sun.COM 20079172SFei.Feng@Sun.COM return (RT2860_SUCCESS); 20089172SFei.Feng@Sun.COM } 20099172SFei.Feng@Sun.COM 20109172SFei.Feng@Sun.COM /* 20119172SFei.Feng@Sun.COM * Reading and writing from/to the BBP is different from RT2560 and RT2661. 20129172SFei.Feng@Sun.COM * We access the BBP through the 8051 microcontroller unit which means that 20139172SFei.Feng@Sun.COM * the microcode must be loaded first. 20149172SFei.Feng@Sun.COM */ 20159172SFei.Feng@Sun.COM static uint8_t 20169172SFei.Feng@Sun.COM rt2860_mcu_bbp_read(struct rt2860_softc *sc, uint8_t reg) 20179172SFei.Feng@Sun.COM { 20189172SFei.Feng@Sun.COM uint32_t val; 20199172SFei.Feng@Sun.COM int ntries; 20209172SFei.Feng@Sun.COM 20219172SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) { 20229172SFei.Feng@Sun.COM if (!(RT2860_READ(sc, 20239172SFei.Feng@Sun.COM RT2860_H2M_BBPAGENT) & RT2860_BBP_CSR_KICK)) 20249172SFei.Feng@Sun.COM break; 20259172SFei.Feng@Sun.COM DELAY(1); 20269172SFei.Feng@Sun.COM } 20279172SFei.Feng@Sun.COM if (ntries == 100) { 20289172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_mcu_bbp_read():" 20299172SFei.Feng@Sun.COM "could not read from BBP through MCU\n"); 20309172SFei.Feng@Sun.COM return (0); 20319172SFei.Feng@Sun.COM } 20329172SFei.Feng@Sun.COM 20339172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_H2M_BBPAGENT, RT2860_BBP_RW_PARALLEL | 20349172SFei.Feng@Sun.COM RT2860_BBP_CSR_KICK | RT2860_BBP_CSR_READ | reg << 8); 20359172SFei.Feng@Sun.COM 20369172SFei.Feng@Sun.COM (void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_BBP, 0); 20379172SFei.Feng@Sun.COM DELAY(1000); 20389172SFei.Feng@Sun.COM 20399172SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) { 20409172SFei.Feng@Sun.COM val = RT2860_READ(sc, RT2860_H2M_BBPAGENT); 20419172SFei.Feng@Sun.COM if (!(val & RT2860_BBP_CSR_KICK)) 20429172SFei.Feng@Sun.COM return (val & 0xff); 20439172SFei.Feng@Sun.COM DELAY(1); 20449172SFei.Feng@Sun.COM } 20459172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_mcu_bbp_read():" 20469172SFei.Feng@Sun.COM "could not read from BBP through MCU\n"); 20479172SFei.Feng@Sun.COM 20489172SFei.Feng@Sun.COM return (0); 20499172SFei.Feng@Sun.COM } 20509172SFei.Feng@Sun.COM 20519172SFei.Feng@Sun.COM static void 20529172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(struct rt2860_softc *sc, uint8_t reg, uint8_t val) 20539172SFei.Feng@Sun.COM { 20549172SFei.Feng@Sun.COM int ntries; 20559172SFei.Feng@Sun.COM 20569172SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) { 20579172SFei.Feng@Sun.COM if (!(RT2860_READ(sc, 20589172SFei.Feng@Sun.COM RT2860_H2M_BBPAGENT) & RT2860_BBP_CSR_KICK)) 20599172SFei.Feng@Sun.COM break; 20609172SFei.Feng@Sun.COM DELAY(1); 20619172SFei.Feng@Sun.COM } 20629172SFei.Feng@Sun.COM if (ntries == 100) { 20639172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_mcu_bbp_write():" 20649172SFei.Feng@Sun.COM "could not write to BBP through MCU\n"); 20659172SFei.Feng@Sun.COM return; 20669172SFei.Feng@Sun.COM } 20679172SFei.Feng@Sun.COM 20689172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_H2M_BBPAGENT, RT2860_BBP_RW_PARALLEL | 20699172SFei.Feng@Sun.COM RT2860_BBP_CSR_KICK | reg << 8 | val); 20709172SFei.Feng@Sun.COM 20719172SFei.Feng@Sun.COM (void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_BBP, 0); 20729172SFei.Feng@Sun.COM DELAY(1000); 20739172SFei.Feng@Sun.COM } 20749172SFei.Feng@Sun.COM 20759172SFei.Feng@Sun.COM static int 20769172SFei.Feng@Sun.COM rt2860_bbp_init(struct rt2860_softc *sc) 20779172SFei.Feng@Sun.COM { 20789172SFei.Feng@Sun.COM int i, ntries; 20799172SFei.Feng@Sun.COM 20809172SFei.Feng@Sun.COM /* wait for BBP to wake up */ 20819172SFei.Feng@Sun.COM for (ntries = 0; ntries < 20; ntries++) { 20829172SFei.Feng@Sun.COM uint8_t bbp0 = rt2860_mcu_bbp_read(sc, 0); 20839172SFei.Feng@Sun.COM if (bbp0 != 0 && bbp0 != 0xff) 20849172SFei.Feng@Sun.COM break; 20859172SFei.Feng@Sun.COM } 20869172SFei.Feng@Sun.COM if (ntries == 20) { 20879172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_bbp_init():" 20889172SFei.Feng@Sun.COM "timeout waiting for BBP to wake up\n"); 20899172SFei.Feng@Sun.COM return (ETIMEDOUT); 20909172SFei.Feng@Sun.COM } 20919172SFei.Feng@Sun.COM 20929172SFei.Feng@Sun.COM /* initialize BBP registers to default values */ 20939172SFei.Feng@Sun.COM for (i = 0; i < 12; i++) { 20949172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, rt2860_def_bbp[i].reg, 20959172SFei.Feng@Sun.COM rt2860_def_bbp[i].val); 20969172SFei.Feng@Sun.COM } 20979172SFei.Feng@Sun.COM 20989172SFei.Feng@Sun.COM /* fix BBP69 and BBP73 for RT2860C */ 20999172SFei.Feng@Sun.COM if (sc->mac_rev == 0x28600100) { 21009172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, 69, 0x16); 21019172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, 73, 0x12); 21029172SFei.Feng@Sun.COM } 21039172SFei.Feng@Sun.COM 21049172SFei.Feng@Sun.COM return (0); 21059172SFei.Feng@Sun.COM } 21069172SFei.Feng@Sun.COM 21079172SFei.Feng@Sun.COM static void 21089172SFei.Feng@Sun.COM rt2860_rf_write(struct rt2860_softc *sc, uint8_t reg, uint32_t val) 21099172SFei.Feng@Sun.COM { 21109172SFei.Feng@Sun.COM uint32_t tmp; 21119172SFei.Feng@Sun.COM int ntries; 21129172SFei.Feng@Sun.COM 21139172SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) { 21149172SFei.Feng@Sun.COM if (!(RT2860_READ(sc, RT2860_RF_CSR_CFG0) & RT2860_RF_REG_CTRL)) 21159172SFei.Feng@Sun.COM break; 21169172SFei.Feng@Sun.COM DELAY(1); 21179172SFei.Feng@Sun.COM } 21189172SFei.Feng@Sun.COM if (ntries == 100) { 21199172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_FW, "rwn: rwn_init()" 21209172SFei.Feng@Sun.COM "could not write to RF\n"); 21219172SFei.Feng@Sun.COM return; 21229172SFei.Feng@Sun.COM } 21239172SFei.Feng@Sun.COM 21249172SFei.Feng@Sun.COM /* RF registers are 24-bit on the RT2860 */ 21259172SFei.Feng@Sun.COM tmp = RT2860_RF_REG_CTRL | 24 << RT2860_RF_REG_WIDTH_SHIFT | 21269172SFei.Feng@Sun.COM (val & 0x3fffff) << 2 | (reg & 3); 21279172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_RF_CSR_CFG0, tmp); 21289172SFei.Feng@Sun.COM } 21299172SFei.Feng@Sun.COM 21309172SFei.Feng@Sun.COM static void 21319172SFei.Feng@Sun.COM rt2860_select_chan_group(struct rt2860_softc *sc, int group) 21329172SFei.Feng@Sun.COM { 21339172SFei.Feng@Sun.COM uint32_t tmp; 21349172SFei.Feng@Sun.COM 21359172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, 62, 0x37 - sc->lna[group]); 21369172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, 63, 0x37 - sc->lna[group]); 21379172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, 64, 0x37 - sc->lna[group]); 21389172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, 82, (group == 0) ? 0x62 : 0xf2); 21399172SFei.Feng@Sun.COM 21409172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_TX_BAND_CFG); 21419172SFei.Feng@Sun.COM tmp &= ~(RT2860_5G_BAND_SEL_N | RT2860_5G_BAND_SEL_P); 21429172SFei.Feng@Sun.COM tmp |= (group == 0) ? RT2860_5G_BAND_SEL_N : RT2860_5G_BAND_SEL_P; 21439172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_TX_BAND_CFG, tmp); 21449172SFei.Feng@Sun.COM 21459172SFei.Feng@Sun.COM /* enable appropriate Power Amplifiers and Low Noise Amplifiers */ 21469172SFei.Feng@Sun.COM tmp = RT2860_RFTR_EN | RT2860_TRSW_EN; 21479172SFei.Feng@Sun.COM if (group == 0) { /* 2GHz */ 21489172SFei.Feng@Sun.COM tmp |= RT2860_PA_PE_G0_EN | RT2860_LNA_PE_G0_EN; 21499172SFei.Feng@Sun.COM if (sc->ntxchains > 1) 21509172SFei.Feng@Sun.COM tmp |= RT2860_PA_PE_G1_EN; 21519172SFei.Feng@Sun.COM if (sc->nrxchains > 1) 21529172SFei.Feng@Sun.COM tmp |= RT2860_LNA_PE_G1_EN; 21539172SFei.Feng@Sun.COM } else { /* 5GHz */ 21549172SFei.Feng@Sun.COM tmp |= RT2860_PA_PE_A0_EN | RT2860_LNA_PE_A0_EN; 21559172SFei.Feng@Sun.COM if (sc->ntxchains > 1) 21569172SFei.Feng@Sun.COM tmp |= RT2860_PA_PE_A1_EN; 21579172SFei.Feng@Sun.COM if (sc->nrxchains > 1) 21589172SFei.Feng@Sun.COM tmp |= RT2860_LNA_PE_A1_EN; 21599172SFei.Feng@Sun.COM } 21609172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_TX_PIN_CFG, tmp); 21619172SFei.Feng@Sun.COM 21629172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, 66, 0x2e + sc->lna[group]); 21639172SFei.Feng@Sun.COM } 21649172SFei.Feng@Sun.COM static void 21659172SFei.Feng@Sun.COM rt2860_set_chan(struct rt2860_softc *sc, struct ieee80211_channel *c) 21669172SFei.Feng@Sun.COM { 21679172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 21689172SFei.Feng@Sun.COM const struct rfprog *rfprog = rt2860_rf2850; 21699172SFei.Feng@Sun.COM uint_t i, chan, group; 21709172SFei.Feng@Sun.COM uint8_t txpow1, txpow2; 21719172SFei.Feng@Sun.COM uint32_t r2, r3, r4; 21729172SFei.Feng@Sun.COM 21739172SFei.Feng@Sun.COM chan = ieee80211_chan2ieee(ic, c); 21749172SFei.Feng@Sun.COM if (chan == 0 || chan == IEEE80211_CHAN_ANY) { 21759172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_FW, "Unkonwn channel!\n"); 21769172SFei.Feng@Sun.COM return; 21779172SFei.Feng@Sun.COM } 21789172SFei.Feng@Sun.COM 21799172SFei.Feng@Sun.COM /* find the settings for this channel (we know it exists) */ 21809172SFei.Feng@Sun.COM for (i = 0; rfprog[i].chan != chan; ) 21819172SFei.Feng@Sun.COM i++; 21829172SFei.Feng@Sun.COM 21839172SFei.Feng@Sun.COM r2 = rfprog[i].r2; 21849172SFei.Feng@Sun.COM if (sc->ntxchains == 1) 21859172SFei.Feng@Sun.COM r2 |= 1 << 12; /* 1T: disable Tx chain 2 */ 21869172SFei.Feng@Sun.COM if (sc->nrxchains == 1) 21879172SFei.Feng@Sun.COM r2 |= 1 << 15 | 1 << 4; /* 1R: disable Rx chains 2 & 3 */ 21889172SFei.Feng@Sun.COM else if (sc->nrxchains == 2) 21899172SFei.Feng@Sun.COM r2 |= 1 << 4; /* 2R: disable Rx chain 3 */ 21909172SFei.Feng@Sun.COM 21919172SFei.Feng@Sun.COM /* use Tx power values from EEPROM */ 21929172SFei.Feng@Sun.COM txpow1 = sc->txpow1[i]; 21939172SFei.Feng@Sun.COM txpow2 = sc->txpow2[i]; 21949172SFei.Feng@Sun.COM if (IEEE80211_IS_CHAN_5GHZ(c)) { 21959172SFei.Feng@Sun.COM txpow1 = txpow1 << 1 | 1; 21969172SFei.Feng@Sun.COM txpow2 = txpow2 << 1 | 1; 21979172SFei.Feng@Sun.COM } 21989172SFei.Feng@Sun.COM r3 = rfprog[i].r3 | txpow1 << 7; 21999172SFei.Feng@Sun.COM r4 = rfprog[i].r4 | sc->freq << 13 | txpow2 << 4; 22009172SFei.Feng@Sun.COM 22019172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF1, rfprog[i].r1); 22029172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF2, r2); 22039172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF3, r3); 22049172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF4, r4); 22059172SFei.Feng@Sun.COM 22069172SFei.Feng@Sun.COM DELAY(200); 22079172SFei.Feng@Sun.COM 22089172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF1, rfprog[i].r1); 22099172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF2, r2); 22109172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF3, r3 | 1); 22119172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF4, r4); 22129172SFei.Feng@Sun.COM 22139172SFei.Feng@Sun.COM DELAY(200); 22149172SFei.Feng@Sun.COM 22159172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF1, rfprog[i].r1); 22169172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF2, r2); 22179172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF3, r3); 22189172SFei.Feng@Sun.COM rt2860_rf_write(sc, RAL_RF4, r4); 22199172SFei.Feng@Sun.COM 22209172SFei.Feng@Sun.COM /* 802.11a uses a 16 microseconds short interframe space */ 22219172SFei.Feng@Sun.COM sc->sifs = IEEE80211_IS_CHAN_5GHZ(c) ? 16 : 10; 22229172SFei.Feng@Sun.COM 22239172SFei.Feng@Sun.COM /* determine channel group */ 22249172SFei.Feng@Sun.COM if (chan <= 14) 22259172SFei.Feng@Sun.COM group = 0; 22269172SFei.Feng@Sun.COM else if (chan <= 64) 22279172SFei.Feng@Sun.COM group = 1; 22289172SFei.Feng@Sun.COM else if (chan <= 128) 22299172SFei.Feng@Sun.COM group = 2; 22309172SFei.Feng@Sun.COM else 22319172SFei.Feng@Sun.COM group = 3; 22329172SFei.Feng@Sun.COM 22339172SFei.Feng@Sun.COM /* XXX necessary only when group has changed! */ 22349172SFei.Feng@Sun.COM rt2860_select_chan_group(sc, group); 22359172SFei.Feng@Sun.COM 22369172SFei.Feng@Sun.COM DELAY(1000); 22379172SFei.Feng@Sun.COM } 22389172SFei.Feng@Sun.COM 22399172SFei.Feng@Sun.COM static void 22409172SFei.Feng@Sun.COM rt2860_updateprot(struct ieee80211com *ic) 22419172SFei.Feng@Sun.COM { 22429172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)ic; 22439172SFei.Feng@Sun.COM uint32_t tmp; 22449172SFei.Feng@Sun.COM 22459172SFei.Feng@Sun.COM tmp = RT2860_RTSTH_EN | RT2860_PROT_NAV_SHORT | RT2860_TXOP_ALLOW_ALL; 22469172SFei.Feng@Sun.COM /* setup protection frame rate (MCS code) */ 22479172SFei.Feng@Sun.COM tmp |= (ic->ic_curmode == IEEE80211_MODE_11A) ? 0 : 3; 22489172SFei.Feng@Sun.COM 22499172SFei.Feng@Sun.COM /* CCK frames don't require protection */ 22509172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_CCK_PROT_CFG, tmp); 22519172SFei.Feng@Sun.COM 22529172SFei.Feng@Sun.COM if (ic->ic_flags & IEEE80211_F_USEPROT) { 22539172SFei.Feng@Sun.COM if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) 22549172SFei.Feng@Sun.COM tmp |= RT2860_PROT_CTRL_RTS_CTS; 22559172SFei.Feng@Sun.COM else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) 22569172SFei.Feng@Sun.COM tmp |= RT2860_PROT_CTRL_CTS; 22579172SFei.Feng@Sun.COM } 22589172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_OFDM_PROT_CFG, tmp); 22599172SFei.Feng@Sun.COM } 22609172SFei.Feng@Sun.COM 22619172SFei.Feng@Sun.COM static void 22629172SFei.Feng@Sun.COM rt2860_set_leds(struct rt2860_softc *sc, uint16_t which) 22639172SFei.Feng@Sun.COM { 22649172SFei.Feng@Sun.COM (void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_LEDS, 22659172SFei.Feng@Sun.COM which | (sc->leds & 0x7f)); 22669172SFei.Feng@Sun.COM } 22679172SFei.Feng@Sun.COM 22689172SFei.Feng@Sun.COM static int 22699172SFei.Feng@Sun.COM rt2860_init(struct rt2860_softc *sc) 22709172SFei.Feng@Sun.COM { 22719172SFei.Feng@Sun.COM #define N(a) (sizeof (a) / sizeof ((a)[0])) 22729172SFei.Feng@Sun.COM struct ieee80211com *ic; 22739172SFei.Feng@Sun.COM int i, err, qid, ridx, ntries; 22749172SFei.Feng@Sun.COM uint8_t bbp1, bbp3; 22759172SFei.Feng@Sun.COM uint32_t tmp; 22769172SFei.Feng@Sun.COM 22779172SFei.Feng@Sun.COM ic = &sc->sc_ic; 22789172SFei.Feng@Sun.COM 22799172SFei.Feng@Sun.COM rt2860_stop(sc); 22809172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_WPDMA_GLO_CFG); 22819172SFei.Feng@Sun.COM tmp &= 0xff0; 22829172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp | RT2860_TX_WB_DDONE); 22839172SFei.Feng@Sun.COM 22849172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_WPDMA_RST_IDX, 0xffffffff); 22859172SFei.Feng@Sun.COM 22869172SFei.Feng@Sun.COM /* PBF hardware reset */ 22879172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_SYS_CTRL, 0xe1f); 22889172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_SYS_CTRL, 0xe00); 22899172SFei.Feng@Sun.COM 22909172SFei.Feng@Sun.COM if (!(sc->sc_flags & RT2860_FWLOADED)) { 22919172SFei.Feng@Sun.COM if ((err = rt2860_load_microcode(sc)) != 0) { 22929172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_init(): " 22939172SFei.Feng@Sun.COM "could not load 8051 microcode\n"); 22949172SFei.Feng@Sun.COM rt2860_stop(sc); 22959172SFei.Feng@Sun.COM return (err); 22969172SFei.Feng@Sun.COM } 22979172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 22989172SFei.Feng@Sun.COM sc->sc_flags |= RT2860_FWLOADED; 22999172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 23009172SFei.Feng@Sun.COM } 23019172SFei.Feng@Sun.COM 23029172SFei.Feng@Sun.COM rt2860_set_macaddr(sc, ic->ic_macaddr); 23039172SFei.Feng@Sun.COM 23049172SFei.Feng@Sun.COM /* init Tx power for all Tx rates (from EEPROM) */ 23059172SFei.Feng@Sun.COM for (ridx = 0; ridx < 5; ridx++) { 23069172SFei.Feng@Sun.COM if (sc->txpow20mhz[ridx] == 0xffffffff) 23079172SFei.Feng@Sun.COM continue; 23089172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_TX_PWR_CFG(ridx), sc->txpow20mhz[ridx]); 23099172SFei.Feng@Sun.COM } 23109172SFei.Feng@Sun.COM 23119172SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) { 23129172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_WPDMA_GLO_CFG); 23139172SFei.Feng@Sun.COM if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) 23149172SFei.Feng@Sun.COM break; 23159172SFei.Feng@Sun.COM DELAY(1000); 23169172SFei.Feng@Sun.COM } 23179172SFei.Feng@Sun.COM if (ntries == 100) { 23189172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, "rwn: rt2860_init():" 23199172SFei.Feng@Sun.COM "timeout waiting for DMA engine\n"); 23209172SFei.Feng@Sun.COM rt2860_stop(sc); 23219172SFei.Feng@Sun.COM return (ETIMEDOUT); 23229172SFei.Feng@Sun.COM } 23239172SFei.Feng@Sun.COM tmp &= 0xff0; 23249172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp | RT2860_TX_WB_DDONE); 23259172SFei.Feng@Sun.COM 23269172SFei.Feng@Sun.COM /* reset Rx ring and all 6 Tx rings */ 23279172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_WPDMA_RST_IDX, 0x1003f); 23289172SFei.Feng@Sun.COM 23299172SFei.Feng@Sun.COM /* PBF hardware reset */ 23309172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_SYS_CTRL, 0xe1f); 23319172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_SYS_CTRL, 0xe00); 23329172SFei.Feng@Sun.COM 23339172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL, 23349172SFei.Feng@Sun.COM RT2860_BBP_HRST | RT2860_MAC_SRST); 23359172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL, 0); 23369172SFei.Feng@Sun.COM 23379172SFei.Feng@Sun.COM for (i = 0; i < N(rt2860_def_mac); i++) 23389172SFei.Feng@Sun.COM RT2860_WRITE(sc, rt2860_def_mac[i].reg, rt2860_def_mac[i].val); 23399172SFei.Feng@Sun.COM 23409172SFei.Feng@Sun.COM /* wait while MAC is busy */ 23419172SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) { 23429172SFei.Feng@Sun.COM if (!(RT2860_READ(sc, RT2860_MAC_STATUS_REG) & 23439172SFei.Feng@Sun.COM (RT2860_RX_STATUS_BUSY | RT2860_TX_STATUS_BUSY))) 23449172SFei.Feng@Sun.COM break; 23459172SFei.Feng@Sun.COM DELAY(1000); 23469172SFei.Feng@Sun.COM } 23479172SFei.Feng@Sun.COM if (ntries == 100) { 23489172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_FW, "rwn: rt2860_init():" 23499172SFei.Feng@Sun.COM "timeout waiting for MAC\n"); 23509172SFei.Feng@Sun.COM rt2860_stop(sc); 23519172SFei.Feng@Sun.COM return (ETIMEDOUT); 23529172SFei.Feng@Sun.COM } 23539172SFei.Feng@Sun.COM 23549172SFei.Feng@Sun.COM /* clear Host to MCU mailbox */ 23559172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_H2M_BBPAGENT, 0); 23569172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_H2M_MAILBOX, 0); 23579172SFei.Feng@Sun.COM 23589172SFei.Feng@Sun.COM if ((err = rt2860_bbp_init(sc)) != 0) { 23599172SFei.Feng@Sun.COM rt2860_stop(sc); 23609172SFei.Feng@Sun.COM return (err); 23619172SFei.Feng@Sun.COM } 23629172SFei.Feng@Sun.COM 23639172SFei.Feng@Sun.COM /* init Tx rings (4 EDCAs + HCCA + Mgt) */ 23649172SFei.Feng@Sun.COM for (qid = 0; qid < 6; qid++) { 23659172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_TX_BASE_PTR(qid), sc->txq[qid].paddr); 23669172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_TX_MAX_CNT(qid), RT2860_TX_RING_COUNT); 23679172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_TX_CTX_IDX(qid), 0); 23689172SFei.Feng@Sun.COM } 23699172SFei.Feng@Sun.COM 23709172SFei.Feng@Sun.COM /* init Rx ring */ 23719172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_RX_BASE_PTR, sc->rxq.paddr); 23729172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_RX_MAX_CNT, RT2860_RX_RING_COUNT); 23739172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_RX_CALC_IDX, RT2860_RX_RING_COUNT - 1); 23749172SFei.Feng@Sun.COM 23759172SFei.Feng@Sun.COM /* setup maximum buffer sizes */ 23769172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAX_LEN_CFG, 1 << 12 | 23779172SFei.Feng@Sun.COM (sc->sc_dmabuf_size - sizeof (struct rt2860_rxwi) - 2)); 23789172SFei.Feng@Sun.COM 23799172SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) { 23809172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_WPDMA_GLO_CFG); 23819172SFei.Feng@Sun.COM if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) 23829172SFei.Feng@Sun.COM break; 23839172SFei.Feng@Sun.COM DELAY(1000); 23849172SFei.Feng@Sun.COM } 23859172SFei.Feng@Sun.COM if (ntries == 100) { 23869172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, "rwn: rt2860_init():" 23879172SFei.Feng@Sun.COM "timeout waiting for DMA engine\n"); 23889172SFei.Feng@Sun.COM rt2860_stop(sc); 23899172SFei.Feng@Sun.COM return (ETIMEDOUT); 23909172SFei.Feng@Sun.COM } 23919172SFei.Feng@Sun.COM tmp &= 0xff0; 23929172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp | RT2860_TX_WB_DDONE); 23939172SFei.Feng@Sun.COM 23949172SFei.Feng@Sun.COM /* disable interrupts mitigation */ 23959172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_DELAY_INT_CFG, 0); 23969172SFei.Feng@Sun.COM 23979172SFei.Feng@Sun.COM /* write vendor-specific BBP values (from EEPROM) */ 23989172SFei.Feng@Sun.COM for (i = 0; i < 8; i++) { 23999172SFei.Feng@Sun.COM if (sc->bbp[i].reg == 0 || sc->bbp[i].reg == 0xff) 24009172SFei.Feng@Sun.COM continue; 24019172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, sc->bbp[i].reg, sc->bbp[i].val); 24029172SFei.Feng@Sun.COM } 24039172SFei.Feng@Sun.COM 24049172SFei.Feng@Sun.COM /* send LEDs operating mode to microcontroller */ 24059172SFei.Feng@Sun.COM (void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_LED1, sc->led[0]); 24069172SFei.Feng@Sun.COM (void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_LED2, sc->led[1]); 24079172SFei.Feng@Sun.COM (void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_LED3, sc->led[2]); 24089172SFei.Feng@Sun.COM 24099172SFei.Feng@Sun.COM /* disable non-existing Rx chains */ 24109172SFei.Feng@Sun.COM bbp3 = rt2860_mcu_bbp_read(sc, 3); 24119172SFei.Feng@Sun.COM bbp3 &= ~(1 << 3 | 1 << 4); 24129172SFei.Feng@Sun.COM if (sc->nrxchains == 2) 24139172SFei.Feng@Sun.COM bbp3 |= 1 << 3; 24149172SFei.Feng@Sun.COM else if (sc->nrxchains == 3) 24159172SFei.Feng@Sun.COM bbp3 |= 1 << 4; 24169172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, 3, bbp3); 24179172SFei.Feng@Sun.COM 24189172SFei.Feng@Sun.COM /* disable non-existing Tx chains */ 24199172SFei.Feng@Sun.COM bbp1 = rt2860_mcu_bbp_read(sc, 1); 24209172SFei.Feng@Sun.COM if (sc->ntxchains == 1) 24219172SFei.Feng@Sun.COM bbp1 &= ~(1 << 3 | 1 << 4); 24229172SFei.Feng@Sun.COM rt2860_mcu_bbp_write(sc, 1, bbp1); 24239172SFei.Feng@Sun.COM 24249172SFei.Feng@Sun.COM /* select default channel */ 24259172SFei.Feng@Sun.COM rt2860_set_chan(sc, ic->ic_curchan); 24269172SFei.Feng@Sun.COM 24279172SFei.Feng@Sun.COM /* XXX not clear what the following 8051 command does.. */ 24289172SFei.Feng@Sun.COM (void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_BOOT, 0); 24299172SFei.Feng@Sun.COM 24309172SFei.Feng@Sun.COM /* set RTS threshold */ 24319172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_TX_RTS_CFG); 24329172SFei.Feng@Sun.COM tmp &= ~0xffff00; 24339172SFei.Feng@Sun.COM tmp |= ic->ic_rtsthreshold << 8; 24349172SFei.Feng@Sun.COM 24359172SFei.Feng@Sun.COM /* setup initial protection mode */ 24369172SFei.Feng@Sun.COM sc->sc_ic_flags = ic->ic_flags; 24379172SFei.Feng@Sun.COM rt2860_updateprot(ic); 24389172SFei.Feng@Sun.COM 24399172SFei.Feng@Sun.COM /* enable Tx/Rx DMA engine */ 24409172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_TX_EN); 24419172SFei.Feng@Sun.COM for (ntries = 0; ntries < 200; ntries++) { 24429172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_WPDMA_GLO_CFG); 24439172SFei.Feng@Sun.COM if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) 24449172SFei.Feng@Sun.COM break; 24459172SFei.Feng@Sun.COM DELAY(1000); 24469172SFei.Feng@Sun.COM } 24479172SFei.Feng@Sun.COM if (ntries == 200) { 24489172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_DMA, "rwn: rt2860_int():" 24499172SFei.Feng@Sun.COM "timeout waiting for DMA engine\n"); 24509172SFei.Feng@Sun.COM rt2860_stop(sc); 24519172SFei.Feng@Sun.COM return (ETIMEDOUT); 24529172SFei.Feng@Sun.COM } 24539172SFei.Feng@Sun.COM 24549172SFei.Feng@Sun.COM DELAY(50); 24559172SFei.Feng@Sun.COM 24569172SFei.Feng@Sun.COM tmp |= RT2860_TX_WB_DDONE | RT2860_RX_DMA_EN | RT2860_TX_DMA_EN | 24579172SFei.Feng@Sun.COM RT2860_WPDMA_BT_SIZE64 << RT2860_WPDMA_BT_SIZE_SHIFT; 24589172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp); 24599172SFei.Feng@Sun.COM 24609172SFei.Feng@Sun.COM /* turn radio LED on */ 24619172SFei.Feng@Sun.COM rt2860_set_leds(sc, RT2860_LED_RADIO); 24629172SFei.Feng@Sun.COM 24639172SFei.Feng@Sun.COM /* set Rx filter */ 24649172SFei.Feng@Sun.COM tmp = RT2860_DROP_CRC_ERR | RT2860_DROP_PHY_ERR; 24659172SFei.Feng@Sun.COM if (ic->ic_opmode != IEEE80211_M_MONITOR) { 24669172SFei.Feng@Sun.COM tmp |= RT2860_DROP_UC_NOME | RT2860_DROP_DUPL | 24679172SFei.Feng@Sun.COM RT2860_DROP_CTS | RT2860_DROP_BA | RT2860_DROP_ACK | 24689172SFei.Feng@Sun.COM RT2860_DROP_VER_ERR | RT2860_DROP_CTRL_RSV | 24699172SFei.Feng@Sun.COM RT2860_DROP_CFACK | RT2860_DROP_CFEND; 24709172SFei.Feng@Sun.COM if (ic->ic_opmode == IEEE80211_M_STA) 24719172SFei.Feng@Sun.COM tmp |= RT2860_DROP_RTS | RT2860_DROP_PSPOLL; 24729172SFei.Feng@Sun.COM } 24739172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_RX_FILTR_CFG, tmp); 24749172SFei.Feng@Sun.COM 24759172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL, 24769172SFei.Feng@Sun.COM RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); 24779172SFei.Feng@Sun.COM 24789172SFei.Feng@Sun.COM /* clear pending interrupts */ 24799172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_INT_STATUS, 0xffffffff); 24809172SFei.Feng@Sun.COM /* enable interrupts */ 24819172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_INT_MASK, 0x3fffc); 24829172SFei.Feng@Sun.COM 24839172SFei.Feng@Sun.COM if (sc->sc_flags & RT2860_ADVANCED_PS) 24849172SFei.Feng@Sun.COM (void) rt2860_mcu_cmd(sc, RT2860_MCU_CMD_PSLEVEL, sc->pslevel); 24859172SFei.Feng@Sun.COM 24869172SFei.Feng@Sun.COM return (DDI_SUCCESS); 24879172SFei.Feng@Sun.COM } 24889172SFei.Feng@Sun.COM 24899172SFei.Feng@Sun.COM static int 24909172SFei.Feng@Sun.COM rt2860_quiesce(dev_info_t *dip) 24919172SFei.Feng@Sun.COM { 24929172SFei.Feng@Sun.COM struct rt2860_softc *sc; 24939172SFei.Feng@Sun.COM 24949172SFei.Feng@Sun.COM sc = ddi_get_soft_state(rt2860_soft_state_p, ddi_get_instance(dip)); 24959172SFei.Feng@Sun.COM if (sc == NULL) 24969172SFei.Feng@Sun.COM return (DDI_FAILURE); 24979172SFei.Feng@Sun.COM 24989172SFei.Feng@Sun.COM #ifdef DEBUG 24999172SFei.Feng@Sun.COM rt2860_dbg_flags = 0; 25009172SFei.Feng@Sun.COM #endif 25019172SFei.Feng@Sun.COM 25029172SFei.Feng@Sun.COM /* 25039172SFei.Feng@Sun.COM * No more blocking is allowed while we are in quiesce(9E) entry point 25049172SFei.Feng@Sun.COM */ 25059172SFei.Feng@Sun.COM sc->sc_flags |= RT2860_F_QUIESCE; 25069172SFei.Feng@Sun.COM 25079172SFei.Feng@Sun.COM /* 25089172SFei.Feng@Sun.COM * Disable and mask all interrupts 25099172SFei.Feng@Sun.COM */ 25109172SFei.Feng@Sun.COM rt2860_stop(sc); 25119172SFei.Feng@Sun.COM return (DDI_SUCCESS); 25129172SFei.Feng@Sun.COM } 25139172SFei.Feng@Sun.COM 25149172SFei.Feng@Sun.COM static void 25159172SFei.Feng@Sun.COM rt2860_stop(struct rt2860_softc *sc) 25169172SFei.Feng@Sun.COM { 25179172SFei.Feng@Sun.COM int qid; 25189172SFei.Feng@Sun.COM uint32_t tmp; 25199172SFei.Feng@Sun.COM 25209172SFei.Feng@Sun.COM /* by pass if it's quiesced */ 25219172SFei.Feng@Sun.COM if (!(sc->sc_flags & RT2860_F_QUIESCE)) 25229172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 25239172SFei.Feng@Sun.COM if (sc->sc_flags == RT2860_F_RUNNING) 25249172SFei.Feng@Sun.COM rt2860_set_leds(sc, 0); /* turn all LEDs off */ 25259172SFei.Feng@Sun.COM sc->sc_tx_timer = 0; 25269172SFei.Feng@Sun.COM /* by pass if it's quiesced */ 25279172SFei.Feng@Sun.COM if (!(sc->sc_flags & RT2860_F_QUIESCE)) 25289172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 25299172SFei.Feng@Sun.COM 25309172SFei.Feng@Sun.COM /* clear RX WCID search table */ 25319172SFei.Feng@Sun.COM rt2860_set_region_4(sc, RT2860_WCID_ENTRY(0), 0, 512); 25329172SFei.Feng@Sun.COM /* clear pairwise key table */ 25339172SFei.Feng@Sun.COM rt2860_set_region_4(sc, RT2860_PKEY(0), 0, 2048); 25349172SFei.Feng@Sun.COM /* clear IV/EIV table */ 25359172SFei.Feng@Sun.COM rt2860_set_region_4(sc, RT2860_IVEIV(0), 0, 512); 25369172SFei.Feng@Sun.COM /* clear WCID attribute table */ 25379172SFei.Feng@Sun.COM rt2860_set_region_4(sc, RT2860_WCID_ATTR(0), 0, 256); 25389172SFei.Feng@Sun.COM /* clear shared key table */ 25399172SFei.Feng@Sun.COM rt2860_set_region_4(sc, RT2860_SKEY(0, 0), 0, 8 * 32); 25409172SFei.Feng@Sun.COM /* clear shared key mode */ 25419172SFei.Feng@Sun.COM rt2860_set_region_4(sc, RT2860_SKEY_MODE_0_7, 0, 4); 25429172SFei.Feng@Sun.COM 25439172SFei.Feng@Sun.COM /* disable interrupts */ 25449172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_INT_MASK, 0); 25459172SFei.Feng@Sun.COM 25469172SFei.Feng@Sun.COM /* disable Rx */ 25479172SFei.Feng@Sun.COM tmp = RT2860_READ(sc, RT2860_MAC_SYS_CTRL); 25489172SFei.Feng@Sun.COM tmp &= ~(RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); 25499172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL, tmp); 25509172SFei.Feng@Sun.COM 25519172SFei.Feng@Sun.COM /* reset adapter */ 25529172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL, 25539172SFei.Feng@Sun.COM RT2860_BBP_HRST | RT2860_MAC_SRST); 25549172SFei.Feng@Sun.COM RT2860_WRITE(sc, RT2860_MAC_SYS_CTRL, 0); 25559172SFei.Feng@Sun.COM 25569172SFei.Feng@Sun.COM /* reset Tx and Rx rings (and reclaim TXWIs) */ 25579172SFei.Feng@Sun.COM for (qid = 0; qid < 6; qid++) 25589172SFei.Feng@Sun.COM rt2860_reset_tx_ring(sc, &sc->txq[qid]); 25599172SFei.Feng@Sun.COM rt2860_reset_rx_ring(sc, &sc->rxq); 25609172SFei.Feng@Sun.COM 25619172SFei.Feng@Sun.COM /* by pass if it's quiesced */ 25629172SFei.Feng@Sun.COM if (!(sc->sc_flags & RT2860_F_QUIESCE)) 25639172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 25649172SFei.Feng@Sun.COM sc->sc_flags &= ~RT2860_UPD_BEACON; 25659172SFei.Feng@Sun.COM /* by pass if it's quiesced */ 25669172SFei.Feng@Sun.COM if (!(sc->sc_flags & RT2860_F_QUIESCE)) 25679172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 25689172SFei.Feng@Sun.COM } 25699172SFei.Feng@Sun.COM 25709172SFei.Feng@Sun.COM static int 25719172SFei.Feng@Sun.COM rt2860_m_start(void *arg) 25729172SFei.Feng@Sun.COM { 25739172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 25749172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 25759172SFei.Feng@Sun.COM int err; 25769172SFei.Feng@Sun.COM 25779172SFei.Feng@Sun.COM err = rt2860_init(sc); 25789172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 25799172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_m_start():" 25809172SFei.Feng@Sun.COM "Hardware initialization failed\n"); 25819172SFei.Feng@Sun.COM goto fail1; 25829172SFei.Feng@Sun.COM } 25839172SFei.Feng@Sun.COM 25849172SFei.Feng@Sun.COM ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 25859172SFei.Feng@Sun.COM 25869172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 25879172SFei.Feng@Sun.COM sc->sc_flags |= RT2860_F_RUNNING; 25889172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 25899172SFei.Feng@Sun.COM 25909172SFei.Feng@Sun.COM return (err); 25919172SFei.Feng@Sun.COM fail1: 25929172SFei.Feng@Sun.COM rt2860_stop(sc); 25939172SFei.Feng@Sun.COM return (err); 25949172SFei.Feng@Sun.COM } 25959172SFei.Feng@Sun.COM 25969172SFei.Feng@Sun.COM static void 25979172SFei.Feng@Sun.COM rt2860_m_stop(void *arg) 25989172SFei.Feng@Sun.COM { 25999172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 26009172SFei.Feng@Sun.COM 26019172SFei.Feng@Sun.COM (void) rt2860_stop(sc); 26029172SFei.Feng@Sun.COM 26039172SFei.Feng@Sun.COM ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1); 26049172SFei.Feng@Sun.COM 26059172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 26069172SFei.Feng@Sun.COM sc->sc_flags &= ~RT2860_F_RUNNING; 26079172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 26089172SFei.Feng@Sun.COM } 26099172SFei.Feng@Sun.COM 26109172SFei.Feng@Sun.COM static void 26119172SFei.Feng@Sun.COM rt2860_m_ioctl(void* arg, queue_t *wq, mblk_t *mp) 26129172SFei.Feng@Sun.COM { 26139172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 26149172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 26159172SFei.Feng@Sun.COM int err; 26169172SFei.Feng@Sun.COM 26179172SFei.Feng@Sun.COM err = ieee80211_ioctl(ic, wq, mp); 26189172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 26199172SFei.Feng@Sun.COM if (err == ENETRESET) { 26209172SFei.Feng@Sun.COM if (ic->ic_des_esslen) { 26219172SFei.Feng@Sun.COM if (RT2860_IS_RUNNING(sc)) { 26229172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 26239172SFei.Feng@Sun.COM (void) rt2860_init(sc); 26249172SFei.Feng@Sun.COM (void) ieee80211_new_state(ic, 26259172SFei.Feng@Sun.COM IEEE80211_S_SCAN, -1); 26269172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 26279172SFei.Feng@Sun.COM } 26289172SFei.Feng@Sun.COM } 26299172SFei.Feng@Sun.COM } 26309172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 26319172SFei.Feng@Sun.COM } 26329172SFei.Feng@Sun.COM 26339172SFei.Feng@Sun.COM /* 26349172SFei.Feng@Sun.COM * Call back function for get/set proporty 26359172SFei.Feng@Sun.COM */ 26369172SFei.Feng@Sun.COM static int 26379172SFei.Feng@Sun.COM rt2860_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 26389172SFei.Feng@Sun.COM uint_t pr_flags, uint_t wldp_length, void *wldp_buf, uint_t *perm) 26399172SFei.Feng@Sun.COM { 26409172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 26419172SFei.Feng@Sun.COM int err = 0; 26429172SFei.Feng@Sun.COM 26439172SFei.Feng@Sun.COM err = ieee80211_getprop(&sc->sc_ic, pr_name, wldp_pr_num, 26449172SFei.Feng@Sun.COM pr_flags, wldp_length, wldp_buf, perm); 26459172SFei.Feng@Sun.COM 26469172SFei.Feng@Sun.COM return (err); 26479172SFei.Feng@Sun.COM } 26489172SFei.Feng@Sun.COM 26499172SFei.Feng@Sun.COM static int 26509172SFei.Feng@Sun.COM rt2860_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 26519172SFei.Feng@Sun.COM uint_t wldp_length, const void *wldp_buf) 26529172SFei.Feng@Sun.COM { 26539172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 26549172SFei.Feng@Sun.COM ieee80211com_t *ic = &sc->sc_ic; 26559172SFei.Feng@Sun.COM int err; 26569172SFei.Feng@Sun.COM 26579172SFei.Feng@Sun.COM err = ieee80211_setprop(ic, pr_name, wldp_pr_num, wldp_length, 26589172SFei.Feng@Sun.COM wldp_buf); 26599172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 26609172SFei.Feng@Sun.COM if (err == ENETRESET) { 26619172SFei.Feng@Sun.COM if (ic->ic_des_esslen) { 26629172SFei.Feng@Sun.COM if (RT2860_IS_RUNNING(sc)) { 26639172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 26649172SFei.Feng@Sun.COM (void) rt2860_init(sc); 26659172SFei.Feng@Sun.COM (void) ieee80211_new_state(ic, 26669172SFei.Feng@Sun.COM IEEE80211_S_SCAN, -1); 26679172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 26689172SFei.Feng@Sun.COM } 26699172SFei.Feng@Sun.COM } 26709172SFei.Feng@Sun.COM err = 0; 26719172SFei.Feng@Sun.COM } 26729172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 26739172SFei.Feng@Sun.COM return (err); 26749172SFei.Feng@Sun.COM } 26759172SFei.Feng@Sun.COM 26769172SFei.Feng@Sun.COM static mblk_t * 26779172SFei.Feng@Sun.COM rt2860_m_tx(void *arg, mblk_t *mp) 26789172SFei.Feng@Sun.COM { 26799172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 26809172SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 26819172SFei.Feng@Sun.COM mblk_t *next; 26829172SFei.Feng@Sun.COM 26839172SFei.Feng@Sun.COM if (RT2860_IS_SUSPEND(sc)) { 26849172SFei.Feng@Sun.COM freemsgchain(mp); 26859172SFei.Feng@Sun.COM return (NULL); 26869172SFei.Feng@Sun.COM } 26879172SFei.Feng@Sun.COM 26889172SFei.Feng@Sun.COM /* 26899172SFei.Feng@Sun.COM * No data frames go out unless we're associated; this 26909172SFei.Feng@Sun.COM * should not happen as the 802.11 layer does not enable 26919172SFei.Feng@Sun.COM * the xmit queue until we enter the RUN state. 26929172SFei.Feng@Sun.COM */ 26939172SFei.Feng@Sun.COM if (ic->ic_state != IEEE80211_S_RUN) { 26949172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_TX, "rwn: rt2860_tx_data(): " 26959172SFei.Feng@Sun.COM "discard, state %u\n", ic->ic_state); 26969172SFei.Feng@Sun.COM freemsgchain(mp); 26979172SFei.Feng@Sun.COM return (NULL); 26989172SFei.Feng@Sun.COM } 26999172SFei.Feng@Sun.COM 27009172SFei.Feng@Sun.COM while (mp != NULL) { 27019172SFei.Feng@Sun.COM next = mp->b_next; 27029172SFei.Feng@Sun.COM mp->b_next = NULL; 27039172SFei.Feng@Sun.COM if (rt2860_send(ic, mp, IEEE80211_FC0_TYPE_DATA) != 27049172SFei.Feng@Sun.COM DDI_SUCCESS) { 27059172SFei.Feng@Sun.COM mp->b_next = next; 27069172SFei.Feng@Sun.COM break; 27079172SFei.Feng@Sun.COM } 27089172SFei.Feng@Sun.COM mp = next; 27099172SFei.Feng@Sun.COM } 27109172SFei.Feng@Sun.COM return (mp); 27119172SFei.Feng@Sun.COM } 27129172SFei.Feng@Sun.COM 27139172SFei.Feng@Sun.COM /*ARGSUSED*/ 27149172SFei.Feng@Sun.COM static int 27159172SFei.Feng@Sun.COM rt2860_m_unicst(void *arg, const uint8_t *macaddr) 27169172SFei.Feng@Sun.COM { 27179172SFei.Feng@Sun.COM return (ENOTSUP); 27189172SFei.Feng@Sun.COM } 27199172SFei.Feng@Sun.COM 27209172SFei.Feng@Sun.COM /*ARGSUSED*/ 27219172SFei.Feng@Sun.COM static int 27229172SFei.Feng@Sun.COM rt2860_m_multicst(void *arg, boolean_t add, const uint8_t *mca) 27239172SFei.Feng@Sun.COM { 27249172SFei.Feng@Sun.COM return (ENOTSUP); 27259172SFei.Feng@Sun.COM } 27269172SFei.Feng@Sun.COM 27279172SFei.Feng@Sun.COM /*ARGSUSED*/ 27289172SFei.Feng@Sun.COM static int 27299172SFei.Feng@Sun.COM rt2860_m_promisc(void *arg, boolean_t on) 27309172SFei.Feng@Sun.COM { 27319172SFei.Feng@Sun.COM return (0); 27329172SFei.Feng@Sun.COM } 27339172SFei.Feng@Sun.COM 27349172SFei.Feng@Sun.COM static int 27359172SFei.Feng@Sun.COM rt2860_m_stat(void *arg, uint_t stat, uint64_t *val) 27369172SFei.Feng@Sun.COM { 27379172SFei.Feng@Sun.COM struct rt2860_softc *sc = (struct rt2860_softc *)arg; 27389172SFei.Feng@Sun.COM ieee80211com_t *ic = &sc->sc_ic; 27399172SFei.Feng@Sun.COM ieee80211_node_t *ni = ic->ic_bss; 27409172SFei.Feng@Sun.COM struct ieee80211_rateset *rs = &ni->in_rates; 27419172SFei.Feng@Sun.COM 27429172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 27439172SFei.Feng@Sun.COM switch (stat) { 27449172SFei.Feng@Sun.COM case MAC_STAT_IFSPEED: 27459172SFei.Feng@Sun.COM *val = ((ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) ? 27469172SFei.Feng@Sun.COM (rs->ir_rates[ni->in_txrate] & IEEE80211_RATE_VAL) 27479172SFei.Feng@Sun.COM : ic->ic_fixed_rate) / 2 * 1000000; 27489172SFei.Feng@Sun.COM break; 27499172SFei.Feng@Sun.COM case MAC_STAT_NOXMTBUF: 27509172SFei.Feng@Sun.COM *val = sc->sc_tx_nobuf; 27519172SFei.Feng@Sun.COM break; 27529172SFei.Feng@Sun.COM case MAC_STAT_NORCVBUF: 27539172SFei.Feng@Sun.COM *val = sc->sc_rx_nobuf; 27549172SFei.Feng@Sun.COM break; 27559172SFei.Feng@Sun.COM case MAC_STAT_IERRORS: 27569172SFei.Feng@Sun.COM *val = sc->sc_rx_err; 27579172SFei.Feng@Sun.COM break; 27589172SFei.Feng@Sun.COM case MAC_STAT_RBYTES: 27599172SFei.Feng@Sun.COM *val = ic->ic_stats.is_rx_bytes; 27609172SFei.Feng@Sun.COM break; 27619172SFei.Feng@Sun.COM case MAC_STAT_IPACKETS: 27629172SFei.Feng@Sun.COM *val = ic->ic_stats.is_rx_frags; 27639172SFei.Feng@Sun.COM break; 27649172SFei.Feng@Sun.COM case MAC_STAT_OBYTES: 27659172SFei.Feng@Sun.COM *val = ic->ic_stats.is_tx_bytes; 27669172SFei.Feng@Sun.COM break; 27679172SFei.Feng@Sun.COM case MAC_STAT_OPACKETS: 27689172SFei.Feng@Sun.COM *val = ic->ic_stats.is_tx_frags; 27699172SFei.Feng@Sun.COM break; 27709172SFei.Feng@Sun.COM case MAC_STAT_OERRORS: 27719172SFei.Feng@Sun.COM case WIFI_STAT_TX_FAILED: 27729172SFei.Feng@Sun.COM *val = sc->sc_tx_err; 27739172SFei.Feng@Sun.COM break; 27749172SFei.Feng@Sun.COM case WIFI_STAT_TX_RETRANS: 27759172SFei.Feng@Sun.COM *val = sc->sc_tx_retries; 27769172SFei.Feng@Sun.COM break; 27779172SFei.Feng@Sun.COM case WIFI_STAT_FCS_ERRORS: 27789172SFei.Feng@Sun.COM case WIFI_STAT_WEP_ERRORS: 27799172SFei.Feng@Sun.COM case WIFI_STAT_TX_FRAGS: 27809172SFei.Feng@Sun.COM case WIFI_STAT_MCAST_TX: 27819172SFei.Feng@Sun.COM case WIFI_STAT_RTS_SUCCESS: 27829172SFei.Feng@Sun.COM case WIFI_STAT_RTS_FAILURE: 27839172SFei.Feng@Sun.COM case WIFI_STAT_ACK_FAILURE: 27849172SFei.Feng@Sun.COM case WIFI_STAT_RX_FRAGS: 27859172SFei.Feng@Sun.COM case WIFI_STAT_MCAST_RX: 27869172SFei.Feng@Sun.COM case WIFI_STAT_RX_DUPS: 27879172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 27889172SFei.Feng@Sun.COM return (ieee80211_stat(ic, stat, val)); 27899172SFei.Feng@Sun.COM default: 27909172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 27919172SFei.Feng@Sun.COM return (ENOTSUP); 27929172SFei.Feng@Sun.COM } 27939172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 27949172SFei.Feng@Sun.COM 27959172SFei.Feng@Sun.COM return (0); 27969172SFei.Feng@Sun.COM } 27979172SFei.Feng@Sun.COM 27989172SFei.Feng@Sun.COM static int 27999172SFei.Feng@Sun.COM rt2860_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd) 28009172SFei.Feng@Sun.COM { 28019172SFei.Feng@Sun.COM struct rt2860_softc *sc; 28029172SFei.Feng@Sun.COM struct ieee80211com *ic; 28039172SFei.Feng@Sun.COM int i, err, qid, ntries, instance; 28049172SFei.Feng@Sun.COM uint8_t cachelsz; 28059172SFei.Feng@Sun.COM uint16_t command, vendor_id, device_id; 28069172SFei.Feng@Sun.COM char strbuf[32]; 28079172SFei.Feng@Sun.COM wifi_data_t wd = { 0 }; 28089172SFei.Feng@Sun.COM mac_register_t *macp; 28099172SFei.Feng@Sun.COM 28109172SFei.Feng@Sun.COM switch (cmd) { 28119172SFei.Feng@Sun.COM case DDI_ATTACH: 28129172SFei.Feng@Sun.COM break; 28139172SFei.Feng@Sun.COM case DDI_RESUME: 28149172SFei.Feng@Sun.COM sc = ddi_get_soft_state(rt2860_soft_state_p, 28159172SFei.Feng@Sun.COM ddi_get_instance(devinfo)); 28169172SFei.Feng@Sun.COM ASSERT(sc != NULL); 28179172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 28189172SFei.Feng@Sun.COM sc->sc_flags &= ~RT2860_F_SUSPEND; 28199172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 28209172SFei.Feng@Sun.COM if (RT2860_IS_RUNNING(sc)) 28219172SFei.Feng@Sun.COM (void) rt2860_init(sc); 28229172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_RESUME, "rwn: rt2860_attach(): " 28239172SFei.Feng@Sun.COM "resume now\n"); 28249172SFei.Feng@Sun.COM return (DDI_SUCCESS); 28259172SFei.Feng@Sun.COM default: 28269172SFei.Feng@Sun.COM return (DDI_FAILURE); 28279172SFei.Feng@Sun.COM } 28289172SFei.Feng@Sun.COM 28299172SFei.Feng@Sun.COM instance = ddi_get_instance(devinfo); 28309172SFei.Feng@Sun.COM 28319172SFei.Feng@Sun.COM err = ddi_soft_state_zalloc(rt2860_soft_state_p, instance); 28329172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 28339172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 28349172SFei.Feng@Sun.COM "unable to alloc soft_state_p\n"); 28359172SFei.Feng@Sun.COM return (err); 28369172SFei.Feng@Sun.COM } 28379172SFei.Feng@Sun.COM 28389172SFei.Feng@Sun.COM sc = ddi_get_soft_state(rt2860_soft_state_p, instance); 28399172SFei.Feng@Sun.COM ic = (ieee80211com_t *)&sc->sc_ic; 28409172SFei.Feng@Sun.COM sc->sc_dev = devinfo; 28419172SFei.Feng@Sun.COM 28429172SFei.Feng@Sun.COM /* pci configuration */ 28439172SFei.Feng@Sun.COM err = ddi_regs_map_setup(devinfo, 0, &sc->sc_cfg_base, 0, 0, 28449172SFei.Feng@Sun.COM &rwn_csr_accattr, &sc->sc_cfg_handle); 28459172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 28469172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 28479172SFei.Feng@Sun.COM "ddi_regs_map_setup() failed"); 28489172SFei.Feng@Sun.COM goto fail1; 28499172SFei.Feng@Sun.COM } 28509172SFei.Feng@Sun.COM 28519172SFei.Feng@Sun.COM cachelsz = ddi_get8(sc->sc_cfg_handle, 28529172SFei.Feng@Sun.COM (uint8_t *)(sc->sc_cfg_base + PCI_CONF_CACHE_LINESZ)); 28539172SFei.Feng@Sun.COM if (cachelsz == 0) 28549172SFei.Feng@Sun.COM cachelsz = 0x10; 28559172SFei.Feng@Sun.COM sc->sc_cachelsz = cachelsz << 2; 28569172SFei.Feng@Sun.COM sc->sc_dmabuf_size = roundup(IEEE80211_MAX_LEN, sc->sc_cachelsz); 28579172SFei.Feng@Sun.COM 28589172SFei.Feng@Sun.COM vendor_id = ddi_get16(sc->sc_cfg_handle, 28599172SFei.Feng@Sun.COM (uint16_t *)((uintptr_t)(sc->sc_cfg_base) + PCI_CONF_VENID)); 28609172SFei.Feng@Sun.COM device_id = ddi_get16(sc->sc_cfg_handle, 28619172SFei.Feng@Sun.COM (uint16_t *)((uintptr_t)(sc->sc_cfg_base) + PCI_CONF_DEVID)); 28629172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 28639172SFei.Feng@Sun.COM "vendor 0x%x, device id 0x%x, cache size %d\n", 28649172SFei.Feng@Sun.COM vendor_id, device_id, cachelsz); 28659172SFei.Feng@Sun.COM 28669172SFei.Feng@Sun.COM /* 28679172SFei.Feng@Sun.COM * Enable response to memory space accesses, 28689172SFei.Feng@Sun.COM * and enabe bus master. 28699172SFei.Feng@Sun.COM */ 28709172SFei.Feng@Sun.COM command = PCI_COMM_MAE | PCI_COMM_ME; 28719172SFei.Feng@Sun.COM ddi_put16(sc->sc_cfg_handle, 28729172SFei.Feng@Sun.COM (uint16_t *)((uintptr_t)(sc->sc_cfg_base) + PCI_CONF_COMM), 28739172SFei.Feng@Sun.COM command); 28749172SFei.Feng@Sun.COM ddi_put8(sc->sc_cfg_handle, 28759172SFei.Feng@Sun.COM (uint8_t *)(sc->sc_cfg_base + PCI_CONF_LATENCY_TIMER), 0xa8); 28769172SFei.Feng@Sun.COM ddi_put8(sc->sc_cfg_handle, 28779172SFei.Feng@Sun.COM (uint8_t *)(sc->sc_cfg_base + PCI_CONF_ILINE), 0x10); 28789172SFei.Feng@Sun.COM 28799172SFei.Feng@Sun.COM /* pci i/o space */ 28809172SFei.Feng@Sun.COM err = ddi_regs_map_setup(devinfo, 1, 28819172SFei.Feng@Sun.COM &sc->sc_io_base, 0, 0, &rwn_csr_accattr, &sc->sc_io_handle); 28829172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 28839172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 28849172SFei.Feng@Sun.COM "ddi_regs_map_setup() failed"); 28859172SFei.Feng@Sun.COM goto fail2; 28869172SFei.Feng@Sun.COM } 28879172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 28889172SFei.Feng@Sun.COM "PCI configuration is done successfully\n"); 28899172SFei.Feng@Sun.COM 2890*10108SFei.Feng@Sun.COM sc->amrr.amrr_min_success_threshold = 1; 2891*10108SFei.Feng@Sun.COM sc->amrr.amrr_max_success_threshold = 15; 2892*10108SFei.Feng@Sun.COM 28939172SFei.Feng@Sun.COM /* wait for NIC to initialize */ 28949172SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) { 28959172SFei.Feng@Sun.COM sc->mac_rev = RT2860_READ(sc, RT2860_ASIC_VER_ID); 28969172SFei.Feng@Sun.COM if (sc->mac_rev != 0 && sc->mac_rev != 0xffffffff) 28979172SFei.Feng@Sun.COM break; 28989172SFei.Feng@Sun.COM DELAY(10); 28999172SFei.Feng@Sun.COM } 29009172SFei.Feng@Sun.COM if (ntries == 100) { 29019172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 29029172SFei.Feng@Sun.COM "timeout waiting for NIC initialize\n"); 29039172SFei.Feng@Sun.COM return (DDI_FAILURE); 29049172SFei.Feng@Sun.COM } 29059172SFei.Feng@Sun.COM 29069172SFei.Feng@Sun.COM if ((sc->mac_rev >> 16) != 0x2860 && 29079172SFei.Feng@Sun.COM (device_id == PRODUCT_RALINK_RT2890 || 29089172SFei.Feng@Sun.COM device_id == PRODUCT_RALINK_RT2790 || 29099172SFei.Feng@Sun.COM device_id == PRODUCT_AWT_RT2890)) 29109172SFei.Feng@Sun.COM sc->sc_flags |= RT2860_ADVANCED_PS; 29119172SFei.Feng@Sun.COM 29129172SFei.Feng@Sun.COM /* retrieve RF rev. no and various other things from EEPROM */ 29139172SFei.Feng@Sun.COM (void) rt2860_read_eeprom(sc); 29149172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 29159172SFei.Feng@Sun.COM "MAC/BBP RT%X (rev 0x%04X), RF %s (%dT%dR)\n", 29169172SFei.Feng@Sun.COM sc->mac_rev >> 16, sc->mac_rev & 0xffff, 29179172SFei.Feng@Sun.COM rt2860_get_rf(sc->rf_rev), sc->ntxchains, sc->nrxchains); 29189172SFei.Feng@Sun.COM 29199172SFei.Feng@Sun.COM /* 29209172SFei.Feng@Sun.COM * Allocate Tx (4 EDCAs + HCCA + Mgt) and Rx rings. 29219172SFei.Feng@Sun.COM */ 29229172SFei.Feng@Sun.COM for (qid = 0; qid < 6; qid++) { 29239172SFei.Feng@Sun.COM if ((err = rt2860_alloc_tx_ring(sc, &sc->txq[qid])) != 0) { 29249172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 29259172SFei.Feng@Sun.COM "could not allocate Tx ring %d\n", qid); 29269172SFei.Feng@Sun.COM goto fail3; 29279172SFei.Feng@Sun.COM } 29289172SFei.Feng@Sun.COM } 29299172SFei.Feng@Sun.COM 29309172SFei.Feng@Sun.COM if ((err = rt2860_alloc_rx_ring(sc, &sc->rxq)) != 0) { 29319172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 29329172SFei.Feng@Sun.COM "could not allocte Rx ring\n"); 29339172SFei.Feng@Sun.COM goto fail4; 29349172SFei.Feng@Sun.COM } 29359172SFei.Feng@Sun.COM 29369172SFei.Feng@Sun.COM if ((err = rt2860_alloc_tx_pool(sc)) != 0) { 29379172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 29389172SFei.Feng@Sun.COM "could not allocte Tx pool\n"); 29399172SFei.Feng@Sun.COM goto fail5; 29409172SFei.Feng@Sun.COM } 29419172SFei.Feng@Sun.COM 29429172SFei.Feng@Sun.COM mutex_init(&sc->sc_genlock, NULL, MUTEX_DRIVER, NULL); 29439172SFei.Feng@Sun.COM mutex_init(&sc->sc_txlock, NULL, MUTEX_DRIVER, NULL); 29449172SFei.Feng@Sun.COM mutex_init(&sc->sc_rxlock, NULL, MUTEX_DRIVER, NULL); 29459172SFei.Feng@Sun.COM 29469172SFei.Feng@Sun.COM /* mgmt ring is broken on RT2860C, use EDCA AC VO ring instead */ 29479172SFei.Feng@Sun.COM sc->mgtqid = (sc->mac_rev == 0x28600100) ? EDCA_AC_VO : 5; 29489172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach():" 29499172SFei.Feng@Sun.COM "mgtqid = %x\n", sc->mgtqid); 29509172SFei.Feng@Sun.COM 29519172SFei.Feng@Sun.COM ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ 29529172SFei.Feng@Sun.COM ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ 29539172SFei.Feng@Sun.COM ic->ic_state = IEEE80211_S_INIT; 29549172SFei.Feng@Sun.COM 29559172SFei.Feng@Sun.COM /* set device capabilities */ 29569172SFei.Feng@Sun.COM ic->ic_caps = 29579172SFei.Feng@Sun.COM IEEE80211_C_TXPMGT | /* tx power management */ 29589172SFei.Feng@Sun.COM IEEE80211_C_SHPREAMBLE | /* short preamble supported */ 29599172SFei.Feng@Sun.COM IEEE80211_C_SHSLOT; /* short slot time supported */ 29609172SFei.Feng@Sun.COM 29619172SFei.Feng@Sun.COM /* WPA/WPA2 support */ 29629172SFei.Feng@Sun.COM ic->ic_caps |= IEEE80211_C_WPA; /* Support WPA/WPA2 */ 29639172SFei.Feng@Sun.COM 29649172SFei.Feng@Sun.COM /* set supported .11b and .11g rates */ 2965*10108SFei.Feng@Sun.COM ic->ic_sup_rates[IEEE80211_MODE_11B] = rt2860_rateset_11b; 2966*10108SFei.Feng@Sun.COM ic->ic_sup_rates[IEEE80211_MODE_11G] = rt2860_rateset_11g; 29679172SFei.Feng@Sun.COM 29689172SFei.Feng@Sun.COM /* set supported .11b and .11g channels (1 through 14) */ 29699172SFei.Feng@Sun.COM for (i = 1; i <= 14; i++) { 29709172SFei.Feng@Sun.COM ic->ic_sup_channels[i].ich_freq = 29719172SFei.Feng@Sun.COM ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); 29729172SFei.Feng@Sun.COM ic->ic_sup_channels[i].ich_flags = 29739172SFei.Feng@Sun.COM IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | 29749172SFei.Feng@Sun.COM IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; 29759172SFei.Feng@Sun.COM } 29769172SFei.Feng@Sun.COM 2977*10108SFei.Feng@Sun.COM ic->ic_maxrssi = 63; 29789172SFei.Feng@Sun.COM ic->ic_xmit = rt2860_send; 29799172SFei.Feng@Sun.COM 29809172SFei.Feng@Sun.COM ieee80211_attach(ic); 29819172SFei.Feng@Sun.COM 29829172SFei.Feng@Sun.COM /* register WPA door */ 29839172SFei.Feng@Sun.COM ieee80211_register_door(ic, ddi_driver_name(devinfo), 29849172SFei.Feng@Sun.COM ddi_get_instance(devinfo)); 29859172SFei.Feng@Sun.COM 29869172SFei.Feng@Sun.COM /* override state transition machine */ 29879172SFei.Feng@Sun.COM sc->sc_newstate = ic->ic_newstate; 29889172SFei.Feng@Sun.COM ic->ic_newstate = rt2860_newstate; 29899172SFei.Feng@Sun.COM ieee80211_media_init(ic); 29909172SFei.Feng@Sun.COM ic->ic_def_txkey = 0; 29919172SFei.Feng@Sun.COM 29929172SFei.Feng@Sun.COM err = ddi_add_softintr(devinfo, DDI_SOFTINT_LOW, 29939172SFei.Feng@Sun.COM &sc->sc_softintr_hdl, NULL, 0, rt2860_softintr, (caddr_t)sc); 29949172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 29959172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 29969172SFei.Feng@Sun.COM "ddi_add_softintr() failed"); 29979172SFei.Feng@Sun.COM goto fail8; 29989172SFei.Feng@Sun.COM } 29999172SFei.Feng@Sun.COM 30009172SFei.Feng@Sun.COM err = ddi_get_iblock_cookie(devinfo, 0, &sc->sc_iblock); 30019172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 30029172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 30039172SFei.Feng@Sun.COM "Can not get iblock cookie for INT\n"); 30049172SFei.Feng@Sun.COM goto fail7; 30059172SFei.Feng@Sun.COM } 30069172SFei.Feng@Sun.COM 30079172SFei.Feng@Sun.COM err = ddi_add_intr(devinfo, 0, NULL, NULL, rt2860_intr, (caddr_t)sc); 30089172SFei.Feng@Sun.COM if (err != DDI_SUCCESS) { 30099172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 30109172SFei.Feng@Sun.COM "unable to add device interrupt handler\n"); 30119172SFei.Feng@Sun.COM goto fail7; 30129172SFei.Feng@Sun.COM } 30139172SFei.Feng@Sun.COM 30149172SFei.Feng@Sun.COM /* 30159172SFei.Feng@Sun.COM * Provide initial settings for the WiFi plugin; whenever this 30169172SFei.Feng@Sun.COM * information changes, we need to call mac_plugindata_update() 30179172SFei.Feng@Sun.COM */ 30189172SFei.Feng@Sun.COM wd.wd_opmode = ic->ic_opmode; 30199172SFei.Feng@Sun.COM wd.wd_secalloc = WIFI_SEC_NONE; 30209172SFei.Feng@Sun.COM IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid); 30219172SFei.Feng@Sun.COM 30229172SFei.Feng@Sun.COM if ((macp = mac_alloc(MAC_VERSION)) == NULL) { 30239172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 30249172SFei.Feng@Sun.COM "MAC version mismatch\n"); 30259172SFei.Feng@Sun.COM goto fail9; 30269172SFei.Feng@Sun.COM } 30279172SFei.Feng@Sun.COM 30289172SFei.Feng@Sun.COM macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI; 30299172SFei.Feng@Sun.COM macp->m_driver = sc; 30309172SFei.Feng@Sun.COM macp->m_dip = devinfo; 30319172SFei.Feng@Sun.COM macp->m_src_addr = ic->ic_macaddr; 30329172SFei.Feng@Sun.COM macp->m_callbacks = &rt2860_m_callbacks; 30339172SFei.Feng@Sun.COM macp->m_min_sdu = 0; 30349172SFei.Feng@Sun.COM macp->m_max_sdu = IEEE80211_MTU; 30359172SFei.Feng@Sun.COM macp->m_pdata = &wd; 30369172SFei.Feng@Sun.COM macp->m_pdata_size = sizeof (wd); 30379172SFei.Feng@Sun.COM 30389172SFei.Feng@Sun.COM err = mac_register(macp, &ic->ic_mach); 30399172SFei.Feng@Sun.COM mac_free(macp); 30409172SFei.Feng@Sun.COM if (err != 0) { 30419172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach(): " 30429172SFei.Feng@Sun.COM "mac_register err %x\n", err); 30439172SFei.Feng@Sun.COM goto fail9; 30449172SFei.Feng@Sun.COM } 30459172SFei.Feng@Sun.COM 30469172SFei.Feng@Sun.COM /* 30479172SFei.Feng@Sun.COM * Create minor node of type DDI_NT_NET_WIFI 30489172SFei.Feng@Sun.COM */ 30499172SFei.Feng@Sun.COM (void) snprintf(strbuf, sizeof (strbuf), "%s%d", 30509172SFei.Feng@Sun.COM "rwn", instance); 30519172SFei.Feng@Sun.COM err = ddi_create_minor_node(devinfo, strbuf, S_IFCHR, 30529172SFei.Feng@Sun.COM instance + 1, DDI_NT_NET_WIFI, 0); 30539172SFei.Feng@Sun.COM 30549172SFei.Feng@Sun.COM /* 30559172SFei.Feng@Sun.COM * Notify link is down now 30569172SFei.Feng@Sun.COM */ 30579172SFei.Feng@Sun.COM mac_link_update(ic->ic_mach, LINK_STATE_DOWN); 30589172SFei.Feng@Sun.COM 30599172SFei.Feng@Sun.COM sc->sc_flags &= ~RT2860_F_RUNNING; 30609172SFei.Feng@Sun.COM 30619172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_MSG, "rwn: rt2860_attach() successfully.\n"); 30629172SFei.Feng@Sun.COM return (DDI_SUCCESS); 30639172SFei.Feng@Sun.COM fail9: 30649172SFei.Feng@Sun.COM ddi_remove_softintr(sc->sc_softintr_hdl); 30659172SFei.Feng@Sun.COM fail8: 30669172SFei.Feng@Sun.COM ddi_remove_intr(devinfo, 0, sc->sc_iblock); 30679172SFei.Feng@Sun.COM fail7: 30689172SFei.Feng@Sun.COM mutex_destroy(&sc->sc_genlock); 30699172SFei.Feng@Sun.COM mutex_destroy(&sc->sc_txlock); 30709172SFei.Feng@Sun.COM mutex_destroy(&sc->sc_rxlock); 30719172SFei.Feng@Sun.COM fail6: 30729172SFei.Feng@Sun.COM rt2860_free_tx_pool(sc); 30739172SFei.Feng@Sun.COM fail5: 30749172SFei.Feng@Sun.COM rt2860_free_rx_ring(sc, &sc->rxq); 30759172SFei.Feng@Sun.COM fail4: 30769172SFei.Feng@Sun.COM while (--qid >= 0) 30779172SFei.Feng@Sun.COM rt2860_free_tx_ring(sc, &sc->txq[qid]); 30789172SFei.Feng@Sun.COM fail3: 30799172SFei.Feng@Sun.COM ddi_regs_map_free(&sc->sc_io_handle); 30809172SFei.Feng@Sun.COM fail2: 30819172SFei.Feng@Sun.COM ddi_regs_map_free(&sc->sc_cfg_handle); 30829172SFei.Feng@Sun.COM fail1: 30839172SFei.Feng@Sun.COM return (err); 30849172SFei.Feng@Sun.COM } 30859172SFei.Feng@Sun.COM 30869172SFei.Feng@Sun.COM static int 30879172SFei.Feng@Sun.COM rt2860_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd) 30889172SFei.Feng@Sun.COM { 30899172SFei.Feng@Sun.COM struct rt2860_softc *sc; 30909172SFei.Feng@Sun.COM int qid; 30919172SFei.Feng@Sun.COM 30929172SFei.Feng@Sun.COM sc = ddi_get_soft_state(rt2860_soft_state_p, ddi_get_instance(devinfo)); 30939172SFei.Feng@Sun.COM 30949172SFei.Feng@Sun.COM switch (cmd) { 30959172SFei.Feng@Sun.COM case DDI_DETACH: 30969172SFei.Feng@Sun.COM break; 30979172SFei.Feng@Sun.COM case DDI_SUSPEND: 30989172SFei.Feng@Sun.COM if (RT2860_IS_RUNNING(sc)) 30999172SFei.Feng@Sun.COM rt2860_stop(sc); 31009172SFei.Feng@Sun.COM RT2860_GLOCK(sc); 31019172SFei.Feng@Sun.COM sc->sc_flags &= ~RT2860_FWLOADED; 31029172SFei.Feng@Sun.COM sc->sc_flags |= RT2860_F_SUSPEND; 31039172SFei.Feng@Sun.COM RT2860_GUNLOCK(sc); 31049172SFei.Feng@Sun.COM RWN_DEBUG(RT2860_DBG_RESUME, "rwn: rt2860_detach(): " 31059172SFei.Feng@Sun.COM "suspend now\n"); 31069172SFei.Feng@Sun.COM return (DDI_SUCCESS); 31079172SFei.Feng@Sun.COM default: 31089172SFei.Feng@Sun.COM return (DDI_FAILURE); 31099172SFei.Feng@Sun.COM } 31109172SFei.Feng@Sun.COM 31119172SFei.Feng@Sun.COM if (mac_disable(sc->sc_ic.ic_mach) != 0) 31129172SFei.Feng@Sun.COM return (DDI_FAILURE); 31139172SFei.Feng@Sun.COM 31149172SFei.Feng@Sun.COM rt2860_stop(sc); 31159172SFei.Feng@Sun.COM 31169172SFei.Feng@Sun.COM /* 31179172SFei.Feng@Sun.COM * Unregister from the MAC layer subsystem 31189172SFei.Feng@Sun.COM */ 31199172SFei.Feng@Sun.COM (void) mac_unregister(sc->sc_ic.ic_mach); 31209172SFei.Feng@Sun.COM 31219172SFei.Feng@Sun.COM ddi_remove_intr(devinfo, 0, sc->sc_iblock); 31229172SFei.Feng@Sun.COM ddi_remove_softintr(sc->sc_softintr_hdl); 31239172SFei.Feng@Sun.COM 31249172SFei.Feng@Sun.COM /* 31259172SFei.Feng@Sun.COM * detach ieee80211 layer 31269172SFei.Feng@Sun.COM */ 31279172SFei.Feng@Sun.COM ieee80211_detach(&sc->sc_ic); 31289172SFei.Feng@Sun.COM 31299172SFei.Feng@Sun.COM rt2860_free_tx_pool(sc); 31309172SFei.Feng@Sun.COM rt2860_free_rx_ring(sc, &sc->rxq); 31319172SFei.Feng@Sun.COM for (qid = 0; qid < 6; qid++) 31329172SFei.Feng@Sun.COM rt2860_free_tx_ring(sc, &sc->txq[qid]); 31339172SFei.Feng@Sun.COM 31349172SFei.Feng@Sun.COM ddi_regs_map_free(&sc->sc_io_handle); 31359172SFei.Feng@Sun.COM 31369172SFei.Feng@Sun.COM mutex_destroy(&sc->sc_genlock); 31379172SFei.Feng@Sun.COM mutex_destroy(&sc->sc_txlock); 31389172SFei.Feng@Sun.COM mutex_destroy(&sc->sc_rxlock); 31399172SFei.Feng@Sun.COM 31409172SFei.Feng@Sun.COM ddi_remove_minor_node(devinfo, NULL); 31419172SFei.Feng@Sun.COM ddi_soft_state_free(rt2860_soft_state_p, ddi_get_instance(devinfo)); 31429172SFei.Feng@Sun.COM 31439172SFei.Feng@Sun.COM return (DDI_SUCCESS); 31449172SFei.Feng@Sun.COM } 31459172SFei.Feng@Sun.COM 31469172SFei.Feng@Sun.COM int 31479172SFei.Feng@Sun.COM _info(struct modinfo *modinfop) 31489172SFei.Feng@Sun.COM { 31499172SFei.Feng@Sun.COM return (mod_info(&modlinkage, modinfop)); 31509172SFei.Feng@Sun.COM } 31519172SFei.Feng@Sun.COM 31529172SFei.Feng@Sun.COM int 31539172SFei.Feng@Sun.COM _init(void) 31549172SFei.Feng@Sun.COM { 31559172SFei.Feng@Sun.COM int status; 31569172SFei.Feng@Sun.COM 31579172SFei.Feng@Sun.COM status = ddi_soft_state_init(&rt2860_soft_state_p, 31589172SFei.Feng@Sun.COM sizeof (struct rt2860_softc), 1); 31599172SFei.Feng@Sun.COM if (status != 0) 31609172SFei.Feng@Sun.COM return (status); 31619172SFei.Feng@Sun.COM 31629172SFei.Feng@Sun.COM mac_init_ops(&rwn_dev_ops, "rwn"); 31639172SFei.Feng@Sun.COM status = mod_install(&modlinkage); 31649172SFei.Feng@Sun.COM if (status != 0) { 31659172SFei.Feng@Sun.COM mac_fini_ops(&rwn_dev_ops); 31669172SFei.Feng@Sun.COM ddi_soft_state_fini(&rt2860_soft_state_p); 31679172SFei.Feng@Sun.COM } 31689172SFei.Feng@Sun.COM return (status); 31699172SFei.Feng@Sun.COM } 31709172SFei.Feng@Sun.COM 31719172SFei.Feng@Sun.COM int 31729172SFei.Feng@Sun.COM _fini(void) 31739172SFei.Feng@Sun.COM { 31749172SFei.Feng@Sun.COM int status; 31759172SFei.Feng@Sun.COM 31769172SFei.Feng@Sun.COM status = mod_remove(&modlinkage); 31779172SFei.Feng@Sun.COM if (status == 0) { 31789172SFei.Feng@Sun.COM mac_fini_ops(&rwn_dev_ops); 31799172SFei.Feng@Sun.COM ddi_soft_state_fini(&rt2860_soft_state_p); 31809172SFei.Feng@Sun.COM } 31819172SFei.Feng@Sun.COM return (status); 31829172SFei.Feng@Sun.COM } 3183