19983SFei.Feng@Sun.COM /*
2*11878SVenu.Iyer@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
39983SFei.Feng@Sun.COM * Use is subject to license terms.
49983SFei.Feng@Sun.COM */
59983SFei.Feng@Sun.COM
69983SFei.Feng@Sun.COM /*
79983SFei.Feng@Sun.COM * Copyright (c) 2006
89983SFei.Feng@Sun.COM * Damien Bergamini <damien.bergamini@free.fr>
99983SFei.Feng@Sun.COM *
109983SFei.Feng@Sun.COM * Permission to use, copy, modify, and distribute this software for any
119983SFei.Feng@Sun.COM * purpose with or without fee is hereby granted, provided that the above
129983SFei.Feng@Sun.COM * copyright notice and this permission notice appear in all copies.
139983SFei.Feng@Sun.COM *
149983SFei.Feng@Sun.COM * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
159983SFei.Feng@Sun.COM * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
169983SFei.Feng@Sun.COM * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
179983SFei.Feng@Sun.COM * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
189983SFei.Feng@Sun.COM * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
199983SFei.Feng@Sun.COM * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
209983SFei.Feng@Sun.COM * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
219983SFei.Feng@Sun.COM */
229983SFei.Feng@Sun.COM
239983SFei.Feng@Sun.COM /*
249983SFei.Feng@Sun.COM * Ralink Technology RT2561, RT2561S and RT2661 chipset driver
259983SFei.Feng@Sun.COM * http://www.ralinktech.com/
269983SFei.Feng@Sun.COM */
279983SFei.Feng@Sun.COM
289983SFei.Feng@Sun.COM #include <sys/types.h>
299983SFei.Feng@Sun.COM #include <sys/byteorder.h>
309983SFei.Feng@Sun.COM #include <sys/conf.h>
319983SFei.Feng@Sun.COM #include <sys/cmn_err.h>
329983SFei.Feng@Sun.COM #include <sys/stat.h>
339983SFei.Feng@Sun.COM #include <sys/ddi.h>
349983SFei.Feng@Sun.COM #include <sys/sunddi.h>
359983SFei.Feng@Sun.COM #include <sys/strsubr.h>
369983SFei.Feng@Sun.COM #include <sys/ethernet.h>
379983SFei.Feng@Sun.COM #include <inet/common.h>
389983SFei.Feng@Sun.COM #include <inet/nd.h>
399983SFei.Feng@Sun.COM #include <inet/mi.h>
409983SFei.Feng@Sun.COM #include <sys/note.h>
419983SFei.Feng@Sun.COM #include <sys/stream.h>
429983SFei.Feng@Sun.COM #include <sys/strsun.h>
439983SFei.Feng@Sun.COM #include <sys/modctl.h>
449983SFei.Feng@Sun.COM #include <sys/devops.h>
459983SFei.Feng@Sun.COM #include <sys/dlpi.h>
469983SFei.Feng@Sun.COM #include <sys/mac_provider.h>
479983SFei.Feng@Sun.COM #include <sys/mac_wifi.h>
489983SFei.Feng@Sun.COM #include <sys/net80211.h>
499983SFei.Feng@Sun.COM #include <sys/net80211_proto.h>
509983SFei.Feng@Sun.COM #include <sys/varargs.h>
519983SFei.Feng@Sun.COM #include <sys/policy.h>
529983SFei.Feng@Sun.COM #include <sys/pci.h>
539983SFei.Feng@Sun.COM #include <sys/crypto/common.h>
549983SFei.Feng@Sun.COM #include <sys/crypto/api.h>
559983SFei.Feng@Sun.COM #include <inet/wifi_ioctl.h>
569983SFei.Feng@Sun.COM
579983SFei.Feng@Sun.COM #include "rt2661_reg.h"
589983SFei.Feng@Sun.COM #include "rt2661_var.h"
599983SFei.Feng@Sun.COM #include "rt2661_ucode.h"
609983SFei.Feng@Sun.COM
619983SFei.Feng@Sun.COM #define RT2661_DBG_80211 (1 << 0)
629983SFei.Feng@Sun.COM #define RT2661_DBG_DMA (1 << 1)
639983SFei.Feng@Sun.COM #define RT2661_DBG_EEPROM (1 << 2)
649983SFei.Feng@Sun.COM #define RT2661_DBG_FW (1 << 3)
659983SFei.Feng@Sun.COM #define RT2661_DBG_HW (1 << 4)
669983SFei.Feng@Sun.COM #define RT2661_DBG_INTR (1 << 5)
679983SFei.Feng@Sun.COM #define RT2661_DBG_RX (1 << 6)
689983SFei.Feng@Sun.COM #define RT2661_DBG_SCAN (1 << 7)
699983SFei.Feng@Sun.COM #define RT2661_DBG_TX (1 << 8)
709983SFei.Feng@Sun.COM #define RT2661_DBG_RADIO (1 << 9)
719983SFei.Feng@Sun.COM #define RT2661_DBG_RESUME (1 << 10)
729983SFei.Feng@Sun.COM #define RT2661_DBG_MSG (1 << 11)
739983SFei.Feng@Sun.COM
749983SFei.Feng@Sun.COM uint32_t rt2661_dbg_flags = 0;
759983SFei.Feng@Sun.COM
769983SFei.Feng@Sun.COM #ifdef DEBUG
779983SFei.Feng@Sun.COM #define RWD_DEBUG \
789983SFei.Feng@Sun.COM rt2661_debug
799983SFei.Feng@Sun.COM #else
809983SFei.Feng@Sun.COM #define RWD_DEBUG
819983SFei.Feng@Sun.COM #endif
829983SFei.Feng@Sun.COM
839983SFei.Feng@Sun.COM static void *rt2661_soft_state_p = NULL;
849983SFei.Feng@Sun.COM
859983SFei.Feng@Sun.COM static const uint8_t *ucode = NULL;
869983SFei.Feng@Sun.COM int usize;
879983SFei.Feng@Sun.COM
889983SFei.Feng@Sun.COM static const struct {
899983SFei.Feng@Sun.COM uint32_t reg;
909983SFei.Feng@Sun.COM uint32_t val;
919983SFei.Feng@Sun.COM } rt2661_def_mac[] = {
929983SFei.Feng@Sun.COM RT2661_DEF_MAC
939983SFei.Feng@Sun.COM };
949983SFei.Feng@Sun.COM
959983SFei.Feng@Sun.COM static const struct {
969983SFei.Feng@Sun.COM uint8_t reg;
979983SFei.Feng@Sun.COM uint8_t val;
989983SFei.Feng@Sun.COM } rt2661_def_bbp[] = {
999983SFei.Feng@Sun.COM RT2661_DEF_BBP
1009983SFei.Feng@Sun.COM };
1019983SFei.Feng@Sun.COM
1029983SFei.Feng@Sun.COM static const struct rfprog {
1039983SFei.Feng@Sun.COM uint8_t chan;
1049983SFei.Feng@Sun.COM uint32_t r1, r2, r3, r4;
1059983SFei.Feng@Sun.COM } rt2661_rf5225_1[] = {
1069983SFei.Feng@Sun.COM RT2661_RF5225_1
1079983SFei.Feng@Sun.COM }, rt2661_rf5225_2[] = {
1089983SFei.Feng@Sun.COM RT2661_RF5225_2
1099983SFei.Feng@Sun.COM };
1109983SFei.Feng@Sun.COM
1119983SFei.Feng@Sun.COM /*
1129983SFei.Feng@Sun.COM * PIO access attributes for registers
1139983SFei.Feng@Sun.COM */
1149983SFei.Feng@Sun.COM static ddi_device_acc_attr_t rt2661_csr_accattr = {
1159983SFei.Feng@Sun.COM DDI_DEVICE_ATTR_V0,
1169983SFei.Feng@Sun.COM DDI_STRUCTURE_LE_ACC,
1179983SFei.Feng@Sun.COM DDI_STRICTORDER_ACC
1189983SFei.Feng@Sun.COM };
1199983SFei.Feng@Sun.COM
1209983SFei.Feng@Sun.COM /*
1219983SFei.Feng@Sun.COM * DMA access attributes for descriptors: NOT to be byte swapped.
1229983SFei.Feng@Sun.COM */
1239983SFei.Feng@Sun.COM static ddi_device_acc_attr_t rt2661_desc_accattr = {
1249983SFei.Feng@Sun.COM DDI_DEVICE_ATTR_V0,
1259983SFei.Feng@Sun.COM DDI_STRUCTURE_LE_ACC,
1269983SFei.Feng@Sun.COM DDI_STRICTORDER_ACC
1279983SFei.Feng@Sun.COM };
1289983SFei.Feng@Sun.COM
1299983SFei.Feng@Sun.COM static ddi_device_acc_attr_t rt2661_buf_accattr = {
1309983SFei.Feng@Sun.COM DDI_DEVICE_ATTR_V0,
1319983SFei.Feng@Sun.COM DDI_NEVERSWAP_ACC,
1329983SFei.Feng@Sun.COM DDI_STRICTORDER_ACC,
1339983SFei.Feng@Sun.COM DDI_DEFAULT_ACC
1349983SFei.Feng@Sun.COM };
1359983SFei.Feng@Sun.COM
1369983SFei.Feng@Sun.COM /*
1379983SFei.Feng@Sun.COM * Describes the chip's DMA engine
1389983SFei.Feng@Sun.COM */
1399983SFei.Feng@Sun.COM static ddi_dma_attr_t rt2661_dma_attr = {
1409983SFei.Feng@Sun.COM DMA_ATTR_V0, /* dma_attr version */
1419983SFei.Feng@Sun.COM 0x0, /* dma_attr_addr_lo */
1429983SFei.Feng@Sun.COM 0xffffffffU, /* dma_attr_addr_hi */
1439983SFei.Feng@Sun.COM 0xffffffffU, /* dma_attr_count_max */
1449983SFei.Feng@Sun.COM 1, /* dma_attr_align */
1459983SFei.Feng@Sun.COM 0x00000fff, /* dma_attr_burstsizes */
1469983SFei.Feng@Sun.COM 1, /* dma_attr_minxfer */
1479983SFei.Feng@Sun.COM 0xffffffffU, /* dma_attr_maxxfer */
1489983SFei.Feng@Sun.COM 0xffffffffU, /* dma_attr_seg */
1499983SFei.Feng@Sun.COM 1, /* dma_attr_sgllen */
1509983SFei.Feng@Sun.COM 1, /* dma_attr_granular */
1519983SFei.Feng@Sun.COM 0 /* dma_attr_flags */
1529983SFei.Feng@Sun.COM };
1539983SFei.Feng@Sun.COM
1549983SFei.Feng@Sun.COM static const struct ieee80211_rateset rt2661_rateset_11b =
1559983SFei.Feng@Sun.COM { 4, { 2, 4, 11, 22 } };
1569983SFei.Feng@Sun.COM
1579983SFei.Feng@Sun.COM static const struct ieee80211_rateset rt2661_rateset_11g =
1589983SFei.Feng@Sun.COM { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
1599983SFei.Feng@Sun.COM
1609983SFei.Feng@Sun.COM
1619983SFei.Feng@Sun.COM static const char *rt2661_get_rf(int);
1629983SFei.Feng@Sun.COM
1639983SFei.Feng@Sun.COM static void rt2661_read_eeprom(struct rt2661_softc *);
1649983SFei.Feng@Sun.COM static uint16_t rt2661_eeprom_read(struct rt2661_softc *, uint8_t);
1659983SFei.Feng@Sun.COM static int rt2661_load_microcode(struct rt2661_softc *,
1669983SFei.Feng@Sun.COM const uint8_t *, int);
1679983SFei.Feng@Sun.COM
1689983SFei.Feng@Sun.COM static int rt2661_alloc_dma_mem(dev_info_t *, ddi_dma_attr_t *, size_t,
1699983SFei.Feng@Sun.COM ddi_device_acc_attr_t *, uint_t, uint_t, struct dma_area *);
1709983SFei.Feng@Sun.COM static void rt2661_free_dma_mem(struct dma_area *);
1719983SFei.Feng@Sun.COM static int rt2661_alloc_tx_ring(struct rt2661_softc *,
1729983SFei.Feng@Sun.COM struct rt2661_tx_ring *, int);
1739983SFei.Feng@Sun.COM static void rt2661_reset_tx_ring(struct rt2661_softc *,
1749983SFei.Feng@Sun.COM struct rt2661_tx_ring *);
1759983SFei.Feng@Sun.COM static void rt2661_free_tx_ring(struct rt2661_softc *,
1769983SFei.Feng@Sun.COM struct rt2661_tx_ring *);
1779983SFei.Feng@Sun.COM static int rt2661_alloc_rx_ring(struct rt2661_softc *,
1789983SFei.Feng@Sun.COM struct rt2661_rx_ring *, int);
1799983SFei.Feng@Sun.COM static void rt2661_reset_rx_ring(struct rt2661_softc *,
1809983SFei.Feng@Sun.COM struct rt2661_rx_ring *);
1819983SFei.Feng@Sun.COM static void rt2661_free_rx_ring(struct rt2661_softc *,
1829983SFei.Feng@Sun.COM struct rt2661_rx_ring *);
1839983SFei.Feng@Sun.COM static void rt2661_tx_dma_intr(struct rt2661_softc *,
1849983SFei.Feng@Sun.COM struct rt2661_tx_ring *);
1859983SFei.Feng@Sun.COM static void rt2661_tx_intr(struct rt2661_softc *);
1869983SFei.Feng@Sun.COM static void rt2661_rx_intr(struct rt2661_softc *);
1879983SFei.Feng@Sun.COM static uint_t rt2661_softintr(caddr_t, caddr_t);
1889983SFei.Feng@Sun.COM static void rt2661_mcu_wakeup(struct rt2661_softc *);
1899983SFei.Feng@Sun.COM static void rt2661_mcu_cmd_intr(struct rt2661_softc *);
1909983SFei.Feng@Sun.COM static uint_t rt2661_intr(caddr_t, caddr_t);
1919983SFei.Feng@Sun.COM
1929983SFei.Feng@Sun.COM static uint16_t rt2661_txtime(int, int, uint32_t);
1939983SFei.Feng@Sun.COM static int rt2661_ack_rate(struct ieee80211com *, int);
1949983SFei.Feng@Sun.COM static uint8_t rt2661_plcp_signal(int);
1959983SFei.Feng@Sun.COM static void rt2661_setup_tx_desc(struct rt2661_softc *,
1969983SFei.Feng@Sun.COM struct rt2661_tx_desc *, uint32_t, uint16_t, int,
1979983SFei.Feng@Sun.COM int, int);
1989983SFei.Feng@Sun.COM
1999983SFei.Feng@Sun.COM static int rt2661_get_rssi(struct rt2661_softc *, uint8_t);
2009983SFei.Feng@Sun.COM
2019983SFei.Feng@Sun.COM static int rt2661_send(ieee80211com_t *, mblk_t *);
2029983SFei.Feng@Sun.COM static int rt2661_mgmt_send(ieee80211com_t *, mblk_t *, uint8_t);
2039983SFei.Feng@Sun.COM
2049983SFei.Feng@Sun.COM static void rt2661_amrr_node_init(const struct rt2661_amrr *,
2059983SFei.Feng@Sun.COM struct rt2661_amrr_node *);
2069983SFei.Feng@Sun.COM static void rt2661_amrr_choose(struct rt2661_amrr *,
2079983SFei.Feng@Sun.COM struct ieee80211_node *, struct rt2661_amrr_node *);
2089983SFei.Feng@Sun.COM
2099983SFei.Feng@Sun.COM static void rt2661_update_promisc(struct rt2661_softc *);
2109983SFei.Feng@Sun.COM static void rt2661_updateslot(struct ieee80211com *, int);
2119983SFei.Feng@Sun.COM static void rt2661_set_slottime(struct rt2661_softc *);
2129983SFei.Feng@Sun.COM static void rt2661_enable_mrr(struct rt2661_softc *);
2139983SFei.Feng@Sun.COM static void rt2661_set_txpreamble(struct rt2661_softc *);
2149983SFei.Feng@Sun.COM static void rt2661_set_basicrates(struct rt2661_softc *);
2159983SFei.Feng@Sun.COM static void rt2661_set_bssid(struct rt2661_softc *, const uint8_t *);
2169983SFei.Feng@Sun.COM static void rt2661_newassoc(struct ieee80211com *, struct ieee80211_node *);
2179983SFei.Feng@Sun.COM static void rt2661_updatestats(void *);
2189983SFei.Feng@Sun.COM static void rt2661_rx_tune(struct rt2661_softc *);
2199983SFei.Feng@Sun.COM static void rt2661_enable_tsf_sync(struct rt2661_softc *);
2209983SFei.Feng@Sun.COM static int rt2661_newstate(struct ieee80211com *,
2219983SFei.Feng@Sun.COM enum ieee80211_state, int);
2229983SFei.Feng@Sun.COM
2239983SFei.Feng@Sun.COM static void rt2661_set_macaddr(struct rt2661_softc *, const uint8_t *);
2249983SFei.Feng@Sun.COM static int rt2661_bbp_init(struct rt2661_softc *);
2259983SFei.Feng@Sun.COM static uint8_t rt2661_bbp_read(struct rt2661_softc *, uint8_t);
2269983SFei.Feng@Sun.COM static void rt2661_bbp_write(struct rt2661_softc *, uint8_t, uint8_t);
2279983SFei.Feng@Sun.COM static void rt2661_select_band(struct rt2661_softc *,
2289983SFei.Feng@Sun.COM struct ieee80211_channel *);
2299983SFei.Feng@Sun.COM static void rt2661_select_antenna(struct rt2661_softc *);
2309983SFei.Feng@Sun.COM static void rt2661_rf_write(struct rt2661_softc *, uint8_t, uint32_t);
2319983SFei.Feng@Sun.COM static void rt2661_set_chan(struct rt2661_softc *,
2329983SFei.Feng@Sun.COM struct ieee80211_channel *);
2339983SFei.Feng@Sun.COM
2349983SFei.Feng@Sun.COM static void rt2661_stop_locked(struct rt2661_softc *);
2359983SFei.Feng@Sun.COM static int rt2661_init(struct rt2661_softc *);
2369983SFei.Feng@Sun.COM static void rt2661_stop(struct rt2661_softc *);
2379983SFei.Feng@Sun.COM /*
2389983SFei.Feng@Sun.COM * device operations
2399983SFei.Feng@Sun.COM */
2409983SFei.Feng@Sun.COM static int rt2661_attach(dev_info_t *, ddi_attach_cmd_t);
2419983SFei.Feng@Sun.COM static int rt2661_detach(dev_info_t *, ddi_detach_cmd_t);
2429983SFei.Feng@Sun.COM static int rt2661_quiesce(dev_info_t *);
2439983SFei.Feng@Sun.COM
2449983SFei.Feng@Sun.COM /*
2459983SFei.Feng@Sun.COM * Module Loading Data & Entry Points
2469983SFei.Feng@Sun.COM */
2479983SFei.Feng@Sun.COM DDI_DEFINE_STREAM_OPS(rwd_dev_ops, nulldev, nulldev, rt2661_attach,
2489983SFei.Feng@Sun.COM rt2661_detach, nodev, NULL, D_MP, NULL, rt2661_quiesce);
2499983SFei.Feng@Sun.COM
2509983SFei.Feng@Sun.COM static struct modldrv rwd_modldrv = {
2519983SFei.Feng@Sun.COM &mod_driverops, /* Type of module. This one is a driver */
2529983SFei.Feng@Sun.COM "Ralink RT2661 driver v1.1", /* short description */
2539983SFei.Feng@Sun.COM &rwd_dev_ops /* driver specific ops */
2549983SFei.Feng@Sun.COM };
2559983SFei.Feng@Sun.COM
2569983SFei.Feng@Sun.COM static struct modlinkage modlinkage = {
2579983SFei.Feng@Sun.COM MODREV_1,
2589983SFei.Feng@Sun.COM (void *)&rwd_modldrv,
2599983SFei.Feng@Sun.COM NULL
2609983SFei.Feng@Sun.COM };
2619983SFei.Feng@Sun.COM
2629983SFei.Feng@Sun.COM static int rt2661_m_stat(void *, uint_t, uint64_t *);
2639983SFei.Feng@Sun.COM static int rt2661_m_start(void *);
2649983SFei.Feng@Sun.COM static void rt2661_m_stop(void *);
2659983SFei.Feng@Sun.COM static int rt2661_m_promisc(void *, boolean_t);
2669983SFei.Feng@Sun.COM static int rt2661_m_multicst(void *, boolean_t, const uint8_t *);
2679983SFei.Feng@Sun.COM static int rt2661_m_unicst(void *, const uint8_t *);
2689983SFei.Feng@Sun.COM static mblk_t *rt2661_m_tx(void *, mblk_t *);
2699983SFei.Feng@Sun.COM static void rt2661_m_ioctl(void *, queue_t *, mblk_t *);
2709983SFei.Feng@Sun.COM static int rt2661_m_setprop(void *arg, const char *pr_name,
2719983SFei.Feng@Sun.COM mac_prop_id_t wldp_pr_num,
2729983SFei.Feng@Sun.COM uint_t wldp_length, const void *wldp_buf);
2739983SFei.Feng@Sun.COM static int rt2661_m_getprop(void *arg, const char *pr_name,
274*11878SVenu.Iyer@Sun.COM mac_prop_id_t wldp_pr_num, uint_t wldp_length,
275*11878SVenu.Iyer@Sun.COM void *wldp_buf);
276*11878SVenu.Iyer@Sun.COM static void rt2661_m_propinfo(void *arg, const char *pr_name,
277*11878SVenu.Iyer@Sun.COM mac_prop_id_t wldp_pr_num, mac_prop_info_handle_t mph);
2789983SFei.Feng@Sun.COM
2799983SFei.Feng@Sun.COM static mac_callbacks_t rt2661_m_callbacks = {
280*11878SVenu.Iyer@Sun.COM MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
2819983SFei.Feng@Sun.COM rt2661_m_stat,
2829983SFei.Feng@Sun.COM rt2661_m_start,
2839983SFei.Feng@Sun.COM rt2661_m_stop,
2849983SFei.Feng@Sun.COM rt2661_m_promisc,
2859983SFei.Feng@Sun.COM rt2661_m_multicst,
2869983SFei.Feng@Sun.COM rt2661_m_unicst,
2879983SFei.Feng@Sun.COM rt2661_m_tx,
288*11878SVenu.Iyer@Sun.COM NULL,
2899983SFei.Feng@Sun.COM rt2661_m_ioctl,
2909983SFei.Feng@Sun.COM NULL,
2919983SFei.Feng@Sun.COM NULL,
2929983SFei.Feng@Sun.COM NULL,
2939983SFei.Feng@Sun.COM rt2661_m_setprop,
294*11878SVenu.Iyer@Sun.COM rt2661_m_getprop,
295*11878SVenu.Iyer@Sun.COM rt2661_m_propinfo
2969983SFei.Feng@Sun.COM };
2979983SFei.Feng@Sun.COM
2989983SFei.Feng@Sun.COM #ifdef DEBUG
2999983SFei.Feng@Sun.COM void
rt2661_debug(uint32_t dbg_flags,const int8_t * fmt,...)3009983SFei.Feng@Sun.COM rt2661_debug(uint32_t dbg_flags, const int8_t *fmt, ...)
3019983SFei.Feng@Sun.COM {
3029983SFei.Feng@Sun.COM va_list args;
3039983SFei.Feng@Sun.COM
3049983SFei.Feng@Sun.COM if (dbg_flags & rt2661_dbg_flags) {
3059983SFei.Feng@Sun.COM va_start(args, fmt);
3069983SFei.Feng@Sun.COM vcmn_err(CE_CONT, fmt, args);
3079983SFei.Feng@Sun.COM va_end(args);
3089983SFei.Feng@Sun.COM }
3099983SFei.Feng@Sun.COM }
3109983SFei.Feng@Sun.COM #endif
3119983SFei.Feng@Sun.COM
3129983SFei.Feng@Sun.COM /*
3139983SFei.Feng@Sun.COM * Read 16 bits at address 'addr' from the serial EEPROM (either 93C46 or
3149983SFei.Feng@Sun.COM * 93C66).
3159983SFei.Feng@Sun.COM */
3169983SFei.Feng@Sun.COM static uint16_t
rt2661_eeprom_read(struct rt2661_softc * sc,uint8_t addr)3179983SFei.Feng@Sun.COM rt2661_eeprom_read(struct rt2661_softc *sc, uint8_t addr)
3189983SFei.Feng@Sun.COM {
3199983SFei.Feng@Sun.COM uint32_t tmp;
3209983SFei.Feng@Sun.COM uint16_t val;
3219983SFei.Feng@Sun.COM int n;
3229983SFei.Feng@Sun.COM
3239983SFei.Feng@Sun.COM /* clock C once before the first command */
3249983SFei.Feng@Sun.COM RT2661_EEPROM_CTL(sc, 0);
3259983SFei.Feng@Sun.COM
3269983SFei.Feng@Sun.COM RT2661_EEPROM_CTL(sc, RT2661_S);
3279983SFei.Feng@Sun.COM RT2661_EEPROM_CTL(sc, RT2661_S | RT2661_C);
3289983SFei.Feng@Sun.COM RT2661_EEPROM_CTL(sc, RT2661_S);
3299983SFei.Feng@Sun.COM
3309983SFei.Feng@Sun.COM /* write start bit (1) */
3319983SFei.Feng@Sun.COM RT2661_EEPROM_CTL(sc, RT2661_S | RT2661_D);
3329983SFei.Feng@Sun.COM RT2661_EEPROM_CTL(sc, RT2661_S | RT2661_D | RT2661_C);
3339983SFei.Feng@Sun.COM
3349983SFei.Feng@Sun.COM /* write READ opcode (10) */
3359983SFei.Feng@Sun.COM RT2661_EEPROM_CTL(sc, RT2661_S | RT2661_D);
3369983SFei.Feng@Sun.COM RT2661_EEPROM_CTL(sc, RT2661_S | RT2661_D | RT2661_C);
3379983SFei.Feng@Sun.COM RT2661_EEPROM_CTL(sc, RT2661_S);
3389983SFei.Feng@Sun.COM RT2661_EEPROM_CTL(sc, RT2661_S | RT2661_C);
3399983SFei.Feng@Sun.COM
3409983SFei.Feng@Sun.COM /* write address (A5-A0 or A7-A0) */
3419983SFei.Feng@Sun.COM n = (RT2661_READ(sc, RT2661_E2PROM_CSR) & RT2661_93C46) ? 5 : 7;
3429983SFei.Feng@Sun.COM for (; n >= 0; n--) {
3439983SFei.Feng@Sun.COM RT2661_EEPROM_CTL(sc, RT2661_S |
3449983SFei.Feng@Sun.COM (((addr >> n) & 1) << RT2661_SHIFT_D));
3459983SFei.Feng@Sun.COM RT2661_EEPROM_CTL(sc, RT2661_S |
3469983SFei.Feng@Sun.COM (((addr >> n) & 1) << RT2661_SHIFT_D) | RT2661_C);
3479983SFei.Feng@Sun.COM }
3489983SFei.Feng@Sun.COM
3499983SFei.Feng@Sun.COM RT2661_EEPROM_CTL(sc, RT2661_S);
3509983SFei.Feng@Sun.COM
3519983SFei.Feng@Sun.COM /* read data Q15-Q0 */
3529983SFei.Feng@Sun.COM val = 0;
3539983SFei.Feng@Sun.COM for (n = 15; n >= 0; n--) {
3549983SFei.Feng@Sun.COM RT2661_EEPROM_CTL(sc, RT2661_S | RT2661_C);
3559983SFei.Feng@Sun.COM tmp = RT2661_READ(sc, RT2661_E2PROM_CSR);
3569983SFei.Feng@Sun.COM val |= ((tmp & RT2661_Q) >> RT2661_SHIFT_Q) << n;
3579983SFei.Feng@Sun.COM RT2661_EEPROM_CTL(sc, RT2661_S);
3589983SFei.Feng@Sun.COM }
3599983SFei.Feng@Sun.COM
3609983SFei.Feng@Sun.COM RT2661_EEPROM_CTL(sc, 0);
3619983SFei.Feng@Sun.COM
3629983SFei.Feng@Sun.COM /* clear Chip Select and clock C */
3639983SFei.Feng@Sun.COM RT2661_EEPROM_CTL(sc, RT2661_S);
3649983SFei.Feng@Sun.COM RT2661_EEPROM_CTL(sc, 0);
3659983SFei.Feng@Sun.COM RT2661_EEPROM_CTL(sc, RT2661_C);
3669983SFei.Feng@Sun.COM
3679983SFei.Feng@Sun.COM return (val);
3689983SFei.Feng@Sun.COM }
3699983SFei.Feng@Sun.COM
3709983SFei.Feng@Sun.COM
3719983SFei.Feng@Sun.COM static void
rt2661_read_eeprom(struct rt2661_softc * sc)3729983SFei.Feng@Sun.COM rt2661_read_eeprom(struct rt2661_softc *sc)
3739983SFei.Feng@Sun.COM {
3749983SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
3759983SFei.Feng@Sun.COM uint16_t val;
3769983SFei.Feng@Sun.COM int i;
3779983SFei.Feng@Sun.COM
3789983SFei.Feng@Sun.COM /* read MAC address */
3799983SFei.Feng@Sun.COM val = rt2661_eeprom_read(sc, RT2661_EEPROM_MAC01);
3809983SFei.Feng@Sun.COM ic->ic_macaddr[0] = val & 0xff;
3819983SFei.Feng@Sun.COM ic->ic_macaddr[1] = val >> 8;
3829983SFei.Feng@Sun.COM
3839983SFei.Feng@Sun.COM val = rt2661_eeprom_read(sc, RT2661_EEPROM_MAC23);
3849983SFei.Feng@Sun.COM ic->ic_macaddr[2] = val & 0xff;
3859983SFei.Feng@Sun.COM ic->ic_macaddr[3] = val >> 8;
3869983SFei.Feng@Sun.COM
3879983SFei.Feng@Sun.COM val = rt2661_eeprom_read(sc, RT2661_EEPROM_MAC45);
3889983SFei.Feng@Sun.COM ic->ic_macaddr[4] = val & 0xff;
3899983SFei.Feng@Sun.COM ic->ic_macaddr[5] = val >> 8;
3909983SFei.Feng@Sun.COM
3919983SFei.Feng@Sun.COM val = rt2661_eeprom_read(sc, RT2661_EEPROM_ANTENNA);
3929983SFei.Feng@Sun.COM /* XXX: test if different from 0xffff? */
3939983SFei.Feng@Sun.COM sc->rf_rev = (val >> 11) & 0x1f;
3949983SFei.Feng@Sun.COM sc->hw_radio = (val >> 10) & 0x1;
3959983SFei.Feng@Sun.COM sc->rx_ant = (val >> 4) & 0x3;
3969983SFei.Feng@Sun.COM sc->tx_ant = (val >> 2) & 0x3;
3979983SFei.Feng@Sun.COM sc->nb_ant = val & 0x3;
3989983SFei.Feng@Sun.COM
3999983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_read_eeprom(): "
4009983SFei.Feng@Sun.COM "RF revision=%d\n", sc->rf_rev);
4019983SFei.Feng@Sun.COM
4029983SFei.Feng@Sun.COM val = rt2661_eeprom_read(sc, RT2661_EEPROM_CONFIG2);
4039983SFei.Feng@Sun.COM sc->ext_5ghz_lna = (val >> 6) & 0x1;
4049983SFei.Feng@Sun.COM sc->ext_2ghz_lna = (val >> 4) & 0x1;
4059983SFei.Feng@Sun.COM
4069983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_read_eeprom(): "
4079983SFei.Feng@Sun.COM "External 2GHz LNA=%d\nExternal 5GHz LNA=%d\n",
4089983SFei.Feng@Sun.COM sc->ext_2ghz_lna, sc->ext_5ghz_lna);
4099983SFei.Feng@Sun.COM
4109983SFei.Feng@Sun.COM val = rt2661_eeprom_read(sc, RT2661_EEPROM_RSSI_2GHZ_OFFSET);
4119983SFei.Feng@Sun.COM if ((val & 0xff) != 0xff)
4129983SFei.Feng@Sun.COM sc->rssi_2ghz_corr = (int8_t)(val & 0xff);
4139983SFei.Feng@Sun.COM
4149983SFei.Feng@Sun.COM val = rt2661_eeprom_read(sc, RT2661_EEPROM_RSSI_5GHZ_OFFSET);
4159983SFei.Feng@Sun.COM if ((val & 0xff) != 0xff)
4169983SFei.Feng@Sun.COM sc->rssi_5ghz_corr = (int8_t)(val & 0xff);
4179983SFei.Feng@Sun.COM
4189983SFei.Feng@Sun.COM /* adjust RSSI correction for external low-noise amplifier */
4199983SFei.Feng@Sun.COM if (sc->ext_2ghz_lna)
4209983SFei.Feng@Sun.COM sc->rssi_2ghz_corr -= 14;
4219983SFei.Feng@Sun.COM if (sc->ext_5ghz_lna)
4229983SFei.Feng@Sun.COM sc->rssi_5ghz_corr -= 14;
4239983SFei.Feng@Sun.COM
4249983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_read_eeprom(): "
4259983SFei.Feng@Sun.COM "RSSI 2GHz corr=%d\nRSSI 5GHz corr=%d\n",
4269983SFei.Feng@Sun.COM sc->rssi_2ghz_corr, sc->rssi_5ghz_corr);
4279983SFei.Feng@Sun.COM
4289983SFei.Feng@Sun.COM val = rt2661_eeprom_read(sc, RT2661_EEPROM_FREQ_OFFSET);
4299983SFei.Feng@Sun.COM if ((val >> 8) != 0xff)
4309983SFei.Feng@Sun.COM sc->rfprog = (val >> 8) & 0x3;
4319983SFei.Feng@Sun.COM if ((val & 0xff) != 0xff)
4329983SFei.Feng@Sun.COM sc->rffreq = val & 0xff;
4339983SFei.Feng@Sun.COM
4349983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_read_eeprom(): "
4359983SFei.Feng@Sun.COM "RF prog=%d\nRF freq=%d\n", sc->rfprog, sc->rffreq);
4369983SFei.Feng@Sun.COM
4379983SFei.Feng@Sun.COM /* read Tx power for all a/b/g channels */
4389983SFei.Feng@Sun.COM for (i = 0; i < 19; i++) {
4399983SFei.Feng@Sun.COM val = rt2661_eeprom_read(sc, RT2661_EEPROM_TXPOWER + i);
4409983SFei.Feng@Sun.COM sc->txpow[i * 2] = (int8_t)(val >> 8);
4419983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_read_eeprom(): "
4429983SFei.Feng@Sun.COM "Channel=%d Tx power=%d\n",
4439983SFei.Feng@Sun.COM rt2661_rf5225_1[i * 2].chan, sc->txpow[i * 2]);
4449983SFei.Feng@Sun.COM sc->txpow[i * 2 + 1] = (int8_t)(val & 0xff);
4459983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_read_eeprom(): "
4469983SFei.Feng@Sun.COM "Channel=%d Tx power=%d\n",
4479983SFei.Feng@Sun.COM rt2661_rf5225_1[i * 2 + 1].chan, sc->txpow[i * 2 + 1]);
4489983SFei.Feng@Sun.COM }
4499983SFei.Feng@Sun.COM
4509983SFei.Feng@Sun.COM /* read vendor-specific BBP values */
4519983SFei.Feng@Sun.COM for (i = 0; i < 16; i++) {
4529983SFei.Feng@Sun.COM val = rt2661_eeprom_read(sc, RT2661_EEPROM_BBP_BASE + i);
4539983SFei.Feng@Sun.COM if (val == 0 || val == 0xffff)
4549983SFei.Feng@Sun.COM continue;
4559983SFei.Feng@Sun.COM sc->bbp_prom[i].reg = val >> 8;
4569983SFei.Feng@Sun.COM sc->bbp_prom[i].val = val & 0xff;
4579983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_read_eeprom(): "
4589983SFei.Feng@Sun.COM "BBP R%d=%02x\n", sc->bbp_prom[i].reg,
4599983SFei.Feng@Sun.COM sc->bbp_prom[i].val);
4609983SFei.Feng@Sun.COM }
4619983SFei.Feng@Sun.COM }
4629983SFei.Feng@Sun.COM
4639983SFei.Feng@Sun.COM static const char *
rt2661_get_rf(int rev)4649983SFei.Feng@Sun.COM rt2661_get_rf(int rev)
4659983SFei.Feng@Sun.COM {
4669983SFei.Feng@Sun.COM switch (rev) {
4679983SFei.Feng@Sun.COM case RT2661_RF_5225: return "RT5225";
4689983SFei.Feng@Sun.COM case RT2661_RF_5325: return "RT5325 (MIMO XR)";
4699983SFei.Feng@Sun.COM case RT2661_RF_2527: return "RT2527";
4709983SFei.Feng@Sun.COM case RT2661_RF_2529: return "RT2529 (MIMO XR)";
4719983SFei.Feng@Sun.COM default: return "unknown";
4729983SFei.Feng@Sun.COM }
4739983SFei.Feng@Sun.COM }
4749983SFei.Feng@Sun.COM
4759983SFei.Feng@Sun.COM static int
rt2661_load_microcode(struct rt2661_softc * sc,const uint8_t * ucode_p,int size)4769983SFei.Feng@Sun.COM rt2661_load_microcode(struct rt2661_softc *sc, const uint8_t *ucode_p, int size)
4779983SFei.Feng@Sun.COM {
4789983SFei.Feng@Sun.COM int ntries;
4799983SFei.Feng@Sun.COM uint32_t off, i;
4809983SFei.Feng@Sun.COM const uint8_t *fptr;
4819983SFei.Feng@Sun.COM
4829983SFei.Feng@Sun.COM fptr = ucode_p;
4839983SFei.Feng@Sun.COM off = RT2661_MCU_CODE_BASE;
4849983SFei.Feng@Sun.COM
4859983SFei.Feng@Sun.COM /* reset 8051 */
4869983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MCU_CNTL_CSR, RT2661_MCU_RESET);
4879983SFei.Feng@Sun.COM
4889983SFei.Feng@Sun.COM /* cancel any pending Host to MCU command */
4899983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_H2M_MAILBOX_CSR, 0);
4909983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_M2H_CMD_DONE_CSR, 0xffffffff);
4919983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_HOST_CMD_CSR, 0);
4929983SFei.Feng@Sun.COM
4939983SFei.Feng@Sun.COM /* write 8051's microcode */
4949983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MCU_CNTL_CSR,
4959983SFei.Feng@Sun.COM RT2661_MCU_RESET | RT2661_MCU_SEL);
4969983SFei.Feng@Sun.COM /* RT2661_WRITE_REGION_1(sc, RT2661_MCU_CODE_BASE, ucode, size); */
4979983SFei.Feng@Sun.COM
4989983SFei.Feng@Sun.COM for (i = 0; i < size; i++) {
4999983SFei.Feng@Sun.COM RT2661_MEM_WRITE1(sc, off++, *fptr++);
5009983SFei.Feng@Sun.COM }
5019983SFei.Feng@Sun.COM
5029983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MCU_CNTL_CSR, RT2661_MCU_RESET);
5039983SFei.Feng@Sun.COM
5049983SFei.Feng@Sun.COM /* kick 8051's ass */
5059983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MCU_CNTL_CSR, 0);
5069983SFei.Feng@Sun.COM
5079983SFei.Feng@Sun.COM /* wait for 8051 to initialize */
5089983SFei.Feng@Sun.COM for (ntries = 0; ntries < 500; ntries++) {
5099983SFei.Feng@Sun.COM if (RT2661_READ(sc, RT2661_MCU_CNTL_CSR) & RT2661_MCU_READY)
5109983SFei.Feng@Sun.COM break;
5119983SFei.Feng@Sun.COM DELAY(100);
5129983SFei.Feng@Sun.COM }
5139983SFei.Feng@Sun.COM if (ntries == 500) {
5149983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_load_microcode(): "
5159983SFei.Feng@Sun.COM "timeout waiting for MCU to initialize\n");
5169983SFei.Feng@Sun.COM return (RT2661_FAILURE);
5179983SFei.Feng@Sun.COM }
5189983SFei.Feng@Sun.COM
5199983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_load_microcode(): "
5209983SFei.Feng@Sun.COM "MCU initialized successfully\n");
5219983SFei.Feng@Sun.COM return (RT2661_SUCCESS);
5229983SFei.Feng@Sun.COM }
5239983SFei.Feng@Sun.COM
5249983SFei.Feng@Sun.COM /*
5259983SFei.Feng@Sun.COM * Allocate an DMA memory and a DMA handle for accessing it
5269983SFei.Feng@Sun.COM */
5279983SFei.Feng@Sun.COM static int
rt2661_alloc_dma_mem(dev_info_t * devinfo,ddi_dma_attr_t * dma_attr,size_t memsize,ddi_device_acc_attr_t * attr_p,uint_t alloc_flags,uint_t bind_flags,struct dma_area * dma_p)5289983SFei.Feng@Sun.COM rt2661_alloc_dma_mem(dev_info_t *devinfo, ddi_dma_attr_t *dma_attr,
5299983SFei.Feng@Sun.COM size_t memsize, ddi_device_acc_attr_t *attr_p, uint_t alloc_flags,
5309983SFei.Feng@Sun.COM uint_t bind_flags, struct dma_area *dma_p)
5319983SFei.Feng@Sun.COM {
5329983SFei.Feng@Sun.COM int err;
5339983SFei.Feng@Sun.COM
5349983SFei.Feng@Sun.COM /*
5359983SFei.Feng@Sun.COM * Allocate handle
5369983SFei.Feng@Sun.COM */
5379983SFei.Feng@Sun.COM err = ddi_dma_alloc_handle(devinfo, dma_attr,
5389983SFei.Feng@Sun.COM DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl);
5399983SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
5409983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rwd_allo_dma_mem(): "
5419983SFei.Feng@Sun.COM "failed to alloc handle\n");
5429983SFei.Feng@Sun.COM goto fail1;
5439983SFei.Feng@Sun.COM }
5449983SFei.Feng@Sun.COM
5459983SFei.Feng@Sun.COM /*
5469983SFei.Feng@Sun.COM * Allocate memory
5479983SFei.Feng@Sun.COM */
5489983SFei.Feng@Sun.COM err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize, attr_p,
5499983SFei.Feng@Sun.COM alloc_flags, DDI_DMA_SLEEP, NULL, &dma_p->mem_va,
5509983SFei.Feng@Sun.COM &dma_p->alength, &dma_p->acc_hdl);
5519983SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
5529983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rwd_alloc_dma_mem(): "
5539983SFei.Feng@Sun.COM "failed to alloc mem\n");
5549983SFei.Feng@Sun.COM goto fail2;
5559983SFei.Feng@Sun.COM }
5569983SFei.Feng@Sun.COM
5579983SFei.Feng@Sun.COM /*
5589983SFei.Feng@Sun.COM * Bind the two together
5599983SFei.Feng@Sun.COM */
5609983SFei.Feng@Sun.COM err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL,
5619983SFei.Feng@Sun.COM dma_p->mem_va, dma_p->alength, bind_flags,
5629983SFei.Feng@Sun.COM DDI_DMA_SLEEP, NULL, &dma_p->cookie, &dma_p->ncookies);
5639983SFei.Feng@Sun.COM if (err != DDI_DMA_MAPPED) {
5649983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rwd_alloc_dma_mem(): "
5659983SFei.Feng@Sun.COM "failed to bind handle\n");
5669983SFei.Feng@Sun.COM goto fail3;
5679983SFei.Feng@Sun.COM }
5689983SFei.Feng@Sun.COM
5699983SFei.Feng@Sun.COM if (dma_p->ncookies != 1) {
5709983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rwd_alloc_dma_mem(): "
5719983SFei.Feng@Sun.COM "failed to alloc cookies\n");
5729983SFei.Feng@Sun.COM goto fail4;
5739983SFei.Feng@Sun.COM }
5749983SFei.Feng@Sun.COM
5759983SFei.Feng@Sun.COM dma_p->nslots = ~0U;
5769983SFei.Feng@Sun.COM dma_p->size = ~0U;
5779983SFei.Feng@Sun.COM dma_p->token = ~0U;
5789983SFei.Feng@Sun.COM dma_p->offset = 0;
5799983SFei.Feng@Sun.COM return (DDI_SUCCESS);
5809983SFei.Feng@Sun.COM
5819983SFei.Feng@Sun.COM fail4:
5829983SFei.Feng@Sun.COM (void) ddi_dma_unbind_handle(dma_p->dma_hdl);
5839983SFei.Feng@Sun.COM fail3:
5849983SFei.Feng@Sun.COM ddi_dma_mem_free(&dma_p->acc_hdl);
5859983SFei.Feng@Sun.COM fail2:
5869983SFei.Feng@Sun.COM ddi_dma_free_handle(&dma_p->dma_hdl);
5879983SFei.Feng@Sun.COM fail1:
5889983SFei.Feng@Sun.COM return (err);
5899983SFei.Feng@Sun.COM }
5909983SFei.Feng@Sun.COM
5919983SFei.Feng@Sun.COM static void
rt2661_free_dma_mem(struct dma_area * dma_p)5929983SFei.Feng@Sun.COM rt2661_free_dma_mem(struct dma_area *dma_p)
5939983SFei.Feng@Sun.COM {
5949983SFei.Feng@Sun.COM if (dma_p->dma_hdl != NULL) {
5959983SFei.Feng@Sun.COM (void) ddi_dma_unbind_handle(dma_p->dma_hdl);
5969983SFei.Feng@Sun.COM if (dma_p->acc_hdl != NULL) {
5979983SFei.Feng@Sun.COM ddi_dma_mem_free(&dma_p->acc_hdl);
5989983SFei.Feng@Sun.COM dma_p->acc_hdl = NULL;
5999983SFei.Feng@Sun.COM }
6009983SFei.Feng@Sun.COM ddi_dma_free_handle(&dma_p->dma_hdl);
6019983SFei.Feng@Sun.COM dma_p->ncookies = 0;
6029983SFei.Feng@Sun.COM dma_p->dma_hdl = NULL;
6039983SFei.Feng@Sun.COM }
6049983SFei.Feng@Sun.COM }
6059983SFei.Feng@Sun.COM
6069983SFei.Feng@Sun.COM /*ARGSUSED*/
6079983SFei.Feng@Sun.COM static int
rt2661_alloc_tx_ring(struct rt2661_softc * sc,struct rt2661_tx_ring * ring,int count)6089983SFei.Feng@Sun.COM rt2661_alloc_tx_ring(struct rt2661_softc *sc,
6099983SFei.Feng@Sun.COM struct rt2661_tx_ring *ring, int count)
6109983SFei.Feng@Sun.COM {
6119983SFei.Feng@Sun.COM struct rt2661_tx_desc *desc;
6129983SFei.Feng@Sun.COM struct rt2661_tx_data *data;
6139983SFei.Feng@Sun.COM int i, err, size, len;
6149983SFei.Feng@Sun.COM
6159983SFei.Feng@Sun.COM size = count * RT2661_TX_DESC_SIZE;
6169983SFei.Feng@Sun.COM len = count * sizeof (struct rt2661_tx_data);
6179983SFei.Feng@Sun.COM
6189983SFei.Feng@Sun.COM ring->count = count;
6199983SFei.Feng@Sun.COM ring->queued = 0;
6209983SFei.Feng@Sun.COM ring->cur = 0;
6219983SFei.Feng@Sun.COM ring->next = 0;
6229983SFei.Feng@Sun.COM ring->stat = 0;
6239983SFei.Feng@Sun.COM
6249983SFei.Feng@Sun.COM err = rt2661_alloc_dma_mem(sc->sc_dev, &rt2661_dma_attr, size,
6259983SFei.Feng@Sun.COM &rt2661_desc_accattr, DDI_DMA_CONSISTENT,
6269983SFei.Feng@Sun.COM DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
6279983SFei.Feng@Sun.COM &ring->txdesc_dma);
6289983SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
6299983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_DMA, "rwd: rt2661_alloc_tx_ring(): "
6309983SFei.Feng@Sun.COM "failed to alloc dma mem\n");
6319983SFei.Feng@Sun.COM goto fail1;
6329983SFei.Feng@Sun.COM }
6339983SFei.Feng@Sun.COM
6349983SFei.Feng@Sun.COM ring->desc = (struct rt2661_tx_desc *)ring->txdesc_dma.mem_va;
6359983SFei.Feng@Sun.COM (void) bzero(ring->desc, size);
6369983SFei.Feng@Sun.COM ring->paddr = ring->txdesc_dma.cookie.dmac_address;
6379983SFei.Feng@Sun.COM
6389983SFei.Feng@Sun.COM ring->data = kmem_zalloc(len, KM_NOSLEEP);
6399983SFei.Feng@Sun.COM if (ring->data == NULL) {
6409983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_DMA, "rwd: rt2661_alloc_tx_ring(): "
6419983SFei.Feng@Sun.COM "failed to alloc tx buffer\n");
6429983SFei.Feng@Sun.COM goto fail2;
6439983SFei.Feng@Sun.COM }
6449983SFei.Feng@Sun.COM
6459983SFei.Feng@Sun.COM for (i = 0; i < count; i++) {
6469983SFei.Feng@Sun.COM desc = &ring->desc[i];
6479983SFei.Feng@Sun.COM data = &ring->data[i];
6489983SFei.Feng@Sun.COM err = rt2661_alloc_dma_mem(sc->sc_dev,
6499983SFei.Feng@Sun.COM &rt2661_dma_attr, sc->sc_dmabuf_size,
6509983SFei.Feng@Sun.COM &rt2661_buf_accattr, DDI_DMA_CONSISTENT,
6519983SFei.Feng@Sun.COM DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
6529983SFei.Feng@Sun.COM &data->txdata_dma);
6539983SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
6549983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_DMA,
6559983SFei.Feng@Sun.COM "rwd: rt2661_alloc_tx_ring(): "
6569983SFei.Feng@Sun.COM "failed to alloc tx buffer dma\n");
6579983SFei.Feng@Sun.COM while (i >= 0) {
6589983SFei.Feng@Sun.COM rt2661_free_dma_mem(&ring->data[i].txdata_dma);
6599983SFei.Feng@Sun.COM i--;
6609983SFei.Feng@Sun.COM }
6619983SFei.Feng@Sun.COM goto fail3;
6629983SFei.Feng@Sun.COM }
6639983SFei.Feng@Sun.COM desc->addr[0] = data->txdata_dma.cookie.dmac_address;
6649983SFei.Feng@Sun.COM data->buf = data->txdata_dma.mem_va;
6659983SFei.Feng@Sun.COM data->paddr = data->txdata_dma.cookie.dmac_address;
6669983SFei.Feng@Sun.COM }
6679983SFei.Feng@Sun.COM
6689983SFei.Feng@Sun.COM (void) ddi_dma_sync(ring->txdesc_dma.dma_hdl,
6699983SFei.Feng@Sun.COM 0, size, DDI_DMA_SYNC_FORDEV);
6709983SFei.Feng@Sun.COM return (DDI_SUCCESS);
6719983SFei.Feng@Sun.COM fail3:
6729983SFei.Feng@Sun.COM if (ring->data)
6739983SFei.Feng@Sun.COM kmem_free(ring->data,
6749983SFei.Feng@Sun.COM count * sizeof (struct rt2661_tx_data));
6759983SFei.Feng@Sun.COM fail2:
6769983SFei.Feng@Sun.COM rt2661_free_dma_mem(&ring->txdesc_dma);
6779983SFei.Feng@Sun.COM fail1:
6789983SFei.Feng@Sun.COM return (err);
6799983SFei.Feng@Sun.COM }
6809983SFei.Feng@Sun.COM
6819983SFei.Feng@Sun.COM static void
rt2661_reset_tx_ring(struct rt2661_softc * sc,struct rt2661_tx_ring * ring)6829983SFei.Feng@Sun.COM rt2661_reset_tx_ring(struct rt2661_softc *sc, struct rt2661_tx_ring *ring)
6839983SFei.Feng@Sun.COM {
6849983SFei.Feng@Sun.COM struct rt2661_tx_desc *desc;
6859983SFei.Feng@Sun.COM struct rt2661_tx_data *data;
6869983SFei.Feng@Sun.COM int i;
6879983SFei.Feng@Sun.COM
6889983SFei.Feng@Sun.COM for (i = 0; i < ring->count; i++) {
6899983SFei.Feng@Sun.COM desc = &ring->desc[i];
6909983SFei.Feng@Sun.COM data = &ring->data[i];
6919983SFei.Feng@Sun.COM
6929983SFei.Feng@Sun.COM if (data->ni != NULL) {
6939983SFei.Feng@Sun.COM ieee80211_free_node(data->ni);
6949983SFei.Feng@Sun.COM data->ni = NULL;
6959983SFei.Feng@Sun.COM }
6969983SFei.Feng@Sun.COM
6979983SFei.Feng@Sun.COM desc->flags = 0;
6989983SFei.Feng@Sun.COM }
6999983SFei.Feng@Sun.COM
7009983SFei.Feng@Sun.COM if (!RT2661_IS_FASTREBOOT(sc))
7019983SFei.Feng@Sun.COM (void) ddi_dma_sync(ring->txdesc_dma.dma_hdl, 0,
7029983SFei.Feng@Sun.COM ring->count * sizeof (struct rt2661_tx_desc),
7039983SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV);
7049983SFei.Feng@Sun.COM
7059983SFei.Feng@Sun.COM ring->queued = 0;
7069983SFei.Feng@Sun.COM ring->cur = ring->next = ring->stat = 0;
7079983SFei.Feng@Sun.COM }
7089983SFei.Feng@Sun.COM
7099983SFei.Feng@Sun.COM
7109983SFei.Feng@Sun.COM /*ARGSUSED*/
7119983SFei.Feng@Sun.COM static void
rt2661_free_tx_ring(struct rt2661_softc * sc,struct rt2661_tx_ring * ring)7129983SFei.Feng@Sun.COM rt2661_free_tx_ring(struct rt2661_softc *sc, struct rt2661_tx_ring *ring)
7139983SFei.Feng@Sun.COM {
7149983SFei.Feng@Sun.COM struct rt2661_tx_data *data;
7159983SFei.Feng@Sun.COM int i;
7169983SFei.Feng@Sun.COM
7179983SFei.Feng@Sun.COM if (ring->desc != NULL) {
7189983SFei.Feng@Sun.COM rt2661_free_dma_mem(&ring->txdesc_dma);
7199983SFei.Feng@Sun.COM }
7209983SFei.Feng@Sun.COM
7219983SFei.Feng@Sun.COM if (ring->data != NULL) {
7229983SFei.Feng@Sun.COM for (i = 0; i < ring->count; i++) {
7239983SFei.Feng@Sun.COM data = &ring->data[i];
7249983SFei.Feng@Sun.COM rt2661_free_dma_mem(&data->txdata_dma);
7259983SFei.Feng@Sun.COM if (data->ni != NULL) {
7269983SFei.Feng@Sun.COM ieee80211_free_node(data->ni);
7279983SFei.Feng@Sun.COM data->ni = NULL;
7289983SFei.Feng@Sun.COM }
7299983SFei.Feng@Sun.COM }
7309983SFei.Feng@Sun.COM kmem_free(ring->data,
7319983SFei.Feng@Sun.COM ring->count * sizeof (struct rt2661_tx_data));
7329983SFei.Feng@Sun.COM }
7339983SFei.Feng@Sun.COM }
7349983SFei.Feng@Sun.COM
7359983SFei.Feng@Sun.COM /*ARGSUSED*/
7369983SFei.Feng@Sun.COM static int
rt2661_alloc_rx_ring(struct rt2661_softc * sc,struct rt2661_rx_ring * ring,int count)7379983SFei.Feng@Sun.COM rt2661_alloc_rx_ring(struct rt2661_softc *sc,
7389983SFei.Feng@Sun.COM struct rt2661_rx_ring *ring, int count)
7399983SFei.Feng@Sun.COM {
7409983SFei.Feng@Sun.COM struct rt2661_rx_desc *desc;
7419983SFei.Feng@Sun.COM struct rt2661_rx_data *data;
7429983SFei.Feng@Sun.COM int i, err, len, size;
7439983SFei.Feng@Sun.COM
7449983SFei.Feng@Sun.COM size = count * RT2661_RX_DESC_SIZE;
7459983SFei.Feng@Sun.COM len = count * sizeof (struct rt2661_rx_data);
7469983SFei.Feng@Sun.COM
7479983SFei.Feng@Sun.COM ring->count = count;
7489983SFei.Feng@Sun.COM ring->cur = 0;
7499983SFei.Feng@Sun.COM ring->next = 0;
7509983SFei.Feng@Sun.COM
7519983SFei.Feng@Sun.COM err = rt2661_alloc_dma_mem(sc->sc_dev, &rt2661_dma_attr, size,
7529983SFei.Feng@Sun.COM &rt2661_desc_accattr, DDI_DMA_CONSISTENT,
7539983SFei.Feng@Sun.COM DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
7549983SFei.Feng@Sun.COM &ring->rxdesc_dma);
7559983SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
7569983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_DMA, "rwd: rt2661_alloc_rx_ring(): "
7579983SFei.Feng@Sun.COM "failed to alloc dma mem\n");
7589983SFei.Feng@Sun.COM goto fail1;
7599983SFei.Feng@Sun.COM }
7609983SFei.Feng@Sun.COM
7619983SFei.Feng@Sun.COM ring->desc = (struct rt2661_rx_desc *)ring->rxdesc_dma.mem_va;
7629983SFei.Feng@Sun.COM (void) bzero(ring->desc, size);
7639983SFei.Feng@Sun.COM ring->paddr = ring->rxdesc_dma.cookie.dmac_address;
7649983SFei.Feng@Sun.COM
7659983SFei.Feng@Sun.COM ring->data = kmem_zalloc(len, KM_NOSLEEP);
7669983SFei.Feng@Sun.COM if (ring->data == NULL) {
7679983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_alloc_rx_ring(): "
7689983SFei.Feng@Sun.COM "failed to alloc rx buffer\n");
7699983SFei.Feng@Sun.COM goto fail2;
7709983SFei.Feng@Sun.COM }
7719983SFei.Feng@Sun.COM
7729983SFei.Feng@Sun.COM for (i = 0; i < count; i++) {
7739983SFei.Feng@Sun.COM desc = &ring->desc[i];
7749983SFei.Feng@Sun.COM data = &ring->data[i];
7759983SFei.Feng@Sun.COM err = rt2661_alloc_dma_mem(sc->sc_dev,
7769983SFei.Feng@Sun.COM &rt2661_dma_attr, sc->sc_dmabuf_size,
7779983SFei.Feng@Sun.COM &rt2661_buf_accattr, DDI_DMA_CONSISTENT,
7789983SFei.Feng@Sun.COM DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
7799983SFei.Feng@Sun.COM &data->rxdata_dma);
7809983SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
7819983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_DMA,
7829983SFei.Feng@Sun.COM "rwd: rt2661_alloc_rx_ring(): "
7839983SFei.Feng@Sun.COM "failed to alloc rx buffer dma\n");
7849983SFei.Feng@Sun.COM while (i >= 0) {
7859983SFei.Feng@Sun.COM rt2661_free_dma_mem(&ring->data[i].rxdata_dma);
7869983SFei.Feng@Sun.COM i--;
7879983SFei.Feng@Sun.COM }
7889983SFei.Feng@Sun.COM goto fail3;
7899983SFei.Feng@Sun.COM }
7909983SFei.Feng@Sun.COM data->buf = data->rxdata_dma.mem_va;
7919983SFei.Feng@Sun.COM data->paddr = data->rxdata_dma.cookie.dmac_address;
7929983SFei.Feng@Sun.COM desc->flags = LE_32(RT2661_RX_BUSY);
7939983SFei.Feng@Sun.COM desc->physaddr = LE_32(data->paddr);
7949983SFei.Feng@Sun.COM }
7959983SFei.Feng@Sun.COM
7969983SFei.Feng@Sun.COM (void) ddi_dma_sync(ring->rxdesc_dma.dma_hdl,
7979983SFei.Feng@Sun.COM 0, size, DDI_DMA_SYNC_FORDEV);
7989983SFei.Feng@Sun.COM return (DDI_SUCCESS);
7999983SFei.Feng@Sun.COM fail3:
8009983SFei.Feng@Sun.COM if (ring->data)
8019983SFei.Feng@Sun.COM kmem_free(ring->data,
8029983SFei.Feng@Sun.COM count * sizeof (struct rt2661_rx_data));
8039983SFei.Feng@Sun.COM fail2:
8049983SFei.Feng@Sun.COM rt2661_free_dma_mem(&ring->rxdesc_dma);
8059983SFei.Feng@Sun.COM fail1:
8069983SFei.Feng@Sun.COM return (err);
8079983SFei.Feng@Sun.COM }
8089983SFei.Feng@Sun.COM
8099983SFei.Feng@Sun.COM static void
rt2661_reset_rx_ring(struct rt2661_softc * sc,struct rt2661_rx_ring * ring)8109983SFei.Feng@Sun.COM rt2661_reset_rx_ring(struct rt2661_softc *sc, struct rt2661_rx_ring *ring)
8119983SFei.Feng@Sun.COM {
8129983SFei.Feng@Sun.COM int i;
8139983SFei.Feng@Sun.COM
8149983SFei.Feng@Sun.COM for (i = 0; i < ring->count; i++)
8159983SFei.Feng@Sun.COM ring->desc[i].flags = LE_32(RT2661_RX_BUSY);
8169983SFei.Feng@Sun.COM
8179983SFei.Feng@Sun.COM if (!RT2661_IS_FASTREBOOT(sc))
8189983SFei.Feng@Sun.COM (void) ddi_dma_sync(ring->rxdesc_dma.dma_hdl, 0,
8199983SFei.Feng@Sun.COM ring->count * sizeof (struct rt2661_rx_ring),
8209983SFei.Feng@Sun.COM DDI_DMA_SYNC_FORKERNEL);
8219983SFei.Feng@Sun.COM
8229983SFei.Feng@Sun.COM ring->cur = ring->next = 0;
8239983SFei.Feng@Sun.COM }
8249983SFei.Feng@Sun.COM
8259983SFei.Feng@Sun.COM /*ARGSUSED*/
8269983SFei.Feng@Sun.COM static void
rt2661_free_rx_ring(struct rt2661_softc * sc,struct rt2661_rx_ring * ring)8279983SFei.Feng@Sun.COM rt2661_free_rx_ring(struct rt2661_softc *sc, struct rt2661_rx_ring *ring)
8289983SFei.Feng@Sun.COM {
8299983SFei.Feng@Sun.COM struct rt2661_rx_data *data;
8309983SFei.Feng@Sun.COM int i;
8319983SFei.Feng@Sun.COM
8329983SFei.Feng@Sun.COM if (ring->desc != NULL) {
8339983SFei.Feng@Sun.COM rt2661_free_dma_mem(&ring->rxdesc_dma);
8349983SFei.Feng@Sun.COM }
8359983SFei.Feng@Sun.COM
8369983SFei.Feng@Sun.COM if (ring->data != NULL) {
8379983SFei.Feng@Sun.COM for (i = 0; i < ring->count; i++) {
8389983SFei.Feng@Sun.COM data = &ring->data[i];
8399983SFei.Feng@Sun.COM rt2661_free_dma_mem(&data->rxdata_dma);
8409983SFei.Feng@Sun.COM }
8419983SFei.Feng@Sun.COM kmem_free(ring->data,
8429983SFei.Feng@Sun.COM ring->count * sizeof (struct rt2661_rx_data));
8439983SFei.Feng@Sun.COM }
8449983SFei.Feng@Sun.COM }
8459983SFei.Feng@Sun.COM
8469983SFei.Feng@Sun.COM static void
rt2661_tx_dma_intr(struct rt2661_softc * sc,struct rt2661_tx_ring * ring)8479983SFei.Feng@Sun.COM rt2661_tx_dma_intr(struct rt2661_softc *sc, struct rt2661_tx_ring *ring)
8489983SFei.Feng@Sun.COM {
8499983SFei.Feng@Sun.COM struct rt2661_tx_desc *desc;
8509983SFei.Feng@Sun.COM struct rt2661_tx_data *data;
8519983SFei.Feng@Sun.COM
8529983SFei.Feng@Sun.COM for (;;) {
8539983SFei.Feng@Sun.COM desc = &ring->desc[ring->next];
8549983SFei.Feng@Sun.COM data = &ring->data[ring->next];
8559983SFei.Feng@Sun.COM
8569983SFei.Feng@Sun.COM (void) ddi_dma_sync(ring->txdesc_dma.dma_hdl,
8579983SFei.Feng@Sun.COM ring->next * RT2661_TX_DESC_SIZE,
8589983SFei.Feng@Sun.COM RT2661_TX_DESC_SIZE,
8599983SFei.Feng@Sun.COM DDI_DMA_SYNC_FORKERNEL);
8609983SFei.Feng@Sun.COM
8619983SFei.Feng@Sun.COM if ((LE_32(desc->flags) & RT2661_TX_BUSY) ||
8629983SFei.Feng@Sun.COM !(LE_32(desc->flags) & RT2661_TX_VALID))
8639983SFei.Feng@Sun.COM break;
8649983SFei.Feng@Sun.COM
8659983SFei.Feng@Sun.COM (void) ddi_dma_sync(data->txdata_dma.dma_hdl,
8669983SFei.Feng@Sun.COM 0, sc->sc_dmabuf_size,
8679983SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV);
8689983SFei.Feng@Sun.COM
8699983SFei.Feng@Sun.COM /* descriptor is no longer valid */
8709983SFei.Feng@Sun.COM desc->flags &= ~LE_32(RT2661_TX_VALID);
8719983SFei.Feng@Sun.COM
8729983SFei.Feng@Sun.COM (void) ddi_dma_sync(ring->txdesc_dma.dma_hdl,
8739983SFei.Feng@Sun.COM ring->next * RT2661_TX_DESC_SIZE,
8749983SFei.Feng@Sun.COM RT2661_TX_DESC_SIZE,
8759983SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV);
8769983SFei.Feng@Sun.COM
8779983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_TX, "rwd: rt2661_tx_dma_intr(): "
8789983SFei.Feng@Sun.COM "tx dma done q=%p idx=%u\n", ring, ring->next);
8799983SFei.Feng@Sun.COM
8809983SFei.Feng@Sun.COM if (++ring->next >= ring->count) /* faster than % count */
8819983SFei.Feng@Sun.COM ring->next = 0;
8829983SFei.Feng@Sun.COM }
8839983SFei.Feng@Sun.COM }
8849983SFei.Feng@Sun.COM
8859983SFei.Feng@Sun.COM static void
rt2661_tx_intr(struct rt2661_softc * sc)8869983SFei.Feng@Sun.COM rt2661_tx_intr(struct rt2661_softc *sc)
8879983SFei.Feng@Sun.COM {
8889983SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
8899983SFei.Feng@Sun.COM struct rt2661_tx_ring *ring;
8909983SFei.Feng@Sun.COM struct rt2661_tx_data *data;
8919983SFei.Feng@Sun.COM struct rt2661_node *rn;
8929983SFei.Feng@Sun.COM
8939983SFei.Feng@Sun.COM uint32_t val;
8949983SFei.Feng@Sun.COM int qid, retrycnt;
8959983SFei.Feng@Sun.COM
8969983SFei.Feng@Sun.COM for (;;) {
8979983SFei.Feng@Sun.COM val = RT2661_READ(sc, RT2661_STA_CSR4);
8989983SFei.Feng@Sun.COM if (!(val & RT2661_TX_STAT_VALID))
8999983SFei.Feng@Sun.COM break;
9009983SFei.Feng@Sun.COM
9019983SFei.Feng@Sun.COM /* retrieve the queue in which this frame was send */
9029983SFei.Feng@Sun.COM qid = RT2661_TX_QID(val);
9039983SFei.Feng@Sun.COM ring = (qid <= 3) ? &sc->txq[qid] : &sc->mgtq;
9049983SFei.Feng@Sun.COM
9059983SFei.Feng@Sun.COM /* retrieve rate control algorithm context */
9069983SFei.Feng@Sun.COM data = &ring->data[ring->stat];
9079983SFei.Feng@Sun.COM rn = (struct rt2661_node *)data->ni;
9089983SFei.Feng@Sun.COM
9099983SFei.Feng@Sun.COM /* if no frame has been sent, ignore */
9109983SFei.Feng@Sun.COM if (rn == NULL) {
9119983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_TX, "rwd: rt2661_tx_intr(): "
9129983SFei.Feng@Sun.COM "no frame has been send, ignore\n");
9139983SFei.Feng@Sun.COM continue;
9149983SFei.Feng@Sun.COM }
9159983SFei.Feng@Sun.COM
9169983SFei.Feng@Sun.COM switch (RT2661_TX_RESULT(val)) {
9179983SFei.Feng@Sun.COM case RT2661_TX_SUCCESS:
9189983SFei.Feng@Sun.COM retrycnt = RT2661_TX_RETRYCNT(val);
9199983SFei.Feng@Sun.COM
9209983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_TX, "rwd: rt2661_tx_intr(): "
9219983SFei.Feng@Sun.COM "data frame sent successfully after "
9229983SFei.Feng@Sun.COM "%d retries\n", retrycnt);
9239983SFei.Feng@Sun.COM rn->amn.amn_txcnt++;
9249983SFei.Feng@Sun.COM if (retrycnt > 0) {
9259983SFei.Feng@Sun.COM rn->amn.amn_retrycnt++;
9269983SFei.Feng@Sun.COM sc->sc_tx_retries++;
9279983SFei.Feng@Sun.COM }
9289983SFei.Feng@Sun.COM break;
9299983SFei.Feng@Sun.COM case RT2661_TX_RETRY_FAIL:
9309983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_TX, "rwd: rt2661_tx_intr(): "
9319983SFei.Feng@Sun.COM "sending data frame failed (too much retries)\n");
9329983SFei.Feng@Sun.COM rn->amn.amn_txcnt++;
9339983SFei.Feng@Sun.COM rn->amn.amn_retrycnt++;
9349983SFei.Feng@Sun.COM break;
9359983SFei.Feng@Sun.COM default:
9369983SFei.Feng@Sun.COM /* other failure */
9379983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_TX, "rwd: rt2661_tx_intr():"
9389983SFei.Feng@Sun.COM "sending data frame failed 0x%08x\n", val);
9399983SFei.Feng@Sun.COM }
9409983SFei.Feng@Sun.COM
9419983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_TX, "rwd: rt2661_tx_intr(): "
9429983SFei.Feng@Sun.COM "tx done q=%d idx=%u\n", qid, ring->stat);
9439983SFei.Feng@Sun.COM
9449983SFei.Feng@Sun.COM ieee80211_free_node(data->ni);
9459983SFei.Feng@Sun.COM data->ni = NULL;
9469983SFei.Feng@Sun.COM
9479983SFei.Feng@Sun.COM ring->queued--;
9489983SFei.Feng@Sun.COM
9499983SFei.Feng@Sun.COM /* faster than % count */
9509983SFei.Feng@Sun.COM if (++ring->stat >= ring->count)
9519983SFei.Feng@Sun.COM ring->stat = 0;
9529983SFei.Feng@Sun.COM
9539983SFei.Feng@Sun.COM if (sc->sc_need_sched) {
9549983SFei.Feng@Sun.COM sc->sc_need_sched = 0;
9559983SFei.Feng@Sun.COM mac_tx_update(ic->ic_mach);
9569983SFei.Feng@Sun.COM }
9579983SFei.Feng@Sun.COM }
9589983SFei.Feng@Sun.COM sc->sc_tx_timer = 0;
9599983SFei.Feng@Sun.COM }
9609983SFei.Feng@Sun.COM
9619983SFei.Feng@Sun.COM static void
rt2661_rx_intr(struct rt2661_softc * sc)9629983SFei.Feng@Sun.COM rt2661_rx_intr(struct rt2661_softc *sc)
9639983SFei.Feng@Sun.COM {
9649983SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
9659983SFei.Feng@Sun.COM struct rt2661_rx_ring *ring;
9669983SFei.Feng@Sun.COM struct rt2661_rx_desc *desc;
9679983SFei.Feng@Sun.COM struct rt2661_rx_data *data;
9689983SFei.Feng@Sun.COM struct ieee80211_frame *wh;
9699983SFei.Feng@Sun.COM struct ieee80211_node *ni;
9709983SFei.Feng@Sun.COM
9719983SFei.Feng@Sun.COM mblk_t *m;
9729983SFei.Feng@Sun.COM uint8_t *rxbuf;
9739983SFei.Feng@Sun.COM uint32_t pktlen;
9749983SFei.Feng@Sun.COM
9759983SFei.Feng@Sun.COM mutex_enter(&sc->sc_rxlock);
9769983SFei.Feng@Sun.COM ring = &sc->rxq;
9779983SFei.Feng@Sun.COM
9789983SFei.Feng@Sun.COM for (;;) {
9799983SFei.Feng@Sun.COM int rssi;
9809983SFei.Feng@Sun.COM
9819983SFei.Feng@Sun.COM desc = &ring->desc[ring->cur];
9829983SFei.Feng@Sun.COM data = &ring->data[ring->cur];
9839983SFei.Feng@Sun.COM
9849983SFei.Feng@Sun.COM (void) ddi_dma_sync(ring->rxdesc_dma.dma_hdl,
9859983SFei.Feng@Sun.COM ring->cur * RT2661_RX_DESC_SIZE,
9869983SFei.Feng@Sun.COM RT2661_RX_DESC_SIZE,
9879983SFei.Feng@Sun.COM DDI_DMA_SYNC_FORKERNEL);
9889983SFei.Feng@Sun.COM
9899983SFei.Feng@Sun.COM
9909983SFei.Feng@Sun.COM if (LE_32(desc->flags) & RT2661_RX_BUSY)
9919983SFei.Feng@Sun.COM break;
9929983SFei.Feng@Sun.COM
9939983SFei.Feng@Sun.COM if ((LE_32(desc->flags) & RT2661_RX_PHY_ERROR) ||
9949983SFei.Feng@Sun.COM (LE_32(desc->flags) & RT2661_RX_CRC_ERROR)) {
9959983SFei.Feng@Sun.COM /*
9969983SFei.Feng@Sun.COM * This should not happen since we did not request
9979983SFei.Feng@Sun.COM * to receive those frames when we filled TXRX_CSR0.
9989983SFei.Feng@Sun.COM */
9999983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_rx_intr(): "
10009983SFei.Feng@Sun.COM "PHY or CRC error flags 0x%08x\n",
10019983SFei.Feng@Sun.COM LE_32(desc->flags));
10029983SFei.Feng@Sun.COM sc->sc_rx_err++;
10039983SFei.Feng@Sun.COM goto skip;
10049983SFei.Feng@Sun.COM }
10059983SFei.Feng@Sun.COM
10069983SFei.Feng@Sun.COM if ((LE_32(desc->flags) & RT2661_RX_CIPHER_MASK) != 0) {
10079983SFei.Feng@Sun.COM sc->sc_rx_err++;
10089983SFei.Feng@Sun.COM goto skip;
10099983SFei.Feng@Sun.COM }
10109983SFei.Feng@Sun.COM
10119983SFei.Feng@Sun.COM (void) ddi_dma_sync(data->rxdata_dma.dma_hdl,
10129983SFei.Feng@Sun.COM 0, sc->sc_dmabuf_size,
10139983SFei.Feng@Sun.COM DDI_DMA_SYNC_FORCPU);
10149983SFei.Feng@Sun.COM
10159983SFei.Feng@Sun.COM rxbuf = (uint8_t *)data->rxdata_dma.mem_va;
10169983SFei.Feng@Sun.COM desc->physaddr = LE_32(data->rxdata_dma.cookie.dmac_address);
10179983SFei.Feng@Sun.COM pktlen = (LE_32(desc->flags) >> 16) & 0xfff;
10189983SFei.Feng@Sun.COM if ((pktlen < sizeof (struct ieee80211_frame_min)) ||
10199983SFei.Feng@Sun.COM (pktlen > sc->sc_dmabuf_size)) {
10209983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_rx_intr(): "
10219983SFei.Feng@Sun.COM "bad fram length=%u\n", pktlen);
10229983SFei.Feng@Sun.COM sc->sc_rx_err++;
10239983SFei.Feng@Sun.COM goto skip;
10249983SFei.Feng@Sun.COM }
10259983SFei.Feng@Sun.COM
10269983SFei.Feng@Sun.COM if ((m = allocb(pktlen, BPRI_MED)) == NULL) {
10279983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_rx_intr(): "
10289983SFei.Feng@Sun.COM "allocate mblk failed.\n");
10299983SFei.Feng@Sun.COM sc->sc_rx_nobuf++;
10309983SFei.Feng@Sun.COM goto skip;
10319983SFei.Feng@Sun.COM }
10329983SFei.Feng@Sun.COM
10339983SFei.Feng@Sun.COM bcopy(rxbuf, m->b_rptr, pktlen);
10349983SFei.Feng@Sun.COM m->b_wptr += pktlen;
10359983SFei.Feng@Sun.COM
10369983SFei.Feng@Sun.COM wh = (struct ieee80211_frame *)m->b_rptr;
10379983SFei.Feng@Sun.COM ni = ieee80211_find_rxnode(ic, wh);
10389983SFei.Feng@Sun.COM
10399983SFei.Feng@Sun.COM rssi = rt2661_get_rssi(sc, desc->rssi);
10409983SFei.Feng@Sun.COM /* send the frame to the 802.11 layer */
104111387SSurya.Prakki@Sun.COM (void) ieee80211_input(ic, m, ni, rssi + 95, 0);
10429983SFei.Feng@Sun.COM
10439983SFei.Feng@Sun.COM sc->avg_rssi = (rssi + 7 * sc->avg_rssi) / 8;
10449983SFei.Feng@Sun.COM
10459983SFei.Feng@Sun.COM /* node is no longer needed */
10469983SFei.Feng@Sun.COM ieee80211_free_node(ni);
10479983SFei.Feng@Sun.COM skip:
10489983SFei.Feng@Sun.COM desc->flags |= LE_32(RT2661_RX_BUSY);
10499983SFei.Feng@Sun.COM
10509983SFei.Feng@Sun.COM (void) ddi_dma_sync(ring->rxdesc_dma.dma_hdl,
10519983SFei.Feng@Sun.COM ring->cur * RT2661_RX_DESC_SIZE,
10529983SFei.Feng@Sun.COM RT2661_RX_DESC_SIZE,
10539983SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV);
10549983SFei.Feng@Sun.COM
10559983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_INTR, "rwd: rt2661_rx_intr(): "
10569983SFei.Feng@Sun.COM "rx intr idx=%u\n", sc->rxq.cur);
10579983SFei.Feng@Sun.COM ring->cur = (ring->cur + 1) % RT2661_RX_RING_COUNT;
10589983SFei.Feng@Sun.COM }
10599983SFei.Feng@Sun.COM mutex_exit(&sc->sc_rxlock);
10609983SFei.Feng@Sun.COM }
10619983SFei.Feng@Sun.COM
10629983SFei.Feng@Sun.COM /*ARGSUSED*/
10639983SFei.Feng@Sun.COM static uint_t
rt2661_softintr(caddr_t data,caddr_t unused)10649983SFei.Feng@Sun.COM rt2661_softintr(caddr_t data, caddr_t unused)
10659983SFei.Feng@Sun.COM {
10669983SFei.Feng@Sun.COM struct rt2661_softc *sc = (struct rt2661_softc *)data;
10679983SFei.Feng@Sun.COM
10689983SFei.Feng@Sun.COM if (sc->sc_rx_pend) {
10699983SFei.Feng@Sun.COM sc->sc_rx_pend = 0;
10709983SFei.Feng@Sun.COM rt2661_rx_intr(sc);
10719983SFei.Feng@Sun.COM return (DDI_INTR_CLAIMED);
10729983SFei.Feng@Sun.COM }
10739983SFei.Feng@Sun.COM return (DDI_INTR_UNCLAIMED);
10749983SFei.Feng@Sun.COM }
10759983SFei.Feng@Sun.COM
10769983SFei.Feng@Sun.COM static int
rt2661_tx_cmd(struct rt2661_softc * sc,uint8_t cmd,uint16_t arg)10779983SFei.Feng@Sun.COM rt2661_tx_cmd(struct rt2661_softc *sc, uint8_t cmd, uint16_t arg)
10789983SFei.Feng@Sun.COM {
10799983SFei.Feng@Sun.COM if (RT2661_READ(sc, RT2661_H2M_MAILBOX_CSR) & RT2661_H2M_BUSY)
10809983SFei.Feng@Sun.COM return (EIO); /* there is already a command pending */
10819983SFei.Feng@Sun.COM
10829983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_H2M_MAILBOX_CSR,
10839983SFei.Feng@Sun.COM RT2661_H2M_BUSY | RT2661_TOKEN_NO_INTR << 16 | arg);
10849983SFei.Feng@Sun.COM
10859983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_HOST_CMD_CSR, RT2661_KICK_CMD | cmd);
10869983SFei.Feng@Sun.COM
10879983SFei.Feng@Sun.COM return (0);
10889983SFei.Feng@Sun.COM }
10899983SFei.Feng@Sun.COM
10909983SFei.Feng@Sun.COM static void
rt2661_mcu_wakeup(struct rt2661_softc * sc)10919983SFei.Feng@Sun.COM rt2661_mcu_wakeup(struct rt2661_softc *sc)
10929983SFei.Feng@Sun.COM {
10939983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MAC_CSR11, 5 << 16);
10949983SFei.Feng@Sun.COM
10959983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_SOFT_RESET_CSR, 0x7);
10969983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_IO_CNTL_CSR, 0x18);
10979983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_PCI_USEC_CSR, 0x20);
10989983SFei.Feng@Sun.COM
10999983SFei.Feng@Sun.COM /* send wakeup command to MCU */
11009983SFei.Feng@Sun.COM (void) rt2661_tx_cmd(sc, RT2661_MCU_CMD_WAKEUP, 0);
11019983SFei.Feng@Sun.COM }
11029983SFei.Feng@Sun.COM
11039983SFei.Feng@Sun.COM static void
rt2661_mcu_cmd_intr(struct rt2661_softc * sc)11049983SFei.Feng@Sun.COM rt2661_mcu_cmd_intr(struct rt2661_softc *sc)
11059983SFei.Feng@Sun.COM {
11069983SFei.Feng@Sun.COM (void) RT2661_READ(sc, RT2661_M2H_CMD_DONE_CSR);
11079983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_M2H_CMD_DONE_CSR, 0xffffffff);
11089983SFei.Feng@Sun.COM }
11099983SFei.Feng@Sun.COM
11109983SFei.Feng@Sun.COM /*ARGSUSED*/
11119983SFei.Feng@Sun.COM static uint_t
rt2661_intr(caddr_t arg,caddr_t unused)11129983SFei.Feng@Sun.COM rt2661_intr(caddr_t arg, caddr_t unused)
11139983SFei.Feng@Sun.COM {
11149983SFei.Feng@Sun.COM struct rt2661_softc *sc = (struct rt2661_softc *)arg;
11159983SFei.Feng@Sun.COM uint32_t r1, r2;
11169983SFei.Feng@Sun.COM
11179983SFei.Feng@Sun.COM RT2661_GLOCK(sc);
11189983SFei.Feng@Sun.COM
11199983SFei.Feng@Sun.COM if (!RT2661_IS_RUNNING(sc) || RT2661_IS_SUSPEND(sc)) {
11209983SFei.Feng@Sun.COM RT2661_GUNLOCK(sc);
11219983SFei.Feng@Sun.COM return (DDI_INTR_UNCLAIMED);
11229983SFei.Feng@Sun.COM }
11239983SFei.Feng@Sun.COM
11249983SFei.Feng@Sun.COM r1 = RT2661_READ(sc, RT2661_INT_SOURCE_CSR);
11259983SFei.Feng@Sun.COM r2 = RT2661_READ(sc, RT2661_MCU_INT_SOURCE_CSR);
11269983SFei.Feng@Sun.COM if (r1 == 0 && r2 == 0) {
11279983SFei.Feng@Sun.COM RT2661_GUNLOCK(sc);
11289983SFei.Feng@Sun.COM return (DDI_INTR_UNCLAIMED); /* not for us */
11299983SFei.Feng@Sun.COM }
11309983SFei.Feng@Sun.COM
11319983SFei.Feng@Sun.COM /* disable MAC and MCU interrupts */
11329983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_INT_MASK_CSR, 0xffffff7f);
11339983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MCU_INT_MASK_CSR, 0xffffffff);
11349983SFei.Feng@Sun.COM
11359983SFei.Feng@Sun.COM /* acknowledge interrupts */
11369983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_INT_SOURCE_CSR, r1);
11379983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MCU_INT_SOURCE_CSR, r2);
11389983SFei.Feng@Sun.COM
11399983SFei.Feng@Sun.COM if (r1 & RT2661_MGT_DONE) {
11409983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_INTR, "rwd: rt2661_intr(): "
11419983SFei.Feng@Sun.COM "RT2661_MGT_DONE\n");
11429983SFei.Feng@Sun.COM rt2661_tx_dma_intr(sc, &sc->mgtq);
11439983SFei.Feng@Sun.COM }
11449983SFei.Feng@Sun.COM
11459983SFei.Feng@Sun.COM if (r1 & RT2661_RX_DONE) {
11469983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_INTR, "rwd: rt2661_intr(): "
11479983SFei.Feng@Sun.COM "RT2661_RX_DONE\n");
11489983SFei.Feng@Sun.COM sc->sc_rx_pend = 1;
11499983SFei.Feng@Sun.COM (void) ddi_intr_trigger_softint(sc->sc_softintr_hdl, NULL);
11509983SFei.Feng@Sun.COM }
11519983SFei.Feng@Sun.COM
11529983SFei.Feng@Sun.COM if (r1 & RT2661_TX0_DMA_DONE) {
11539983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_INTR, "rwd: rt2661_intr(): "
11549983SFei.Feng@Sun.COM "RT2661_TX0_DMA_DONE\n");
11559983SFei.Feng@Sun.COM rt2661_tx_dma_intr(sc, &sc->txq[0]);
11569983SFei.Feng@Sun.COM }
11579983SFei.Feng@Sun.COM
11589983SFei.Feng@Sun.COM if (r1 & RT2661_TX1_DMA_DONE) {
11599983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_INTR, "rwd: rt2661_intr(): "
11609983SFei.Feng@Sun.COM "RT2661_TX1_DMA_DONE\n");
11619983SFei.Feng@Sun.COM rt2661_tx_dma_intr(sc, &sc->txq[1]);
11629983SFei.Feng@Sun.COM }
11639983SFei.Feng@Sun.COM
11649983SFei.Feng@Sun.COM if (r1 & RT2661_TX2_DMA_DONE) {
11659983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_INTR, "rwd: rt2661_intr(): "
11669983SFei.Feng@Sun.COM "RT2661_TX2_DMA_DONE\n");
11679983SFei.Feng@Sun.COM rt2661_tx_dma_intr(sc, &sc->txq[2]);
11689983SFei.Feng@Sun.COM }
11699983SFei.Feng@Sun.COM
11709983SFei.Feng@Sun.COM if (r1 & RT2661_TX3_DMA_DONE) {
11719983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_INTR, "rwd: rt2661_intr(): "
11729983SFei.Feng@Sun.COM "RT2661_TX3_DMA_DONE\n");
11739983SFei.Feng@Sun.COM rt2661_tx_dma_intr(sc, &sc->txq[3]);
11749983SFei.Feng@Sun.COM }
11759983SFei.Feng@Sun.COM
11769983SFei.Feng@Sun.COM if (r1 & RT2661_TX_DONE) {
11779983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_INTR, "rwd: rt2661_intr(): "
11789983SFei.Feng@Sun.COM "RT2661_TX_DONE\n");
11799983SFei.Feng@Sun.COM rt2661_tx_intr(sc);
11809983SFei.Feng@Sun.COM }
11819983SFei.Feng@Sun.COM
11829983SFei.Feng@Sun.COM if (r2 & RT2661_MCU_CMD_DONE) {
11839983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_INTR, "rwd: rt2661_intr(): "
11849983SFei.Feng@Sun.COM "RT2661_MCU_CMD_DONE\n");
11859983SFei.Feng@Sun.COM rt2661_mcu_cmd_intr(sc);
11869983SFei.Feng@Sun.COM }
11879983SFei.Feng@Sun.COM
11889983SFei.Feng@Sun.COM if (r2 & RT2661_MCU_WAKEUP) {
11899983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_INTR, "rwd: rt2661_intr(): "
11909983SFei.Feng@Sun.COM "RT2661_MCU_WAKEUP\n");
11919983SFei.Feng@Sun.COM rt2661_mcu_wakeup(sc);
11929983SFei.Feng@Sun.COM }
11939983SFei.Feng@Sun.COM
11949983SFei.Feng@Sun.COM /* re-enable MAC and MCU interrupts */
11959983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_INT_MASK_CSR, 0x0000ff10);
11969983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MCU_INT_MASK_CSR, 0);
11979983SFei.Feng@Sun.COM
11989983SFei.Feng@Sun.COM RT2661_GUNLOCK(sc);
11999983SFei.Feng@Sun.COM return (RT2661_SUCCESS);
12009983SFei.Feng@Sun.COM }
12019983SFei.Feng@Sun.COM
12029983SFei.Feng@Sun.COM /*
12039983SFei.Feng@Sun.COM * Retrieve the "Received Signal Strength Indicator" from the raw values
12049983SFei.Feng@Sun.COM * contained in Rx descriptors. The computation depends on which band the
12059983SFei.Feng@Sun.COM * frame was received. Correction values taken from the reference driver.
12069983SFei.Feng@Sun.COM */
12079983SFei.Feng@Sun.COM static int
rt2661_get_rssi(struct rt2661_softc * sc,uint8_t raw)12089983SFei.Feng@Sun.COM rt2661_get_rssi(struct rt2661_softc *sc, uint8_t raw)
12099983SFei.Feng@Sun.COM {
12109983SFei.Feng@Sun.COM int lna, agc, rssi;
12119983SFei.Feng@Sun.COM
12129983SFei.Feng@Sun.COM lna = (raw >> 5) & 0x3;
12139983SFei.Feng@Sun.COM agc = raw & 0x1f;
12149983SFei.Feng@Sun.COM
12159983SFei.Feng@Sun.COM rssi = 2 * agc;
12169983SFei.Feng@Sun.COM
12179983SFei.Feng@Sun.COM if (IEEE80211_IS_CHAN_2GHZ(sc->sc_curchan)) {
12189983SFei.Feng@Sun.COM rssi += sc->rssi_2ghz_corr;
12199983SFei.Feng@Sun.COM
12209983SFei.Feng@Sun.COM if (lna == 1)
12219983SFei.Feng@Sun.COM rssi -= 64;
12229983SFei.Feng@Sun.COM else if (lna == 2)
12239983SFei.Feng@Sun.COM rssi -= 74;
12249983SFei.Feng@Sun.COM else if (lna == 3)
12259983SFei.Feng@Sun.COM rssi -= 90;
12269983SFei.Feng@Sun.COM } else {
12279983SFei.Feng@Sun.COM rssi += sc->rssi_5ghz_corr;
12289983SFei.Feng@Sun.COM
12299983SFei.Feng@Sun.COM if (lna == 1)
12309983SFei.Feng@Sun.COM rssi -= 64;
12319983SFei.Feng@Sun.COM else if (lna == 2)
12329983SFei.Feng@Sun.COM rssi -= 86;
12339983SFei.Feng@Sun.COM else if (lna == 3)
12349983SFei.Feng@Sun.COM rssi -= 100;
12359983SFei.Feng@Sun.COM }
12369983SFei.Feng@Sun.COM return (rssi);
12379983SFei.Feng@Sun.COM }
12389983SFei.Feng@Sun.COM
12399983SFei.Feng@Sun.COM /* quickly determine if a given rate is CCK or OFDM */
12409983SFei.Feng@Sun.COM #define RT2661_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22)
12419983SFei.Feng@Sun.COM
12429983SFei.Feng@Sun.COM #define RT2661_ACK_SIZE 14 /* 10 + 4(FCS) */
12439983SFei.Feng@Sun.COM #define RT2661_CTS_SIZE 14 /* 10 + 4(FCS) */
12449983SFei.Feng@Sun.COM
12459983SFei.Feng@Sun.COM #define RT2661_SIFS 10 /* us */
12469983SFei.Feng@Sun.COM
12479983SFei.Feng@Sun.COM /*
12489983SFei.Feng@Sun.COM * Return the expected ack rate for a frame transmitted at rate `rate'.
12499983SFei.Feng@Sun.COM * XXX: this should depend on the destination node basic rate set.
12509983SFei.Feng@Sun.COM */
12519983SFei.Feng@Sun.COM static int
rt2661_ack_rate(struct ieee80211com * ic,int rate)12529983SFei.Feng@Sun.COM rt2661_ack_rate(struct ieee80211com *ic, int rate)
12539983SFei.Feng@Sun.COM {
12549983SFei.Feng@Sun.COM switch (rate) {
12559983SFei.Feng@Sun.COM /* CCK rates */
12569983SFei.Feng@Sun.COM case 2:
12579983SFei.Feng@Sun.COM return (2);
12589983SFei.Feng@Sun.COM case 4:
12599983SFei.Feng@Sun.COM case 11:
12609983SFei.Feng@Sun.COM case 22:
12619983SFei.Feng@Sun.COM return ((ic->ic_curmode == IEEE80211_MODE_11B) ? 4 : rate);
12629983SFei.Feng@Sun.COM
12639983SFei.Feng@Sun.COM /* OFDM rates */
12649983SFei.Feng@Sun.COM case 12:
12659983SFei.Feng@Sun.COM case 18:
12669983SFei.Feng@Sun.COM return (12);
12679983SFei.Feng@Sun.COM case 24:
12689983SFei.Feng@Sun.COM case 36:
12699983SFei.Feng@Sun.COM return (24);
12709983SFei.Feng@Sun.COM case 48:
12719983SFei.Feng@Sun.COM case 72:
12729983SFei.Feng@Sun.COM case 96:
12739983SFei.Feng@Sun.COM case 108:
12749983SFei.Feng@Sun.COM return (48);
12759983SFei.Feng@Sun.COM }
12769983SFei.Feng@Sun.COM
12779983SFei.Feng@Sun.COM /* default to 1Mbps */
12789983SFei.Feng@Sun.COM return (2);
12799983SFei.Feng@Sun.COM }
12809983SFei.Feng@Sun.COM
12819983SFei.Feng@Sun.COM /*
12829983SFei.Feng@Sun.COM * Compute the duration (in us) needed to transmit `len' bytes at rate `rate'.
12839983SFei.Feng@Sun.COM * The function automatically determines the operating mode depending on the
12849983SFei.Feng@Sun.COM * given rate. `flags' indicates whether short preamble is in use or not.
12859983SFei.Feng@Sun.COM */
12869983SFei.Feng@Sun.COM static uint16_t
rt2661_txtime(int len,int rate,uint32_t flags)12879983SFei.Feng@Sun.COM rt2661_txtime(int len, int rate, uint32_t flags)
12889983SFei.Feng@Sun.COM {
12899983SFei.Feng@Sun.COM uint16_t txtime;
12909983SFei.Feng@Sun.COM
12919983SFei.Feng@Sun.COM if (RT2661_RATE_IS_OFDM(rate)) {
12929983SFei.Feng@Sun.COM /* IEEE Std 802.11a-1999, pp. 37 */
12939983SFei.Feng@Sun.COM txtime = (8 + 4 * len + 3 + rate - 1) / rate;
12949983SFei.Feng@Sun.COM txtime = 16 + 4 + 4 * txtime + 6;
12959983SFei.Feng@Sun.COM } else {
12969983SFei.Feng@Sun.COM /* IEEE Std 802.11b-1999, pp. 28 */
12979983SFei.Feng@Sun.COM txtime = (16 * len + rate - 1) / rate;
12989983SFei.Feng@Sun.COM if (rate != 2 && (flags & IEEE80211_F_SHPREAMBLE))
12999983SFei.Feng@Sun.COM txtime += 72 + 24;
13009983SFei.Feng@Sun.COM else
13019983SFei.Feng@Sun.COM txtime += 144 + 48;
13029983SFei.Feng@Sun.COM }
13039983SFei.Feng@Sun.COM
13049983SFei.Feng@Sun.COM return (txtime);
13059983SFei.Feng@Sun.COM }
13069983SFei.Feng@Sun.COM
13079983SFei.Feng@Sun.COM static uint8_t
rt2661_plcp_signal(int rate)13089983SFei.Feng@Sun.COM rt2661_plcp_signal(int rate)
13099983SFei.Feng@Sun.COM {
13109983SFei.Feng@Sun.COM switch (rate) {
13119983SFei.Feng@Sun.COM /* CCK rates (returned values are device-dependent) */
13129983SFei.Feng@Sun.COM case 2:
13139983SFei.Feng@Sun.COM return (0x0);
13149983SFei.Feng@Sun.COM case 4:
13159983SFei.Feng@Sun.COM return (0x1);
13169983SFei.Feng@Sun.COM case 11:
13179983SFei.Feng@Sun.COM return (0x2);
13189983SFei.Feng@Sun.COM case 22:
13199983SFei.Feng@Sun.COM return (0x3);
13209983SFei.Feng@Sun.COM
13219983SFei.Feng@Sun.COM /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
13229983SFei.Feng@Sun.COM case 12:
13239983SFei.Feng@Sun.COM return (0xb);
13249983SFei.Feng@Sun.COM case 18:
13259983SFei.Feng@Sun.COM return (0xf);
13269983SFei.Feng@Sun.COM case 24:
13279983SFei.Feng@Sun.COM return (0xa);
13289983SFei.Feng@Sun.COM case 36:
13299983SFei.Feng@Sun.COM return (0xe);
13309983SFei.Feng@Sun.COM case 48:
13319983SFei.Feng@Sun.COM return (0x9);
13329983SFei.Feng@Sun.COM case 72:
13339983SFei.Feng@Sun.COM return (0xd);
13349983SFei.Feng@Sun.COM case 96:
13359983SFei.Feng@Sun.COM return (0x8);
13369983SFei.Feng@Sun.COM case 108:
13379983SFei.Feng@Sun.COM return (0xc);
13389983SFei.Feng@Sun.COM
13399983SFei.Feng@Sun.COM /* unsupported rates (should not get there) */
13409983SFei.Feng@Sun.COM default:
13419983SFei.Feng@Sun.COM return (0xff);
13429983SFei.Feng@Sun.COM }
13439983SFei.Feng@Sun.COM }
13449983SFei.Feng@Sun.COM
13459983SFei.Feng@Sun.COM static void
rt2661_setup_tx_desc(struct rt2661_softc * sc,struct rt2661_tx_desc * desc,uint32_t flags,uint16_t xflags,int len,int rate,int ac)13469983SFei.Feng@Sun.COM rt2661_setup_tx_desc(struct rt2661_softc *sc, struct rt2661_tx_desc *desc,
13479983SFei.Feng@Sun.COM uint32_t flags, uint16_t xflags, int len, int rate, int ac)
13489983SFei.Feng@Sun.COM {
13499983SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
13509983SFei.Feng@Sun.COM uint16_t plcp_length;
13519983SFei.Feng@Sun.COM int remainder;
13529983SFei.Feng@Sun.COM
13539983SFei.Feng@Sun.COM desc->flags = LE_32(flags);
13549983SFei.Feng@Sun.COM desc->flags |= LE_32(len << 16);
13559983SFei.Feng@Sun.COM desc->flags |= LE_32(RT2661_TX_BUSY | RT2661_TX_VALID);
13569983SFei.Feng@Sun.COM
13579983SFei.Feng@Sun.COM desc->xflags = LE_16(xflags);
13589983SFei.Feng@Sun.COM desc->xflags |= LE_16(1 << 13);
13599983SFei.Feng@Sun.COM
13609983SFei.Feng@Sun.COM desc->wme = LE_16(
13619983SFei.Feng@Sun.COM RT2661_QID(ac) |
13629983SFei.Feng@Sun.COM RT2661_AIFSN(2) |
13639983SFei.Feng@Sun.COM RT2661_LOGCWMIN(4) |
13649983SFei.Feng@Sun.COM RT2661_LOGCWMAX(10));
13659983SFei.Feng@Sun.COM
13669983SFei.Feng@Sun.COM /*
13679983SFei.Feng@Sun.COM * Remember in which queue this frame was sent. This field is driver
13689983SFei.Feng@Sun.COM * private data only. It will be made available by the NIC in STA_CSR4
13699983SFei.Feng@Sun.COM * on Tx interrupts.
13709983SFei.Feng@Sun.COM */
13719983SFei.Feng@Sun.COM desc->qid = (uint8_t)ac;
13729983SFei.Feng@Sun.COM
13739983SFei.Feng@Sun.COM /* setup PLCP fields */
13749983SFei.Feng@Sun.COM desc->plcp_signal = rt2661_plcp_signal(rate);
13759983SFei.Feng@Sun.COM desc->plcp_service = 4;
13769983SFei.Feng@Sun.COM
13779983SFei.Feng@Sun.COM len += IEEE80211_CRC_LEN;
13789983SFei.Feng@Sun.COM
13799983SFei.Feng@Sun.COM if (RT2661_RATE_IS_OFDM(rate)) {
13809983SFei.Feng@Sun.COM desc->flags |= LE_32(RT2661_TX_OFDM);
13819983SFei.Feng@Sun.COM
13829983SFei.Feng@Sun.COM plcp_length = len & 0xfff;
13839983SFei.Feng@Sun.COM desc->plcp_length_hi = plcp_length >> 6;
13849983SFei.Feng@Sun.COM desc->plcp_length_lo = plcp_length & 0x3f;
13859983SFei.Feng@Sun.COM } else {
13869983SFei.Feng@Sun.COM plcp_length = (16 * len + rate - 1) / rate;
13879983SFei.Feng@Sun.COM if (rate == 22) {
13889983SFei.Feng@Sun.COM remainder = (16 * len) % 22;
13899983SFei.Feng@Sun.COM if (remainder != 0 && remainder < 7)
13909983SFei.Feng@Sun.COM desc->plcp_service |= RT2661_PLCP_LENGEXT;
13919983SFei.Feng@Sun.COM }
13929983SFei.Feng@Sun.COM desc->plcp_length_hi = plcp_length >> 8;
13939983SFei.Feng@Sun.COM desc->plcp_length_lo = plcp_length & 0xff;
13949983SFei.Feng@Sun.COM
13959983SFei.Feng@Sun.COM if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
13969983SFei.Feng@Sun.COM desc->plcp_signal |= 0x08;
13979983SFei.Feng@Sun.COM }
13989983SFei.Feng@Sun.COM
13999983SFei.Feng@Sun.COM /* RT2x61 supports scatter with up to 5 segments */
14009983SFei.Feng@Sun.COM desc->len [0] = LE_16(len);
14019983SFei.Feng@Sun.COM }
14029983SFei.Feng@Sun.COM
14039983SFei.Feng@Sun.COM static int
rt2661_send(ieee80211com_t * ic,mblk_t * mp)14049983SFei.Feng@Sun.COM rt2661_send(ieee80211com_t *ic, mblk_t *mp)
14059983SFei.Feng@Sun.COM {
14069983SFei.Feng@Sun.COM struct rt2661_softc *sc = (struct rt2661_softc *)ic;
14079983SFei.Feng@Sun.COM struct rt2661_tx_ring *ring;
14089983SFei.Feng@Sun.COM struct rt2661_tx_desc *desc;
14099983SFei.Feng@Sun.COM struct rt2661_tx_data *data;
14109983SFei.Feng@Sun.COM struct ieee80211_frame *wh;
14119983SFei.Feng@Sun.COM struct ieee80211_node *ni;
14129983SFei.Feng@Sun.COM
14139983SFei.Feng@Sun.COM int err, off, rate;
14149983SFei.Feng@Sun.COM int mblen, pktlen;
14159983SFei.Feng@Sun.COM mblk_t *m, *m0;
14169983SFei.Feng@Sun.COM uint16_t dur;
14179983SFei.Feng@Sun.COM uint32_t flags = 0;
14189983SFei.Feng@Sun.COM
14199983SFei.Feng@Sun.COM mutex_enter(&sc->sc_txlock);
14209983SFei.Feng@Sun.COM ring = &sc->txq[0];
14219983SFei.Feng@Sun.COM err = DDI_SUCCESS;
14229983SFei.Feng@Sun.COM
14239983SFei.Feng@Sun.COM if (ring->queued > RT2661_TX_RING_COUNT - 8) {
14249983SFei.Feng@Sun.COM sc->sc_need_sched = 1;
14259983SFei.Feng@Sun.COM sc->sc_tx_nobuf++;
14269983SFei.Feng@Sun.COM err = ENOMEM;
14279983SFei.Feng@Sun.COM goto fail1;
14289983SFei.Feng@Sun.COM }
14299983SFei.Feng@Sun.COM
14309983SFei.Feng@Sun.COM m = allocb(msgdsize(mp) + 32, BPRI_MED);
14319983SFei.Feng@Sun.COM if (m == NULL) {
14329983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_TX, "rwd: rt2661_send():"
14339983SFei.Feng@Sun.COM "can't alloc mblk.\n");
14349983SFei.Feng@Sun.COM err = DDI_FAILURE;
14359983SFei.Feng@Sun.COM goto fail1;
14369983SFei.Feng@Sun.COM }
14379983SFei.Feng@Sun.COM
14389983SFei.Feng@Sun.COM for (off = 0, m0 = mp; m0 != NULL; m0 = m0->b_cont) {
14399983SFei.Feng@Sun.COM mblen = MBLKL(m0);
14409983SFei.Feng@Sun.COM (void) bcopy(m0->b_rptr, m->b_rptr + off, mblen);
14419983SFei.Feng@Sun.COM off += mblen;
14429983SFei.Feng@Sun.COM }
14439983SFei.Feng@Sun.COM m->b_wptr += off;
14449983SFei.Feng@Sun.COM
14459983SFei.Feng@Sun.COM wh = (struct ieee80211_frame *)m->b_rptr;
14469983SFei.Feng@Sun.COM ni = ieee80211_find_txnode(ic, wh->i_addr1);
14479983SFei.Feng@Sun.COM if (ni == NULL) {
14489983SFei.Feng@Sun.COM err = DDI_FAILURE;
14499983SFei.Feng@Sun.COM sc->sc_tx_err++;
14509983SFei.Feng@Sun.COM goto fail2;
14519983SFei.Feng@Sun.COM }
14529983SFei.Feng@Sun.COM
14539983SFei.Feng@Sun.COM (void) ieee80211_encap(ic, m, ni);
14549983SFei.Feng@Sun.COM
14559983SFei.Feng@Sun.COM if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
14569983SFei.Feng@Sun.COM struct ieee80211_key *k;
14579983SFei.Feng@Sun.COM k = ieee80211_crypto_encap(ic, m);
14589983SFei.Feng@Sun.COM if (k == NULL) {
14599983SFei.Feng@Sun.COM sc->sc_tx_err++;
14609983SFei.Feng@Sun.COM err = DDI_FAILURE;
14619983SFei.Feng@Sun.COM goto fail3;
14629983SFei.Feng@Sun.COM }
14639983SFei.Feng@Sun.COM /* packet header may have moved, reset our local pointer */
14649983SFei.Feng@Sun.COM wh = (struct ieee80211_frame *)m->b_rptr;
14659983SFei.Feng@Sun.COM }
14669983SFei.Feng@Sun.COM
14679983SFei.Feng@Sun.COM pktlen = msgdsize(m);
14689983SFei.Feng@Sun.COM
14699983SFei.Feng@Sun.COM desc = &ring->desc[ring->cur];
14709983SFei.Feng@Sun.COM data = &ring->data[ring->cur];
14719983SFei.Feng@Sun.COM data->ni = ieee80211_ref_node(ni);
14729983SFei.Feng@Sun.COM
14739983SFei.Feng@Sun.COM /* pickup a rate */
14749983SFei.Feng@Sun.COM if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
14759983SFei.Feng@Sun.COM /* multicast frames are sent at the lowest avail. rate */
14769983SFei.Feng@Sun.COM rate = ni->in_rates.ir_rates[0];
14779983SFei.Feng@Sun.COM } else if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
14789983SFei.Feng@Sun.COM rate = ic->ic_sup_rates[ic->ic_curmode].
14799983SFei.Feng@Sun.COM ir_rates[ic->ic_fixed_rate];
14809983SFei.Feng@Sun.COM } else
14819983SFei.Feng@Sun.COM rate = ni->in_rates.ir_rates[ni->in_txrate];
14829983SFei.Feng@Sun.COM if (rate == 0)
14839983SFei.Feng@Sun.COM rate = 2; /* XXX should not happen */
14849983SFei.Feng@Sun.COM rate &= IEEE80211_RATE_VAL;
14859983SFei.Feng@Sun.COM
14869983SFei.Feng@Sun.COM if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
14879983SFei.Feng@Sun.COM flags |= RT2661_TX_NEED_ACK;
14889983SFei.Feng@Sun.COM
14899983SFei.Feng@Sun.COM dur = rt2661_txtime(RT2661_ACK_SIZE,
14909983SFei.Feng@Sun.COM rt2661_ack_rate(ic, rate), ic->ic_flags) + sc->sifs;
14919983SFei.Feng@Sun.COM *(uint16_t *)wh->i_dur = LE_16(dur);
14929983SFei.Feng@Sun.COM }
14939983SFei.Feng@Sun.COM
14949983SFei.Feng@Sun.COM bcopy(m->b_rptr, data->buf, pktlen);
14959983SFei.Feng@Sun.COM rt2661_setup_tx_desc(sc, desc, flags, 0, pktlen, rate, 0);
14969983SFei.Feng@Sun.COM
14979983SFei.Feng@Sun.COM (void) ddi_dma_sync(data->txdata_dma.dma_hdl,
14989983SFei.Feng@Sun.COM 0, pktlen,
14999983SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV);
15009983SFei.Feng@Sun.COM
15019983SFei.Feng@Sun.COM (void) ddi_dma_sync(ring->txdesc_dma.dma_hdl,
15029983SFei.Feng@Sun.COM ring->cur * RT2661_TX_DESC_SIZE,
15039983SFei.Feng@Sun.COM RT2661_TX_DESC_SIZE,
15049983SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV);
15059983SFei.Feng@Sun.COM
15069983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_TX, "rwd: rt2661_send(): "
15079983SFei.Feng@Sun.COM "sending data frame len=%u idx=%u rate=%u\n",
15089983SFei.Feng@Sun.COM pktlen, ring->cur, rate);
15099983SFei.Feng@Sun.COM
15109983SFei.Feng@Sun.COM /* kick Tx */
15119983SFei.Feng@Sun.COM ring->queued++;
15129983SFei.Feng@Sun.COM ring->cur = (ring->cur + 1) % RT2661_TX_RING_COUNT;
15139983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_TX_CNTL_CSR, 1 << 0);
15149983SFei.Feng@Sun.COM
15159983SFei.Feng@Sun.COM ic->ic_stats.is_tx_frags++;
15169983SFei.Feng@Sun.COM ic->ic_stats.is_tx_bytes += pktlen;
15179983SFei.Feng@Sun.COM fail3:
15189983SFei.Feng@Sun.COM ieee80211_free_node(ni);
15199983SFei.Feng@Sun.COM fail2:
15209983SFei.Feng@Sun.COM freemsg(m);
15219983SFei.Feng@Sun.COM fail1:
15229983SFei.Feng@Sun.COM if (err == DDI_SUCCESS)
15239983SFei.Feng@Sun.COM freemsg(mp);
15249983SFei.Feng@Sun.COM mutex_exit(&sc->sc_txlock);
15259983SFei.Feng@Sun.COM return (err);
15269983SFei.Feng@Sun.COM }
15279983SFei.Feng@Sun.COM
15289983SFei.Feng@Sun.COM /*ARGSUSED*/
15299983SFei.Feng@Sun.COM static int
rt2661_mgmt_send(ieee80211com_t * ic,mblk_t * mp,uint8_t type)15309983SFei.Feng@Sun.COM rt2661_mgmt_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
15319983SFei.Feng@Sun.COM {
15329983SFei.Feng@Sun.COM struct rt2661_softc *sc = (struct rt2661_softc *)ic;
15339983SFei.Feng@Sun.COM struct rt2661_tx_ring *ring;
15349983SFei.Feng@Sun.COM struct rt2661_tx_desc *desc;
15359983SFei.Feng@Sun.COM struct rt2661_tx_data *data;
15369983SFei.Feng@Sun.COM struct ieee80211_frame *wh;
15379983SFei.Feng@Sun.COM struct ieee80211_node *ni;
15389983SFei.Feng@Sun.COM
15399983SFei.Feng@Sun.COM int err, off, rate;
15409983SFei.Feng@Sun.COM int mblen, pktlen;
15419983SFei.Feng@Sun.COM mblk_t *m, *m0;
15429983SFei.Feng@Sun.COM uint16_t dur;
15439983SFei.Feng@Sun.COM uint32_t flags = 0;
15449983SFei.Feng@Sun.COM
15459983SFei.Feng@Sun.COM if ((!RT2661_IS_RUNNING(sc)) || RT2661_IS_SUSPEND(sc)) {
15469983SFei.Feng@Sun.COM err = ENXIO;
15479983SFei.Feng@Sun.COM goto fail1;
15489983SFei.Feng@Sun.COM }
15499983SFei.Feng@Sun.COM
15509983SFei.Feng@Sun.COM ring = &sc->mgtq;
15519983SFei.Feng@Sun.COM err = DDI_SUCCESS;
15529983SFei.Feng@Sun.COM
15539983SFei.Feng@Sun.COM if (ring->queued >= RT2661_MGT_RING_COUNT) {
15549983SFei.Feng@Sun.COM sc->sc_tx_nobuf++;
15559983SFei.Feng@Sun.COM err = ENOMEM;
15569983SFei.Feng@Sun.COM goto fail1;
15579983SFei.Feng@Sun.COM }
15589983SFei.Feng@Sun.COM
15599983SFei.Feng@Sun.COM m = allocb(msgdsize(mp) + 32, BPRI_MED);
15609983SFei.Feng@Sun.COM if (m == NULL) {
15619983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_TX, "rwd: rt2661_mgmt_send():"
15629983SFei.Feng@Sun.COM "can't alloc mblk.\n");
15639983SFei.Feng@Sun.COM err = DDI_FAILURE;
15649983SFei.Feng@Sun.COM goto fail1;
15659983SFei.Feng@Sun.COM }
15669983SFei.Feng@Sun.COM
15679983SFei.Feng@Sun.COM for (off = 0, m0 = mp; m0 != NULL; m0 = m0->b_cont) {
15689983SFei.Feng@Sun.COM mblen = MBLKL(m0);
15699983SFei.Feng@Sun.COM (void) bcopy(m0->b_rptr, m->b_rptr + off, mblen);
15709983SFei.Feng@Sun.COM off += mblen;
15719983SFei.Feng@Sun.COM }
15729983SFei.Feng@Sun.COM m->b_wptr += off;
15739983SFei.Feng@Sun.COM
15749983SFei.Feng@Sun.COM wh = (struct ieee80211_frame *)m->b_rptr;
15759983SFei.Feng@Sun.COM ni = ieee80211_find_txnode(ic, wh->i_addr1);
15769983SFei.Feng@Sun.COM if (ni == NULL) {
15779983SFei.Feng@Sun.COM err = DDI_FAILURE;
15789983SFei.Feng@Sun.COM sc->sc_tx_err++;
15799983SFei.Feng@Sun.COM goto fail2;
15809983SFei.Feng@Sun.COM }
15819983SFei.Feng@Sun.COM
15829983SFei.Feng@Sun.COM if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
15839983SFei.Feng@Sun.COM struct ieee80211_key *k;
15849983SFei.Feng@Sun.COM k = ieee80211_crypto_encap(ic, m);
15859983SFei.Feng@Sun.COM if (k == NULL) {
15869983SFei.Feng@Sun.COM sc->sc_tx_err++;
15879983SFei.Feng@Sun.COM err = DDI_FAILURE;
15889983SFei.Feng@Sun.COM goto fail3;
15899983SFei.Feng@Sun.COM }
15909983SFei.Feng@Sun.COM /* packet header may have moved, reset our local pointer */
15919983SFei.Feng@Sun.COM wh = (struct ieee80211_frame *)m->b_rptr;
15929983SFei.Feng@Sun.COM }
15939983SFei.Feng@Sun.COM
15949983SFei.Feng@Sun.COM pktlen = msgdsize(m);
15959983SFei.Feng@Sun.COM
15969983SFei.Feng@Sun.COM desc = &ring->desc[ring->cur];
15979983SFei.Feng@Sun.COM data = &ring->data[ring->cur];
15989983SFei.Feng@Sun.COM data->ni = ieee80211_ref_node(ni);
15999983SFei.Feng@Sun.COM
16009983SFei.Feng@Sun.COM /* send mgt frames at the lowest available rate */
16019983SFei.Feng@Sun.COM rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
16029983SFei.Feng@Sun.COM
16039983SFei.Feng@Sun.COM if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
16049983SFei.Feng@Sun.COM flags |= RT2661_TX_NEED_ACK;
16059983SFei.Feng@Sun.COM
16069983SFei.Feng@Sun.COM dur = rt2661_txtime(RT2661_ACK_SIZE,
16079983SFei.Feng@Sun.COM rate, ic->ic_flags) + sc->sifs;
16089983SFei.Feng@Sun.COM *(uint16_t *)wh->i_dur = LE_16(dur);
16099983SFei.Feng@Sun.COM
16109983SFei.Feng@Sun.COM /* tell hardware to add timestamp in probe responses */
16119983SFei.Feng@Sun.COM if ((wh->i_fc[0] &
16129983SFei.Feng@Sun.COM (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
16139983SFei.Feng@Sun.COM (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP))
16149983SFei.Feng@Sun.COM flags |= RT2661_TX_TIMESTAMP;
16159983SFei.Feng@Sun.COM }
16169983SFei.Feng@Sun.COM
16179983SFei.Feng@Sun.COM bcopy(m->b_rptr, data->buf, pktlen);
16189983SFei.Feng@Sun.COM rt2661_setup_tx_desc(sc, desc, flags, 0, pktlen, rate, RT2661_QID_MGT);
16199983SFei.Feng@Sun.COM
16209983SFei.Feng@Sun.COM (void) ddi_dma_sync(data->txdata_dma.dma_hdl,
16219983SFei.Feng@Sun.COM 0, pktlen,
16229983SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV);
16239983SFei.Feng@Sun.COM
16249983SFei.Feng@Sun.COM (void) ddi_dma_sync(ring->txdesc_dma.dma_hdl,
16259983SFei.Feng@Sun.COM ring->cur * RT2661_TX_DESC_SIZE,
16269983SFei.Feng@Sun.COM RT2661_TX_DESC_SIZE,
16279983SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV);
16289983SFei.Feng@Sun.COM
16299983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_TX, "rwd: rt2661_mgmt_send(): "
16309983SFei.Feng@Sun.COM "sending mgmt frame len=%u idx=%u rate=%u\n",
16319983SFei.Feng@Sun.COM pktlen, ring->cur, rate);
16329983SFei.Feng@Sun.COM
16339983SFei.Feng@Sun.COM /* kick Tx */
16349983SFei.Feng@Sun.COM ring->queued++;
16359983SFei.Feng@Sun.COM ring->cur = (ring->cur + 1) % RT2661_MGT_RING_COUNT;
16369983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_TX_CNTL_CSR, RT2661_KICK_MGT);
16379983SFei.Feng@Sun.COM
16389983SFei.Feng@Sun.COM ic->ic_stats.is_tx_frags++;
16399983SFei.Feng@Sun.COM ic->ic_stats.is_tx_bytes += pktlen;
16409983SFei.Feng@Sun.COM
16419983SFei.Feng@Sun.COM fail3:
16429983SFei.Feng@Sun.COM ieee80211_free_node(ni);
16439983SFei.Feng@Sun.COM fail2:
16449983SFei.Feng@Sun.COM freemsg(m);
16459983SFei.Feng@Sun.COM fail1:
16469983SFei.Feng@Sun.COM freemsg(mp);
16479983SFei.Feng@Sun.COM return (err);
16489983SFei.Feng@Sun.COM }
16499983SFei.Feng@Sun.COM
16509983SFei.Feng@Sun.COM static void
rt2661_amrr_node_init(const struct rt2661_amrr * amrr,struct rt2661_amrr_node * amn)16519983SFei.Feng@Sun.COM rt2661_amrr_node_init(const struct rt2661_amrr *amrr,
16529983SFei.Feng@Sun.COM struct rt2661_amrr_node *amn)
16539983SFei.Feng@Sun.COM {
16549983SFei.Feng@Sun.COM amn->amn_success = 0;
16559983SFei.Feng@Sun.COM amn->amn_recovery = 0;
16569983SFei.Feng@Sun.COM amn->amn_txcnt = amn->amn_retrycnt = 0;
16579983SFei.Feng@Sun.COM amn->amn_success_threshold = amrr->amrr_min_success_threshold;
16589983SFei.Feng@Sun.COM }
16599983SFei.Feng@Sun.COM
16609983SFei.Feng@Sun.COM static void
rt2661_amrr_choose(struct rt2661_amrr * amrr,struct ieee80211_node * ni,struct rt2661_amrr_node * amn)16619983SFei.Feng@Sun.COM rt2661_amrr_choose(struct rt2661_amrr *amrr, struct ieee80211_node *ni,
16629983SFei.Feng@Sun.COM struct rt2661_amrr_node *amn)
16639983SFei.Feng@Sun.COM {
16649983SFei.Feng@Sun.COM #define RV(rate) ((rate) & IEEE80211_RATE_VAL)
16659983SFei.Feng@Sun.COM #define is_success(amn) \
16669983SFei.Feng@Sun.COM ((amn)->amn_retrycnt < (amn)->amn_txcnt / 10)
16679983SFei.Feng@Sun.COM #define is_failure(amn) \
16689983SFei.Feng@Sun.COM ((amn)->amn_retrycnt > (amn)->amn_txcnt / 3)
16699983SFei.Feng@Sun.COM #define is_enough(amn) \
16709983SFei.Feng@Sun.COM ((amn)->amn_txcnt > 10)
16719983SFei.Feng@Sun.COM #define is_min_rate(ni) \
16729983SFei.Feng@Sun.COM ((ni)->in_txrate == 0)
16739983SFei.Feng@Sun.COM #define is_max_rate(ni) \
16749983SFei.Feng@Sun.COM ((ni)->in_txrate == (ni)->in_rates.ir_nrates - 1)
16759983SFei.Feng@Sun.COM #define increase_rate(ni) \
16769983SFei.Feng@Sun.COM ((ni)->in_txrate++)
16779983SFei.Feng@Sun.COM #define decrease_rate(ni) \
16789983SFei.Feng@Sun.COM ((ni)->in_txrate--)
16799983SFei.Feng@Sun.COM #define reset_cnt(amn) \
16809983SFei.Feng@Sun.COM { (amn)->amn_txcnt = (amn)->amn_retrycnt = 0; }
16819983SFei.Feng@Sun.COM
16829983SFei.Feng@Sun.COM int need_change = 0;
16839983SFei.Feng@Sun.COM
16849983SFei.Feng@Sun.COM if (is_success(amn) && is_enough(amn)) {
16859983SFei.Feng@Sun.COM amn->amn_success++;
16869983SFei.Feng@Sun.COM if (amn->amn_success >= amn->amn_success_threshold &&
16879983SFei.Feng@Sun.COM !is_max_rate(ni)) {
16889983SFei.Feng@Sun.COM amn->amn_recovery = 1;
16899983SFei.Feng@Sun.COM amn->amn_success = 0;
16909983SFei.Feng@Sun.COM increase_rate(ni);
16919983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_amrr_choose(): "
16929983SFei.Feng@Sun.COM "increase rate = %d, #tx = %d, #retries = %d\n",
16939983SFei.Feng@Sun.COM RV(ni->in_rates.ir_rates[ni->in_txrate]),
16949983SFei.Feng@Sun.COM amn->amn_txcnt, amn->amn_retrycnt);
16959983SFei.Feng@Sun.COM need_change = 1;
16969983SFei.Feng@Sun.COM } else
16979983SFei.Feng@Sun.COM amn->amn_recovery = 0;
16989983SFei.Feng@Sun.COM } else if (is_failure(amn)) {
16999983SFei.Feng@Sun.COM amn->amn_success = 0;
17009983SFei.Feng@Sun.COM if (!is_min_rate(ni)) {
17019983SFei.Feng@Sun.COM if (amn->amn_recovery) {
17029983SFei.Feng@Sun.COM amn->amn_success_threshold *= 2;
17039983SFei.Feng@Sun.COM if (amn->amn_success_threshold >
17049983SFei.Feng@Sun.COM amrr->amrr_max_success_threshold)
17059983SFei.Feng@Sun.COM amn->amn_success_threshold =
17069983SFei.Feng@Sun.COM amrr->amrr_max_success_threshold;
17079983SFei.Feng@Sun.COM } else {
17089983SFei.Feng@Sun.COM amn->amn_success_threshold =
17099983SFei.Feng@Sun.COM amrr->amrr_min_success_threshold;
17109983SFei.Feng@Sun.COM }
17119983SFei.Feng@Sun.COM decrease_rate(ni);
17129983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_amrr_choose(): "
17139983SFei.Feng@Sun.COM "decrease rate = %d, #tx = %d, #retries = %d\n",
17149983SFei.Feng@Sun.COM RV(ni->in_rates.ir_rates[ni->in_txrate]),
17159983SFei.Feng@Sun.COM amn->amn_txcnt, amn->amn_retrycnt);
17169983SFei.Feng@Sun.COM need_change = 1;
17179983SFei.Feng@Sun.COM }
17189983SFei.Feng@Sun.COM amn->amn_recovery = 0;
17199983SFei.Feng@Sun.COM }
17209983SFei.Feng@Sun.COM
17219983SFei.Feng@Sun.COM if (is_enough(amn) || need_change)
17229983SFei.Feng@Sun.COM reset_cnt(amn);
17239983SFei.Feng@Sun.COM #undef RV
17249983SFei.Feng@Sun.COM
17259983SFei.Feng@Sun.COM }
17269983SFei.Feng@Sun.COM
17279983SFei.Feng@Sun.COM static void
rt2661_update_promisc(struct rt2661_softc * sc)17289983SFei.Feng@Sun.COM rt2661_update_promisc(struct rt2661_softc *sc)
17299983SFei.Feng@Sun.COM {
17309983SFei.Feng@Sun.COM uint32_t tmp;
17319983SFei.Feng@Sun.COM
17329983SFei.Feng@Sun.COM tmp = RT2661_READ(sc, RT2661_TXRX_CSR0);
17339983SFei.Feng@Sun.COM
17349983SFei.Feng@Sun.COM tmp &= ~RT2661_DROP_NOT_TO_ME;
17359983SFei.Feng@Sun.COM if (!(sc->sc_rcr & RT2661_RCR_PROMISC))
17369983SFei.Feng@Sun.COM tmp |= RT2661_DROP_NOT_TO_ME;
17379983SFei.Feng@Sun.COM
17389983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_TXRX_CSR0, tmp);
17399983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_update_promisc(): "
17409983SFei.Feng@Sun.COM "%s promiscuous mode\n",
17419983SFei.Feng@Sun.COM (sc->sc_rcr & RT2661_RCR_PROMISC) ? "entering" : "leaving");
17429983SFei.Feng@Sun.COM }
17439983SFei.Feng@Sun.COM
17449983SFei.Feng@Sun.COM static void
rt2661_updateslot(struct ieee80211com * ic,int onoff)17459983SFei.Feng@Sun.COM rt2661_updateslot(struct ieee80211com *ic, int onoff)
17469983SFei.Feng@Sun.COM {
17479983SFei.Feng@Sun.COM struct rt2661_softc *sc = (struct rt2661_softc *)ic;
17489983SFei.Feng@Sun.COM uint8_t slottime;
17499983SFei.Feng@Sun.COM uint32_t tmp;
17509983SFei.Feng@Sun.COM
17519983SFei.Feng@Sun.COM slottime = (onoff ? 9 : 20);
17529983SFei.Feng@Sun.COM
17539983SFei.Feng@Sun.COM tmp = RT2661_READ(sc, RT2661_MAC_CSR9);
17549983SFei.Feng@Sun.COM tmp = (tmp & ~0xff) | slottime;
17559983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MAC_CSR9, tmp);
17569983SFei.Feng@Sun.COM
17579983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_updateslot(): "
17589983SFei.Feng@Sun.COM "setting slot time to %uus\n", slottime);
17599983SFei.Feng@Sun.COM }
17609983SFei.Feng@Sun.COM
17619983SFei.Feng@Sun.COM static void
rt2661_set_slottime(struct rt2661_softc * sc)17629983SFei.Feng@Sun.COM rt2661_set_slottime(struct rt2661_softc *sc)
17639983SFei.Feng@Sun.COM {
17649983SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
17659983SFei.Feng@Sun.COM uint8_t slottime;
17669983SFei.Feng@Sun.COM uint32_t tmp;
17679983SFei.Feng@Sun.COM
17689983SFei.Feng@Sun.COM slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20;
17699983SFei.Feng@Sun.COM
17709983SFei.Feng@Sun.COM tmp = RT2661_READ(sc, RT2661_MAC_CSR9);
17719983SFei.Feng@Sun.COM tmp = (tmp & ~0xff) | slottime;
17729983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MAC_CSR9, tmp);
17739983SFei.Feng@Sun.COM
17749983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_set_slottime(): "
17759983SFei.Feng@Sun.COM "setting slot time to %uus\n", slottime);
17769983SFei.Feng@Sun.COM }
17779983SFei.Feng@Sun.COM
17789983SFei.Feng@Sun.COM
17799983SFei.Feng@Sun.COM /*
17809983SFei.Feng@Sun.COM * Enable multi-rate retries for frames sent at OFDM rates.
17819983SFei.Feng@Sun.COM * In 802.11b/g mode, allow fallback to CCK rates.
17829983SFei.Feng@Sun.COM */
17839983SFei.Feng@Sun.COM static void
rt2661_enable_mrr(struct rt2661_softc * sc)17849983SFei.Feng@Sun.COM rt2661_enable_mrr(struct rt2661_softc *sc)
17859983SFei.Feng@Sun.COM {
17869983SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
17879983SFei.Feng@Sun.COM uint32_t tmp;
17889983SFei.Feng@Sun.COM
17899983SFei.Feng@Sun.COM tmp = RT2661_READ(sc, RT2661_TXRX_CSR4);
17909983SFei.Feng@Sun.COM
17919983SFei.Feng@Sun.COM tmp &= ~RT2661_MRR_CCK_FALLBACK;
17929983SFei.Feng@Sun.COM if (!IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
17939983SFei.Feng@Sun.COM tmp |= RT2661_MRR_CCK_FALLBACK;
17949983SFei.Feng@Sun.COM tmp |= RT2661_MRR_ENABLED;
17959983SFei.Feng@Sun.COM
17969983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_TXRX_CSR4, tmp);
17979983SFei.Feng@Sun.COM }
17989983SFei.Feng@Sun.COM
17999983SFei.Feng@Sun.COM static void
rt2661_set_txpreamble(struct rt2661_softc * sc)18009983SFei.Feng@Sun.COM rt2661_set_txpreamble(struct rt2661_softc *sc)
18019983SFei.Feng@Sun.COM {
18029983SFei.Feng@Sun.COM uint32_t tmp;
18039983SFei.Feng@Sun.COM
18049983SFei.Feng@Sun.COM tmp = RT2661_READ(sc, RT2661_TXRX_CSR4);
18059983SFei.Feng@Sun.COM
18069983SFei.Feng@Sun.COM tmp &= ~RT2661_SHORT_PREAMBLE;
18079983SFei.Feng@Sun.COM if (sc->sc_ic.ic_flags & IEEE80211_F_SHPREAMBLE)
18089983SFei.Feng@Sun.COM tmp |= RT2661_SHORT_PREAMBLE;
18099983SFei.Feng@Sun.COM
18109983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_TXRX_CSR4, tmp);
18119983SFei.Feng@Sun.COM }
18129983SFei.Feng@Sun.COM
18139983SFei.Feng@Sun.COM static void
rt2661_set_basicrates(struct rt2661_softc * sc)18149983SFei.Feng@Sun.COM rt2661_set_basicrates(struct rt2661_softc *sc)
18159983SFei.Feng@Sun.COM {
18169983SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
18179983SFei.Feng@Sun.COM
18189983SFei.Feng@Sun.COM /* update basic rate set */
18199983SFei.Feng@Sun.COM if (ic->ic_curmode == IEEE80211_MODE_11B) {
18209983SFei.Feng@Sun.COM /* 11b basic rates: 1, 2Mbps */
18219983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_TXRX_CSR5, 0x3);
18229983SFei.Feng@Sun.COM } else if (ic->ic_curmode == IEEE80211_MODE_11A) {
18239983SFei.Feng@Sun.COM /* 11a basic rates: 6, 12, 24Mbps */
18249983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_TXRX_CSR5, 0x150);
18259983SFei.Feng@Sun.COM } else {
18269983SFei.Feng@Sun.COM /* 11b/g basic rates: 1, 2, 5.5, 11Mbps */
18279983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_TXRX_CSR5, 0xf);
18289983SFei.Feng@Sun.COM }
18299983SFei.Feng@Sun.COM }
18309983SFei.Feng@Sun.COM
18319983SFei.Feng@Sun.COM static void
rt2661_set_bssid(struct rt2661_softc * sc,const uint8_t * bssid)18329983SFei.Feng@Sun.COM rt2661_set_bssid(struct rt2661_softc *sc, const uint8_t *bssid)
18339983SFei.Feng@Sun.COM {
18349983SFei.Feng@Sun.COM uint32_t tmp;
18359983SFei.Feng@Sun.COM
18369983SFei.Feng@Sun.COM tmp = bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24;
18379983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MAC_CSR4, tmp);
18389983SFei.Feng@Sun.COM
18399983SFei.Feng@Sun.COM tmp = bssid[4] | bssid[5] << 8 | RT2661_ONE_BSSID << 16;
18409983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MAC_CSR5, tmp);
18419983SFei.Feng@Sun.COM }
18429983SFei.Feng@Sun.COM
18439983SFei.Feng@Sun.COM /*
18449983SFei.Feng@Sun.COM * Enable TSF synchronization and tell h/w to start sending beacons for IBSS
18459983SFei.Feng@Sun.COM * and HostAP operating modes.
18469983SFei.Feng@Sun.COM */
18479983SFei.Feng@Sun.COM static void
rt2661_enable_tsf_sync(struct rt2661_softc * sc)18489983SFei.Feng@Sun.COM rt2661_enable_tsf_sync(struct rt2661_softc *sc)
18499983SFei.Feng@Sun.COM {
18509983SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
18519983SFei.Feng@Sun.COM uint32_t tmp;
18529983SFei.Feng@Sun.COM
18539983SFei.Feng@Sun.COM tmp = RT2661_READ(sc, RT2661_TXRX_CSR9) & 0xff000000;
18549983SFei.Feng@Sun.COM
18559983SFei.Feng@Sun.COM /* set beacon interval (in 1/16ms unit) */
18569983SFei.Feng@Sun.COM tmp |= ic->ic_bss->in_intval * 16;
18579983SFei.Feng@Sun.COM
18589983SFei.Feng@Sun.COM tmp |= RT2661_TSF_TICKING | RT2661_ENABLE_TBTT;
18599983SFei.Feng@Sun.COM if (ic->ic_opmode == IEEE80211_M_STA)
18609983SFei.Feng@Sun.COM tmp |= RT2661_TSF_MODE(1);
18619983SFei.Feng@Sun.COM
18629983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_TXRX_CSR9, tmp);
18639983SFei.Feng@Sun.COM }
18649983SFei.Feng@Sun.COM
18659983SFei.Feng@Sun.COM
18669983SFei.Feng@Sun.COM static void
rt2661_next_scan(void * arg)18679983SFei.Feng@Sun.COM rt2661_next_scan(void *arg)
18689983SFei.Feng@Sun.COM {
18699983SFei.Feng@Sun.COM struct rt2661_softc *sc = arg;
18709983SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
18719983SFei.Feng@Sun.COM
18729983SFei.Feng@Sun.COM if (ic->ic_state == IEEE80211_S_SCAN)
18739983SFei.Feng@Sun.COM (void) ieee80211_next_scan(ic);
18749983SFei.Feng@Sun.COM }
18759983SFei.Feng@Sun.COM
18769983SFei.Feng@Sun.COM static void
rt2661_newassoc(struct ieee80211com * ic,struct ieee80211_node * ni)18779983SFei.Feng@Sun.COM rt2661_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni)
18789983SFei.Feng@Sun.COM {
18799983SFei.Feng@Sun.COM struct rt2661_softc *sc = (struct rt2661_softc *)ic;
18809983SFei.Feng@Sun.COM int i;
18819983SFei.Feng@Sun.COM
18829983SFei.Feng@Sun.COM rt2661_amrr_node_init(&sc->amrr, &((struct rt2661_node *)ni)->amn);
18839983SFei.Feng@Sun.COM
18849983SFei.Feng@Sun.COM /* set rate to some reasonable initial value */
18859983SFei.Feng@Sun.COM i = ni->in_rates.ir_nrates - 1;
18869983SFei.Feng@Sun.COM while (i > 0 && ((ni->in_rates.ir_rates[i] & IEEE80211_RATE_VAL) > 72))
18879983SFei.Feng@Sun.COM i--;
18889983SFei.Feng@Sun.COM
18899983SFei.Feng@Sun.COM ni->in_txrate = i;
18909983SFei.Feng@Sun.COM }
18919983SFei.Feng@Sun.COM
18929983SFei.Feng@Sun.COM static void
rt2661_iter_func(void * arg,struct ieee80211_node * ni)18939983SFei.Feng@Sun.COM rt2661_iter_func(void *arg, struct ieee80211_node *ni)
18949983SFei.Feng@Sun.COM {
18959983SFei.Feng@Sun.COM struct rt2661_softc *sc = arg;
18969983SFei.Feng@Sun.COM struct rt2661_node *rn = (struct rt2661_node *)ni;
18979983SFei.Feng@Sun.COM
18989983SFei.Feng@Sun.COM rt2661_amrr_choose(&sc->amrr, ni, &rn->amn);
18999983SFei.Feng@Sun.COM
19009983SFei.Feng@Sun.COM }
19019983SFei.Feng@Sun.COM
19029983SFei.Feng@Sun.COM /*
19039983SFei.Feng@Sun.COM * Dynamically tune Rx sensitivity (BBP register 17) based on average RSSI and
19049983SFei.Feng@Sun.COM * false CCA count. This function is called periodically (every seconds) when
19059983SFei.Feng@Sun.COM * in the RUN state. Values taken from the reference driver.
19069983SFei.Feng@Sun.COM */
19079983SFei.Feng@Sun.COM static void
rt2661_rx_tune(struct rt2661_softc * sc)19089983SFei.Feng@Sun.COM rt2661_rx_tune(struct rt2661_softc *sc)
19099983SFei.Feng@Sun.COM {
19109983SFei.Feng@Sun.COM uint8_t bbp17;
19119983SFei.Feng@Sun.COM uint16_t cca;
19129983SFei.Feng@Sun.COM int lo, hi, dbm;
19139983SFei.Feng@Sun.COM
19149983SFei.Feng@Sun.COM /*
19159983SFei.Feng@Sun.COM * Tuning range depends on operating band and on the presence of an
19169983SFei.Feng@Sun.COM * external low-noise amplifier.
19179983SFei.Feng@Sun.COM */
19189983SFei.Feng@Sun.COM lo = 0x20;
19199983SFei.Feng@Sun.COM if (IEEE80211_IS_CHAN_5GHZ(sc->sc_curchan))
19209983SFei.Feng@Sun.COM lo += 0x08;
19219983SFei.Feng@Sun.COM if ((IEEE80211_IS_CHAN_2GHZ(sc->sc_curchan) && sc->ext_2ghz_lna) ||
19229983SFei.Feng@Sun.COM (IEEE80211_IS_CHAN_5GHZ(sc->sc_curchan) && sc->ext_5ghz_lna))
19239983SFei.Feng@Sun.COM lo += 0x10;
19249983SFei.Feng@Sun.COM hi = lo + 0x20;
19259983SFei.Feng@Sun.COM
19269983SFei.Feng@Sun.COM dbm = sc->avg_rssi;
19279983SFei.Feng@Sun.COM /* retrieve false CCA count since last call (clear on read) */
19289983SFei.Feng@Sun.COM cca = RT2661_READ(sc, RT2661_STA_CSR1) & 0xffff;
19299983SFei.Feng@Sun.COM
19309983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_INTR, "rwd: rt2661_rx_tune(): "
19319983SFei.Feng@Sun.COM "RSSI=%ddBm false CCA=%d\n", dbm, cca);
19329983SFei.Feng@Sun.COM
19339983SFei.Feng@Sun.COM if (dbm < -74) {
19349983SFei.Feng@Sun.COM /* very bad RSSI, tune using false CCA count */
19359983SFei.Feng@Sun.COM bbp17 = sc->bbp17; /* current value */
19369983SFei.Feng@Sun.COM
19379983SFei.Feng@Sun.COM hi -= 2 * (-74 - dbm);
19389983SFei.Feng@Sun.COM if (hi < lo)
19399983SFei.Feng@Sun.COM hi = lo;
19409983SFei.Feng@Sun.COM
19419983SFei.Feng@Sun.COM if (bbp17 > hi)
19429983SFei.Feng@Sun.COM bbp17 = (uint8_t)hi;
19439983SFei.Feng@Sun.COM else if (cca > 512)
19449983SFei.Feng@Sun.COM bbp17 = (uint8_t)min(bbp17 + 1, hi);
19459983SFei.Feng@Sun.COM else if (cca < 100)
19469983SFei.Feng@Sun.COM bbp17 = (uint8_t)max(bbp17 - 1, lo);
19479983SFei.Feng@Sun.COM
19489983SFei.Feng@Sun.COM } else if (dbm < -66) {
19499983SFei.Feng@Sun.COM bbp17 = lo + 0x08;
19509983SFei.Feng@Sun.COM } else if (dbm < -58) {
19519983SFei.Feng@Sun.COM bbp17 = lo + 0x10;
19529983SFei.Feng@Sun.COM } else if (dbm < -35) {
19539983SFei.Feng@Sun.COM bbp17 = (uint8_t)hi;
19549983SFei.Feng@Sun.COM } else { /* very good RSSI >= -35dBm */
19559983SFei.Feng@Sun.COM bbp17 = 0x60; /* very low sensitivity */
19569983SFei.Feng@Sun.COM }
19579983SFei.Feng@Sun.COM
19589983SFei.Feng@Sun.COM if (bbp17 != sc->bbp17) {
19599983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_rx_tune(): "
19609983SFei.Feng@Sun.COM "BBP17 %x->%x\n", sc->bbp17, bbp17);
19619983SFei.Feng@Sun.COM rt2661_bbp_write(sc, 17, bbp17);
19629983SFei.Feng@Sun.COM sc->bbp17 = bbp17;
19639983SFei.Feng@Sun.COM }
19649983SFei.Feng@Sun.COM }
19659983SFei.Feng@Sun.COM
19669983SFei.Feng@Sun.COM /*
19679983SFei.Feng@Sun.COM * This function is called periodically (every 500ms) in RUN state to update
19689983SFei.Feng@Sun.COM * various settings like rate control statistics or Rx sensitivity.
19699983SFei.Feng@Sun.COM */
19709983SFei.Feng@Sun.COM static void
rt2661_updatestats(void * arg)19719983SFei.Feng@Sun.COM rt2661_updatestats(void *arg)
19729983SFei.Feng@Sun.COM {
19739983SFei.Feng@Sun.COM struct rt2661_softc *sc = arg;
19749983SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
19759983SFei.Feng@Sun.COM
19769983SFei.Feng@Sun.COM if (ic->ic_opmode == IEEE80211_M_STA)
19779983SFei.Feng@Sun.COM rt2661_iter_func(sc, ic->ic_bss);
19789983SFei.Feng@Sun.COM else
19799983SFei.Feng@Sun.COM ieee80211_iterate_nodes(&ic->ic_sta, rt2661_iter_func, arg);
19809983SFei.Feng@Sun.COM
19819983SFei.Feng@Sun.COM /* update rx sensitivity every 1 sec */
19829983SFei.Feng@Sun.COM if (++sc->ncalls & 1)
19839983SFei.Feng@Sun.COM rt2661_rx_tune(sc);
19849983SFei.Feng@Sun.COM
19859983SFei.Feng@Sun.COM sc->sc_rssadapt_id = timeout(rt2661_updatestats, (void *)sc,
19869983SFei.Feng@Sun.COM drv_usectohz(200 * 1000));
19879983SFei.Feng@Sun.COM }
19889983SFei.Feng@Sun.COM
19899983SFei.Feng@Sun.COM static int
rt2661_newstate(struct ieee80211com * ic,enum ieee80211_state nstate,int arg)19909983SFei.Feng@Sun.COM rt2661_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
19919983SFei.Feng@Sun.COM {
19929983SFei.Feng@Sun.COM struct rt2661_softc *sc = (struct rt2661_softc *)ic;
19939983SFei.Feng@Sun.COM enum ieee80211_state ostate;
19949983SFei.Feng@Sun.COM struct ieee80211_node *ni;
19959983SFei.Feng@Sun.COM uint32_t tmp;
19969983SFei.Feng@Sun.COM int err;
19979983SFei.Feng@Sun.COM
19989983SFei.Feng@Sun.COM RT2661_GLOCK(sc);
19999983SFei.Feng@Sun.COM
20009983SFei.Feng@Sun.COM ostate = ic->ic_state;
20019983SFei.Feng@Sun.COM sc->sc_ostate = ostate;
20029983SFei.Feng@Sun.COM
20039983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt26661_newstate(): "
20049983SFei.Feng@Sun.COM "%x -> %x\n", ostate, nstate);
20059983SFei.Feng@Sun.COM
20069983SFei.Feng@Sun.COM if (sc->sc_scan_id != 0) {
20079983SFei.Feng@Sun.COM (void) untimeout(sc->sc_scan_id);
20089983SFei.Feng@Sun.COM sc->sc_scan_id = 0;
20099983SFei.Feng@Sun.COM }
20109983SFei.Feng@Sun.COM
20119983SFei.Feng@Sun.COM if (sc->sc_rssadapt_id) {
20129983SFei.Feng@Sun.COM (void) untimeout(sc->sc_rssadapt_id);
20139983SFei.Feng@Sun.COM sc->sc_rssadapt_id = 0;
20149983SFei.Feng@Sun.COM }
20159983SFei.Feng@Sun.COM
20169983SFei.Feng@Sun.COM switch (nstate) {
20179983SFei.Feng@Sun.COM case IEEE80211_S_INIT:
20189983SFei.Feng@Sun.COM if (ostate == IEEE80211_S_RUN) {
20199983SFei.Feng@Sun.COM /* abort TSF synchronization */
20209983SFei.Feng@Sun.COM tmp = RT2661_READ(sc, RT2661_TXRX_CSR9);
20219983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_TXRX_CSR9, tmp & ~0x00ffffff);
20229983SFei.Feng@Sun.COM }
20239983SFei.Feng@Sun.COM break;
20249983SFei.Feng@Sun.COM case IEEE80211_S_SCAN:
20259983SFei.Feng@Sun.COM rt2661_set_chan(sc, ic->ic_curchan);
20269983SFei.Feng@Sun.COM sc->sc_scan_id = timeout(rt2661_next_scan, (void *)sc,
20279983SFei.Feng@Sun.COM drv_usectohz(200000));
20289983SFei.Feng@Sun.COM break;
20299983SFei.Feng@Sun.COM case IEEE80211_S_AUTH:
20309983SFei.Feng@Sun.COM case IEEE80211_S_ASSOC:
20319983SFei.Feng@Sun.COM rt2661_set_chan(sc, ic->ic_curchan);
20329983SFei.Feng@Sun.COM break;
20339983SFei.Feng@Sun.COM case IEEE80211_S_RUN:
20349983SFei.Feng@Sun.COM rt2661_set_chan(sc, ic->ic_curchan);
20359983SFei.Feng@Sun.COM
20369983SFei.Feng@Sun.COM ni = ic->ic_bss;
20379983SFei.Feng@Sun.COM if (ic->ic_opmode != IEEE80211_M_MONITOR) {
20389983SFei.Feng@Sun.COM rt2661_set_slottime(sc);
20399983SFei.Feng@Sun.COM rt2661_enable_mrr(sc);
20409983SFei.Feng@Sun.COM rt2661_set_txpreamble(sc);
20419983SFei.Feng@Sun.COM rt2661_set_basicrates(sc);
20429983SFei.Feng@Sun.COM rt2661_set_bssid(sc, ni->in_bssid);
20439983SFei.Feng@Sun.COM }
20449983SFei.Feng@Sun.COM
20459983SFei.Feng@Sun.COM if (ic->ic_opmode == IEEE80211_M_STA) {
20469983SFei.Feng@Sun.COM /* fake a join to init the tx rate */
20479983SFei.Feng@Sun.COM rt2661_newassoc(ic, ni);
20489983SFei.Feng@Sun.COM }
20499983SFei.Feng@Sun.COM
20509983SFei.Feng@Sun.COM if (ic->ic_opmode != IEEE80211_M_MONITOR) {
20519983SFei.Feng@Sun.COM sc->ncalls = 0;
20529983SFei.Feng@Sun.COM sc->avg_rssi = -95; /* reset EMA */
20539983SFei.Feng@Sun.COM sc->sc_rssadapt_id = timeout(rt2661_updatestats,
20549983SFei.Feng@Sun.COM (void *)sc, drv_usectohz(200 * 1000));
20559983SFei.Feng@Sun.COM rt2661_enable_tsf_sync(sc);
20569983SFei.Feng@Sun.COM }
20579983SFei.Feng@Sun.COM break;
20589983SFei.Feng@Sun.COM default:
20599983SFei.Feng@Sun.COM break;
20609983SFei.Feng@Sun.COM }
20619983SFei.Feng@Sun.COM
20629983SFei.Feng@Sun.COM RT2661_GUNLOCK(sc);
20639983SFei.Feng@Sun.COM
20649983SFei.Feng@Sun.COM err = sc->sc_newstate(ic, nstate, arg);
20659983SFei.Feng@Sun.COM return (err);
20669983SFei.Feng@Sun.COM }
20679983SFei.Feng@Sun.COM
20689983SFei.Feng@Sun.COM /*ARGSUSED*/
20699983SFei.Feng@Sun.COM static struct ieee80211_node *
rt2661_node_alloc(ieee80211com_t * ic)20709983SFei.Feng@Sun.COM rt2661_node_alloc(ieee80211com_t *ic)
20719983SFei.Feng@Sun.COM {
20729983SFei.Feng@Sun.COM struct rt2661_node *rn;
20739983SFei.Feng@Sun.COM
20749983SFei.Feng@Sun.COM rn = kmem_zalloc(sizeof (struct rt2661_node), KM_SLEEP);
20759983SFei.Feng@Sun.COM return ((rn != NULL) ? &rn->ni : NULL);
20769983SFei.Feng@Sun.COM }
20779983SFei.Feng@Sun.COM
20789983SFei.Feng@Sun.COM static void
rt2661_node_free(struct ieee80211_node * in)20799983SFei.Feng@Sun.COM rt2661_node_free(struct ieee80211_node *in)
20809983SFei.Feng@Sun.COM {
20819983SFei.Feng@Sun.COM struct ieee80211com *ic = in->in_ic;
20829983SFei.Feng@Sun.COM
20839983SFei.Feng@Sun.COM ic->ic_node_cleanup(in);
20849983SFei.Feng@Sun.COM if (in->in_wpa_ie != NULL)
20859983SFei.Feng@Sun.COM ieee80211_free(in->in_wpa_ie);
20869983SFei.Feng@Sun.COM kmem_free(in, sizeof (struct rt2661_node));
20879983SFei.Feng@Sun.COM }
20889983SFei.Feng@Sun.COM
20899983SFei.Feng@Sun.COM static void
rt2661_stop_locked(struct rt2661_softc * sc)20909983SFei.Feng@Sun.COM rt2661_stop_locked(struct rt2661_softc *sc)
20919983SFei.Feng@Sun.COM {
20929983SFei.Feng@Sun.COM uint32_t tmp;
20939983SFei.Feng@Sun.COM
20949983SFei.Feng@Sun.COM if (RT2661_IS_RUNNING(sc)) {
20959983SFei.Feng@Sun.COM sc->sc_tx_timer = 0;
20969983SFei.Feng@Sun.COM
20979983SFei.Feng@Sun.COM /* abort Tx (for all 5 Tx rings) */
20989983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_TX_CNTL_CSR, 0x1f << 16);
20999983SFei.Feng@Sun.COM
21009983SFei.Feng@Sun.COM /* disable Rx (value remains after reset!) */
21019983SFei.Feng@Sun.COM tmp = RT2661_READ(sc, RT2661_TXRX_CSR0);
21029983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_TXRX_CSR0, tmp | RT2661_DISABLE_RX);
21039983SFei.Feng@Sun.COM
21049983SFei.Feng@Sun.COM /* reset ASIC */
21059983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MAC_CSR1, 3);
21069983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MAC_CSR1, 0);
21079983SFei.Feng@Sun.COM
21089983SFei.Feng@Sun.COM /* disable interrupts */
21099983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_INT_MASK_CSR, 0xffffff7f);
21109983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MCU_INT_MASK_CSR, 0xffffffff);
21119983SFei.Feng@Sun.COM
21129983SFei.Feng@Sun.COM /* clear any pending interrupt */
21139983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_INT_SOURCE_CSR, 0xffffffff);
21149983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MCU_INT_SOURCE_CSR, 0xffffffff);
21159983SFei.Feng@Sun.COM
21169983SFei.Feng@Sun.COM /* reset Tx and Rx rings */
21179983SFei.Feng@Sun.COM rt2661_reset_tx_ring(sc, &sc->txq[0]);
21189983SFei.Feng@Sun.COM rt2661_reset_tx_ring(sc, &sc->txq[1]);
21199983SFei.Feng@Sun.COM rt2661_reset_tx_ring(sc, &sc->txq[2]);
21209983SFei.Feng@Sun.COM rt2661_reset_tx_ring(sc, &sc->txq[3]);
21219983SFei.Feng@Sun.COM rt2661_reset_tx_ring(sc, &sc->mgtq);
21229983SFei.Feng@Sun.COM rt2661_reset_rx_ring(sc, &sc->rxq);
21239983SFei.Feng@Sun.COM }
21249983SFei.Feng@Sun.COM }
21259983SFei.Feng@Sun.COM
21269983SFei.Feng@Sun.COM static void
rt2661_set_macaddr(struct rt2661_softc * sc,const uint8_t * addr)21279983SFei.Feng@Sun.COM rt2661_set_macaddr(struct rt2661_softc *sc, const uint8_t *addr)
21289983SFei.Feng@Sun.COM {
21299983SFei.Feng@Sun.COM uint32_t tmp;
21309983SFei.Feng@Sun.COM
21319983SFei.Feng@Sun.COM tmp = addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24;
21329983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MAC_CSR2, tmp);
21339983SFei.Feng@Sun.COM
21349983SFei.Feng@Sun.COM tmp = addr[4] | addr[5] << 8 | 0xff << 16;
21359983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MAC_CSR3, tmp);
21369983SFei.Feng@Sun.COM }
21379983SFei.Feng@Sun.COM
21389983SFei.Feng@Sun.COM static uint8_t
rt2661_bbp_read(struct rt2661_softc * sc,uint8_t reg)21399983SFei.Feng@Sun.COM rt2661_bbp_read(struct rt2661_softc *sc, uint8_t reg)
21409983SFei.Feng@Sun.COM {
21419983SFei.Feng@Sun.COM uint32_t val;
21429983SFei.Feng@Sun.COM int ntries;
21439983SFei.Feng@Sun.COM
21449983SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) {
21459983SFei.Feng@Sun.COM if (!(RT2661_READ(sc, RT2661_PHY_CSR3) & RT2661_BBP_BUSY))
21469983SFei.Feng@Sun.COM break;
21479983SFei.Feng@Sun.COM DELAY(1);
21489983SFei.Feng@Sun.COM }
21499983SFei.Feng@Sun.COM if (ntries == 100) {
21509983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_bbp_read(): "
21519983SFei.Feng@Sun.COM "could not read from BBP\n");
21529983SFei.Feng@Sun.COM return (0);
21539983SFei.Feng@Sun.COM }
21549983SFei.Feng@Sun.COM
21559983SFei.Feng@Sun.COM val = RT2661_BBP_BUSY | RT2661_BBP_READ | reg << 8;
21569983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_PHY_CSR3, val);
21579983SFei.Feng@Sun.COM
21589983SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) {
21599983SFei.Feng@Sun.COM val = RT2661_READ(sc, RT2661_PHY_CSR3);
21609983SFei.Feng@Sun.COM if (!(val & RT2661_BBP_BUSY))
21619983SFei.Feng@Sun.COM return (val & 0xff);
21629983SFei.Feng@Sun.COM DELAY(1);
21639983SFei.Feng@Sun.COM }
21649983SFei.Feng@Sun.COM
21659983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_bbp_read(): "
21669983SFei.Feng@Sun.COM "could not read from BBP\n");
21679983SFei.Feng@Sun.COM return (0);
21689983SFei.Feng@Sun.COM }
21699983SFei.Feng@Sun.COM
21709983SFei.Feng@Sun.COM static int
rt2661_bbp_init(struct rt2661_softc * sc)21719983SFei.Feng@Sun.COM rt2661_bbp_init(struct rt2661_softc *sc)
21729983SFei.Feng@Sun.COM {
21739983SFei.Feng@Sun.COM #define N(a) (sizeof (a) / sizeof ((a)[0]))
21749983SFei.Feng@Sun.COM
21759983SFei.Feng@Sun.COM int i, ntries;
21769983SFei.Feng@Sun.COM uint8_t val;
21779983SFei.Feng@Sun.COM
21789983SFei.Feng@Sun.COM /* wait for BBP to be ready */
21799983SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) {
21809983SFei.Feng@Sun.COM val = rt2661_bbp_read(sc, 0);
21819983SFei.Feng@Sun.COM if (val != 0 && val != 0xff)
21829983SFei.Feng@Sun.COM break;
21839983SFei.Feng@Sun.COM DELAY(100);
21849983SFei.Feng@Sun.COM }
21859983SFei.Feng@Sun.COM if (ntries == 100) {
21869983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_bbp_init(): "
21879983SFei.Feng@Sun.COM "timeout waiting for BBP\n");
21889983SFei.Feng@Sun.COM return (RT2661_FAILURE);
21899983SFei.Feng@Sun.COM }
21909983SFei.Feng@Sun.COM
21919983SFei.Feng@Sun.COM /* initialize BBP registers to default values */
21929983SFei.Feng@Sun.COM for (i = 0; i < N(rt2661_def_bbp); i++) {
21939983SFei.Feng@Sun.COM rt2661_bbp_write(sc, rt2661_def_bbp[i].reg,
21949983SFei.Feng@Sun.COM rt2661_def_bbp[i].val);
21959983SFei.Feng@Sun.COM }
21969983SFei.Feng@Sun.COM
21979983SFei.Feng@Sun.COM /* write vendor-specific BBP values (from EEPROM) */
21989983SFei.Feng@Sun.COM for (i = 0; i < 16; i++) {
21999983SFei.Feng@Sun.COM if (sc->bbp_prom[i].reg == 0)
22009983SFei.Feng@Sun.COM continue;
22019983SFei.Feng@Sun.COM rt2661_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val);
22029983SFei.Feng@Sun.COM }
22039983SFei.Feng@Sun.COM
22049983SFei.Feng@Sun.COM return (RT2661_SUCCESS);
22059983SFei.Feng@Sun.COM #undef N
22069983SFei.Feng@Sun.COM }
22079983SFei.Feng@Sun.COM
22089983SFei.Feng@Sun.COM static void
rt2661_bbp_write(struct rt2661_softc * sc,uint8_t reg,uint8_t val)22099983SFei.Feng@Sun.COM rt2661_bbp_write(struct rt2661_softc *sc, uint8_t reg, uint8_t val)
22109983SFei.Feng@Sun.COM {
22119983SFei.Feng@Sun.COM uint32_t tmp;
22129983SFei.Feng@Sun.COM int ntries;
22139983SFei.Feng@Sun.COM
22149983SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) {
22159983SFei.Feng@Sun.COM if (!(RT2661_READ(sc, RT2661_PHY_CSR3) & RT2661_BBP_BUSY))
22169983SFei.Feng@Sun.COM break;
22179983SFei.Feng@Sun.COM DELAY(1);
22189983SFei.Feng@Sun.COM }
22199983SFei.Feng@Sun.COM if (ntries == 100) {
22209983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_bbp_write(): "
22219983SFei.Feng@Sun.COM "could not write to BBP\n");
22229983SFei.Feng@Sun.COM return;
22239983SFei.Feng@Sun.COM }
22249983SFei.Feng@Sun.COM
22259983SFei.Feng@Sun.COM tmp = RT2661_BBP_BUSY | (reg & 0x7f) << 8 | val;
22269983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_PHY_CSR3, tmp);
22279983SFei.Feng@Sun.COM
22289983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_HW, "rwd: rt2661_bbp_write(): "
22299983SFei.Feng@Sun.COM "BBP R%u <- 0x%02x\n", reg, val);
22309983SFei.Feng@Sun.COM }
22319983SFei.Feng@Sun.COM
22329983SFei.Feng@Sun.COM /*
22339983SFei.Feng@Sun.COM * Reprogram MAC/BBP to switch to a new band. Values taken from the reference
22349983SFei.Feng@Sun.COM * driver.
22359983SFei.Feng@Sun.COM */
22369983SFei.Feng@Sun.COM static void
rt2661_select_band(struct rt2661_softc * sc,struct ieee80211_channel * c)22379983SFei.Feng@Sun.COM rt2661_select_band(struct rt2661_softc *sc, struct ieee80211_channel *c)
22389983SFei.Feng@Sun.COM {
22399983SFei.Feng@Sun.COM uint8_t bbp17, bbp35, bbp96, bbp97, bbp98, bbp104;
22409983SFei.Feng@Sun.COM uint32_t tmp;
22419983SFei.Feng@Sun.COM
22429983SFei.Feng@Sun.COM /* update all BBP registers that depend on the band */
22439983SFei.Feng@Sun.COM bbp17 = 0x20; bbp96 = 0x48; bbp104 = 0x2c;
22449983SFei.Feng@Sun.COM bbp35 = 0x50; bbp97 = 0x48; bbp98 = 0x48;
22459983SFei.Feng@Sun.COM if (IEEE80211_IS_CHAN_5GHZ(c)) {
22469983SFei.Feng@Sun.COM bbp17 += 0x08; bbp96 += 0x10; bbp104 += 0x0c;
22479983SFei.Feng@Sun.COM bbp35 += 0x10; bbp97 += 0x10; bbp98 += 0x10;
22489983SFei.Feng@Sun.COM }
22499983SFei.Feng@Sun.COM if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) ||
22509983SFei.Feng@Sun.COM (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) {
22519983SFei.Feng@Sun.COM bbp17 += 0x10; bbp96 += 0x10; bbp104 += 0x10;
22529983SFei.Feng@Sun.COM }
22539983SFei.Feng@Sun.COM
22549983SFei.Feng@Sun.COM sc->bbp17 = bbp17;
22559983SFei.Feng@Sun.COM rt2661_bbp_write(sc, 17, bbp17);
22569983SFei.Feng@Sun.COM rt2661_bbp_write(sc, 96, bbp96);
22579983SFei.Feng@Sun.COM rt2661_bbp_write(sc, 104, bbp104);
22589983SFei.Feng@Sun.COM
22599983SFei.Feng@Sun.COM if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) ||
22609983SFei.Feng@Sun.COM (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) {
22619983SFei.Feng@Sun.COM rt2661_bbp_write(sc, 75, 0x80);
22629983SFei.Feng@Sun.COM rt2661_bbp_write(sc, 86, 0x80);
22639983SFei.Feng@Sun.COM rt2661_bbp_write(sc, 88, 0x80);
22649983SFei.Feng@Sun.COM }
22659983SFei.Feng@Sun.COM
22669983SFei.Feng@Sun.COM rt2661_bbp_write(sc, 35, bbp35);
22679983SFei.Feng@Sun.COM rt2661_bbp_write(sc, 97, bbp97);
22689983SFei.Feng@Sun.COM rt2661_bbp_write(sc, 98, bbp98);
22699983SFei.Feng@Sun.COM
22709983SFei.Feng@Sun.COM tmp = RT2661_READ(sc, RT2661_PHY_CSR0);
22719983SFei.Feng@Sun.COM tmp &= ~(RT2661_PA_PE_2GHZ | RT2661_PA_PE_5GHZ);
22729983SFei.Feng@Sun.COM if (IEEE80211_IS_CHAN_2GHZ(c))
22739983SFei.Feng@Sun.COM tmp |= RT2661_PA_PE_2GHZ;
22749983SFei.Feng@Sun.COM else
22759983SFei.Feng@Sun.COM tmp |= RT2661_PA_PE_5GHZ;
22769983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_PHY_CSR0, tmp);
22779983SFei.Feng@Sun.COM
22789983SFei.Feng@Sun.COM /* 802.11a uses a 16 microseconds short interframe space */
22799983SFei.Feng@Sun.COM sc->sifs = IEEE80211_IS_CHAN_5GHZ(c) ? 16 : 10;
22809983SFei.Feng@Sun.COM }
22819983SFei.Feng@Sun.COM
22829983SFei.Feng@Sun.COM static void
rt2661_select_antenna(struct rt2661_softc * sc)22839983SFei.Feng@Sun.COM rt2661_select_antenna(struct rt2661_softc *sc)
22849983SFei.Feng@Sun.COM {
22859983SFei.Feng@Sun.COM uint8_t bbp4, bbp77;
22869983SFei.Feng@Sun.COM uint32_t tmp;
22879983SFei.Feng@Sun.COM
22889983SFei.Feng@Sun.COM bbp4 = rt2661_bbp_read(sc, 4);
22899983SFei.Feng@Sun.COM bbp77 = rt2661_bbp_read(sc, 77);
22909983SFei.Feng@Sun.COM
22919983SFei.Feng@Sun.COM /* TBD */
22929983SFei.Feng@Sun.COM
22939983SFei.Feng@Sun.COM /* make sure Rx is disabled before switching antenna */
22949983SFei.Feng@Sun.COM tmp = RT2661_READ(sc, RT2661_TXRX_CSR0);
22959983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_TXRX_CSR0, tmp | RT2661_DISABLE_RX);
22969983SFei.Feng@Sun.COM
22979983SFei.Feng@Sun.COM rt2661_bbp_write(sc, 4, bbp4);
22989983SFei.Feng@Sun.COM rt2661_bbp_write(sc, 77, bbp77);
22999983SFei.Feng@Sun.COM
23009983SFei.Feng@Sun.COM /* restore Rx filter */
23019983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_TXRX_CSR0, tmp);
23029983SFei.Feng@Sun.COM }
23039983SFei.Feng@Sun.COM
23049983SFei.Feng@Sun.COM static void
rt2661_rf_write(struct rt2661_softc * sc,uint8_t reg,uint32_t val)23059983SFei.Feng@Sun.COM rt2661_rf_write(struct rt2661_softc *sc, uint8_t reg, uint32_t val)
23069983SFei.Feng@Sun.COM {
23079983SFei.Feng@Sun.COM uint32_t tmp;
23089983SFei.Feng@Sun.COM int ntries;
23099983SFei.Feng@Sun.COM
23109983SFei.Feng@Sun.COM for (ntries = 0; ntries < 100; ntries++) {
23119983SFei.Feng@Sun.COM if (!(RT2661_READ(sc, RT2661_PHY_CSR4) & RT2661_RF_BUSY))
23129983SFei.Feng@Sun.COM break;
23139983SFei.Feng@Sun.COM DELAY(1);
23149983SFei.Feng@Sun.COM }
23159983SFei.Feng@Sun.COM if (ntries == 100) {
23169983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_rf_write(): "
23179983SFei.Feng@Sun.COM "could not write to RF\n");
23189983SFei.Feng@Sun.COM return;
23199983SFei.Feng@Sun.COM }
23209983SFei.Feng@Sun.COM
23219983SFei.Feng@Sun.COM tmp = RT2661_RF_BUSY | RT2661_RF_21BIT | (val & 0x1fffff) << 2 |
23229983SFei.Feng@Sun.COM (reg & 3);
23239983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_PHY_CSR4, tmp);
23249983SFei.Feng@Sun.COM
23259983SFei.Feng@Sun.COM /* remember last written value in sc */
23269983SFei.Feng@Sun.COM sc->rf_regs[reg] = val;
23279983SFei.Feng@Sun.COM
23289983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_FW, "rwd: rt2661_rf_write(): "
23299983SFei.Feng@Sun.COM "RF R[%u] <- 0x%05x\n", reg & 3, val & 0x1fffff);
23309983SFei.Feng@Sun.COM }
23319983SFei.Feng@Sun.COM
23329983SFei.Feng@Sun.COM static void
rt2661_set_chan(struct rt2661_softc * sc,struct ieee80211_channel * c)23339983SFei.Feng@Sun.COM rt2661_set_chan(struct rt2661_softc *sc, struct ieee80211_channel *c)
23349983SFei.Feng@Sun.COM {
23359983SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
23369983SFei.Feng@Sun.COM const struct rfprog *rfprog;
23379983SFei.Feng@Sun.COM uint8_t bbp3, bbp94 = RT2661_BBPR94_DEFAULT;
23389983SFei.Feng@Sun.COM int8_t power;
23399983SFei.Feng@Sun.COM uint_t i, chan;
23409983SFei.Feng@Sun.COM
23419983SFei.Feng@Sun.COM chan = ieee80211_chan2ieee(ic, c);
23429983SFei.Feng@Sun.COM if (chan == 0 || chan == IEEE80211_CHAN_ANY)
23439983SFei.Feng@Sun.COM return;
23449983SFei.Feng@Sun.COM
23459983SFei.Feng@Sun.COM /* select the appropriate RF settings based on what EEPROM says */
23469983SFei.Feng@Sun.COM rfprog = (sc->rfprog == 0) ? rt2661_rf5225_1 : rt2661_rf5225_2;
23479983SFei.Feng@Sun.COM
23489983SFei.Feng@Sun.COM /* find the settings for this channel (we know it exists) */
23499983SFei.Feng@Sun.COM i = 0;
23509983SFei.Feng@Sun.COM while (rfprog[i].chan != chan)
23519983SFei.Feng@Sun.COM i++;
23529983SFei.Feng@Sun.COM
23539983SFei.Feng@Sun.COM power = sc->txpow[i];
23549983SFei.Feng@Sun.COM if (power < 0) {
23559983SFei.Feng@Sun.COM bbp94 += power;
23569983SFei.Feng@Sun.COM power = 0;
23579983SFei.Feng@Sun.COM } else if (power > 31) {
23589983SFei.Feng@Sun.COM bbp94 += power - 31;
23599983SFei.Feng@Sun.COM power = 31;
23609983SFei.Feng@Sun.COM }
23619983SFei.Feng@Sun.COM
23629983SFei.Feng@Sun.COM /*
23639983SFei.Feng@Sun.COM * If we are switching from the 2GHz band to the 5GHz band or
23649983SFei.Feng@Sun.COM * vice-versa, BBP registers need to be reprogrammed.
23659983SFei.Feng@Sun.COM */
23669983SFei.Feng@Sun.COM if (ic->ic_flags != sc->sc_curchan->ich_flags) {
23679983SFei.Feng@Sun.COM rt2661_select_band(sc, c);
23689983SFei.Feng@Sun.COM rt2661_select_antenna(sc);
23699983SFei.Feng@Sun.COM }
23709983SFei.Feng@Sun.COM sc->sc_curchan = c;
23719983SFei.Feng@Sun.COM
23729983SFei.Feng@Sun.COM rt2661_rf_write(sc, RT2661_RF1, rfprog[i].r1);
23739983SFei.Feng@Sun.COM rt2661_rf_write(sc, RT2661_RF2, rfprog[i].r2);
23749983SFei.Feng@Sun.COM rt2661_rf_write(sc, RT2661_RF3, rfprog[i].r3 | power << 7);
23759983SFei.Feng@Sun.COM rt2661_rf_write(sc, RT2661_RF4, rfprog[i].r4 | sc->rffreq << 10);
23769983SFei.Feng@Sun.COM
23779983SFei.Feng@Sun.COM DELAY(200);
23789983SFei.Feng@Sun.COM
23799983SFei.Feng@Sun.COM rt2661_rf_write(sc, RT2661_RF1, rfprog[i].r1);
23809983SFei.Feng@Sun.COM rt2661_rf_write(sc, RT2661_RF2, rfprog[i].r2);
23819983SFei.Feng@Sun.COM rt2661_rf_write(sc, RT2661_RF3, rfprog[i].r3 | power << 7 | 1);
23829983SFei.Feng@Sun.COM rt2661_rf_write(sc, RT2661_RF4, rfprog[i].r4 | sc->rffreq << 10);
23839983SFei.Feng@Sun.COM
23849983SFei.Feng@Sun.COM DELAY(200);
23859983SFei.Feng@Sun.COM
23869983SFei.Feng@Sun.COM rt2661_rf_write(sc, RT2661_RF1, rfprog[i].r1);
23879983SFei.Feng@Sun.COM rt2661_rf_write(sc, RT2661_RF2, rfprog[i].r2);
23889983SFei.Feng@Sun.COM rt2661_rf_write(sc, RT2661_RF3, rfprog[i].r3 | power << 7);
23899983SFei.Feng@Sun.COM rt2661_rf_write(sc, RT2661_RF4, rfprog[i].r4 | sc->rffreq << 10);
23909983SFei.Feng@Sun.COM
23919983SFei.Feng@Sun.COM /* enable smart mode for MIMO-capable RFs */
23929983SFei.Feng@Sun.COM bbp3 = rt2661_bbp_read(sc, 3);
23939983SFei.Feng@Sun.COM
23949983SFei.Feng@Sun.COM bbp3 &= ~RT2661_SMART_MODE;
23959983SFei.Feng@Sun.COM if (sc->rf_rev == RT2661_RF_5325 || sc->rf_rev == RT2661_RF_2529)
23969983SFei.Feng@Sun.COM bbp3 |= RT2661_SMART_MODE;
23979983SFei.Feng@Sun.COM
23989983SFei.Feng@Sun.COM rt2661_bbp_write(sc, 3, bbp3);
23999983SFei.Feng@Sun.COM
24009983SFei.Feng@Sun.COM if (bbp94 != RT2661_BBPR94_DEFAULT)
24019983SFei.Feng@Sun.COM rt2661_bbp_write(sc, 94, bbp94);
24029983SFei.Feng@Sun.COM
24039983SFei.Feng@Sun.COM /* 5GHz radio needs a 1ms delay here */
24049983SFei.Feng@Sun.COM if (IEEE80211_IS_CHAN_5GHZ(c))
24059983SFei.Feng@Sun.COM DELAY(1000);
24069983SFei.Feng@Sun.COM }
24079983SFei.Feng@Sun.COM
24089983SFei.Feng@Sun.COM static int
rt2661_init(struct rt2661_softc * sc)24099983SFei.Feng@Sun.COM rt2661_init(struct rt2661_softc *sc)
24109983SFei.Feng@Sun.COM {
24119983SFei.Feng@Sun.COM #define N(a) (sizeof (a) / sizeof ((a)[0]))
24129983SFei.Feng@Sun.COM
24139983SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
24149983SFei.Feng@Sun.COM uint32_t tmp, sta[3], *fptr;
24159983SFei.Feng@Sun.COM int i, err, off, ntries;
24169983SFei.Feng@Sun.COM
24179983SFei.Feng@Sun.COM RT2661_GLOCK(sc);
24189983SFei.Feng@Sun.COM
24199983SFei.Feng@Sun.COM rt2661_stop_locked(sc);
24209983SFei.Feng@Sun.COM
24219983SFei.Feng@Sun.COM if (!RT2661_IS_FWLOADED(sc)) {
24229983SFei.Feng@Sun.COM err = rt2661_load_microcode(sc, ucode, usize);
24239983SFei.Feng@Sun.COM if (err != RT2661_SUCCESS) {
24249983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_attach(): "
24259983SFei.Feng@Sun.COM "could not load 8051 microcode\n");
24269983SFei.Feng@Sun.COM return (DDI_FAILURE);
24279983SFei.Feng@Sun.COM }
24289983SFei.Feng@Sun.COM sc->sc_flags |= RT2661_F_FWLOADED;
24299983SFei.Feng@Sun.COM }
24309983SFei.Feng@Sun.COM
24319983SFei.Feng@Sun.COM /* initialize Tx rings */
24329983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_AC1_BASE_CSR, sc->txq[1].paddr);
24339983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_AC0_BASE_CSR, sc->txq[0].paddr);
24349983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_AC2_BASE_CSR, sc->txq[2].paddr);
24359983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_AC3_BASE_CSR, sc->txq[3].paddr);
24369983SFei.Feng@Sun.COM
24379983SFei.Feng@Sun.COM /* initialize Mgt ring */
24389983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MGT_BASE_CSR, sc->mgtq.paddr);
24399983SFei.Feng@Sun.COM
24409983SFei.Feng@Sun.COM /* initialize Rx ring */
24419983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_RX_BASE_CSR, sc->rxq.paddr);
24429983SFei.Feng@Sun.COM
24439983SFei.Feng@Sun.COM /* initialize Tx rings sizes */
24449983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_TX_RING_CSR0,
24459983SFei.Feng@Sun.COM RT2661_TX_RING_COUNT << 24 |
24469983SFei.Feng@Sun.COM RT2661_TX_RING_COUNT << 16 |
24479983SFei.Feng@Sun.COM RT2661_TX_RING_COUNT << 8 |
24489983SFei.Feng@Sun.COM RT2661_TX_RING_COUNT);
24499983SFei.Feng@Sun.COM
24509983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_TX_RING_CSR1,
24519983SFei.Feng@Sun.COM RT2661_TX_DESC_WSIZE << 16 |
24529983SFei.Feng@Sun.COM RT2661_TX_RING_COUNT << 8 |
24539983SFei.Feng@Sun.COM RT2661_MGT_RING_COUNT);
24549983SFei.Feng@Sun.COM
24559983SFei.Feng@Sun.COM /* initialize Rx rings */
24569983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_RX_RING_CSR,
24579983SFei.Feng@Sun.COM RT2661_RX_DESC_BACK << 16 |
24589983SFei.Feng@Sun.COM RT2661_RX_DESC_WSIZE << 8 |
24599983SFei.Feng@Sun.COM RT2661_RX_RING_COUNT);
24609983SFei.Feng@Sun.COM
24619983SFei.Feng@Sun.COM /* XXX: some magic here */
24629983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_TX_DMA_DST_CSR, 0xaa);
24639983SFei.Feng@Sun.COM
24649983SFei.Feng@Sun.COM /* load base addresses of all 5 Tx rings (4 data + 1 mgt) */
24659983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_LOAD_TX_RING_CSR, 0x1f);
24669983SFei.Feng@Sun.COM
24679983SFei.Feng@Sun.COM /* load base address of Rx ring */
24689983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_RX_CNTL_CSR, 2);
24699983SFei.Feng@Sun.COM
24709983SFei.Feng@Sun.COM /* initialize MAC registers to default values */
24719983SFei.Feng@Sun.COM for (i = 0; i < N(rt2661_def_mac); i++)
24729983SFei.Feng@Sun.COM RT2661_WRITE(sc, rt2661_def_mac[i].reg, rt2661_def_mac[i].val);
24739983SFei.Feng@Sun.COM
24749983SFei.Feng@Sun.COM rt2661_set_macaddr(sc, ic->ic_macaddr);
24759983SFei.Feng@Sun.COM
24769983SFei.Feng@Sun.COM /* set host ready */
24779983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MAC_CSR1, 3);
24789983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MAC_CSR1, 0);
24799983SFei.Feng@Sun.COM
24809983SFei.Feng@Sun.COM /* wait for BBP/RF to wakeup */
24819983SFei.Feng@Sun.COM for (ntries = 0; ntries < 1000; ntries++) {
24829983SFei.Feng@Sun.COM if (RT2661_READ(sc, RT2661_MAC_CSR12) & 8)
24839983SFei.Feng@Sun.COM break;
24849983SFei.Feng@Sun.COM DELAY(1000);
24859983SFei.Feng@Sun.COM }
24869983SFei.Feng@Sun.COM if (ntries == 1000) {
24879983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_attach(): "
24889983SFei.Feng@Sun.COM "timeout waiting for BBP/RF to wakeup\n");
24899983SFei.Feng@Sun.COM rt2661_stop_locked(sc);
24909983SFei.Feng@Sun.COM RT2661_GUNLOCK(sc);
24919983SFei.Feng@Sun.COM return (DDI_FAILURE);
24929983SFei.Feng@Sun.COM }
24939983SFei.Feng@Sun.COM
24949983SFei.Feng@Sun.COM if (rt2661_bbp_init(sc) != RT2661_SUCCESS) {
24959983SFei.Feng@Sun.COM rt2661_stop_locked(sc);
24969983SFei.Feng@Sun.COM RT2661_GUNLOCK(sc);
24979983SFei.Feng@Sun.COM return (DDI_FAILURE);
24989983SFei.Feng@Sun.COM }
24999983SFei.Feng@Sun.COM
25009983SFei.Feng@Sun.COM /* select default channel */
25019983SFei.Feng@Sun.COM sc->sc_curchan = ic->ic_bss->in_chan = ic->ic_curchan;
25029983SFei.Feng@Sun.COM rt2661_select_band(sc, sc->sc_curchan);
25039983SFei.Feng@Sun.COM rt2661_select_antenna(sc);
25049983SFei.Feng@Sun.COM rt2661_set_chan(sc, sc->sc_curchan);
25059983SFei.Feng@Sun.COM
25069983SFei.Feng@Sun.COM /* update Rx filter */
25079983SFei.Feng@Sun.COM tmp = RT2661_READ(sc, RT2661_TXRX_CSR0) & 0xffff;
25089983SFei.Feng@Sun.COM
25099983SFei.Feng@Sun.COM tmp |= RT2661_DROP_PHY_ERROR | RT2661_DROP_CRC_ERROR;
25109983SFei.Feng@Sun.COM if (ic->ic_opmode != IEEE80211_M_MONITOR) {
25119983SFei.Feng@Sun.COM tmp |= RT2661_DROP_CTL | RT2661_DROP_VER_ERROR |
25129983SFei.Feng@Sun.COM RT2661_DROP_ACKCTS;
25139983SFei.Feng@Sun.COM if (ic->ic_opmode != IEEE80211_M_HOSTAP)
25149983SFei.Feng@Sun.COM tmp |= RT2661_DROP_TODS;
25159983SFei.Feng@Sun.COM if (!(sc->sc_rcr & RT2661_RCR_PROMISC))
25169983SFei.Feng@Sun.COM tmp |= RT2661_DROP_NOT_TO_ME;
25179983SFei.Feng@Sun.COM }
25189983SFei.Feng@Sun.COM
25199983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_TXRX_CSR0, tmp);
25209983SFei.Feng@Sun.COM
25219983SFei.Feng@Sun.COM /* clear STA registers */
25229983SFei.Feng@Sun.COM off = RT2661_STA_CSR0;
25239983SFei.Feng@Sun.COM fptr = sta;
25249983SFei.Feng@Sun.COM for (i = 0; i < N(sta); i++) {
25259983SFei.Feng@Sun.COM *fptr = RT2661_MEM_READ1(sc, off++);
25269983SFei.Feng@Sun.COM }
25279983SFei.Feng@Sun.COM
25289983SFei.Feng@Sun.COM /* initialize ASIC */
25299983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MAC_CSR1, 4);
25309983SFei.Feng@Sun.COM
25319983SFei.Feng@Sun.COM /* clear any pending interrupt */
25329983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_INT_SOURCE_CSR, 0xffffffff);
25339983SFei.Feng@Sun.COM
25349983SFei.Feng@Sun.COM /* enable interrupts */
25359983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_INT_MASK_CSR, 0x0000ff10);
25369983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_MCU_INT_MASK_CSR, 0);
25379983SFei.Feng@Sun.COM
25389983SFei.Feng@Sun.COM /* kick Rx */
25399983SFei.Feng@Sun.COM RT2661_WRITE(sc, RT2661_RX_CNTL_CSR, 1);
25409983SFei.Feng@Sun.COM RT2661_GUNLOCK(sc);
25419983SFei.Feng@Sun.COM
25429983SFei.Feng@Sun.COM #undef N
25439983SFei.Feng@Sun.COM return (DDI_SUCCESS);
25449983SFei.Feng@Sun.COM }
25459983SFei.Feng@Sun.COM
25469983SFei.Feng@Sun.COM static void
rt2661_stop(struct rt2661_softc * sc)25479983SFei.Feng@Sun.COM rt2661_stop(struct rt2661_softc *sc)
25489983SFei.Feng@Sun.COM {
25499983SFei.Feng@Sun.COM if (!RT2661_IS_FASTREBOOT(sc))
25509983SFei.Feng@Sun.COM RT2661_GLOCK(sc);
25519983SFei.Feng@Sun.COM rt2661_stop_locked(sc);
25529983SFei.Feng@Sun.COM if (!RT2661_IS_FASTREBOOT(sc))
25539983SFei.Feng@Sun.COM RT2661_GUNLOCK(sc);
25549983SFei.Feng@Sun.COM }
25559983SFei.Feng@Sun.COM
25569983SFei.Feng@Sun.COM static int
rt2661_m_start(void * arg)25579983SFei.Feng@Sun.COM rt2661_m_start(void *arg)
25589983SFei.Feng@Sun.COM {
25599983SFei.Feng@Sun.COM struct rt2661_softc *sc = (struct rt2661_softc *)arg;
25609983SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
25619983SFei.Feng@Sun.COM int err;
25629983SFei.Feng@Sun.COM
25639983SFei.Feng@Sun.COM err = rt2661_init(sc);
25649983SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
25659983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_m_start():"
25669983SFei.Feng@Sun.COM "Hardware initialization failed\n");
25679983SFei.Feng@Sun.COM goto fail1;
25689983SFei.Feng@Sun.COM }
25699983SFei.Feng@Sun.COM
25709983SFei.Feng@Sun.COM ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
25719983SFei.Feng@Sun.COM
25729983SFei.Feng@Sun.COM RT2661_GLOCK(sc);
25739983SFei.Feng@Sun.COM sc->sc_flags |= RT2661_F_RUNNING;
25749983SFei.Feng@Sun.COM RT2661_GUNLOCK(sc);
25759983SFei.Feng@Sun.COM
25769983SFei.Feng@Sun.COM return (DDI_SUCCESS);
25779983SFei.Feng@Sun.COM fail1:
25789983SFei.Feng@Sun.COM rt2661_stop(sc);
25799983SFei.Feng@Sun.COM return (err);
25809983SFei.Feng@Sun.COM }
25819983SFei.Feng@Sun.COM
25829983SFei.Feng@Sun.COM static void
rt2661_m_stop(void * arg)25839983SFei.Feng@Sun.COM rt2661_m_stop(void *arg)
25849983SFei.Feng@Sun.COM {
25859983SFei.Feng@Sun.COM struct rt2661_softc *sc = (struct rt2661_softc *)arg;
25869983SFei.Feng@Sun.COM
25879983SFei.Feng@Sun.COM (void) rt2661_stop(sc);
25889983SFei.Feng@Sun.COM
25899983SFei.Feng@Sun.COM ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1);
25909983SFei.Feng@Sun.COM
25919983SFei.Feng@Sun.COM RT2661_GLOCK(sc);
25929983SFei.Feng@Sun.COM sc->sc_flags &= ~RT2661_F_RUNNING;
25939983SFei.Feng@Sun.COM RT2661_GUNLOCK(sc);
25949983SFei.Feng@Sun.COM }
25959983SFei.Feng@Sun.COM
25969983SFei.Feng@Sun.COM static void
rt2661_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)25979983SFei.Feng@Sun.COM rt2661_m_ioctl(void* arg, queue_t *wq, mblk_t *mp)
25989983SFei.Feng@Sun.COM {
25999983SFei.Feng@Sun.COM struct rt2661_softc *sc = (struct rt2661_softc *)arg;
26009983SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
26019983SFei.Feng@Sun.COM int err;
26029983SFei.Feng@Sun.COM
26039983SFei.Feng@Sun.COM err = ieee80211_ioctl(ic, wq, mp);
26049983SFei.Feng@Sun.COM RT2661_GLOCK(sc);
26059983SFei.Feng@Sun.COM if (err == ENETRESET) {
26069983SFei.Feng@Sun.COM if (ic->ic_des_esslen) {
26079983SFei.Feng@Sun.COM if (RT2661_IS_RUNNING(sc)) {
26089983SFei.Feng@Sun.COM RT2661_GUNLOCK(sc);
26099983SFei.Feng@Sun.COM (void) rt2661_init(sc);
26109983SFei.Feng@Sun.COM (void) ieee80211_new_state(ic,
26119983SFei.Feng@Sun.COM IEEE80211_S_SCAN, -1);
26129983SFei.Feng@Sun.COM RT2661_GLOCK(sc);
26139983SFei.Feng@Sun.COM }
26149983SFei.Feng@Sun.COM }
26159983SFei.Feng@Sun.COM }
26169983SFei.Feng@Sun.COM RT2661_GUNLOCK(sc);
26179983SFei.Feng@Sun.COM }
26189983SFei.Feng@Sun.COM
26199983SFei.Feng@Sun.COM /*
26209983SFei.Feng@Sun.COM * Call back function for get/set proporty
26219983SFei.Feng@Sun.COM */
26229983SFei.Feng@Sun.COM static int
rt2661_m_getprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,void * wldp_buf)26239983SFei.Feng@Sun.COM rt2661_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2624*11878SVenu.Iyer@Sun.COM uint_t wldp_length, void *wldp_buf)
26259983SFei.Feng@Sun.COM {
26269983SFei.Feng@Sun.COM struct rt2661_softc *sc = (struct rt2661_softc *)arg;
26279983SFei.Feng@Sun.COM int err = 0;
26289983SFei.Feng@Sun.COM
26299983SFei.Feng@Sun.COM err = ieee80211_getprop(&sc->sc_ic, pr_name, wldp_pr_num,
2630*11878SVenu.Iyer@Sun.COM wldp_length, wldp_buf);
26319983SFei.Feng@Sun.COM
26329983SFei.Feng@Sun.COM return (err);
26339983SFei.Feng@Sun.COM }
26349983SFei.Feng@Sun.COM
2635*11878SVenu.Iyer@Sun.COM static void
rt2661_m_propinfo(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,mac_prop_info_handle_t mph)2636*11878SVenu.Iyer@Sun.COM rt2661_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2637*11878SVenu.Iyer@Sun.COM mac_prop_info_handle_t mph)
2638*11878SVenu.Iyer@Sun.COM {
2639*11878SVenu.Iyer@Sun.COM struct rt2661_softc *sc = (struct rt2661_softc *)arg;
2640*11878SVenu.Iyer@Sun.COM
2641*11878SVenu.Iyer@Sun.COM ieee80211_propinfo(&sc->sc_ic, pr_name, wldp_pr_num, mph);
2642*11878SVenu.Iyer@Sun.COM }
2643*11878SVenu.Iyer@Sun.COM
26449983SFei.Feng@Sun.COM static int
rt2661_m_setprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,const void * wldp_buf)26459983SFei.Feng@Sun.COM rt2661_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
26469983SFei.Feng@Sun.COM uint_t wldp_length, const void *wldp_buf)
26479983SFei.Feng@Sun.COM {
26489983SFei.Feng@Sun.COM struct rt2661_softc *sc = (struct rt2661_softc *)arg;
26499983SFei.Feng@Sun.COM ieee80211com_t *ic = &sc->sc_ic;
26509983SFei.Feng@Sun.COM int err;
26519983SFei.Feng@Sun.COM
26529983SFei.Feng@Sun.COM err = ieee80211_setprop(ic, pr_name, wldp_pr_num, wldp_length,
26539983SFei.Feng@Sun.COM wldp_buf);
26549983SFei.Feng@Sun.COM RT2661_GLOCK(sc);
26559983SFei.Feng@Sun.COM if (err == ENETRESET) {
26569983SFei.Feng@Sun.COM if (ic->ic_des_esslen) {
26579983SFei.Feng@Sun.COM if (RT2661_IS_RUNNING(sc)) {
26589983SFei.Feng@Sun.COM RT2661_GUNLOCK(sc);
26599983SFei.Feng@Sun.COM (void) rt2661_init(sc);
26609983SFei.Feng@Sun.COM (void) ieee80211_new_state(ic,
26619983SFei.Feng@Sun.COM IEEE80211_S_SCAN, -1);
26629983SFei.Feng@Sun.COM RT2661_GLOCK(sc);
26639983SFei.Feng@Sun.COM }
26649983SFei.Feng@Sun.COM }
26659983SFei.Feng@Sun.COM err = 0;
26669983SFei.Feng@Sun.COM }
26679983SFei.Feng@Sun.COM RT2661_GUNLOCK(sc);
26689983SFei.Feng@Sun.COM return (err);
26699983SFei.Feng@Sun.COM }
26709983SFei.Feng@Sun.COM
26719983SFei.Feng@Sun.COM static mblk_t *
rt2661_m_tx(void * arg,mblk_t * mp)26729983SFei.Feng@Sun.COM rt2661_m_tx(void *arg, mblk_t *mp)
26739983SFei.Feng@Sun.COM {
26749983SFei.Feng@Sun.COM struct rt2661_softc *sc = (struct rt2661_softc *)arg;
26759983SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
26769983SFei.Feng@Sun.COM mblk_t *next;
26779983SFei.Feng@Sun.COM
26789983SFei.Feng@Sun.COM if (RT2661_IS_SUSPEND(sc)) {
26799983SFei.Feng@Sun.COM freemsgchain(mp);
26809983SFei.Feng@Sun.COM return (NULL);
26819983SFei.Feng@Sun.COM }
26829983SFei.Feng@Sun.COM
26839983SFei.Feng@Sun.COM /*
26849983SFei.Feng@Sun.COM * No data frames go out unless we're associated; this
26859983SFei.Feng@Sun.COM * should not happen as the 802.11 layer does not enable
26869983SFei.Feng@Sun.COM * the xmit queue until we enter the RUN state.
26879983SFei.Feng@Sun.COM */
26889983SFei.Feng@Sun.COM if (ic->ic_state != IEEE80211_S_RUN) {
26899983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_TX, "rwd: rt2661_tx_data(): "
26909983SFei.Feng@Sun.COM "discard, state %u\n", ic->ic_state);
26919983SFei.Feng@Sun.COM freemsgchain(mp);
26929983SFei.Feng@Sun.COM return (NULL);
26939983SFei.Feng@Sun.COM }
26949983SFei.Feng@Sun.COM
26959983SFei.Feng@Sun.COM while (mp != NULL) {
26969983SFei.Feng@Sun.COM next = mp->b_next;
26979983SFei.Feng@Sun.COM mp->b_next = NULL;
26989983SFei.Feng@Sun.COM if (rt2661_send(ic, mp) !=
26999983SFei.Feng@Sun.COM DDI_SUCCESS) {
27009983SFei.Feng@Sun.COM mp->b_next = next;
27019983SFei.Feng@Sun.COM break;
27029983SFei.Feng@Sun.COM }
27039983SFei.Feng@Sun.COM mp = next;
27049983SFei.Feng@Sun.COM }
27059983SFei.Feng@Sun.COM return (mp);
27069983SFei.Feng@Sun.COM }
27079983SFei.Feng@Sun.COM
27089983SFei.Feng@Sun.COM /*ARGSUSED*/
27099983SFei.Feng@Sun.COM static int
rt2661_m_unicst(void * arg,const uint8_t * macaddr)27109983SFei.Feng@Sun.COM rt2661_m_unicst(void *arg, const uint8_t *macaddr)
27119983SFei.Feng@Sun.COM {
27129983SFei.Feng@Sun.COM return (ENOTSUP);
27139983SFei.Feng@Sun.COM }
27149983SFei.Feng@Sun.COM
27159983SFei.Feng@Sun.COM /*ARGSUSED*/
27169983SFei.Feng@Sun.COM static int
rt2661_m_multicst(void * arg,boolean_t add,const uint8_t * mca)27179983SFei.Feng@Sun.COM rt2661_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
27189983SFei.Feng@Sun.COM {
27199983SFei.Feng@Sun.COM return (ENOTSUP);
27209983SFei.Feng@Sun.COM }
27219983SFei.Feng@Sun.COM
27229983SFei.Feng@Sun.COM /*ARGSUSED*/
27239983SFei.Feng@Sun.COM static int
rt2661_m_promisc(void * arg,boolean_t on)27249983SFei.Feng@Sun.COM rt2661_m_promisc(void *arg, boolean_t on)
27259983SFei.Feng@Sun.COM {
27269983SFei.Feng@Sun.COM struct rt2661_softc *sc = (struct rt2661_softc *)arg;
27279983SFei.Feng@Sun.COM
27289983SFei.Feng@Sun.COM if (on) {
27299983SFei.Feng@Sun.COM sc->sc_rcr |= RT2661_RCR_PROMISC;
27309983SFei.Feng@Sun.COM sc->sc_rcr |= RT2661_RCR_MULTI;
27319983SFei.Feng@Sun.COM } else {
27329983SFei.Feng@Sun.COM sc->sc_rcr &= ~RT2661_RCR_PROMISC;
27339983SFei.Feng@Sun.COM sc->sc_rcr &= ~RT2661_RCR_MULTI;
27349983SFei.Feng@Sun.COM }
27359983SFei.Feng@Sun.COM
27369983SFei.Feng@Sun.COM rt2661_update_promisc(sc);
27379983SFei.Feng@Sun.COM return (0);
27389983SFei.Feng@Sun.COM }
27399983SFei.Feng@Sun.COM
27409983SFei.Feng@Sun.COM static int
rt2661_m_stat(void * arg,uint_t stat,uint64_t * val)27419983SFei.Feng@Sun.COM rt2661_m_stat(void *arg, uint_t stat, uint64_t *val)
27429983SFei.Feng@Sun.COM {
27439983SFei.Feng@Sun.COM struct rt2661_softc *sc = (struct rt2661_softc *)arg;
27449983SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
27459983SFei.Feng@Sun.COM struct ieee80211_node *ni = ic->ic_bss;
27469983SFei.Feng@Sun.COM struct ieee80211_rateset *rs = &ni->in_rates;
27479983SFei.Feng@Sun.COM
27489983SFei.Feng@Sun.COM RT2661_GLOCK(sc);
27499983SFei.Feng@Sun.COM switch (stat) {
27509983SFei.Feng@Sun.COM case MAC_STAT_IFSPEED:
27519983SFei.Feng@Sun.COM *val = ((ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) ?
27529983SFei.Feng@Sun.COM (rs->ir_rates[ni->in_txrate] & IEEE80211_RATE_VAL)
27539983SFei.Feng@Sun.COM : ic->ic_fixed_rate) / 2 * 1000000;
27549983SFei.Feng@Sun.COM break;
27559983SFei.Feng@Sun.COM case MAC_STAT_NOXMTBUF:
27569983SFei.Feng@Sun.COM *val = sc->sc_tx_nobuf;
27579983SFei.Feng@Sun.COM break;
27589983SFei.Feng@Sun.COM case MAC_STAT_NORCVBUF:
27599983SFei.Feng@Sun.COM *val = sc->sc_rx_nobuf;
27609983SFei.Feng@Sun.COM break;
27619983SFei.Feng@Sun.COM case MAC_STAT_IERRORS:
27629983SFei.Feng@Sun.COM *val = sc->sc_rx_err;
27639983SFei.Feng@Sun.COM break;
27649983SFei.Feng@Sun.COM case MAC_STAT_RBYTES:
27659983SFei.Feng@Sun.COM *val = ic->ic_stats.is_rx_bytes;
27669983SFei.Feng@Sun.COM break;
27679983SFei.Feng@Sun.COM case MAC_STAT_IPACKETS:
27689983SFei.Feng@Sun.COM *val = ic->ic_stats.is_rx_frags;
27699983SFei.Feng@Sun.COM break;
27709983SFei.Feng@Sun.COM case MAC_STAT_OBYTES:
27719983SFei.Feng@Sun.COM *val = ic->ic_stats.is_tx_bytes;
27729983SFei.Feng@Sun.COM break;
27739983SFei.Feng@Sun.COM case MAC_STAT_OPACKETS:
27749983SFei.Feng@Sun.COM *val = ic->ic_stats.is_tx_frags;
27759983SFei.Feng@Sun.COM break;
27769983SFei.Feng@Sun.COM case MAC_STAT_OERRORS:
27779983SFei.Feng@Sun.COM case WIFI_STAT_TX_FAILED:
27789983SFei.Feng@Sun.COM *val = sc->sc_tx_err;
27799983SFei.Feng@Sun.COM break;
27809983SFei.Feng@Sun.COM case WIFI_STAT_TX_RETRANS:
27819983SFei.Feng@Sun.COM *val = sc->sc_tx_retries;
27829983SFei.Feng@Sun.COM break;
27839983SFei.Feng@Sun.COM case WIFI_STAT_FCS_ERRORS:
27849983SFei.Feng@Sun.COM case WIFI_STAT_WEP_ERRORS:
27859983SFei.Feng@Sun.COM case WIFI_STAT_TX_FRAGS:
27869983SFei.Feng@Sun.COM case WIFI_STAT_MCAST_TX:
27879983SFei.Feng@Sun.COM case WIFI_STAT_RTS_SUCCESS:
27889983SFei.Feng@Sun.COM case WIFI_STAT_RTS_FAILURE:
27899983SFei.Feng@Sun.COM case WIFI_STAT_ACK_FAILURE:
27909983SFei.Feng@Sun.COM case WIFI_STAT_RX_FRAGS:
27919983SFei.Feng@Sun.COM case WIFI_STAT_MCAST_RX:
27929983SFei.Feng@Sun.COM case WIFI_STAT_RX_DUPS:
27939983SFei.Feng@Sun.COM RT2661_GUNLOCK(sc);
27949983SFei.Feng@Sun.COM return (ieee80211_stat(ic, stat, val));
27959983SFei.Feng@Sun.COM default:
27969983SFei.Feng@Sun.COM RT2661_GUNLOCK(sc);
27979983SFei.Feng@Sun.COM return (ENOTSUP);
27989983SFei.Feng@Sun.COM }
27999983SFei.Feng@Sun.COM RT2661_GUNLOCK(sc);
28009983SFei.Feng@Sun.COM
28019983SFei.Feng@Sun.COM return (0);
28029983SFei.Feng@Sun.COM }
28039983SFei.Feng@Sun.COM
28049983SFei.Feng@Sun.COM static int
rt2661_attach(dev_info_t * devinfo,ddi_attach_cmd_t cmd)28059983SFei.Feng@Sun.COM rt2661_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
28069983SFei.Feng@Sun.COM {
28079983SFei.Feng@Sun.COM struct rt2661_softc *sc;
28089983SFei.Feng@Sun.COM struct ieee80211com *ic;
28099983SFei.Feng@Sun.COM
28109983SFei.Feng@Sun.COM int i, ac, err, ntries, instance;
28119983SFei.Feng@Sun.COM int intr_type, intr_count, intr_actual;
28129983SFei.Feng@Sun.COM char strbuf[32];
28139983SFei.Feng@Sun.COM uint8_t cachelsz;
28149983SFei.Feng@Sun.COM uint16_t command, vendor_id, device_id;
28159983SFei.Feng@Sun.COM uint32_t val;
28169983SFei.Feng@Sun.COM
28179983SFei.Feng@Sun.COM wifi_data_t wd = { 0 };
28189983SFei.Feng@Sun.COM mac_register_t *macp;
28199983SFei.Feng@Sun.COM
28209983SFei.Feng@Sun.COM switch (cmd) {
28219983SFei.Feng@Sun.COM case DDI_ATTACH:
28229983SFei.Feng@Sun.COM break;
28239983SFei.Feng@Sun.COM case DDI_RESUME:
28249983SFei.Feng@Sun.COM sc = ddi_get_soft_state(rt2661_soft_state_p,
28259983SFei.Feng@Sun.COM ddi_get_instance(devinfo));
28269983SFei.Feng@Sun.COM ASSERT(sc != NULL);
28279983SFei.Feng@Sun.COM RT2661_GLOCK(sc);
28289983SFei.Feng@Sun.COM sc->sc_flags &= ~RT2661_F_SUSPEND;
28299983SFei.Feng@Sun.COM RT2661_GUNLOCK(sc);
28309983SFei.Feng@Sun.COM if (RT2661_IS_RUNNING(sc))
28319983SFei.Feng@Sun.COM (void) rt2661_init(sc);
28329983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_attach(): "
28339983SFei.Feng@Sun.COM "resume now\n");
28349983SFei.Feng@Sun.COM return (DDI_SUCCESS);
28359983SFei.Feng@Sun.COM default:
28369983SFei.Feng@Sun.COM return (DDI_FAILURE);
28379983SFei.Feng@Sun.COM }
28389983SFei.Feng@Sun.COM
28399983SFei.Feng@Sun.COM instance = ddi_get_instance(devinfo);
28409983SFei.Feng@Sun.COM
28419983SFei.Feng@Sun.COM err = ddi_soft_state_zalloc(rt2661_soft_state_p, instance);
28429983SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
28439983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_attach(): "
28449983SFei.Feng@Sun.COM "unable to alloc soft_state_p\n");
28459983SFei.Feng@Sun.COM return (err);
28469983SFei.Feng@Sun.COM }
28479983SFei.Feng@Sun.COM
28489983SFei.Feng@Sun.COM sc = ddi_get_soft_state(rt2661_soft_state_p, instance);
28499983SFei.Feng@Sun.COM ic = (struct ieee80211com *)&sc->sc_ic;
28509983SFei.Feng@Sun.COM sc->sc_dev = devinfo;
28519983SFei.Feng@Sun.COM
28529983SFei.Feng@Sun.COM /* PCI configuration */
28539983SFei.Feng@Sun.COM err = ddi_regs_map_setup(devinfo, 0, &sc->sc_cfg_base, 0, 0,
28549983SFei.Feng@Sun.COM &rt2661_csr_accattr, &sc->sc_cfg_handle);
28559983SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
28569983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_attach(): "
28579983SFei.Feng@Sun.COM "ddi_regs_map_setup() failed");
28589983SFei.Feng@Sun.COM goto fail1;
28599983SFei.Feng@Sun.COM }
28609983SFei.Feng@Sun.COM
28619983SFei.Feng@Sun.COM cachelsz = ddi_get8(sc->sc_cfg_handle,
28629983SFei.Feng@Sun.COM (uint8_t *)(sc->sc_cfg_base + PCI_CONF_CACHE_LINESZ));
28639983SFei.Feng@Sun.COM if (cachelsz == 0)
28649983SFei.Feng@Sun.COM cachelsz = 0x10;
28659983SFei.Feng@Sun.COM sc->sc_cachelsz = cachelsz << 2;
28669983SFei.Feng@Sun.COM sc->sc_dmabuf_size = roundup(IEEE80211_MAX_LEN, sc->sc_cachelsz);
28679983SFei.Feng@Sun.COM
28689983SFei.Feng@Sun.COM vendor_id = ddi_get16(sc->sc_cfg_handle,
28699983SFei.Feng@Sun.COM (uint16_t *)((uintptr_t)(sc->sc_cfg_base) + PCI_CONF_VENID));
28709983SFei.Feng@Sun.COM device_id = ddi_get16(sc->sc_cfg_handle,
28719983SFei.Feng@Sun.COM (uint16_t *)((uintptr_t)(sc->sc_cfg_base) + PCI_CONF_DEVID));
28729983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_attach(): "
28739983SFei.Feng@Sun.COM "vendor 0x%x, device id 0x%x, cache size %d\n",
28749983SFei.Feng@Sun.COM vendor_id, device_id, cachelsz);
28759983SFei.Feng@Sun.COM
28769983SFei.Feng@Sun.COM /*
28779983SFei.Feng@Sun.COM * Enable response to memory space accesses,
28789983SFei.Feng@Sun.COM * and enabe bus master.
28799983SFei.Feng@Sun.COM */
28809983SFei.Feng@Sun.COM command = PCI_COMM_MAE | PCI_COMM_ME;
28819983SFei.Feng@Sun.COM ddi_put16(sc->sc_cfg_handle,
28829983SFei.Feng@Sun.COM (uint16_t *)((uintptr_t)(sc->sc_cfg_base) + PCI_CONF_COMM),
28839983SFei.Feng@Sun.COM command);
28849983SFei.Feng@Sun.COM ddi_put8(sc->sc_cfg_handle,
28859983SFei.Feng@Sun.COM (uint8_t *)(sc->sc_cfg_base + PCI_CONF_LATENCY_TIMER), 0xa8);
28869983SFei.Feng@Sun.COM ddi_put8(sc->sc_cfg_handle,
28879983SFei.Feng@Sun.COM (uint8_t *)(sc->sc_cfg_base + PCI_CONF_ILINE), 0x10);
28889983SFei.Feng@Sun.COM
28899983SFei.Feng@Sun.COM /* pci i/o space */
28909983SFei.Feng@Sun.COM err = ddi_regs_map_setup(devinfo, 1,
28919983SFei.Feng@Sun.COM &sc->sc_io_base, 0, 0, &rt2661_csr_accattr, &sc->sc_io_handle);
28929983SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
28939983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_attach(): "
28949983SFei.Feng@Sun.COM "ddi_regs_map_setup() failed");
28959983SFei.Feng@Sun.COM goto fail2;
28969983SFei.Feng@Sun.COM }
28979983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_attach(): "
28989983SFei.Feng@Sun.COM "PCI configuration is done successfully\n");
28999983SFei.Feng@Sun.COM
29009983SFei.Feng@Sun.COM err = ddi_intr_get_supported_types(devinfo, &intr_type);
29019983SFei.Feng@Sun.COM if ((err != DDI_SUCCESS) || (!(intr_type & DDI_INTR_TYPE_FIXED))) {
29029983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_attach(): "
29039983SFei.Feng@Sun.COM "fixed type interrupt is not supported\n");
29049983SFei.Feng@Sun.COM goto fail3;
29059983SFei.Feng@Sun.COM }
29069983SFei.Feng@Sun.COM
29079983SFei.Feng@Sun.COM err = ddi_intr_get_nintrs(devinfo, DDI_INTR_TYPE_FIXED, &intr_count);
29089983SFei.Feng@Sun.COM if ((err != DDI_SUCCESS) || (intr_count != 1)) {
29099983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_attach(): "
29109983SFei.Feng@Sun.COM "no fixed interrupts\n");
29119983SFei.Feng@Sun.COM goto fail3;
29129983SFei.Feng@Sun.COM }
29139983SFei.Feng@Sun.COM
29149983SFei.Feng@Sun.COM sc->sc_intr_htable = kmem_zalloc(sizeof (ddi_intr_handle_t), KM_SLEEP);
29159983SFei.Feng@Sun.COM
29169983SFei.Feng@Sun.COM err = ddi_intr_alloc(devinfo, sc->sc_intr_htable,
29179983SFei.Feng@Sun.COM DDI_INTR_TYPE_FIXED, 0, intr_count, &intr_actual, 0);
29189983SFei.Feng@Sun.COM if ((err != DDI_SUCCESS) || (intr_actual != 1)) {
29199983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_attach(): "
29209983SFei.Feng@Sun.COM "ddi_intr_alloc() failed 0x%x\n", err);
29219983SFei.Feng@Sun.COM goto faili4;
29229983SFei.Feng@Sun.COM }
29239983SFei.Feng@Sun.COM
29249983SFei.Feng@Sun.COM err = ddi_intr_get_pri(sc->sc_intr_htable[0], &sc->sc_intr_pri);
29259983SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
29269983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_attach(): "
29279983SFei.Feng@Sun.COM "ddi_intr_get_pri() failed 0x%x\n", err);
29289983SFei.Feng@Sun.COM goto faili5;
29299983SFei.Feng@Sun.COM }
29309983SFei.Feng@Sun.COM
29319983SFei.Feng@Sun.COM sc->amrr.amrr_min_success_threshold = 1;
29329983SFei.Feng@Sun.COM sc->amrr.amrr_max_success_threshold = 15;
29339983SFei.Feng@Sun.COM
29349983SFei.Feng@Sun.COM /* wait for NIC to initialize */
29359983SFei.Feng@Sun.COM for (ntries = 0; ntries < 1000; ntries++) {
29369983SFei.Feng@Sun.COM if ((val = RT2661_READ(sc, RT2661_MAC_CSR0)) != 0)
29379983SFei.Feng@Sun.COM break;
29389983SFei.Feng@Sun.COM DELAY(1000);
29399983SFei.Feng@Sun.COM }
29409983SFei.Feng@Sun.COM if (ntries == 1000) {
29419983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_attach(): "
29429983SFei.Feng@Sun.COM "timeout waiting for NIC to initialize\n");
29439983SFei.Feng@Sun.COM goto faili5;
29449983SFei.Feng@Sun.COM }
29459983SFei.Feng@Sun.COM
29469983SFei.Feng@Sun.COM /* retrieve RF rev. no and various other things from EEPROM */
29479983SFei.Feng@Sun.COM rt2661_read_eeprom(sc);
29489983SFei.Feng@Sun.COM
29499983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_attach(): "
29509983SFei.Feng@Sun.COM "MAC/BBP RT%X, RF %s\n"
29519983SFei.Feng@Sun.COM "MAC address is: %x:%x:%x:%x:%x:%x\n", val,
29529983SFei.Feng@Sun.COM rt2661_get_rf(sc->rf_rev),
29539983SFei.Feng@Sun.COM ic->ic_macaddr[0], ic->ic_macaddr[1], ic->ic_macaddr[2],
29549983SFei.Feng@Sun.COM ic->ic_macaddr[3], ic->ic_macaddr[4], ic->ic_macaddr[5]);
29559983SFei.Feng@Sun.COM
29569983SFei.Feng@Sun.COM /*
29579983SFei.Feng@Sun.COM * Load 8051 microcode into NIC.
29589983SFei.Feng@Sun.COM */
29599983SFei.Feng@Sun.COM switch (device_id) {
29609983SFei.Feng@Sun.COM case 0x0301:
29619983SFei.Feng@Sun.COM ucode = rt2561s_ucode;
29629983SFei.Feng@Sun.COM usize = sizeof (rt2561s_ucode);
29639983SFei.Feng@Sun.COM break;
29649983SFei.Feng@Sun.COM case 0x0302:
29659983SFei.Feng@Sun.COM ucode = rt2561_ucode;
29669983SFei.Feng@Sun.COM usize = sizeof (rt2561_ucode);
29679983SFei.Feng@Sun.COM break;
29689983SFei.Feng@Sun.COM case 0x0401:
29699983SFei.Feng@Sun.COM ucode = rt2661_ucode;
29709983SFei.Feng@Sun.COM usize = sizeof (rt2661_ucode);
29719983SFei.Feng@Sun.COM break;
29729983SFei.Feng@Sun.COM }
29739983SFei.Feng@Sun.COM
29749983SFei.Feng@Sun.COM err = rt2661_load_microcode(sc, ucode, usize);
29759983SFei.Feng@Sun.COM if (err != RT2661_SUCCESS) {
29769983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_attach(): "
29779983SFei.Feng@Sun.COM "could not load 8051 microcode\n");
29789983SFei.Feng@Sun.COM goto faili5;
29799983SFei.Feng@Sun.COM }
29809983SFei.Feng@Sun.COM
29819983SFei.Feng@Sun.COM sc->sc_flags = 0;
29829983SFei.Feng@Sun.COM sc->sc_flags |= RT2661_F_FWLOADED;
29839983SFei.Feng@Sun.COM
29849983SFei.Feng@Sun.COM /*
29859983SFei.Feng@Sun.COM * Allocate Tx and Rx rings.
29869983SFei.Feng@Sun.COM */
29879983SFei.Feng@Sun.COM for (ac = 0; ac < 4; ac++) {
29889983SFei.Feng@Sun.COM err = rt2661_alloc_tx_ring(sc, &sc->txq[ac],
29899983SFei.Feng@Sun.COM RT2661_TX_RING_COUNT);
29909983SFei.Feng@Sun.COM if (err != RT2661_SUCCESS) {
29919983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_DMA, "rwd: rt2661_attach(): "
29929983SFei.Feng@Sun.COM "could not allocate Tx ring %d\n", ac);
29939983SFei.Feng@Sun.COM goto fail4;
29949983SFei.Feng@Sun.COM }
29959983SFei.Feng@Sun.COM }
29969983SFei.Feng@Sun.COM
29979983SFei.Feng@Sun.COM err = rt2661_alloc_tx_ring(sc, &sc->mgtq, RT2661_MGT_RING_COUNT);
29989983SFei.Feng@Sun.COM if (err != RT2661_SUCCESS) {
29999983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_DMA, "rwd: rt2661_attach(): "
30009983SFei.Feng@Sun.COM "could not allocate Mgt ring\n");
30019983SFei.Feng@Sun.COM goto fail5;
30029983SFei.Feng@Sun.COM }
30039983SFei.Feng@Sun.COM
30049983SFei.Feng@Sun.COM err = rt2661_alloc_rx_ring(sc, &sc->rxq, RT2661_RX_RING_COUNT);
30059983SFei.Feng@Sun.COM if (err != RT2661_SUCCESS) {
30069983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_DMA, "rwd: rt2661_attach(): "
30079983SFei.Feng@Sun.COM "could not allocate Rx ring\n");
30089983SFei.Feng@Sun.COM goto fail6;
30099983SFei.Feng@Sun.COM }
30109983SFei.Feng@Sun.COM
30119983SFei.Feng@Sun.COM mutex_init(&sc->sc_genlock, NULL, MUTEX_DRIVER, NULL);
30129983SFei.Feng@Sun.COM mutex_init(&sc->sc_txlock, NULL, MUTEX_DRIVER, NULL);
30139983SFei.Feng@Sun.COM mutex_init(&sc->sc_rxlock, NULL, MUTEX_DRIVER, NULL);
30149983SFei.Feng@Sun.COM
30159983SFei.Feng@Sun.COM ic->ic_phytype = IEEE80211_T_OFDM;
30169983SFei.Feng@Sun.COM ic->ic_opmode = IEEE80211_M_STA;
30179983SFei.Feng@Sun.COM ic->ic_state = IEEE80211_S_INIT;
30189983SFei.Feng@Sun.COM
30199983SFei.Feng@Sun.COM /* set device capabilities */
30209983SFei.Feng@Sun.COM ic->ic_caps =
30219983SFei.Feng@Sun.COM IEEE80211_C_TXPMGT |
30229983SFei.Feng@Sun.COM IEEE80211_C_SHPREAMBLE |
30239983SFei.Feng@Sun.COM IEEE80211_C_SHSLOT;
30249983SFei.Feng@Sun.COM
30259983SFei.Feng@Sun.COM /* WPA/WPA2 support */
30269983SFei.Feng@Sun.COM ic->ic_caps |= IEEE80211_C_WPA;
30279983SFei.Feng@Sun.COM
30289983SFei.Feng@Sun.COM /* set supported .11b and .11g rates */
30299983SFei.Feng@Sun.COM ic->ic_sup_rates[IEEE80211_MODE_11B] = rt2661_rateset_11b;
30309983SFei.Feng@Sun.COM ic->ic_sup_rates[IEEE80211_MODE_11G] = rt2661_rateset_11g;
30319983SFei.Feng@Sun.COM
30329983SFei.Feng@Sun.COM /* set supported .11b and .11g channels (1 through 14) */
30339983SFei.Feng@Sun.COM for (i = 1; i <= 14; i++) {
30349983SFei.Feng@Sun.COM ic->ic_sup_channels[i].ich_freq =
30359983SFei.Feng@Sun.COM ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
30369983SFei.Feng@Sun.COM ic->ic_sup_channels[i].ich_flags =
30379983SFei.Feng@Sun.COM IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
30389983SFei.Feng@Sun.COM IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
30399983SFei.Feng@Sun.COM }
30409983SFei.Feng@Sun.COM
30419983SFei.Feng@Sun.COM ic->ic_maxrssi = 63;
30429983SFei.Feng@Sun.COM ic->ic_xmit = rt2661_mgmt_send;
30439983SFei.Feng@Sun.COM
30449983SFei.Feng@Sun.COM ieee80211_attach(ic);
30459983SFei.Feng@Sun.COM
30469983SFei.Feng@Sun.COM /* register WPA door */
30479983SFei.Feng@Sun.COM ieee80211_register_door(ic, ddi_driver_name(devinfo),
30489983SFei.Feng@Sun.COM ddi_get_instance(devinfo));
30499983SFei.Feng@Sun.COM
30509983SFei.Feng@Sun.COM ic->ic_node_alloc = rt2661_node_alloc;
30519983SFei.Feng@Sun.COM ic->ic_node_free = rt2661_node_free;
30529983SFei.Feng@Sun.COM ic->ic_set_shortslot = rt2661_updateslot;
30539983SFei.Feng@Sun.COM
30549983SFei.Feng@Sun.COM /* override state transition machine */
30559983SFei.Feng@Sun.COM sc->sc_newstate = ic->ic_newstate;
30569983SFei.Feng@Sun.COM ic->ic_newstate = rt2661_newstate;
30579983SFei.Feng@Sun.COM ieee80211_media_init(ic);
30589983SFei.Feng@Sun.COM ic->ic_def_txkey = 0;
30599983SFei.Feng@Sun.COM
30609983SFei.Feng@Sun.COM err = ddi_intr_add_softint(devinfo, &sc->sc_softintr_hdl,
30619983SFei.Feng@Sun.COM DDI_INTR_SOFTPRI_MAX, rt2661_softintr, (caddr_t)sc);
30629983SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
30639983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_attach(): "
30649983SFei.Feng@Sun.COM "ddi_add_softintr() failed");
30659983SFei.Feng@Sun.COM goto fail7;
30669983SFei.Feng@Sun.COM }
30679983SFei.Feng@Sun.COM
30689983SFei.Feng@Sun.COM err = ddi_intr_add_handler(sc->sc_intr_htable[0], rt2661_intr,
30699983SFei.Feng@Sun.COM (caddr_t)sc, NULL);
30709983SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
30719983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_attach(): "
30729983SFei.Feng@Sun.COM "ddi_intr_addr_handle() failed\n");
30739983SFei.Feng@Sun.COM goto fail8;
30749983SFei.Feng@Sun.COM }
30759983SFei.Feng@Sun.COM
30769983SFei.Feng@Sun.COM err = ddi_intr_enable(sc->sc_intr_htable[0]);
30779983SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
30789983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd; rt2661_attach(): "
30799983SFei.Feng@Sun.COM "ddi_intr_enable() failed\n");
30809983SFei.Feng@Sun.COM goto fail9;
30819983SFei.Feng@Sun.COM }
30829983SFei.Feng@Sun.COM
30839983SFei.Feng@Sun.COM /*
30849983SFei.Feng@Sun.COM * Provide initial settings for the WiFi plugin; whenever this
30859983SFei.Feng@Sun.COM * information changes, we need to call mac_plugindata_update()
30869983SFei.Feng@Sun.COM */
30879983SFei.Feng@Sun.COM wd.wd_opmode = ic->ic_opmode;
30889983SFei.Feng@Sun.COM wd.wd_secalloc = WIFI_SEC_NONE;
30899983SFei.Feng@Sun.COM IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
30909983SFei.Feng@Sun.COM
30919983SFei.Feng@Sun.COM if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
30929983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_attach(): "
30939983SFei.Feng@Sun.COM "MAC version mismatch\n");
30949983SFei.Feng@Sun.COM goto fail10;
30959983SFei.Feng@Sun.COM }
30969983SFei.Feng@Sun.COM
30979983SFei.Feng@Sun.COM macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI;
30989983SFei.Feng@Sun.COM macp->m_driver = sc;
30999983SFei.Feng@Sun.COM macp->m_dip = devinfo;
31009983SFei.Feng@Sun.COM macp->m_src_addr = ic->ic_macaddr;
31019983SFei.Feng@Sun.COM macp->m_callbacks = &rt2661_m_callbacks;
31029983SFei.Feng@Sun.COM macp->m_min_sdu = 0;
31039983SFei.Feng@Sun.COM macp->m_max_sdu = IEEE80211_MTU;
31049983SFei.Feng@Sun.COM macp->m_pdata = &wd;
31059983SFei.Feng@Sun.COM macp->m_pdata_size = sizeof (wd);
31069983SFei.Feng@Sun.COM
31079983SFei.Feng@Sun.COM err = mac_register(macp, &ic->ic_mach);
31089983SFei.Feng@Sun.COM mac_free(macp);
31099983SFei.Feng@Sun.COM if (err != 0) {
31109983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_attach(): "
31119983SFei.Feng@Sun.COM "mac_register err %x\n", err);
31129983SFei.Feng@Sun.COM goto fail10;
31139983SFei.Feng@Sun.COM }
31149983SFei.Feng@Sun.COM
31159983SFei.Feng@Sun.COM /*
31169983SFei.Feng@Sun.COM * Create minor node of type DDI_NT_NET_WIFI
31179983SFei.Feng@Sun.COM */
31189983SFei.Feng@Sun.COM (void) snprintf(strbuf, sizeof (strbuf), "%s%d",
31199983SFei.Feng@Sun.COM "rwd", instance);
31209983SFei.Feng@Sun.COM err = ddi_create_minor_node(devinfo, strbuf, S_IFCHR,
31219983SFei.Feng@Sun.COM instance + 1, DDI_NT_NET_WIFI, 0);
31229983SFei.Feng@Sun.COM
31239983SFei.Feng@Sun.COM /*
31249983SFei.Feng@Sun.COM * Notify link is down now
31259983SFei.Feng@Sun.COM */
31269983SFei.Feng@Sun.COM mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
31279983SFei.Feng@Sun.COM
31289983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_attach(): "
31299983SFei.Feng@Sun.COM "attach successfully\n");
31309983SFei.Feng@Sun.COM return (DDI_SUCCESS);
31319983SFei.Feng@Sun.COM
31329983SFei.Feng@Sun.COM fail10:
31339983SFei.Feng@Sun.COM (void) ddi_intr_disable(sc->sc_intr_htable[0]);
31349983SFei.Feng@Sun.COM fail9:
31359983SFei.Feng@Sun.COM (void) ddi_intr_remove_handler(sc->sc_intr_htable[0]);
31369983SFei.Feng@Sun.COM fail8:
31379983SFei.Feng@Sun.COM (void) ddi_intr_remove_softint(sc->sc_softintr_hdl);
31389983SFei.Feng@Sun.COM sc->sc_softintr_hdl = NULL;
31399983SFei.Feng@Sun.COM fail7:
31409983SFei.Feng@Sun.COM mutex_destroy(&sc->sc_genlock);
31419983SFei.Feng@Sun.COM mutex_destroy(&sc->sc_txlock);
31429983SFei.Feng@Sun.COM mutex_destroy(&sc->sc_rxlock);
31439983SFei.Feng@Sun.COM fail6:
31449983SFei.Feng@Sun.COM rt2661_free_rx_ring(sc, &sc->rxq);
31459983SFei.Feng@Sun.COM fail5:
31469983SFei.Feng@Sun.COM rt2661_free_tx_ring(sc, &sc->mgtq);
31479983SFei.Feng@Sun.COM fail4:
31489983SFei.Feng@Sun.COM while (--ac >= 0)
31499983SFei.Feng@Sun.COM rt2661_free_tx_ring(sc, &sc->txq[ac]);
31509983SFei.Feng@Sun.COM faili5:
31519983SFei.Feng@Sun.COM (void) ddi_intr_free(sc->sc_intr_htable[0]);
31529983SFei.Feng@Sun.COM faili4:
31539983SFei.Feng@Sun.COM kmem_free(sc->sc_intr_htable, sizeof (ddi_intr_handle_t));
31549983SFei.Feng@Sun.COM fail3:
31559983SFei.Feng@Sun.COM ddi_regs_map_free(&sc->sc_io_handle);
31569983SFei.Feng@Sun.COM fail2:
31579983SFei.Feng@Sun.COM ddi_regs_map_free(&sc->sc_cfg_handle);
31589983SFei.Feng@Sun.COM fail1:
31599983SFei.Feng@Sun.COM return (DDI_FAILURE);
31609983SFei.Feng@Sun.COM }
31619983SFei.Feng@Sun.COM
31629983SFei.Feng@Sun.COM static int
rt2661_detach(dev_info_t * devinfo,ddi_detach_cmd_t cmd)31639983SFei.Feng@Sun.COM rt2661_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
31649983SFei.Feng@Sun.COM {
31659983SFei.Feng@Sun.COM
31669983SFei.Feng@Sun.COM struct rt2661_softc *sc;
31679983SFei.Feng@Sun.COM
31689983SFei.Feng@Sun.COM sc = ddi_get_soft_state(rt2661_soft_state_p, ddi_get_instance(devinfo));
31699983SFei.Feng@Sun.COM
31709983SFei.Feng@Sun.COM switch (cmd) {
31719983SFei.Feng@Sun.COM case DDI_DETACH:
31729983SFei.Feng@Sun.COM break;
31739983SFei.Feng@Sun.COM case DDI_SUSPEND:
31749983SFei.Feng@Sun.COM if (RT2661_IS_RUNNING(sc))
31759983SFei.Feng@Sun.COM rt2661_stop(sc);
31769983SFei.Feng@Sun.COM RT2661_GLOCK(sc);
31779983SFei.Feng@Sun.COM sc->sc_flags |= RT2661_F_SUSPEND;
31789983SFei.Feng@Sun.COM sc->sc_flags &= ~RT2661_F_FWLOADED;
31799983SFei.Feng@Sun.COM RT2661_GUNLOCK(sc);
31809983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_detach(): "
31819983SFei.Feng@Sun.COM "suspend now\n");
31829983SFei.Feng@Sun.COM return (DDI_SUCCESS);
31839983SFei.Feng@Sun.COM default:
31849983SFei.Feng@Sun.COM return (DDI_FAILURE);
31859983SFei.Feng@Sun.COM }
31869983SFei.Feng@Sun.COM
31879983SFei.Feng@Sun.COM if (mac_disable(sc->sc_ic.ic_mach) != 0)
31889983SFei.Feng@Sun.COM return (DDI_FAILURE);
31899983SFei.Feng@Sun.COM
31909983SFei.Feng@Sun.COM /*
31919983SFei.Feng@Sun.COM * Unregister from the MAC layer subsystem
31929983SFei.Feng@Sun.COM */
31939983SFei.Feng@Sun.COM (void) mac_unregister(sc->sc_ic.ic_mach);
31949983SFei.Feng@Sun.COM
31959983SFei.Feng@Sun.COM (void) ddi_intr_remove_softint(sc->sc_softintr_hdl);
31969983SFei.Feng@Sun.COM sc->sc_softintr_hdl = NULL;
31979983SFei.Feng@Sun.COM (void) ddi_intr_disable(sc->sc_intr_htable[0]);
31989983SFei.Feng@Sun.COM (void) ddi_intr_remove_handler(sc->sc_intr_htable[0]);
31999983SFei.Feng@Sun.COM (void) ddi_intr_free(sc->sc_intr_htable[0]);
32009983SFei.Feng@Sun.COM kmem_free(sc->sc_intr_htable, sizeof (ddi_intr_handle_t));
32019983SFei.Feng@Sun.COM
32029983SFei.Feng@Sun.COM /*
32039983SFei.Feng@Sun.COM * detach ieee80211 layer
32049983SFei.Feng@Sun.COM */
32059983SFei.Feng@Sun.COM ieee80211_detach(&sc->sc_ic);
32069983SFei.Feng@Sun.COM
32079983SFei.Feng@Sun.COM mutex_destroy(&sc->sc_genlock);
32089983SFei.Feng@Sun.COM mutex_destroy(&sc->sc_txlock);
32099983SFei.Feng@Sun.COM mutex_destroy(&sc->sc_rxlock);
32109983SFei.Feng@Sun.COM
32119983SFei.Feng@Sun.COM rt2661_free_tx_ring(sc, &sc->txq[0]);
32129983SFei.Feng@Sun.COM rt2661_free_tx_ring(sc, &sc->txq[1]);
32139983SFei.Feng@Sun.COM rt2661_free_tx_ring(sc, &sc->txq[2]);
32149983SFei.Feng@Sun.COM rt2661_free_tx_ring(sc, &sc->txq[3]);
32159983SFei.Feng@Sun.COM rt2661_free_tx_ring(sc, &sc->mgtq);
32169983SFei.Feng@Sun.COM rt2661_free_rx_ring(sc, &sc->rxq);
32179983SFei.Feng@Sun.COM
32189983SFei.Feng@Sun.COM ddi_regs_map_free(&sc->sc_io_handle);
32199983SFei.Feng@Sun.COM ddi_regs_map_free(&sc->sc_cfg_handle);
32209983SFei.Feng@Sun.COM
32219983SFei.Feng@Sun.COM ddi_remove_minor_node(devinfo, NULL);
32229983SFei.Feng@Sun.COM ddi_soft_state_free(rt2661_soft_state_p, ddi_get_instance(devinfo));
32239983SFei.Feng@Sun.COM
32249983SFei.Feng@Sun.COM RWD_DEBUG(RT2661_DBG_MSG, "rwd: rt2661_detach(): "
32259983SFei.Feng@Sun.COM "detach successfully\n");
32269983SFei.Feng@Sun.COM return (DDI_SUCCESS);
32279983SFei.Feng@Sun.COM }
32289983SFei.Feng@Sun.COM
32299983SFei.Feng@Sun.COM static int
rt2661_quiesce(dev_info_t * dip)32309983SFei.Feng@Sun.COM rt2661_quiesce(dev_info_t *dip)
32319983SFei.Feng@Sun.COM {
32329983SFei.Feng@Sun.COM struct rt2661_softc *sc;
32339983SFei.Feng@Sun.COM
32349983SFei.Feng@Sun.COM sc = ddi_get_soft_state(rt2661_soft_state_p, ddi_get_instance(dip));
32359983SFei.Feng@Sun.COM if (sc == NULL)
32369983SFei.Feng@Sun.COM return (DDI_FAILURE);
32379983SFei.Feng@Sun.COM
32389983SFei.Feng@Sun.COM #ifdef DEBUG
32399983SFei.Feng@Sun.COM rt2661_dbg_flags = 0;
32409983SFei.Feng@Sun.COM #endif
32419983SFei.Feng@Sun.COM
32429983SFei.Feng@Sun.COM /*
32439983SFei.Feng@Sun.COM * No more blocking is allowed while we are in quiesce(9E) entry point
32449983SFei.Feng@Sun.COM */
32459983SFei.Feng@Sun.COM sc->sc_flags |= RT2661_F_QUIESCE;
32469983SFei.Feng@Sun.COM
32479983SFei.Feng@Sun.COM /*
32489983SFei.Feng@Sun.COM * Disable all interrupts
32499983SFei.Feng@Sun.COM */
32509983SFei.Feng@Sun.COM rt2661_stop(sc);
32519983SFei.Feng@Sun.COM return (DDI_SUCCESS);
32529983SFei.Feng@Sun.COM }
32539983SFei.Feng@Sun.COM
32549983SFei.Feng@Sun.COM int
_info(struct modinfo * modinfop)32559983SFei.Feng@Sun.COM _info(struct modinfo *modinfop)
32569983SFei.Feng@Sun.COM {
32579983SFei.Feng@Sun.COM return (mod_info(&modlinkage, modinfop));
32589983SFei.Feng@Sun.COM }
32599983SFei.Feng@Sun.COM
32609983SFei.Feng@Sun.COM int
_init(void)32619983SFei.Feng@Sun.COM _init(void)
32629983SFei.Feng@Sun.COM {
32639983SFei.Feng@Sun.COM int status;
32649983SFei.Feng@Sun.COM
32659983SFei.Feng@Sun.COM status = ddi_soft_state_init(&rt2661_soft_state_p,
32669983SFei.Feng@Sun.COM sizeof (struct rt2661_softc), 1);
32679983SFei.Feng@Sun.COM if (status != 0)
32689983SFei.Feng@Sun.COM return (status);
32699983SFei.Feng@Sun.COM
32709983SFei.Feng@Sun.COM mac_init_ops(&rwd_dev_ops, "rwd");
32719983SFei.Feng@Sun.COM status = mod_install(&modlinkage);
32729983SFei.Feng@Sun.COM if (status != 0) {
32739983SFei.Feng@Sun.COM mac_fini_ops(&rwd_dev_ops);
32749983SFei.Feng@Sun.COM ddi_soft_state_fini(&rt2661_soft_state_p);
32759983SFei.Feng@Sun.COM }
32769983SFei.Feng@Sun.COM return (status);
32779983SFei.Feng@Sun.COM }
32789983SFei.Feng@Sun.COM
32799983SFei.Feng@Sun.COM int
_fini(void)32809983SFei.Feng@Sun.COM _fini(void)
32819983SFei.Feng@Sun.COM {
32829983SFei.Feng@Sun.COM int status;
32839983SFei.Feng@Sun.COM
32849983SFei.Feng@Sun.COM status = mod_remove(&modlinkage);
32859983SFei.Feng@Sun.COM if (status == 0) {
32869983SFei.Feng@Sun.COM mac_fini_ops(&rwd_dev_ops);
32879983SFei.Feng@Sun.COM ddi_soft_state_fini(&rt2661_soft_state_p);
32889983SFei.Feng@Sun.COM }
32899983SFei.Feng@Sun.COM return (status);
32909983SFei.Feng@Sun.COM }
3291