184f7a9b9Shx147065 /* 2*0dc2366fSVenugopal Iyer * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 384f7a9b9Shx147065 * Use is subject to license terms. 484f7a9b9Shx147065 */ 584f7a9b9Shx147065 684f7a9b9Shx147065 /* 784f7a9b9Shx147065 * Copyright (c) 2006 884f7a9b9Shx147065 * Damien Bergamini <damien.bergamini@free.fr> 984f7a9b9Shx147065 * 1084f7a9b9Shx147065 * Permission to use, copy, modify, and distribute this software for any 1184f7a9b9Shx147065 * purpose with or without fee is hereby granted, provided that the above 1284f7a9b9Shx147065 * copyright notice and this permission notice appear in all copies. 1384f7a9b9Shx147065 * 1484f7a9b9Shx147065 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1584f7a9b9Shx147065 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1684f7a9b9Shx147065 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1784f7a9b9Shx147065 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1884f7a9b9Shx147065 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1984f7a9b9Shx147065 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 2084f7a9b9Shx147065 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 2184f7a9b9Shx147065 */ 2284f7a9b9Shx147065 2384f7a9b9Shx147065 /* 2484f7a9b9Shx147065 * Driver for Intel PRO/Wireless 3945ABG 802.11 network adapters. 2584f7a9b9Shx147065 */ 2684f7a9b9Shx147065 2784f7a9b9Shx147065 #include <sys/types.h> 2884f7a9b9Shx147065 #include <sys/byteorder.h> 2984f7a9b9Shx147065 #include <sys/conf.h> 3084f7a9b9Shx147065 #include <sys/cmn_err.h> 3184f7a9b9Shx147065 #include <sys/stat.h> 3284f7a9b9Shx147065 #include <sys/ddi.h> 3384f7a9b9Shx147065 #include <sys/sunddi.h> 3484f7a9b9Shx147065 #include <sys/strsubr.h> 3584f7a9b9Shx147065 #include <sys/ethernet.h> 3684f7a9b9Shx147065 #include <inet/common.h> 3784f7a9b9Shx147065 #include <inet/nd.h> 3884f7a9b9Shx147065 #include <inet/mi.h> 3984f7a9b9Shx147065 #include <sys/note.h> 4084f7a9b9Shx147065 #include <sys/stream.h> 4184f7a9b9Shx147065 #include <sys/strsun.h> 4284f7a9b9Shx147065 #include <sys/modctl.h> 4384f7a9b9Shx147065 #include <sys/devops.h> 4484f7a9b9Shx147065 #include <sys/dlpi.h> 45da14cebeSEric Cheng #include <sys/mac_provider.h> 4684f7a9b9Shx147065 #include <sys/mac_wifi.h> 4784f7a9b9Shx147065 #include <sys/net80211.h> 4884f7a9b9Shx147065 #include <sys/net80211_proto.h> 4984f7a9b9Shx147065 #include <sys/varargs.h> 5084f7a9b9Shx147065 #include <sys/policy.h> 5184f7a9b9Shx147065 #include <sys/pci.h> 5284f7a9b9Shx147065 5384f7a9b9Shx147065 #include "wpireg.h" 5484f7a9b9Shx147065 #include "wpivar.h" 5584f7a9b9Shx147065 #include <inet/wifi_ioctl.h> 5684f7a9b9Shx147065 5784f7a9b9Shx147065 #ifdef DEBUG 5884f7a9b9Shx147065 #define WPI_DEBUG_80211 (1 << 0) 5984f7a9b9Shx147065 #define WPI_DEBUG_CMD (1 << 1) 6084f7a9b9Shx147065 #define WPI_DEBUG_DMA (1 << 2) 6184f7a9b9Shx147065 #define WPI_DEBUG_EEPROM (1 << 3) 6284f7a9b9Shx147065 #define WPI_DEBUG_FW (1 << 4) 6384f7a9b9Shx147065 #define WPI_DEBUG_HW (1 << 5) 6484f7a9b9Shx147065 #define WPI_DEBUG_INTR (1 << 6) 6584f7a9b9Shx147065 #define WPI_DEBUG_MRR (1 << 7) 6684f7a9b9Shx147065 #define WPI_DEBUG_PIO (1 << 8) 6784f7a9b9Shx147065 #define WPI_DEBUG_RX (1 << 9) 6884f7a9b9Shx147065 #define WPI_DEBUG_SCAN (1 << 10) 6984f7a9b9Shx147065 #define WPI_DEBUG_TX (1 << 11) 7084f7a9b9Shx147065 #define WPI_DEBUG_RATECTL (1 << 12) 7184f7a9b9Shx147065 #define WPI_DEBUG_RADIO (1 << 13) 7219b23afcShx147065 #define WPI_DEBUG_RESUME (1 << 14) 7384f7a9b9Shx147065 uint32_t wpi_dbg_flags = 0; 7484f7a9b9Shx147065 #define WPI_DBG(x) \ 7584f7a9b9Shx147065 wpi_dbg x 7684f7a9b9Shx147065 #else 7784f7a9b9Shx147065 #define WPI_DBG(x) 7884f7a9b9Shx147065 #endif 7984f7a9b9Shx147065 8084f7a9b9Shx147065 static void *wpi_soft_state_p = NULL; 8184f7a9b9Shx147065 static uint8_t wpi_fw_bin [] = { 8284f7a9b9Shx147065 #include "fw-wpi/ipw3945.ucode.hex" 8384f7a9b9Shx147065 }; 8484f7a9b9Shx147065 8584f7a9b9Shx147065 /* DMA attributes for a shared page */ 8684f7a9b9Shx147065 static ddi_dma_attr_t sh_dma_attr = { 8784f7a9b9Shx147065 DMA_ATTR_V0, /* version of this structure */ 8884f7a9b9Shx147065 0, /* lowest usable address */ 8984f7a9b9Shx147065 0xffffffffU, /* highest usable address */ 9084f7a9b9Shx147065 0xffffffffU, /* maximum DMAable byte count */ 9184f7a9b9Shx147065 0x1000, /* alignment in bytes */ 9284f7a9b9Shx147065 0x1000, /* burst sizes (any?) */ 9384f7a9b9Shx147065 1, /* minimum transfer */ 9484f7a9b9Shx147065 0xffffffffU, /* maximum transfer */ 9584f7a9b9Shx147065 0xffffffffU, /* maximum segment length */ 9684f7a9b9Shx147065 1, /* maximum number of segments */ 9784f7a9b9Shx147065 1, /* granularity */ 9884f7a9b9Shx147065 0, /* flags (reserved) */ 9984f7a9b9Shx147065 }; 10084f7a9b9Shx147065 10184f7a9b9Shx147065 /* DMA attributes for a ring descriptor */ 10284f7a9b9Shx147065 static ddi_dma_attr_t ring_desc_dma_attr = { 10384f7a9b9Shx147065 DMA_ATTR_V0, /* version of this structure */ 10484f7a9b9Shx147065 0, /* lowest usable address */ 10584f7a9b9Shx147065 0xffffffffU, /* highest usable address */ 10684f7a9b9Shx147065 0xffffffffU, /* maximum DMAable byte count */ 10784f7a9b9Shx147065 0x4000, /* alignment in bytes */ 10884f7a9b9Shx147065 0x100, /* burst sizes (any?) */ 10984f7a9b9Shx147065 1, /* minimum transfer */ 11084f7a9b9Shx147065 0xffffffffU, /* maximum transfer */ 11184f7a9b9Shx147065 0xffffffffU, /* maximum segment length */ 11284f7a9b9Shx147065 1, /* maximum number of segments */ 11384f7a9b9Shx147065 1, /* granularity */ 11484f7a9b9Shx147065 0, /* flags (reserved) */ 11584f7a9b9Shx147065 }; 11684f7a9b9Shx147065 11784f7a9b9Shx147065 11884f7a9b9Shx147065 /* DMA attributes for a tx cmd */ 11984f7a9b9Shx147065 static ddi_dma_attr_t tx_cmd_dma_attr = { 12084f7a9b9Shx147065 DMA_ATTR_V0, /* version of this structure */ 12184f7a9b9Shx147065 0, /* lowest usable address */ 12284f7a9b9Shx147065 0xffffffffU, /* highest usable address */ 12384f7a9b9Shx147065 0xffffffffU, /* maximum DMAable byte count */ 12484f7a9b9Shx147065 4, /* alignment in bytes */ 12584f7a9b9Shx147065 0x100, /* burst sizes (any?) */ 12684f7a9b9Shx147065 1, /* minimum transfer */ 12784f7a9b9Shx147065 0xffffffffU, /* maximum transfer */ 12884f7a9b9Shx147065 0xffffffffU, /* maximum segment length */ 12984f7a9b9Shx147065 1, /* maximum number of segments */ 13084f7a9b9Shx147065 1, /* granularity */ 13184f7a9b9Shx147065 0, /* flags (reserved) */ 13284f7a9b9Shx147065 }; 13384f7a9b9Shx147065 13484f7a9b9Shx147065 /* DMA attributes for a rx buffer */ 13584f7a9b9Shx147065 static ddi_dma_attr_t rx_buffer_dma_attr = { 13684f7a9b9Shx147065 DMA_ATTR_V0, /* version of this structure */ 13784f7a9b9Shx147065 0, /* lowest usable address */ 13884f7a9b9Shx147065 0xffffffffU, /* highest usable address */ 13984f7a9b9Shx147065 0xffffffffU, /* maximum DMAable byte count */ 14084f7a9b9Shx147065 1, /* alignment in bytes */ 14184f7a9b9Shx147065 0x100, /* burst sizes (any?) */ 14284f7a9b9Shx147065 1, /* minimum transfer */ 14384f7a9b9Shx147065 0xffffffffU, /* maximum transfer */ 14484f7a9b9Shx147065 0xffffffffU, /* maximum segment length */ 14584f7a9b9Shx147065 1, /* maximum number of segments */ 14684f7a9b9Shx147065 1, /* granularity */ 14784f7a9b9Shx147065 0, /* flags (reserved) */ 14884f7a9b9Shx147065 }; 14984f7a9b9Shx147065 15084f7a9b9Shx147065 /* 15184f7a9b9Shx147065 * DMA attributes for a tx buffer. 15284f7a9b9Shx147065 * the maximum number of segments is 4 for the hardware. 15384f7a9b9Shx147065 * now all the wifi drivers put the whole frame in a single 15484f7a9b9Shx147065 * descriptor, so we define the maximum number of segments 4, 15584f7a9b9Shx147065 * just the same as the rx_buffer. we consider leverage the HW 15684f7a9b9Shx147065 * ability in the future, that is why we don't define rx and tx 15784f7a9b9Shx147065 * buffer_dma_attr as the same. 15884f7a9b9Shx147065 */ 15984f7a9b9Shx147065 static ddi_dma_attr_t tx_buffer_dma_attr = { 16084f7a9b9Shx147065 DMA_ATTR_V0, /* version of this structure */ 16184f7a9b9Shx147065 0, /* lowest usable address */ 16284f7a9b9Shx147065 0xffffffffU, /* highest usable address */ 16384f7a9b9Shx147065 0xffffffffU, /* maximum DMAable byte count */ 16484f7a9b9Shx147065 1, /* alignment in bytes */ 16584f7a9b9Shx147065 0x100, /* burst sizes (any?) */ 16684f7a9b9Shx147065 1, /* minimum transfer */ 16784f7a9b9Shx147065 0xffffffffU, /* maximum transfer */ 16884f7a9b9Shx147065 0xffffffffU, /* maximum segment length */ 16984f7a9b9Shx147065 1, /* maximum number of segments */ 17084f7a9b9Shx147065 1, /* granularity */ 17184f7a9b9Shx147065 0, /* flags (reserved) */ 17284f7a9b9Shx147065 }; 17384f7a9b9Shx147065 17484f7a9b9Shx147065 /* DMA attributes for a load firmware */ 17584f7a9b9Shx147065 static ddi_dma_attr_t fw_buffer_dma_attr = { 17684f7a9b9Shx147065 DMA_ATTR_V0, /* version of this structure */ 17784f7a9b9Shx147065 0, /* lowest usable address */ 17884f7a9b9Shx147065 0xffffffffU, /* highest usable address */ 17984f7a9b9Shx147065 0x7fffffff, /* maximum DMAable byte count */ 18084f7a9b9Shx147065 4, /* alignment in bytes */ 18184f7a9b9Shx147065 0x100, /* burst sizes (any?) */ 18284f7a9b9Shx147065 1, /* minimum transfer */ 18384f7a9b9Shx147065 0xffffffffU, /* maximum transfer */ 18484f7a9b9Shx147065 0xffffffffU, /* maximum segment length */ 18584f7a9b9Shx147065 4, /* maximum number of segments */ 18684f7a9b9Shx147065 1, /* granularity */ 18784f7a9b9Shx147065 0, /* flags (reserved) */ 18884f7a9b9Shx147065 }; 18984f7a9b9Shx147065 19084f7a9b9Shx147065 /* regs access attributes */ 19184f7a9b9Shx147065 static ddi_device_acc_attr_t wpi_reg_accattr = { 19284f7a9b9Shx147065 DDI_DEVICE_ATTR_V0, 19384f7a9b9Shx147065 DDI_STRUCTURE_LE_ACC, 19484f7a9b9Shx147065 DDI_STRICTORDER_ACC, 19584f7a9b9Shx147065 DDI_DEFAULT_ACC 19684f7a9b9Shx147065 }; 19784f7a9b9Shx147065 19884f7a9b9Shx147065 /* DMA access attributes */ 19984f7a9b9Shx147065 static ddi_device_acc_attr_t wpi_dma_accattr = { 20084f7a9b9Shx147065 DDI_DEVICE_ATTR_V0, 20184f7a9b9Shx147065 DDI_NEVERSWAP_ACC, 20284f7a9b9Shx147065 DDI_STRICTORDER_ACC, 20384f7a9b9Shx147065 DDI_DEFAULT_ACC 20484f7a9b9Shx147065 }; 20584f7a9b9Shx147065 20684f7a9b9Shx147065 static int wpi_ring_init(wpi_sc_t *); 20784f7a9b9Shx147065 static void wpi_ring_free(wpi_sc_t *); 20884f7a9b9Shx147065 static int wpi_alloc_shared(wpi_sc_t *); 20984f7a9b9Shx147065 static void wpi_free_shared(wpi_sc_t *); 21084f7a9b9Shx147065 static int wpi_alloc_fw_dma(wpi_sc_t *); 21184f7a9b9Shx147065 static void wpi_free_fw_dma(wpi_sc_t *); 21284f7a9b9Shx147065 static int wpi_alloc_rx_ring(wpi_sc_t *); 21384f7a9b9Shx147065 static void wpi_reset_rx_ring(wpi_sc_t *); 21484f7a9b9Shx147065 static void wpi_free_rx_ring(wpi_sc_t *); 21584f7a9b9Shx147065 static int wpi_alloc_tx_ring(wpi_sc_t *, wpi_tx_ring_t *, int, int); 21684f7a9b9Shx147065 static void wpi_reset_tx_ring(wpi_sc_t *, wpi_tx_ring_t *); 21784f7a9b9Shx147065 static void wpi_free_tx_ring(wpi_sc_t *, wpi_tx_ring_t *); 21884f7a9b9Shx147065 21984f7a9b9Shx147065 static ieee80211_node_t *wpi_node_alloc(ieee80211com_t *); 22084f7a9b9Shx147065 static void wpi_node_free(ieee80211_node_t *); 22184f7a9b9Shx147065 static int wpi_newstate(ieee80211com_t *, enum ieee80211_state, int); 2223a1a8936Szf162725 static int wpi_key_set(ieee80211com_t *, const struct ieee80211_key *, 2233a1a8936Szf162725 const uint8_t mac[IEEE80211_ADDR_LEN]); 22484f7a9b9Shx147065 static void wpi_mem_lock(wpi_sc_t *); 22584f7a9b9Shx147065 static void wpi_mem_unlock(wpi_sc_t *); 22684f7a9b9Shx147065 static uint32_t wpi_mem_read(wpi_sc_t *, uint16_t); 22784f7a9b9Shx147065 static void wpi_mem_write(wpi_sc_t *, uint16_t, uint32_t); 22884f7a9b9Shx147065 static void wpi_mem_write_region_4(wpi_sc_t *, uint16_t, 22984f7a9b9Shx147065 const uint32_t *, int); 23084f7a9b9Shx147065 static uint16_t wpi_read_prom_word(wpi_sc_t *, uint32_t); 23184f7a9b9Shx147065 static int wpi_load_microcode(wpi_sc_t *); 23284f7a9b9Shx147065 static int wpi_load_firmware(wpi_sc_t *, uint32_t); 23384f7a9b9Shx147065 static void wpi_rx_intr(wpi_sc_t *, wpi_rx_desc_t *, 23484f7a9b9Shx147065 wpi_rx_data_t *); 23584f7a9b9Shx147065 static void wpi_tx_intr(wpi_sc_t *, wpi_rx_desc_t *, 23684f7a9b9Shx147065 wpi_rx_data_t *); 23784f7a9b9Shx147065 static void wpi_cmd_intr(wpi_sc_t *, wpi_rx_desc_t *); 23884f7a9b9Shx147065 static uint_t wpi_intr(caddr_t); 23984f7a9b9Shx147065 static uint_t wpi_notif_softintr(caddr_t); 24084f7a9b9Shx147065 static uint8_t wpi_plcp_signal(int); 24184f7a9b9Shx147065 static void wpi_read_eeprom(wpi_sc_t *); 24284f7a9b9Shx147065 static int wpi_cmd(wpi_sc_t *, int, const void *, int, int); 24384f7a9b9Shx147065 static int wpi_mrr_setup(wpi_sc_t *); 24484f7a9b9Shx147065 static void wpi_set_led(wpi_sc_t *, uint8_t, uint8_t, uint8_t); 24584f7a9b9Shx147065 static int wpi_auth(wpi_sc_t *); 24684f7a9b9Shx147065 static int wpi_scan(wpi_sc_t *); 24784f7a9b9Shx147065 static int wpi_config(wpi_sc_t *); 24884f7a9b9Shx147065 static void wpi_stop_master(wpi_sc_t *); 24984f7a9b9Shx147065 static int wpi_power_up(wpi_sc_t *); 25084f7a9b9Shx147065 static int wpi_reset(wpi_sc_t *); 25184f7a9b9Shx147065 static void wpi_hw_config(wpi_sc_t *); 25284f7a9b9Shx147065 static int wpi_init(wpi_sc_t *); 25384f7a9b9Shx147065 static void wpi_stop(wpi_sc_t *); 254faceed93Sfei feng - Sun Microsystems - Beijing China static int wpi_quiesce(dev_info_t *dip); 25584f7a9b9Shx147065 static void wpi_amrr_init(wpi_amrr_t *); 25684f7a9b9Shx147065 static void wpi_amrr_timeout(wpi_sc_t *); 25784f7a9b9Shx147065 static void wpi_amrr_ratectl(void *, ieee80211_node_t *); 25884f7a9b9Shx147065 25984f7a9b9Shx147065 static int wpi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 26084f7a9b9Shx147065 static int wpi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 26184f7a9b9Shx147065 26284f7a9b9Shx147065 /* 26384f7a9b9Shx147065 * GLD specific operations 26484f7a9b9Shx147065 */ 26584f7a9b9Shx147065 static int wpi_m_stat(void *arg, uint_t stat, uint64_t *val); 26684f7a9b9Shx147065 static int wpi_m_start(void *arg); 26784f7a9b9Shx147065 static void wpi_m_stop(void *arg); 26884f7a9b9Shx147065 static int wpi_m_unicst(void *arg, const uint8_t *macaddr); 26984f7a9b9Shx147065 static int wpi_m_multicst(void *arg, boolean_t add, const uint8_t *m); 27084f7a9b9Shx147065 static int wpi_m_promisc(void *arg, boolean_t on); 27184f7a9b9Shx147065 static mblk_t *wpi_m_tx(void *arg, mblk_t *mp); 27284f7a9b9Shx147065 static void wpi_m_ioctl(void *arg, queue_t *wq, mblk_t *mp); 273bcb5c89dSSowmini Varadhan static int wpi_m_setprop(void *arg, const char *pr_name, 274bcb5c89dSSowmini Varadhan mac_prop_id_t wldp_pr_num, uint_t wldp_length, const void *wldp_buf); 275bcb5c89dSSowmini Varadhan static int wpi_m_getprop(void *arg, const char *pr_name, 276*0dc2366fSVenugopal Iyer mac_prop_id_t wldp_pr_num, uint_t wldp_lenth, void *wldp_buf); 277*0dc2366fSVenugopal Iyer static void wpi_m_propinfo(void *arg, const char *pr_name, 278*0dc2366fSVenugopal Iyer mac_prop_id_t wldp_pr_num, mac_prop_info_handle_t mph); 27984f7a9b9Shx147065 static void wpi_destroy_locks(wpi_sc_t *sc); 28084f7a9b9Shx147065 static int wpi_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type); 28184f7a9b9Shx147065 static void wpi_thread(wpi_sc_t *sc); 2826f12def4Spengcheng chen - Sun Microsystems - Beijing China static int wpi_fast_recover(wpi_sc_t *sc); 28384f7a9b9Shx147065 28484f7a9b9Shx147065 /* 28584f7a9b9Shx147065 * Supported rates for 802.11a/b/g modes (in 500Kbps unit). 28684f7a9b9Shx147065 */ 28784f7a9b9Shx147065 static const struct ieee80211_rateset wpi_rateset_11b = 28884f7a9b9Shx147065 { 4, { 2, 4, 11, 22 } }; 28984f7a9b9Shx147065 29084f7a9b9Shx147065 static const struct ieee80211_rateset wpi_rateset_11g = 29184f7a9b9Shx147065 { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } }; 29284f7a9b9Shx147065 29384f7a9b9Shx147065 static const uint8_t wpi_ridx_to_signal[] = { 29484f7a9b9Shx147065 /* OFDM: IEEE Std 802.11a-1999, pp. 14 Table 80 */ 29584f7a9b9Shx147065 /* R1-R4 (ral/ural is R4-R1) */ 29684f7a9b9Shx147065 0xd, 0xf, 0x5, 0x7, 0x9, 0xb, 0x1, 0x3, 29784f7a9b9Shx147065 /* CCK: device-dependent */ 29884f7a9b9Shx147065 10, 20, 55, 110 29984f7a9b9Shx147065 }; 30084f7a9b9Shx147065 30184f7a9b9Shx147065 /* 30284f7a9b9Shx147065 * For mfthread only 30384f7a9b9Shx147065 */ 30484f7a9b9Shx147065 extern pri_t minclsyspri; 30584f7a9b9Shx147065 30684f7a9b9Shx147065 /* 30784f7a9b9Shx147065 * Module Loading Data & Entry Points 30884f7a9b9Shx147065 */ 30984f7a9b9Shx147065 DDI_DEFINE_STREAM_OPS(wpi_devops, nulldev, nulldev, wpi_attach, 310faceed93Sfei feng - Sun Microsystems - Beijing China wpi_detach, nodev, NULL, D_MP, NULL, wpi_quiesce); 31184f7a9b9Shx147065 31284f7a9b9Shx147065 static struct modldrv wpi_modldrv = { 31384f7a9b9Shx147065 &mod_driverops, 31484f7a9b9Shx147065 "Intel(R) PRO/Wireless 3945ABG driver", 31584f7a9b9Shx147065 &wpi_devops 31684f7a9b9Shx147065 }; 31784f7a9b9Shx147065 31884f7a9b9Shx147065 static struct modlinkage wpi_modlinkage = { 31984f7a9b9Shx147065 MODREV_1, 32084f7a9b9Shx147065 &wpi_modldrv, 32184f7a9b9Shx147065 NULL 32284f7a9b9Shx147065 }; 32384f7a9b9Shx147065 32484f7a9b9Shx147065 int 32584f7a9b9Shx147065 _init(void) 32684f7a9b9Shx147065 { 32784f7a9b9Shx147065 int status; 32884f7a9b9Shx147065 32984f7a9b9Shx147065 status = ddi_soft_state_init(&wpi_soft_state_p, 33084f7a9b9Shx147065 sizeof (wpi_sc_t), 1); 33184f7a9b9Shx147065 if (status != DDI_SUCCESS) 33284f7a9b9Shx147065 return (status); 33384f7a9b9Shx147065 33484f7a9b9Shx147065 mac_init_ops(&wpi_devops, "wpi"); 33584f7a9b9Shx147065 status = mod_install(&wpi_modlinkage); 33684f7a9b9Shx147065 if (status != DDI_SUCCESS) { 33784f7a9b9Shx147065 mac_fini_ops(&wpi_devops); 33884f7a9b9Shx147065 ddi_soft_state_fini(&wpi_soft_state_p); 33984f7a9b9Shx147065 } 34084f7a9b9Shx147065 34184f7a9b9Shx147065 return (status); 34284f7a9b9Shx147065 } 34384f7a9b9Shx147065 34484f7a9b9Shx147065 int 34584f7a9b9Shx147065 _fini(void) 34684f7a9b9Shx147065 { 34784f7a9b9Shx147065 int status; 34884f7a9b9Shx147065 34984f7a9b9Shx147065 status = mod_remove(&wpi_modlinkage); 35084f7a9b9Shx147065 if (status == DDI_SUCCESS) { 35184f7a9b9Shx147065 mac_fini_ops(&wpi_devops); 35284f7a9b9Shx147065 ddi_soft_state_fini(&wpi_soft_state_p); 35384f7a9b9Shx147065 } 35484f7a9b9Shx147065 35584f7a9b9Shx147065 return (status); 35684f7a9b9Shx147065 } 35784f7a9b9Shx147065 35884f7a9b9Shx147065 int 35984f7a9b9Shx147065 _info(struct modinfo *mip) 36084f7a9b9Shx147065 { 36184f7a9b9Shx147065 return (mod_info(&wpi_modlinkage, mip)); 36284f7a9b9Shx147065 } 36384f7a9b9Shx147065 36484f7a9b9Shx147065 /* 36584f7a9b9Shx147065 * Mac Call Back entries 36684f7a9b9Shx147065 */ 36784f7a9b9Shx147065 mac_callbacks_t wpi_m_callbacks = { 368*0dc2366fSVenugopal Iyer MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO, 36984f7a9b9Shx147065 wpi_m_stat, 37084f7a9b9Shx147065 wpi_m_start, 37184f7a9b9Shx147065 wpi_m_stop, 37284f7a9b9Shx147065 wpi_m_promisc, 37384f7a9b9Shx147065 wpi_m_multicst, 37484f7a9b9Shx147065 wpi_m_unicst, 37584f7a9b9Shx147065 wpi_m_tx, 376*0dc2366fSVenugopal Iyer NULL, 377bcb5c89dSSowmini Varadhan wpi_m_ioctl, 378bcb5c89dSSowmini Varadhan NULL, 379bcb5c89dSSowmini Varadhan NULL, 380bcb5c89dSSowmini Varadhan NULL, 381bcb5c89dSSowmini Varadhan wpi_m_setprop, 382*0dc2366fSVenugopal Iyer wpi_m_getprop, 383*0dc2366fSVenugopal Iyer wpi_m_propinfo 38484f7a9b9Shx147065 }; 38584f7a9b9Shx147065 38684f7a9b9Shx147065 #ifdef DEBUG 38784f7a9b9Shx147065 void 38884f7a9b9Shx147065 wpi_dbg(uint32_t flags, const char *fmt, ...) 38984f7a9b9Shx147065 { 39084f7a9b9Shx147065 va_list ap; 39184f7a9b9Shx147065 39284f7a9b9Shx147065 if (flags & wpi_dbg_flags) { 39384f7a9b9Shx147065 va_start(ap, fmt); 39484f7a9b9Shx147065 vcmn_err(CE_NOTE, fmt, ap); 39584f7a9b9Shx147065 va_end(ap); 39684f7a9b9Shx147065 } 39784f7a9b9Shx147065 } 39884f7a9b9Shx147065 #endif 39984f7a9b9Shx147065 /* 40084f7a9b9Shx147065 * device operations 40184f7a9b9Shx147065 */ 40284f7a9b9Shx147065 int 40384f7a9b9Shx147065 wpi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 40484f7a9b9Shx147065 { 40584f7a9b9Shx147065 wpi_sc_t *sc; 40684f7a9b9Shx147065 ddi_acc_handle_t cfg_handle; 40784f7a9b9Shx147065 caddr_t cfg_base; 40884f7a9b9Shx147065 ieee80211com_t *ic; 40984f7a9b9Shx147065 int instance, err, i; 41084f7a9b9Shx147065 char strbuf[32]; 41184f7a9b9Shx147065 wifi_data_t wd = { 0 }; 41284f7a9b9Shx147065 mac_register_t *macp; 41384f7a9b9Shx147065 41419b23afcShx147065 switch (cmd) { 41519b23afcShx147065 case DDI_ATTACH: 41619b23afcShx147065 break; 41719b23afcShx147065 case DDI_RESUME: 41819b23afcShx147065 sc = ddi_get_soft_state(wpi_soft_state_p, 41919b23afcShx147065 ddi_get_instance(dip)); 42019b23afcShx147065 ASSERT(sc != NULL); 42109539a3cSpengcheng chen - Sun Microsystems - Beijing China 42209539a3cSpengcheng chen - Sun Microsystems - Beijing China mutex_enter(&sc->sc_glock); 42309539a3cSpengcheng chen - Sun Microsystems - Beijing China sc->sc_flags &= ~WPI_F_SUSPEND; 42409539a3cSpengcheng chen - Sun Microsystems - Beijing China mutex_exit(&sc->sc_glock); 42509539a3cSpengcheng chen - Sun Microsystems - Beijing China 426d40f4da4Spengcheng chen - Sun Microsystems - Beijing China if (sc->sc_flags & WPI_F_RUNNING) 427d40f4da4Spengcheng chen - Sun Microsystems - Beijing China (void) wpi_init(sc); 428d40f4da4Spengcheng chen - Sun Microsystems - Beijing China 42919b23afcShx147065 mutex_enter(&sc->sc_glock); 430d40f4da4Spengcheng chen - Sun Microsystems - Beijing China sc->sc_flags |= WPI_F_LAZY_RESUME; 43119b23afcShx147065 mutex_exit(&sc->sc_glock); 432d40f4da4Spengcheng chen - Sun Microsystems - Beijing China 43319b23afcShx147065 WPI_DBG((WPI_DEBUG_RESUME, "wpi: resume \n")); 43419b23afcShx147065 return (DDI_SUCCESS); 43519b23afcShx147065 default: 43684f7a9b9Shx147065 err = DDI_FAILURE; 43784f7a9b9Shx147065 goto attach_fail1; 43884f7a9b9Shx147065 } 43984f7a9b9Shx147065 44084f7a9b9Shx147065 instance = ddi_get_instance(dip); 44184f7a9b9Shx147065 err = ddi_soft_state_zalloc(wpi_soft_state_p, instance); 44284f7a9b9Shx147065 if (err != DDI_SUCCESS) { 44384f7a9b9Shx147065 cmn_err(CE_WARN, 44484f7a9b9Shx147065 "wpi_attach(): failed to allocate soft state\n"); 44584f7a9b9Shx147065 goto attach_fail1; 44684f7a9b9Shx147065 } 44784f7a9b9Shx147065 sc = ddi_get_soft_state(wpi_soft_state_p, instance); 44884f7a9b9Shx147065 sc->sc_dip = dip; 44984f7a9b9Shx147065 45084f7a9b9Shx147065 err = ddi_regs_map_setup(dip, 0, &cfg_base, 0, 0, 45184f7a9b9Shx147065 &wpi_reg_accattr, &cfg_handle); 45284f7a9b9Shx147065 if (err != DDI_SUCCESS) { 45384f7a9b9Shx147065 cmn_err(CE_WARN, 45484f7a9b9Shx147065 "wpi_attach(): failed to map config spaces regs\n"); 45584f7a9b9Shx147065 goto attach_fail2; 45684f7a9b9Shx147065 } 45784f7a9b9Shx147065 sc->sc_rev = ddi_get8(cfg_handle, 45884f7a9b9Shx147065 (uint8_t *)(cfg_base + PCI_CONF_REVID)); 45984f7a9b9Shx147065 ddi_put8(cfg_handle, (uint8_t *)(cfg_base + 0x41), 0); 46084f7a9b9Shx147065 sc->sc_clsz = ddi_get16(cfg_handle, 46184f7a9b9Shx147065 (uint16_t *)(cfg_base + PCI_CONF_CACHE_LINESZ)); 46284f7a9b9Shx147065 ddi_regs_map_free(&cfg_handle); 46384f7a9b9Shx147065 if (!sc->sc_clsz) 46484f7a9b9Shx147065 sc->sc_clsz = 16; 46584f7a9b9Shx147065 sc->sc_clsz = (sc->sc_clsz << 2); 46684f7a9b9Shx147065 sc->sc_dmabuf_sz = roundup(0x1000 + sizeof (struct ieee80211_frame) + 46784f7a9b9Shx147065 IEEE80211_MTU + IEEE80211_CRC_LEN + 46884f7a9b9Shx147065 (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + 46984f7a9b9Shx147065 IEEE80211_WEP_CRCLEN), sc->sc_clsz); 47084f7a9b9Shx147065 /* 47184f7a9b9Shx147065 * Map operating registers 47284f7a9b9Shx147065 */ 47384f7a9b9Shx147065 err = ddi_regs_map_setup(dip, 1, &sc->sc_base, 47484f7a9b9Shx147065 0, 0, &wpi_reg_accattr, &sc->sc_handle); 47584f7a9b9Shx147065 if (err != DDI_SUCCESS) { 47684f7a9b9Shx147065 cmn_err(CE_WARN, 47784f7a9b9Shx147065 "wpi_attach(): failed to map device regs\n"); 47884f7a9b9Shx147065 goto attach_fail2; 47984f7a9b9Shx147065 } 48084f7a9b9Shx147065 48184f7a9b9Shx147065 /* 48284f7a9b9Shx147065 * Allocate shared page. 48384f7a9b9Shx147065 */ 48484f7a9b9Shx147065 err = wpi_alloc_shared(sc); 48584f7a9b9Shx147065 if (err != DDI_SUCCESS) { 48684f7a9b9Shx147065 cmn_err(CE_WARN, "failed to allocate shared page\n"); 48784f7a9b9Shx147065 goto attach_fail3; 48884f7a9b9Shx147065 } 48984f7a9b9Shx147065 49084f7a9b9Shx147065 /* 49184f7a9b9Shx147065 * Get the hw conf, including MAC address, then init all rings. 49284f7a9b9Shx147065 */ 49384f7a9b9Shx147065 wpi_read_eeprom(sc); 49484f7a9b9Shx147065 err = wpi_ring_init(sc); 49584f7a9b9Shx147065 if (err != DDI_SUCCESS) { 49684f7a9b9Shx147065 cmn_err(CE_WARN, "wpi_attach(): " 49784f7a9b9Shx147065 "failed to allocate and initialize ring\n"); 49884f7a9b9Shx147065 goto attach_fail4; 49984f7a9b9Shx147065 } 50084f7a9b9Shx147065 50184f7a9b9Shx147065 sc->sc_hdr = (const wpi_firmware_hdr_t *)wpi_fw_bin; 50284f7a9b9Shx147065 50384f7a9b9Shx147065 /* firmware image layout: |HDR|<--TEXT-->|<--DATA-->|<--BOOT-->| */ 50484f7a9b9Shx147065 sc->sc_text = (const char *)(sc->sc_hdr + 1); 50584f7a9b9Shx147065 sc->sc_data = sc->sc_text + LE_32(sc->sc_hdr->textsz); 50684f7a9b9Shx147065 sc->sc_boot = sc->sc_data + LE_32(sc->sc_hdr->datasz); 50784f7a9b9Shx147065 err = wpi_alloc_fw_dma(sc); 50884f7a9b9Shx147065 if (err != DDI_SUCCESS) { 50984f7a9b9Shx147065 cmn_err(CE_WARN, "wpi_attach(): " 51084f7a9b9Shx147065 "failed to allocate firmware dma\n"); 51184f7a9b9Shx147065 goto attach_fail5; 51284f7a9b9Shx147065 } 51384f7a9b9Shx147065 51484f7a9b9Shx147065 /* 51584f7a9b9Shx147065 * Initialize mutexs and condvars 51684f7a9b9Shx147065 */ 51784f7a9b9Shx147065 err = ddi_get_iblock_cookie(dip, 0, &sc->sc_iblk); 51884f7a9b9Shx147065 if (err != DDI_SUCCESS) { 51984f7a9b9Shx147065 cmn_err(CE_WARN, 52084f7a9b9Shx147065 "wpi_attach(): failed to do ddi_get_iblock_cookie()\n"); 52184f7a9b9Shx147065 goto attach_fail6; 52284f7a9b9Shx147065 } 52384f7a9b9Shx147065 mutex_init(&sc->sc_glock, NULL, MUTEX_DRIVER, sc->sc_iblk); 52484f7a9b9Shx147065 mutex_init(&sc->sc_tx_lock, NULL, MUTEX_DRIVER, sc->sc_iblk); 52584f7a9b9Shx147065 cv_init(&sc->sc_fw_cv, NULL, CV_DRIVER, NULL); 52684f7a9b9Shx147065 cv_init(&sc->sc_cmd_cv, NULL, CV_DRIVER, NULL); 527626dff79Spengcheng chen - Sun Microsystems - Beijing China 52884f7a9b9Shx147065 /* 52984f7a9b9Shx147065 * initialize the mfthread 53084f7a9b9Shx147065 */ 53184f7a9b9Shx147065 mutex_init(&sc->sc_mt_lock, NULL, MUTEX_DRIVER, 53284f7a9b9Shx147065 (void *) sc->sc_iblk); 53384f7a9b9Shx147065 cv_init(&sc->sc_mt_cv, NULL, CV_DRIVER, NULL); 53484f7a9b9Shx147065 sc->sc_mf_thread = NULL; 53584f7a9b9Shx147065 sc->sc_mf_thread_switch = 0; 53684f7a9b9Shx147065 /* 53784f7a9b9Shx147065 * Initialize the wifi part, which will be used by 53884f7a9b9Shx147065 * generic layer 53984f7a9b9Shx147065 */ 54084f7a9b9Shx147065 ic = &sc->sc_ic; 54184f7a9b9Shx147065 ic->ic_phytype = IEEE80211_T_OFDM; 54284f7a9b9Shx147065 ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ 54384f7a9b9Shx147065 ic->ic_state = IEEE80211_S_INIT; 54484f7a9b9Shx147065 ic->ic_maxrssi = 70; /* experimental number */ 54584f7a9b9Shx147065 ic->ic_caps = IEEE80211_C_SHPREAMBLE | IEEE80211_C_TXPMGT | 546239e91abShx147065 IEEE80211_C_PMGT | IEEE80211_C_SHSLOT; 54784f7a9b9Shx147065 5483a1a8936Szf162725 /* 5493a1a8936Szf162725 * use software WEP and TKIP, hardware CCMP; 5503a1a8936Szf162725 */ 5513a1a8936Szf162725 ic->ic_caps |= IEEE80211_C_AES_CCM; 5523a1a8936Szf162725 ic->ic_caps |= IEEE80211_C_WPA; /* Support WPA/WPA2 */ 5533a1a8936Szf162725 55484f7a9b9Shx147065 /* set supported .11b and .11g rates */ 55584f7a9b9Shx147065 ic->ic_sup_rates[IEEE80211_MODE_11B] = wpi_rateset_11b; 55684f7a9b9Shx147065 ic->ic_sup_rates[IEEE80211_MODE_11G] = wpi_rateset_11g; 55784f7a9b9Shx147065 55884f7a9b9Shx147065 /* set supported .11b and .11g channels (1 through 14) */ 55984f7a9b9Shx147065 for (i = 1; i <= 14; i++) { 56084f7a9b9Shx147065 ic->ic_sup_channels[i].ich_freq = 56184f7a9b9Shx147065 ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); 56284f7a9b9Shx147065 ic->ic_sup_channels[i].ich_flags = 56384f7a9b9Shx147065 IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | 564626dff79Spengcheng chen - Sun Microsystems - Beijing China IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ | 565626dff79Spengcheng chen - Sun Microsystems - Beijing China IEEE80211_CHAN_PASSIVE; 56684f7a9b9Shx147065 } 56784f7a9b9Shx147065 ic->ic_ibss_chan = &ic->ic_sup_channels[0]; 56884f7a9b9Shx147065 ic->ic_xmit = wpi_send; 56984f7a9b9Shx147065 /* 57084f7a9b9Shx147065 * init Wifi layer 57184f7a9b9Shx147065 */ 57284f7a9b9Shx147065 ieee80211_attach(ic); 57384f7a9b9Shx147065 5743a1a8936Szf162725 /* register WPA door */ 5753a1a8936Szf162725 ieee80211_register_door(ic, ddi_driver_name(dip), 5763a1a8936Szf162725 ddi_get_instance(dip)); 5773a1a8936Szf162725 57884f7a9b9Shx147065 /* 57984f7a9b9Shx147065 * Override 80211 default routines 58084f7a9b9Shx147065 */ 58184f7a9b9Shx147065 sc->sc_newstate = ic->ic_newstate; 58284f7a9b9Shx147065 ic->ic_newstate = wpi_newstate; 58384f7a9b9Shx147065 ic->ic_node_alloc = wpi_node_alloc; 58484f7a9b9Shx147065 ic->ic_node_free = wpi_node_free; 5853a1a8936Szf162725 ic->ic_crypto.cs_key_set = wpi_key_set; 58684f7a9b9Shx147065 ieee80211_media_init(ic); 58784f7a9b9Shx147065 /* 58884f7a9b9Shx147065 * initialize default tx key 58984f7a9b9Shx147065 */ 59084f7a9b9Shx147065 ic->ic_def_txkey = 0; 59184f7a9b9Shx147065 59284f7a9b9Shx147065 err = ddi_add_softintr(dip, DDI_SOFTINT_LOW, 59384f7a9b9Shx147065 &sc->sc_notif_softint_id, &sc->sc_iblk, NULL, wpi_notif_softintr, 59484f7a9b9Shx147065 (caddr_t)sc); 59584f7a9b9Shx147065 if (err != DDI_SUCCESS) { 59684f7a9b9Shx147065 cmn_err(CE_WARN, 59784f7a9b9Shx147065 "wpi_attach(): failed to do ddi_add_softintr()\n"); 59884f7a9b9Shx147065 goto attach_fail7; 59984f7a9b9Shx147065 } 60084f7a9b9Shx147065 60184f7a9b9Shx147065 /* 60284f7a9b9Shx147065 * Add the interrupt handler 60384f7a9b9Shx147065 */ 60484f7a9b9Shx147065 err = ddi_add_intr(dip, 0, &sc->sc_iblk, NULL, 60584f7a9b9Shx147065 wpi_intr, (caddr_t)sc); 60684f7a9b9Shx147065 if (err != DDI_SUCCESS) { 60784f7a9b9Shx147065 cmn_err(CE_WARN, 60884f7a9b9Shx147065 "wpi_attach(): failed to do ddi_add_intr()\n"); 60984f7a9b9Shx147065 goto attach_fail8; 61084f7a9b9Shx147065 } 61184f7a9b9Shx147065 61284f7a9b9Shx147065 /* 61384f7a9b9Shx147065 * Initialize pointer to device specific functions 61484f7a9b9Shx147065 */ 61584f7a9b9Shx147065 wd.wd_secalloc = WIFI_SEC_NONE; 61684f7a9b9Shx147065 wd.wd_opmode = ic->ic_opmode; 61784f7a9b9Shx147065 IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_macaddr); 61884f7a9b9Shx147065 61984f7a9b9Shx147065 macp = mac_alloc(MAC_VERSION); 62084f7a9b9Shx147065 if (err != DDI_SUCCESS) { 62184f7a9b9Shx147065 cmn_err(CE_WARN, 62284f7a9b9Shx147065 "wpi_attach(): failed to do mac_alloc()\n"); 62384f7a9b9Shx147065 goto attach_fail9; 62484f7a9b9Shx147065 } 62584f7a9b9Shx147065 62684f7a9b9Shx147065 macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI; 62784f7a9b9Shx147065 macp->m_driver = sc; 62884f7a9b9Shx147065 macp->m_dip = dip; 62984f7a9b9Shx147065 macp->m_src_addr = ic->ic_macaddr; 63084f7a9b9Shx147065 macp->m_callbacks = &wpi_m_callbacks; 63184f7a9b9Shx147065 macp->m_min_sdu = 0; 63284f7a9b9Shx147065 macp->m_max_sdu = IEEE80211_MTU; 63384f7a9b9Shx147065 macp->m_pdata = &wd; 63484f7a9b9Shx147065 macp->m_pdata_size = sizeof (wd); 63584f7a9b9Shx147065 63684f7a9b9Shx147065 /* 63784f7a9b9Shx147065 * Register the macp to mac 63884f7a9b9Shx147065 */ 63984f7a9b9Shx147065 err = mac_register(macp, &ic->ic_mach); 64084f7a9b9Shx147065 mac_free(macp); 64184f7a9b9Shx147065 if (err != DDI_SUCCESS) { 64284f7a9b9Shx147065 cmn_err(CE_WARN, 64384f7a9b9Shx147065 "wpi_attach(): failed to do mac_register()\n"); 64484f7a9b9Shx147065 goto attach_fail9; 64584f7a9b9Shx147065 } 64684f7a9b9Shx147065 64784f7a9b9Shx147065 /* 64884f7a9b9Shx147065 * Create minor node of type DDI_NT_NET_WIFI 64984f7a9b9Shx147065 */ 65084f7a9b9Shx147065 (void) snprintf(strbuf, sizeof (strbuf), "wpi%d", instance); 65184f7a9b9Shx147065 err = ddi_create_minor_node(dip, strbuf, S_IFCHR, 65284f7a9b9Shx147065 instance + 1, DDI_NT_NET_WIFI, 0); 65384f7a9b9Shx147065 if (err != DDI_SUCCESS) 65484f7a9b9Shx147065 cmn_err(CE_WARN, 65584f7a9b9Shx147065 "wpi_attach(): failed to do ddi_create_minor_node()\n"); 65684f7a9b9Shx147065 65784f7a9b9Shx147065 /* 65884f7a9b9Shx147065 * Notify link is down now 65984f7a9b9Shx147065 */ 66084f7a9b9Shx147065 mac_link_update(ic->ic_mach, LINK_STATE_DOWN); 66184f7a9b9Shx147065 66284f7a9b9Shx147065 /* 66384f7a9b9Shx147065 * create the mf thread to handle the link status, 66484f7a9b9Shx147065 * recovery fatal error, etc. 66584f7a9b9Shx147065 */ 66684f7a9b9Shx147065 66784f7a9b9Shx147065 sc->sc_mf_thread_switch = 1; 66884f7a9b9Shx147065 if (sc->sc_mf_thread == NULL) 66984f7a9b9Shx147065 sc->sc_mf_thread = thread_create((caddr_t)NULL, 0, 67084f7a9b9Shx147065 wpi_thread, sc, 0, &p0, TS_RUN, minclsyspri); 67184f7a9b9Shx147065 67284f7a9b9Shx147065 sc->sc_flags |= WPI_F_ATTACHED; 67384f7a9b9Shx147065 67484f7a9b9Shx147065 return (DDI_SUCCESS); 67584f7a9b9Shx147065 attach_fail9: 67684f7a9b9Shx147065 ddi_remove_intr(dip, 0, sc->sc_iblk); 67784f7a9b9Shx147065 attach_fail8: 67884f7a9b9Shx147065 ddi_remove_softintr(sc->sc_notif_softint_id); 67984f7a9b9Shx147065 sc->sc_notif_softint_id = NULL; 68084f7a9b9Shx147065 attach_fail7: 68184f7a9b9Shx147065 ieee80211_detach(ic); 68284f7a9b9Shx147065 wpi_destroy_locks(sc); 68384f7a9b9Shx147065 attach_fail6: 68484f7a9b9Shx147065 wpi_free_fw_dma(sc); 68584f7a9b9Shx147065 attach_fail5: 68684f7a9b9Shx147065 wpi_ring_free(sc); 68784f7a9b9Shx147065 attach_fail4: 68884f7a9b9Shx147065 wpi_free_shared(sc); 68984f7a9b9Shx147065 attach_fail3: 69084f7a9b9Shx147065 ddi_regs_map_free(&sc->sc_handle); 69184f7a9b9Shx147065 attach_fail2: 69284f7a9b9Shx147065 ddi_soft_state_free(wpi_soft_state_p, instance); 69384f7a9b9Shx147065 attach_fail1: 69484f7a9b9Shx147065 return (err); 69584f7a9b9Shx147065 } 69684f7a9b9Shx147065 69784f7a9b9Shx147065 int 69884f7a9b9Shx147065 wpi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 69984f7a9b9Shx147065 { 70084f7a9b9Shx147065 wpi_sc_t *sc; 70184f7a9b9Shx147065 int err; 70284f7a9b9Shx147065 70384f7a9b9Shx147065 sc = ddi_get_soft_state(wpi_soft_state_p, ddi_get_instance(dip)); 70484f7a9b9Shx147065 ASSERT(sc != NULL); 70584f7a9b9Shx147065 70619b23afcShx147065 switch (cmd) { 70719b23afcShx147065 case DDI_DETACH: 70819b23afcShx147065 break; 70919b23afcShx147065 case DDI_SUSPEND: 71019b23afcShx147065 mutex_enter(&sc->sc_glock); 71119b23afcShx147065 sc->sc_flags |= WPI_F_SUSPEND; 71219b23afcShx147065 mutex_exit(&sc->sc_glock); 713d40f4da4Spengcheng chen - Sun Microsystems - Beijing China 714d40f4da4Spengcheng chen - Sun Microsystems - Beijing China if (sc->sc_flags & WPI_F_RUNNING) { 715d40f4da4Spengcheng chen - Sun Microsystems - Beijing China wpi_stop(sc); 716d40f4da4Spengcheng chen - Sun Microsystems - Beijing China } 717d40f4da4Spengcheng chen - Sun Microsystems - Beijing China 71819b23afcShx147065 WPI_DBG((WPI_DEBUG_RESUME, "wpi: suspend \n")); 71919b23afcShx147065 return (DDI_SUCCESS); 72019b23afcShx147065 default: 72184f7a9b9Shx147065 return (DDI_FAILURE); 72219b23afcShx147065 } 72384f7a9b9Shx147065 if (!(sc->sc_flags & WPI_F_ATTACHED)) 72484f7a9b9Shx147065 return (DDI_FAILURE); 72584f7a9b9Shx147065 72642516a0cSxinghua wen - Sun Microsystems - Beijing China err = mac_disable(sc->sc_ic.ic_mach); 72742516a0cSxinghua wen - Sun Microsystems - Beijing China if (err != DDI_SUCCESS) 72842516a0cSxinghua wen - Sun Microsystems - Beijing China return (err); 72942516a0cSxinghua wen - Sun Microsystems - Beijing China 73084f7a9b9Shx147065 /* 73184f7a9b9Shx147065 * Destroy the mf_thread 73284f7a9b9Shx147065 */ 73384f7a9b9Shx147065 mutex_enter(&sc->sc_mt_lock); 73484f7a9b9Shx147065 sc->sc_mf_thread_switch = 0; 73584f7a9b9Shx147065 while (sc->sc_mf_thread != NULL) { 73684f7a9b9Shx147065 if (cv_wait_sig(&sc->sc_mt_cv, &sc->sc_mt_lock) == 0) 73784f7a9b9Shx147065 break; 73884f7a9b9Shx147065 } 73984f7a9b9Shx147065 mutex_exit(&sc->sc_mt_lock); 74084f7a9b9Shx147065 74184f7a9b9Shx147065 wpi_stop(sc); 74284f7a9b9Shx147065 74384f7a9b9Shx147065 /* 74484f7a9b9Shx147065 * Unregiste from the MAC layer subsystem 74584f7a9b9Shx147065 */ 74642516a0cSxinghua wen - Sun Microsystems - Beijing China (void) mac_unregister(sc->sc_ic.ic_mach); 74784f7a9b9Shx147065 74884f7a9b9Shx147065 mutex_enter(&sc->sc_glock); 74984f7a9b9Shx147065 wpi_free_fw_dma(sc); 75084f7a9b9Shx147065 wpi_ring_free(sc); 75184f7a9b9Shx147065 wpi_free_shared(sc); 75284f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 75384f7a9b9Shx147065 75484f7a9b9Shx147065 ddi_remove_intr(dip, 0, sc->sc_iblk); 75584f7a9b9Shx147065 ddi_remove_softintr(sc->sc_notif_softint_id); 75684f7a9b9Shx147065 sc->sc_notif_softint_id = NULL; 75784f7a9b9Shx147065 75884f7a9b9Shx147065 /* 75984f7a9b9Shx147065 * detach ieee80211 76084f7a9b9Shx147065 */ 76184f7a9b9Shx147065 ieee80211_detach(&sc->sc_ic); 76284f7a9b9Shx147065 76384f7a9b9Shx147065 wpi_destroy_locks(sc); 76484f7a9b9Shx147065 76584f7a9b9Shx147065 ddi_regs_map_free(&sc->sc_handle); 76684f7a9b9Shx147065 ddi_remove_minor_node(dip, NULL); 76784f7a9b9Shx147065 ddi_soft_state_free(wpi_soft_state_p, ddi_get_instance(dip)); 76884f7a9b9Shx147065 76984f7a9b9Shx147065 return (DDI_SUCCESS); 77084f7a9b9Shx147065 } 77184f7a9b9Shx147065 77284f7a9b9Shx147065 static void 77384f7a9b9Shx147065 wpi_destroy_locks(wpi_sc_t *sc) 77484f7a9b9Shx147065 { 77584f7a9b9Shx147065 cv_destroy(&sc->sc_mt_cv); 77684f7a9b9Shx147065 mutex_destroy(&sc->sc_mt_lock); 77784f7a9b9Shx147065 cv_destroy(&sc->sc_cmd_cv); 77884f7a9b9Shx147065 cv_destroy(&sc->sc_fw_cv); 77984f7a9b9Shx147065 mutex_destroy(&sc->sc_tx_lock); 78084f7a9b9Shx147065 mutex_destroy(&sc->sc_glock); 78184f7a9b9Shx147065 } 78284f7a9b9Shx147065 78384f7a9b9Shx147065 /* 78484f7a9b9Shx147065 * Allocate an area of memory and a DMA handle for accessing it 78584f7a9b9Shx147065 */ 78684f7a9b9Shx147065 static int 78784f7a9b9Shx147065 wpi_alloc_dma_mem(wpi_sc_t *sc, size_t memsize, ddi_dma_attr_t *dma_attr_p, 78884f7a9b9Shx147065 ddi_device_acc_attr_t *acc_attr_p, uint_t dma_flags, wpi_dma_t *dma_p) 78984f7a9b9Shx147065 { 79084f7a9b9Shx147065 caddr_t vaddr; 79184f7a9b9Shx147065 int err; 79284f7a9b9Shx147065 79384f7a9b9Shx147065 /* 79484f7a9b9Shx147065 * Allocate handle 79584f7a9b9Shx147065 */ 79684f7a9b9Shx147065 err = ddi_dma_alloc_handle(sc->sc_dip, dma_attr_p, 79784f7a9b9Shx147065 DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl); 79884f7a9b9Shx147065 if (err != DDI_SUCCESS) { 79984f7a9b9Shx147065 dma_p->dma_hdl = NULL; 80084f7a9b9Shx147065 return (DDI_FAILURE); 80184f7a9b9Shx147065 } 80284f7a9b9Shx147065 80384f7a9b9Shx147065 /* 80484f7a9b9Shx147065 * Allocate memory 80584f7a9b9Shx147065 */ 80684f7a9b9Shx147065 err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize, acc_attr_p, 80784f7a9b9Shx147065 dma_flags & (DDI_DMA_CONSISTENT | DDI_DMA_STREAMING), 80884f7a9b9Shx147065 DDI_DMA_SLEEP, NULL, &vaddr, &dma_p->alength, &dma_p->acc_hdl); 80984f7a9b9Shx147065 if (err != DDI_SUCCESS) { 81084f7a9b9Shx147065 ddi_dma_free_handle(&dma_p->dma_hdl); 81184f7a9b9Shx147065 dma_p->dma_hdl = NULL; 81284f7a9b9Shx147065 dma_p->acc_hdl = NULL; 81384f7a9b9Shx147065 return (DDI_FAILURE); 81484f7a9b9Shx147065 } 81584f7a9b9Shx147065 81684f7a9b9Shx147065 /* 81784f7a9b9Shx147065 * Bind the two together 81884f7a9b9Shx147065 */ 81984f7a9b9Shx147065 dma_p->mem_va = vaddr; 82084f7a9b9Shx147065 err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL, 82184f7a9b9Shx147065 vaddr, dma_p->alength, dma_flags, DDI_DMA_SLEEP, NULL, 82284f7a9b9Shx147065 &dma_p->cookie, &dma_p->ncookies); 82384f7a9b9Shx147065 if (err != DDI_DMA_MAPPED) { 82484f7a9b9Shx147065 ddi_dma_mem_free(&dma_p->acc_hdl); 82584f7a9b9Shx147065 ddi_dma_free_handle(&dma_p->dma_hdl); 82684f7a9b9Shx147065 dma_p->acc_hdl = NULL; 82784f7a9b9Shx147065 dma_p->dma_hdl = NULL; 82884f7a9b9Shx147065 return (DDI_FAILURE); 82984f7a9b9Shx147065 } 83084f7a9b9Shx147065 83184f7a9b9Shx147065 dma_p->nslots = ~0U; 83284f7a9b9Shx147065 dma_p->size = ~0U; 83384f7a9b9Shx147065 dma_p->token = ~0U; 83484f7a9b9Shx147065 dma_p->offset = 0; 83584f7a9b9Shx147065 return (DDI_SUCCESS); 83684f7a9b9Shx147065 } 83784f7a9b9Shx147065 83884f7a9b9Shx147065 /* 83984f7a9b9Shx147065 * Free one allocated area of DMAable memory 84084f7a9b9Shx147065 */ 84184f7a9b9Shx147065 static void 84284f7a9b9Shx147065 wpi_free_dma_mem(wpi_dma_t *dma_p) 84384f7a9b9Shx147065 { 84484f7a9b9Shx147065 if (dma_p->dma_hdl != NULL) { 84584f7a9b9Shx147065 if (dma_p->ncookies) { 84684f7a9b9Shx147065 (void) ddi_dma_unbind_handle(dma_p->dma_hdl); 84784f7a9b9Shx147065 dma_p->ncookies = 0; 84884f7a9b9Shx147065 } 84984f7a9b9Shx147065 ddi_dma_free_handle(&dma_p->dma_hdl); 85084f7a9b9Shx147065 dma_p->dma_hdl = NULL; 85184f7a9b9Shx147065 } 85284f7a9b9Shx147065 85384f7a9b9Shx147065 if (dma_p->acc_hdl != NULL) { 85484f7a9b9Shx147065 ddi_dma_mem_free(&dma_p->acc_hdl); 85584f7a9b9Shx147065 dma_p->acc_hdl = NULL; 85684f7a9b9Shx147065 } 85784f7a9b9Shx147065 } 85884f7a9b9Shx147065 85984f7a9b9Shx147065 /* 86084f7a9b9Shx147065 * Allocate an area of dma memory for firmware load. 86184f7a9b9Shx147065 * Idealy, this allocation should be a one time action, that is, 86284f7a9b9Shx147065 * the memory will be freed after the firmware is uploaded to the 86384f7a9b9Shx147065 * card. but since a recovery mechanism for the fatal firmware need 86484f7a9b9Shx147065 * reload the firmware, and re-allocate dma at run time may be failed, 86584f7a9b9Shx147065 * so we allocate it at attach and keep it in the whole lifecycle of 86684f7a9b9Shx147065 * the driver. 86784f7a9b9Shx147065 */ 86884f7a9b9Shx147065 static int 86984f7a9b9Shx147065 wpi_alloc_fw_dma(wpi_sc_t *sc) 87084f7a9b9Shx147065 { 87184f7a9b9Shx147065 int i, err = DDI_SUCCESS; 87284f7a9b9Shx147065 wpi_dma_t *dma_p; 87384f7a9b9Shx147065 87484f7a9b9Shx147065 err = wpi_alloc_dma_mem(sc, LE_32(sc->sc_hdr->textsz), 87584f7a9b9Shx147065 &fw_buffer_dma_attr, &wpi_dma_accattr, 87684f7a9b9Shx147065 DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 87784f7a9b9Shx147065 &sc->sc_dma_fw_text); 87884f7a9b9Shx147065 dma_p = &sc->sc_dma_fw_text; 87984f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_DMA, "ncookies:%d addr1:%x size1:%x\n", 88084f7a9b9Shx147065 dma_p->ncookies, dma_p->cookie.dmac_address, 88184f7a9b9Shx147065 dma_p->cookie.dmac_size)); 88284f7a9b9Shx147065 if (err != DDI_SUCCESS) { 88384f7a9b9Shx147065 cmn_err(CE_WARN, "wpi_alloc_fw_dma(): failed to alloc" 88484f7a9b9Shx147065 "text dma memory"); 88584f7a9b9Shx147065 goto fail; 88684f7a9b9Shx147065 } 88784f7a9b9Shx147065 for (i = 0; i < dma_p->ncookies; i++) { 88884f7a9b9Shx147065 sc->sc_fw_text_cookie[i] = dma_p->cookie; 88984f7a9b9Shx147065 ddi_dma_nextcookie(dma_p->dma_hdl, &dma_p->cookie); 89084f7a9b9Shx147065 } 89184f7a9b9Shx147065 err = wpi_alloc_dma_mem(sc, LE_32(sc->sc_hdr->datasz), 89284f7a9b9Shx147065 &fw_buffer_dma_attr, &wpi_dma_accattr, 89384f7a9b9Shx147065 DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 89484f7a9b9Shx147065 &sc->sc_dma_fw_data); 89584f7a9b9Shx147065 dma_p = &sc->sc_dma_fw_data; 89684f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_DMA, "ncookies:%d addr1:%x size1:%x\n", 89784f7a9b9Shx147065 dma_p->ncookies, dma_p->cookie.dmac_address, 89884f7a9b9Shx147065 dma_p->cookie.dmac_size)); 89984f7a9b9Shx147065 if (err != DDI_SUCCESS) { 90084f7a9b9Shx147065 cmn_err(CE_WARN, "wpi_alloc_fw_dma(): failed to alloc" 90184f7a9b9Shx147065 "data dma memory"); 90284f7a9b9Shx147065 goto fail; 90384f7a9b9Shx147065 } 90484f7a9b9Shx147065 for (i = 0; i < dma_p->ncookies; i++) { 90584f7a9b9Shx147065 sc->sc_fw_data_cookie[i] = dma_p->cookie; 90684f7a9b9Shx147065 ddi_dma_nextcookie(dma_p->dma_hdl, &dma_p->cookie); 90784f7a9b9Shx147065 } 90884f7a9b9Shx147065 fail: 90984f7a9b9Shx147065 return (err); 91084f7a9b9Shx147065 } 91184f7a9b9Shx147065 91284f7a9b9Shx147065 static void 91384f7a9b9Shx147065 wpi_free_fw_dma(wpi_sc_t *sc) 91484f7a9b9Shx147065 { 91584f7a9b9Shx147065 wpi_free_dma_mem(&sc->sc_dma_fw_text); 91684f7a9b9Shx147065 wpi_free_dma_mem(&sc->sc_dma_fw_data); 91784f7a9b9Shx147065 } 91884f7a9b9Shx147065 91984f7a9b9Shx147065 /* 92084f7a9b9Shx147065 * Allocate a shared page between host and NIC. 92184f7a9b9Shx147065 */ 92284f7a9b9Shx147065 static int 92384f7a9b9Shx147065 wpi_alloc_shared(wpi_sc_t *sc) 92484f7a9b9Shx147065 { 92584f7a9b9Shx147065 int err = DDI_SUCCESS; 92684f7a9b9Shx147065 92784f7a9b9Shx147065 /* must be aligned on a 4K-page boundary */ 92884f7a9b9Shx147065 err = wpi_alloc_dma_mem(sc, sizeof (wpi_shared_t), 92984f7a9b9Shx147065 &sh_dma_attr, &wpi_dma_accattr, 93084f7a9b9Shx147065 DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 93184f7a9b9Shx147065 &sc->sc_dma_sh); 93284f7a9b9Shx147065 if (err != DDI_SUCCESS) 93384f7a9b9Shx147065 goto fail; 93484f7a9b9Shx147065 sc->sc_shared = (wpi_shared_t *)sc->sc_dma_sh.mem_va; 93584f7a9b9Shx147065 return (err); 93684f7a9b9Shx147065 93784f7a9b9Shx147065 fail: 93884f7a9b9Shx147065 wpi_free_shared(sc); 93984f7a9b9Shx147065 return (err); 94084f7a9b9Shx147065 } 94184f7a9b9Shx147065 94284f7a9b9Shx147065 static void 94384f7a9b9Shx147065 wpi_free_shared(wpi_sc_t *sc) 94484f7a9b9Shx147065 { 94584f7a9b9Shx147065 wpi_free_dma_mem(&sc->sc_dma_sh); 94684f7a9b9Shx147065 } 94784f7a9b9Shx147065 94884f7a9b9Shx147065 static int 94984f7a9b9Shx147065 wpi_alloc_rx_ring(wpi_sc_t *sc) 95084f7a9b9Shx147065 { 95184f7a9b9Shx147065 wpi_rx_ring_t *ring; 95284f7a9b9Shx147065 wpi_rx_data_t *data; 95384f7a9b9Shx147065 int i, err = DDI_SUCCESS; 95484f7a9b9Shx147065 95584f7a9b9Shx147065 ring = &sc->sc_rxq; 95684f7a9b9Shx147065 ring->cur = 0; 95784f7a9b9Shx147065 95884f7a9b9Shx147065 err = wpi_alloc_dma_mem(sc, WPI_RX_RING_COUNT * sizeof (uint32_t), 95984f7a9b9Shx147065 &ring_desc_dma_attr, &wpi_dma_accattr, 96084f7a9b9Shx147065 DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 96184f7a9b9Shx147065 &ring->dma_desc); 96284f7a9b9Shx147065 if (err != DDI_SUCCESS) { 96384f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_DMA, "dma alloc rx ring desc failed\n")); 96484f7a9b9Shx147065 goto fail; 96584f7a9b9Shx147065 } 96684f7a9b9Shx147065 ring->desc = (uint32_t *)ring->dma_desc.mem_va; 96784f7a9b9Shx147065 96884f7a9b9Shx147065 /* 96984f7a9b9Shx147065 * Allocate Rx buffers. 97084f7a9b9Shx147065 */ 97184f7a9b9Shx147065 for (i = 0; i < WPI_RX_RING_COUNT; i++) { 97284f7a9b9Shx147065 data = &ring->data[i]; 97384f7a9b9Shx147065 err = wpi_alloc_dma_mem(sc, sc->sc_dmabuf_sz, 97484f7a9b9Shx147065 &rx_buffer_dma_attr, &wpi_dma_accattr, 97584f7a9b9Shx147065 DDI_DMA_READ | DDI_DMA_STREAMING, 97684f7a9b9Shx147065 &data->dma_data); 97784f7a9b9Shx147065 if (err != DDI_SUCCESS) { 97884f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_DMA, "dma alloc rx ring buf[%d] " 97984f7a9b9Shx147065 "failed\n", i)); 98084f7a9b9Shx147065 goto fail; 98184f7a9b9Shx147065 } 98284f7a9b9Shx147065 98384f7a9b9Shx147065 ring->desc[i] = LE_32(data->dma_data.cookie.dmac_address); 98484f7a9b9Shx147065 } 98584f7a9b9Shx147065 98684f7a9b9Shx147065 WPI_DMA_SYNC(ring->dma_desc, DDI_DMA_SYNC_FORDEV); 98784f7a9b9Shx147065 98884f7a9b9Shx147065 return (err); 98984f7a9b9Shx147065 99084f7a9b9Shx147065 fail: 99184f7a9b9Shx147065 wpi_free_rx_ring(sc); 99284f7a9b9Shx147065 return (err); 99384f7a9b9Shx147065 } 99484f7a9b9Shx147065 99584f7a9b9Shx147065 static void 99684f7a9b9Shx147065 wpi_reset_rx_ring(wpi_sc_t *sc) 99784f7a9b9Shx147065 { 99884f7a9b9Shx147065 int ntries; 99984f7a9b9Shx147065 100084f7a9b9Shx147065 wpi_mem_lock(sc); 100184f7a9b9Shx147065 100284f7a9b9Shx147065 WPI_WRITE(sc, WPI_RX_CONFIG, 0); 100384f7a9b9Shx147065 for (ntries = 0; ntries < 2000; ntries++) { 100484f7a9b9Shx147065 if (WPI_READ(sc, WPI_RX_STATUS) & WPI_RX_IDLE) 100584f7a9b9Shx147065 break; 100684f7a9b9Shx147065 DELAY(1000); 100784f7a9b9Shx147065 } 100884f7a9b9Shx147065 if (ntries == 2000) 100984f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_DMA, "timeout resetting Rx ring\n")); 1010626dff79Spengcheng chen - Sun Microsystems - Beijing China 101184f7a9b9Shx147065 wpi_mem_unlock(sc); 101284f7a9b9Shx147065 101384f7a9b9Shx147065 sc->sc_rxq.cur = 0; 101484f7a9b9Shx147065 } 101584f7a9b9Shx147065 101684f7a9b9Shx147065 static void 101784f7a9b9Shx147065 wpi_free_rx_ring(wpi_sc_t *sc) 101884f7a9b9Shx147065 { 101984f7a9b9Shx147065 int i; 102084f7a9b9Shx147065 102184f7a9b9Shx147065 for (i = 0; i < WPI_RX_RING_COUNT; i++) { 102284f7a9b9Shx147065 if (sc->sc_rxq.data[i].dma_data.dma_hdl) 102384f7a9b9Shx147065 WPI_DMA_SYNC(sc->sc_rxq.data[i].dma_data, 102484f7a9b9Shx147065 DDI_DMA_SYNC_FORCPU); 102584f7a9b9Shx147065 wpi_free_dma_mem(&sc->sc_rxq.data[i].dma_data); 102684f7a9b9Shx147065 } 102784f7a9b9Shx147065 102884f7a9b9Shx147065 if (sc->sc_rxq.dma_desc.dma_hdl) 102984f7a9b9Shx147065 WPI_DMA_SYNC(sc->sc_rxq.dma_desc, DDI_DMA_SYNC_FORDEV); 103084f7a9b9Shx147065 wpi_free_dma_mem(&sc->sc_rxq.dma_desc); 103184f7a9b9Shx147065 } 103284f7a9b9Shx147065 103384f7a9b9Shx147065 static int 103484f7a9b9Shx147065 wpi_alloc_tx_ring(wpi_sc_t *sc, wpi_tx_ring_t *ring, int count, int qid) 103584f7a9b9Shx147065 { 103684f7a9b9Shx147065 wpi_tx_data_t *data; 103784f7a9b9Shx147065 wpi_tx_desc_t *desc_h; 103884f7a9b9Shx147065 uint32_t paddr_desc_h; 103984f7a9b9Shx147065 wpi_tx_cmd_t *cmd_h; 104084f7a9b9Shx147065 uint32_t paddr_cmd_h; 104184f7a9b9Shx147065 int i, err = DDI_SUCCESS; 104284f7a9b9Shx147065 104384f7a9b9Shx147065 ring->qid = qid; 104484f7a9b9Shx147065 ring->count = count; 104584f7a9b9Shx147065 ring->queued = 0; 104684f7a9b9Shx147065 ring->cur = 0; 104784f7a9b9Shx147065 104884f7a9b9Shx147065 err = wpi_alloc_dma_mem(sc, count * sizeof (wpi_tx_desc_t), 104984f7a9b9Shx147065 &ring_desc_dma_attr, &wpi_dma_accattr, 105084f7a9b9Shx147065 DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 105184f7a9b9Shx147065 &ring->dma_desc); 105284f7a9b9Shx147065 if (err != DDI_SUCCESS) { 105384f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_DMA, "dma alloc tx ring desc[%d] failed\n", 105484f7a9b9Shx147065 qid)); 105584f7a9b9Shx147065 goto fail; 105684f7a9b9Shx147065 } 105784f7a9b9Shx147065 105884f7a9b9Shx147065 /* update shared page with ring's base address */ 105984f7a9b9Shx147065 sc->sc_shared->txbase[qid] = ring->dma_desc.cookie.dmac_address; 106084f7a9b9Shx147065 106184f7a9b9Shx147065 desc_h = (wpi_tx_desc_t *)ring->dma_desc.mem_va; 106284f7a9b9Shx147065 paddr_desc_h = ring->dma_desc.cookie.dmac_address; 106384f7a9b9Shx147065 106484f7a9b9Shx147065 err = wpi_alloc_dma_mem(sc, count * sizeof (wpi_tx_cmd_t), 106584f7a9b9Shx147065 &tx_cmd_dma_attr, &wpi_dma_accattr, 106684f7a9b9Shx147065 DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 106784f7a9b9Shx147065 &ring->dma_cmd); 106884f7a9b9Shx147065 if (err != DDI_SUCCESS) { 106984f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_DMA, "dma alloc tx ring cmd[%d] failed\n", 107084f7a9b9Shx147065 qid)); 107184f7a9b9Shx147065 goto fail; 107284f7a9b9Shx147065 } 107384f7a9b9Shx147065 107484f7a9b9Shx147065 cmd_h = (wpi_tx_cmd_t *)ring->dma_cmd.mem_va; 107584f7a9b9Shx147065 paddr_cmd_h = ring->dma_cmd.cookie.dmac_address; 107684f7a9b9Shx147065 107784f7a9b9Shx147065 /* 107884f7a9b9Shx147065 * Allocate Tx buffers. 107984f7a9b9Shx147065 */ 108084f7a9b9Shx147065 ring->data = kmem_zalloc(sizeof (wpi_tx_data_t) * count, KM_NOSLEEP); 108184f7a9b9Shx147065 if (ring->data == NULL) { 108284f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_DMA, "could not allocate tx data slots\n")); 108384f7a9b9Shx147065 goto fail; 108484f7a9b9Shx147065 } 108584f7a9b9Shx147065 108684f7a9b9Shx147065 for (i = 0; i < count; i++) { 108784f7a9b9Shx147065 data = &ring->data[i]; 108884f7a9b9Shx147065 err = wpi_alloc_dma_mem(sc, sc->sc_dmabuf_sz, 108984f7a9b9Shx147065 &tx_buffer_dma_attr, &wpi_dma_accattr, 109084f7a9b9Shx147065 DDI_DMA_WRITE | DDI_DMA_STREAMING, 109184f7a9b9Shx147065 &data->dma_data); 109284f7a9b9Shx147065 if (err != DDI_SUCCESS) { 109384f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_DMA, "dma alloc tx ring buf[%d] " 109484f7a9b9Shx147065 "failed\n", i)); 109584f7a9b9Shx147065 goto fail; 109684f7a9b9Shx147065 } 109784f7a9b9Shx147065 109884f7a9b9Shx147065 data->desc = desc_h + i; 109984f7a9b9Shx147065 data->paddr_desc = paddr_desc_h + 110022eb7cb5Sgd78059 ((uintptr_t)data->desc - (uintptr_t)desc_h); 110184f7a9b9Shx147065 data->cmd = cmd_h + i; 110284f7a9b9Shx147065 data->paddr_cmd = paddr_cmd_h + 110322eb7cb5Sgd78059 ((uintptr_t)data->cmd - (uintptr_t)cmd_h); 110484f7a9b9Shx147065 } 110584f7a9b9Shx147065 110684f7a9b9Shx147065 return (err); 110784f7a9b9Shx147065 110884f7a9b9Shx147065 fail: 110984f7a9b9Shx147065 wpi_free_tx_ring(sc, ring); 111084f7a9b9Shx147065 return (err); 111184f7a9b9Shx147065 } 111284f7a9b9Shx147065 111384f7a9b9Shx147065 static void 111484f7a9b9Shx147065 wpi_reset_tx_ring(wpi_sc_t *sc, wpi_tx_ring_t *ring) 111584f7a9b9Shx147065 { 111684f7a9b9Shx147065 wpi_tx_data_t *data; 111784f7a9b9Shx147065 int i, ntries; 111884f7a9b9Shx147065 111984f7a9b9Shx147065 wpi_mem_lock(sc); 112084f7a9b9Shx147065 112184f7a9b9Shx147065 WPI_WRITE(sc, WPI_TX_CONFIG(ring->qid), 0); 112284f7a9b9Shx147065 for (ntries = 0; ntries < 100; ntries++) { 112384f7a9b9Shx147065 if (WPI_READ(sc, WPI_TX_STATUS) & WPI_TX_IDLE(ring->qid)) 112484f7a9b9Shx147065 break; 112584f7a9b9Shx147065 DELAY(10); 112684f7a9b9Shx147065 } 112784f7a9b9Shx147065 #ifdef DEBUG 112884f7a9b9Shx147065 if (ntries == 100 && wpi_dbg_flags > 0) { 112984f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_DMA, "timeout resetting Tx ring %d\n", 113084f7a9b9Shx147065 ring->qid)); 113184f7a9b9Shx147065 } 113284f7a9b9Shx147065 #endif 113384f7a9b9Shx147065 wpi_mem_unlock(sc); 113484f7a9b9Shx147065 1135faceed93Sfei feng - Sun Microsystems - Beijing China if (!(sc->sc_flags & WPI_F_QUIESCED)) { 113684f7a9b9Shx147065 for (i = 0; i < ring->count; i++) { 113784f7a9b9Shx147065 data = &ring->data[i]; 113884f7a9b9Shx147065 WPI_DMA_SYNC(data->dma_data, DDI_DMA_SYNC_FORDEV); 113984f7a9b9Shx147065 } 1140faceed93Sfei feng - Sun Microsystems - Beijing China } 114184f7a9b9Shx147065 114284f7a9b9Shx147065 ring->queued = 0; 114384f7a9b9Shx147065 ring->cur = 0; 114484f7a9b9Shx147065 } 114584f7a9b9Shx147065 114684f7a9b9Shx147065 /*ARGSUSED*/ 114784f7a9b9Shx147065 static void 114884f7a9b9Shx147065 wpi_free_tx_ring(wpi_sc_t *sc, wpi_tx_ring_t *ring) 114984f7a9b9Shx147065 { 115084f7a9b9Shx147065 int i; 115184f7a9b9Shx147065 115284f7a9b9Shx147065 if (ring->dma_desc.dma_hdl != NULL) 115384f7a9b9Shx147065 WPI_DMA_SYNC(ring->dma_desc, DDI_DMA_SYNC_FORDEV); 115484f7a9b9Shx147065 wpi_free_dma_mem(&ring->dma_desc); 115584f7a9b9Shx147065 115684f7a9b9Shx147065 if (ring->dma_cmd.dma_hdl != NULL) 115784f7a9b9Shx147065 WPI_DMA_SYNC(ring->dma_cmd, DDI_DMA_SYNC_FORDEV); 115884f7a9b9Shx147065 wpi_free_dma_mem(&ring->dma_cmd); 115984f7a9b9Shx147065 116084f7a9b9Shx147065 if (ring->data != NULL) { 116184f7a9b9Shx147065 for (i = 0; i < ring->count; i++) { 116284f7a9b9Shx147065 if (ring->data[i].dma_data.dma_hdl) 116384f7a9b9Shx147065 WPI_DMA_SYNC(ring->data[i].dma_data, 116484f7a9b9Shx147065 DDI_DMA_SYNC_FORDEV); 116584f7a9b9Shx147065 wpi_free_dma_mem(&ring->data[i].dma_data); 116684f7a9b9Shx147065 } 116784f7a9b9Shx147065 kmem_free(ring->data, ring->count * sizeof (wpi_tx_data_t)); 1168626dff79Spengcheng chen - Sun Microsystems - Beijing China ring->data = NULL; 116984f7a9b9Shx147065 } 117084f7a9b9Shx147065 } 117184f7a9b9Shx147065 117284f7a9b9Shx147065 static int 117384f7a9b9Shx147065 wpi_ring_init(wpi_sc_t *sc) 117484f7a9b9Shx147065 { 117584f7a9b9Shx147065 int i, err = DDI_SUCCESS; 117684f7a9b9Shx147065 117784f7a9b9Shx147065 for (i = 0; i < 4; i++) { 117884f7a9b9Shx147065 err = wpi_alloc_tx_ring(sc, &sc->sc_txq[i], WPI_TX_RING_COUNT, 117984f7a9b9Shx147065 i); 118084f7a9b9Shx147065 if (err != DDI_SUCCESS) 118184f7a9b9Shx147065 goto fail; 118284f7a9b9Shx147065 } 118384f7a9b9Shx147065 err = wpi_alloc_tx_ring(sc, &sc->sc_cmdq, WPI_CMD_RING_COUNT, 4); 118484f7a9b9Shx147065 if (err != DDI_SUCCESS) 118584f7a9b9Shx147065 goto fail; 118684f7a9b9Shx147065 err = wpi_alloc_tx_ring(sc, &sc->sc_svcq, WPI_SVC_RING_COUNT, 5); 118784f7a9b9Shx147065 if (err != DDI_SUCCESS) 118884f7a9b9Shx147065 goto fail; 118984f7a9b9Shx147065 err = wpi_alloc_rx_ring(sc); 119084f7a9b9Shx147065 if (err != DDI_SUCCESS) 119184f7a9b9Shx147065 goto fail; 119284f7a9b9Shx147065 return (err); 119384f7a9b9Shx147065 119484f7a9b9Shx147065 fail: 119584f7a9b9Shx147065 return (err); 119684f7a9b9Shx147065 } 119784f7a9b9Shx147065 119884f7a9b9Shx147065 static void 119984f7a9b9Shx147065 wpi_ring_free(wpi_sc_t *sc) 120084f7a9b9Shx147065 { 120184f7a9b9Shx147065 int i = 4; 120284f7a9b9Shx147065 120384f7a9b9Shx147065 wpi_free_rx_ring(sc); 120484f7a9b9Shx147065 wpi_free_tx_ring(sc, &sc->sc_svcq); 120584f7a9b9Shx147065 wpi_free_tx_ring(sc, &sc->sc_cmdq); 120684f7a9b9Shx147065 while (--i >= 0) { 120784f7a9b9Shx147065 wpi_free_tx_ring(sc, &sc->sc_txq[i]); 120884f7a9b9Shx147065 } 120984f7a9b9Shx147065 } 121084f7a9b9Shx147065 121184f7a9b9Shx147065 /* ARGSUSED */ 121284f7a9b9Shx147065 static ieee80211_node_t * 121384f7a9b9Shx147065 wpi_node_alloc(ieee80211com_t *ic) 121484f7a9b9Shx147065 { 121584f7a9b9Shx147065 wpi_amrr_t *amrr; 121684f7a9b9Shx147065 121784f7a9b9Shx147065 amrr = kmem_zalloc(sizeof (wpi_amrr_t), KM_SLEEP); 121884f7a9b9Shx147065 if (amrr != NULL) 121984f7a9b9Shx147065 wpi_amrr_init(amrr); 122084f7a9b9Shx147065 return (&amrr->in); 122184f7a9b9Shx147065 } 122284f7a9b9Shx147065 122384f7a9b9Shx147065 static void 122484f7a9b9Shx147065 wpi_node_free(ieee80211_node_t *in) 122584f7a9b9Shx147065 { 122684f7a9b9Shx147065 ieee80211com_t *ic = in->in_ic; 122784f7a9b9Shx147065 122884f7a9b9Shx147065 ic->ic_node_cleanup(in); 12293a1a8936Szf162725 if (in->in_wpa_ie != NULL) 12303a1a8936Szf162725 ieee80211_free(in->in_wpa_ie); 123184f7a9b9Shx147065 kmem_free(in, sizeof (wpi_amrr_t)); 123284f7a9b9Shx147065 } 123384f7a9b9Shx147065 123484f7a9b9Shx147065 /*ARGSUSED*/ 123584f7a9b9Shx147065 static int 123684f7a9b9Shx147065 wpi_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg) 123784f7a9b9Shx147065 { 123884f7a9b9Shx147065 wpi_sc_t *sc = (wpi_sc_t *)ic; 123984f7a9b9Shx147065 ieee80211_node_t *in = ic->ic_bss; 1240bb5141e1Shx147065 enum ieee80211_state ostate; 124184f7a9b9Shx147065 int i, err = WPI_SUCCESS; 124284f7a9b9Shx147065 124384f7a9b9Shx147065 mutex_enter(&sc->sc_glock); 1244626dff79Spengcheng chen - Sun Microsystems - Beijing China ostate = ic->ic_state; 124584f7a9b9Shx147065 switch (nstate) { 124684f7a9b9Shx147065 case IEEE80211_S_SCAN: 1247bb5141e1Shx147065 switch (ostate) { 1248bb5141e1Shx147065 case IEEE80211_S_INIT: 1249626dff79Spengcheng chen - Sun Microsystems - Beijing China { 1250626dff79Spengcheng chen - Sun Microsystems - Beijing China wpi_node_t node; 1251626dff79Spengcheng chen - Sun Microsystems - Beijing China 1252626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_flags |= WPI_F_SCANNING; 1253626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_scan_next = 0; 1254626dff79Spengcheng chen - Sun Microsystems - Beijing China 125584f7a9b9Shx147065 /* make the link LED blink while we're scanning */ 125684f7a9b9Shx147065 wpi_set_led(sc, WPI_LED_LINK, 20, 2); 125784f7a9b9Shx147065 1258626dff79Spengcheng chen - Sun Microsystems - Beijing China /* 1259626dff79Spengcheng chen - Sun Microsystems - Beijing China * clear association to receive beacons from all 1260626dff79Spengcheng chen - Sun Microsystems - Beijing China * BSS'es 1261626dff79Spengcheng chen - Sun Microsystems - Beijing China */ 1262626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_config.state = 0; 1263626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_config.filter &= ~LE_32(WPI_FILTER_BSS); 1264626dff79Spengcheng chen - Sun Microsystems - Beijing China 1265626dff79Spengcheng chen - Sun Microsystems - Beijing China WPI_DBG((WPI_DEBUG_80211, "config chan %d flags %x " 1266626dff79Spengcheng chen - Sun Microsystems - Beijing China "filter %x\n", 1267626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_config.chan, sc->sc_config.flags, 1268626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_config.filter)); 1269626dff79Spengcheng chen - Sun Microsystems - Beijing China 1270626dff79Spengcheng chen - Sun Microsystems - Beijing China err = wpi_cmd(sc, WPI_CMD_CONFIGURE, &sc->sc_config, 1271626dff79Spengcheng chen - Sun Microsystems - Beijing China sizeof (wpi_config_t), 1); 1272626dff79Spengcheng chen - Sun Microsystems - Beijing China if (err != WPI_SUCCESS) { 1273626dff79Spengcheng chen - Sun Microsystems - Beijing China cmn_err(CE_WARN, 1274626dff79Spengcheng chen - Sun Microsystems - Beijing China "could not clear association\n"); 1275626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_flags &= ~WPI_F_SCANNING; 1276626dff79Spengcheng chen - Sun Microsystems - Beijing China mutex_exit(&sc->sc_glock); 1277626dff79Spengcheng chen - Sun Microsystems - Beijing China return (err); 1278626dff79Spengcheng chen - Sun Microsystems - Beijing China } 1279626dff79Spengcheng chen - Sun Microsystems - Beijing China 1280626dff79Spengcheng chen - Sun Microsystems - Beijing China /* add broadcast node to send probe request */ 1281626dff79Spengcheng chen - Sun Microsystems - Beijing China (void) memset(&node, 0, sizeof (node)); 1282626dff79Spengcheng chen - Sun Microsystems - Beijing China (void) memset(&node.bssid, 0xff, IEEE80211_ADDR_LEN); 1283626dff79Spengcheng chen - Sun Microsystems - Beijing China node.id = WPI_ID_BROADCAST; 1284626dff79Spengcheng chen - Sun Microsystems - Beijing China 1285626dff79Spengcheng chen - Sun Microsystems - Beijing China err = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, 1286626dff79Spengcheng chen - Sun Microsystems - Beijing China sizeof (node), 1); 1287626dff79Spengcheng chen - Sun Microsystems - Beijing China if (err != WPI_SUCCESS) { 1288626dff79Spengcheng chen - Sun Microsystems - Beijing China cmn_err(CE_WARN, 1289626dff79Spengcheng chen - Sun Microsystems - Beijing China "could not add broadcast node\n"); 1290626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_flags &= ~WPI_F_SCANNING; 129184f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 129284f7a9b9Shx147065 return (err); 129384f7a9b9Shx147065 } 1294bb5141e1Shx147065 break; 1295bb5141e1Shx147065 } 1296626dff79Spengcheng chen - Sun Microsystems - Beijing China case IEEE80211_S_SCAN: 129784f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 1298626dff79Spengcheng chen - Sun Microsystems - Beijing China /* step to next channel before actual FW scan */ 1299626dff79Spengcheng chen - Sun Microsystems - Beijing China err = sc->sc_newstate(ic, nstate, arg); 1300626dff79Spengcheng chen - Sun Microsystems - Beijing China mutex_enter(&sc->sc_glock); 1301626dff79Spengcheng chen - Sun Microsystems - Beijing China if ((err != 0) || ((err = wpi_scan(sc)) != 0)) { 1302626dff79Spengcheng chen - Sun Microsystems - Beijing China cmn_err(CE_WARN, 1303626dff79Spengcheng chen - Sun Microsystems - Beijing China "could not initiate scan\n"); 1304626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_flags &= ~WPI_F_SCANNING; 1305626dff79Spengcheng chen - Sun Microsystems - Beijing China ieee80211_cancel_scan(ic); 1306626dff79Spengcheng chen - Sun Microsystems - Beijing China } 1307626dff79Spengcheng chen - Sun Microsystems - Beijing China mutex_exit(&sc->sc_glock); 1308626dff79Spengcheng chen - Sun Microsystems - Beijing China return (err); 1309626dff79Spengcheng chen - Sun Microsystems - Beijing China default: 1310626dff79Spengcheng chen - Sun Microsystems - Beijing China break; 1311626dff79Spengcheng chen - Sun Microsystems - Beijing China } 1312626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_clk = 0; 1313626dff79Spengcheng chen - Sun Microsystems - Beijing China break; 131484f7a9b9Shx147065 131584f7a9b9Shx147065 case IEEE80211_S_AUTH: 1316626dff79Spengcheng chen - Sun Microsystems - Beijing China if (ostate == IEEE80211_S_SCAN) { 1317626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_flags &= ~WPI_F_SCANNING; 1318626dff79Spengcheng chen - Sun Microsystems - Beijing China } 1319626dff79Spengcheng chen - Sun Microsystems - Beijing China 132084f7a9b9Shx147065 /* reset state to handle reassociations correctly */ 132184f7a9b9Shx147065 sc->sc_config.state = 0; 132284f7a9b9Shx147065 sc->sc_config.filter &= ~LE_32(WPI_FILTER_BSS); 132384f7a9b9Shx147065 132484f7a9b9Shx147065 if ((err = wpi_auth(sc)) != 0) { 132584f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_80211, 132684f7a9b9Shx147065 "could not send authentication request\n")); 132784f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 132884f7a9b9Shx147065 return (err); 132984f7a9b9Shx147065 } 133084f7a9b9Shx147065 break; 133184f7a9b9Shx147065 133284f7a9b9Shx147065 case IEEE80211_S_RUN: 1333626dff79Spengcheng chen - Sun Microsystems - Beijing China if (ostate == IEEE80211_S_SCAN) { 1334626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_flags &= ~WPI_F_SCANNING; 1335626dff79Spengcheng chen - Sun Microsystems - Beijing China } 1336626dff79Spengcheng chen - Sun Microsystems - Beijing China 133784f7a9b9Shx147065 if (ic->ic_opmode == IEEE80211_M_MONITOR) { 133884f7a9b9Shx147065 /* link LED blinks while monitoring */ 133984f7a9b9Shx147065 wpi_set_led(sc, WPI_LED_LINK, 5, 5); 134084f7a9b9Shx147065 break; 134184f7a9b9Shx147065 } 134284f7a9b9Shx147065 134384f7a9b9Shx147065 if (ic->ic_opmode != IEEE80211_M_STA) { 134484f7a9b9Shx147065 (void) wpi_auth(sc); 134584f7a9b9Shx147065 /* need setup beacon here */ 134684f7a9b9Shx147065 } 134784f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_80211, "wpi: associated.")); 134884f7a9b9Shx147065 134984f7a9b9Shx147065 /* update adapter's configuration */ 135084f7a9b9Shx147065 sc->sc_config.state = LE_16(WPI_CONFIG_ASSOCIATED); 135184f7a9b9Shx147065 /* short preamble/slot time are negotiated when associating */ 135284f7a9b9Shx147065 sc->sc_config.flags &= ~LE_32(WPI_CONFIG_SHPREAMBLE | 135384f7a9b9Shx147065 WPI_CONFIG_SHSLOT); 135484f7a9b9Shx147065 if (ic->ic_flags & IEEE80211_F_SHSLOT) 135584f7a9b9Shx147065 sc->sc_config.flags |= LE_32(WPI_CONFIG_SHSLOT); 135684f7a9b9Shx147065 if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) 135784f7a9b9Shx147065 sc->sc_config.flags |= LE_32(WPI_CONFIG_SHPREAMBLE); 135884f7a9b9Shx147065 sc->sc_config.filter |= LE_32(WPI_FILTER_BSS); 135984f7a9b9Shx147065 if (ic->ic_opmode != IEEE80211_M_STA) 136084f7a9b9Shx147065 sc->sc_config.filter |= LE_32(WPI_FILTER_BEACON); 136184f7a9b9Shx147065 136284f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_80211, "config chan %d flags %x\n", 136384f7a9b9Shx147065 sc->sc_config.chan, sc->sc_config.flags)); 136484f7a9b9Shx147065 err = wpi_cmd(sc, WPI_CMD_CONFIGURE, &sc->sc_config, 136584f7a9b9Shx147065 sizeof (wpi_config_t), 1); 136684f7a9b9Shx147065 if (err != WPI_SUCCESS) { 136784f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_80211, 136884f7a9b9Shx147065 "could not update configuration\n")); 136984f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 137084f7a9b9Shx147065 return (err); 137184f7a9b9Shx147065 } 137284f7a9b9Shx147065 137384f7a9b9Shx147065 /* start automatic rate control */ 137484f7a9b9Shx147065 mutex_enter(&sc->sc_mt_lock); 137584f7a9b9Shx147065 if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) { 137684f7a9b9Shx147065 sc->sc_flags |= WPI_F_RATE_AUTO_CTL; 137784f7a9b9Shx147065 /* set rate to some reasonable initial value */ 1378239e91abShx147065 i = in->in_rates.ir_nrates - 1; 1379239e91abShx147065 while (i > 0 && IEEE80211_RATE(i) > 72) 1380239e91abShx147065 i--; 138184f7a9b9Shx147065 in->in_txrate = i; 138284f7a9b9Shx147065 } else { 138384f7a9b9Shx147065 sc->sc_flags &= ~WPI_F_RATE_AUTO_CTL; 138484f7a9b9Shx147065 } 138584f7a9b9Shx147065 mutex_exit(&sc->sc_mt_lock); 138684f7a9b9Shx147065 138784f7a9b9Shx147065 /* link LED always on while associated */ 138884f7a9b9Shx147065 wpi_set_led(sc, WPI_LED_LINK, 0, 1); 138984f7a9b9Shx147065 break; 139084f7a9b9Shx147065 139184f7a9b9Shx147065 case IEEE80211_S_INIT: 1392626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_flags &= ~WPI_F_SCANNING; 1393626dff79Spengcheng chen - Sun Microsystems - Beijing China break; 1394626dff79Spengcheng chen - Sun Microsystems - Beijing China 139584f7a9b9Shx147065 case IEEE80211_S_ASSOC: 1396626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_flags &= ~WPI_F_SCANNING; 139784f7a9b9Shx147065 break; 139884f7a9b9Shx147065 } 139984f7a9b9Shx147065 140084f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 140184f7a9b9Shx147065 return (sc->sc_newstate(ic, nstate, arg)); 140284f7a9b9Shx147065 } 140384f7a9b9Shx147065 14043a1a8936Szf162725 /*ARGSUSED*/ 14053a1a8936Szf162725 static int wpi_key_set(ieee80211com_t *ic, const struct ieee80211_key *k, 14063a1a8936Szf162725 const uint8_t mac[IEEE80211_ADDR_LEN]) 14073a1a8936Szf162725 { 14083a1a8936Szf162725 wpi_sc_t *sc = (wpi_sc_t *)ic; 14093a1a8936Szf162725 wpi_node_t node; 14103a1a8936Szf162725 int err; 14113a1a8936Szf162725 14123a1a8936Szf162725 switch (k->wk_cipher->ic_cipher) { 14133a1a8936Szf162725 case IEEE80211_CIPHER_WEP: 14143a1a8936Szf162725 case IEEE80211_CIPHER_TKIP: 14153a1a8936Szf162725 return (1); /* sofeware do it. */ 14163a1a8936Szf162725 case IEEE80211_CIPHER_AES_CCM: 14173a1a8936Szf162725 break; 14183a1a8936Szf162725 default: 14193a1a8936Szf162725 return (0); 14203a1a8936Szf162725 } 14213a1a8936Szf162725 sc->sc_config.filter &= ~(WPI_FILTER_NODECRYPTUNI | 14223a1a8936Szf162725 WPI_FILTER_NODECRYPTMUL); 14233a1a8936Szf162725 14243a1a8936Szf162725 mutex_enter(&sc->sc_glock); 14253a1a8936Szf162725 14263a1a8936Szf162725 /* update ap/multicast node */ 14273a1a8936Szf162725 (void) memset(&node, 0, sizeof (node)); 14283a1a8936Szf162725 if (IEEE80211_IS_MULTICAST(mac)) { 14293a1a8936Szf162725 (void) memset(node.bssid, 0xff, 6); 14303a1a8936Szf162725 node.id = WPI_ID_BROADCAST; 14313a1a8936Szf162725 } else { 14323a1a8936Szf162725 IEEE80211_ADDR_COPY(node.bssid, ic->ic_bss->in_bssid); 14333a1a8936Szf162725 node.id = WPI_ID_BSS; 14343a1a8936Szf162725 } 14353a1a8936Szf162725 if (k->wk_flags & IEEE80211_KEY_XMIT) { 14363a1a8936Szf162725 node.key_flags = 0; 14373a1a8936Szf162725 node.keyp = k->wk_keyix; 14383a1a8936Szf162725 } else { 14393a1a8936Szf162725 node.key_flags = (1 << 14); 14403a1a8936Szf162725 node.keyp = k->wk_keyix + 4; 14413a1a8936Szf162725 } 14423a1a8936Szf162725 (void) memcpy(node.key, k->wk_key, k->wk_keylen); 14433a1a8936Szf162725 node.key_flags |= (2 | (1 << 3) | (k->wk_keyix << 8)); 14443a1a8936Szf162725 node.sta_mask = 1; 14453a1a8936Szf162725 node.control = 1; 14463a1a8936Szf162725 err = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof (node), 1); 14473a1a8936Szf162725 if (err != WPI_SUCCESS) { 14483a1a8936Szf162725 cmn_err(CE_WARN, "wpi_key_set():" 14493a1a8936Szf162725 "failed to update ap node\n"); 14503a1a8936Szf162725 mutex_exit(&sc->sc_glock); 14513a1a8936Szf162725 return (0); 14523a1a8936Szf162725 } 14533a1a8936Szf162725 mutex_exit(&sc->sc_glock); 14543a1a8936Szf162725 return (1); 14553a1a8936Szf162725 } 14563a1a8936Szf162725 145784f7a9b9Shx147065 /* 145884f7a9b9Shx147065 * Grab exclusive access to NIC memory. 145984f7a9b9Shx147065 */ 146084f7a9b9Shx147065 static void 146184f7a9b9Shx147065 wpi_mem_lock(wpi_sc_t *sc) 146284f7a9b9Shx147065 { 146384f7a9b9Shx147065 uint32_t tmp; 146484f7a9b9Shx147065 int ntries; 146584f7a9b9Shx147065 146684f7a9b9Shx147065 tmp = WPI_READ(sc, WPI_GPIO_CTL); 146784f7a9b9Shx147065 WPI_WRITE(sc, WPI_GPIO_CTL, tmp | WPI_GPIO_MAC); 146884f7a9b9Shx147065 146984f7a9b9Shx147065 /* spin until we actually get the lock */ 147084f7a9b9Shx147065 for (ntries = 0; ntries < 1000; ntries++) { 147184f7a9b9Shx147065 if ((WPI_READ(sc, WPI_GPIO_CTL) & 147284f7a9b9Shx147065 (WPI_GPIO_CLOCK | WPI_GPIO_SLEEP)) == WPI_GPIO_CLOCK) 147384f7a9b9Shx147065 break; 147484f7a9b9Shx147065 DELAY(10); 147584f7a9b9Shx147065 } 147684f7a9b9Shx147065 if (ntries == 1000) 147784f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_PIO, "could not lock memory\n")); 147884f7a9b9Shx147065 } 147984f7a9b9Shx147065 148084f7a9b9Shx147065 /* 148184f7a9b9Shx147065 * Release lock on NIC memory. 148284f7a9b9Shx147065 */ 148384f7a9b9Shx147065 static void 148484f7a9b9Shx147065 wpi_mem_unlock(wpi_sc_t *sc) 148584f7a9b9Shx147065 { 148684f7a9b9Shx147065 uint32_t tmp = WPI_READ(sc, WPI_GPIO_CTL); 148784f7a9b9Shx147065 WPI_WRITE(sc, WPI_GPIO_CTL, tmp & ~WPI_GPIO_MAC); 148884f7a9b9Shx147065 } 148984f7a9b9Shx147065 149084f7a9b9Shx147065 static uint32_t 149184f7a9b9Shx147065 wpi_mem_read(wpi_sc_t *sc, uint16_t addr) 149284f7a9b9Shx147065 { 149384f7a9b9Shx147065 WPI_WRITE(sc, WPI_READ_MEM_ADDR, WPI_MEM_4 | addr); 149484f7a9b9Shx147065 return (WPI_READ(sc, WPI_READ_MEM_DATA)); 149584f7a9b9Shx147065 } 149684f7a9b9Shx147065 149784f7a9b9Shx147065 static void 149884f7a9b9Shx147065 wpi_mem_write(wpi_sc_t *sc, uint16_t addr, uint32_t data) 149984f7a9b9Shx147065 { 150084f7a9b9Shx147065 WPI_WRITE(sc, WPI_WRITE_MEM_ADDR, WPI_MEM_4 | addr); 150184f7a9b9Shx147065 WPI_WRITE(sc, WPI_WRITE_MEM_DATA, data); 150284f7a9b9Shx147065 } 150384f7a9b9Shx147065 150484f7a9b9Shx147065 static void 150584f7a9b9Shx147065 wpi_mem_write_region_4(wpi_sc_t *sc, uint16_t addr, 150684f7a9b9Shx147065 const uint32_t *data, int wlen) 150784f7a9b9Shx147065 { 150884f7a9b9Shx147065 for (; wlen > 0; wlen--, data++, addr += 4) 150984f7a9b9Shx147065 wpi_mem_write(sc, addr, *data); 151084f7a9b9Shx147065 } 151184f7a9b9Shx147065 151284f7a9b9Shx147065 /* 151384f7a9b9Shx147065 * Read 16 bits from the EEPROM. We access EEPROM through the MAC instead of 151484f7a9b9Shx147065 * using the traditional bit-bang method. 151584f7a9b9Shx147065 */ 151684f7a9b9Shx147065 static uint16_t 151784f7a9b9Shx147065 wpi_read_prom_word(wpi_sc_t *sc, uint32_t addr) 151884f7a9b9Shx147065 { 151984f7a9b9Shx147065 uint32_t val; 152084f7a9b9Shx147065 int ntries; 152184f7a9b9Shx147065 152284f7a9b9Shx147065 WPI_WRITE(sc, WPI_EEPROM_CTL, addr << 2); 152384f7a9b9Shx147065 152484f7a9b9Shx147065 wpi_mem_lock(sc); 152584f7a9b9Shx147065 for (ntries = 0; ntries < 10; ntries++) { 152684f7a9b9Shx147065 if ((val = WPI_READ(sc, WPI_EEPROM_CTL)) & WPI_EEPROM_READY) 152784f7a9b9Shx147065 break; 152884f7a9b9Shx147065 DELAY(10); 152984f7a9b9Shx147065 } 153084f7a9b9Shx147065 wpi_mem_unlock(sc); 153184f7a9b9Shx147065 153284f7a9b9Shx147065 if (ntries == 10) { 153384f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_PIO, "could not read EEPROM\n")); 153484f7a9b9Shx147065 return (0xdead); 153584f7a9b9Shx147065 } 153684f7a9b9Shx147065 return (val >> 16); 153784f7a9b9Shx147065 } 153884f7a9b9Shx147065 153984f7a9b9Shx147065 /* 154084f7a9b9Shx147065 * The firmware boot code is small and is intended to be copied directly into 154184f7a9b9Shx147065 * the NIC internal memory. 154284f7a9b9Shx147065 */ 154384f7a9b9Shx147065 static int 154484f7a9b9Shx147065 wpi_load_microcode(wpi_sc_t *sc) 154584f7a9b9Shx147065 { 154684f7a9b9Shx147065 const char *ucode; 154784f7a9b9Shx147065 int size; 154884f7a9b9Shx147065 154984f7a9b9Shx147065 ucode = sc->sc_boot; 155084f7a9b9Shx147065 size = LE_32(sc->sc_hdr->bootsz); 155184f7a9b9Shx147065 /* check that microcode size is a multiple of 4 */ 155284f7a9b9Shx147065 if (size & 3) 155384f7a9b9Shx147065 return (EINVAL); 155484f7a9b9Shx147065 155584f7a9b9Shx147065 size /= sizeof (uint32_t); 155684f7a9b9Shx147065 155784f7a9b9Shx147065 wpi_mem_lock(sc); 155884f7a9b9Shx147065 155984f7a9b9Shx147065 /* copy microcode image into NIC memory */ 156084f7a9b9Shx147065 wpi_mem_write_region_4(sc, WPI_MEM_UCODE_BASE, (const uint32_t *)ucode, 156184f7a9b9Shx147065 size); 156284f7a9b9Shx147065 156384f7a9b9Shx147065 wpi_mem_write(sc, WPI_MEM_UCODE_SRC, 0); 156484f7a9b9Shx147065 wpi_mem_write(sc, WPI_MEM_UCODE_DST, WPI_FW_TEXT); 156584f7a9b9Shx147065 wpi_mem_write(sc, WPI_MEM_UCODE_SIZE, size); 156684f7a9b9Shx147065 156784f7a9b9Shx147065 /* run microcode */ 156884f7a9b9Shx147065 wpi_mem_write(sc, WPI_MEM_UCODE_CTL, WPI_UC_RUN); 156984f7a9b9Shx147065 157084f7a9b9Shx147065 wpi_mem_unlock(sc); 157184f7a9b9Shx147065 157284f7a9b9Shx147065 return (WPI_SUCCESS); 157384f7a9b9Shx147065 } 157484f7a9b9Shx147065 157584f7a9b9Shx147065 /* 157684f7a9b9Shx147065 * The firmware text and data segments are transferred to the NIC using DMA. 157784f7a9b9Shx147065 * The driver just copies the firmware into DMA-safe memory and tells the NIC 157884f7a9b9Shx147065 * where to find it. Once the NIC has copied the firmware into its internal 157984f7a9b9Shx147065 * memory, we can free our local copy in the driver. 158084f7a9b9Shx147065 */ 158184f7a9b9Shx147065 static int 158284f7a9b9Shx147065 wpi_load_firmware(wpi_sc_t *sc, uint32_t target) 158384f7a9b9Shx147065 { 158484f7a9b9Shx147065 const char *fw; 158584f7a9b9Shx147065 int size; 158684f7a9b9Shx147065 wpi_dma_t *dma_p; 158784f7a9b9Shx147065 ddi_dma_cookie_t *cookie; 158884f7a9b9Shx147065 wpi_tx_desc_t desc; 158984f7a9b9Shx147065 int i, ntries, err = WPI_SUCCESS; 159084f7a9b9Shx147065 159184f7a9b9Shx147065 /* only text and data here */ 159284f7a9b9Shx147065 if (target == WPI_FW_TEXT) { 159384f7a9b9Shx147065 fw = sc->sc_text; 159484f7a9b9Shx147065 size = LE_32(sc->sc_hdr->textsz); 159584f7a9b9Shx147065 dma_p = &sc->sc_dma_fw_text; 159684f7a9b9Shx147065 cookie = sc->sc_fw_text_cookie; 159784f7a9b9Shx147065 } else { 159884f7a9b9Shx147065 fw = sc->sc_data; 159984f7a9b9Shx147065 size = LE_32(sc->sc_hdr->datasz); 160084f7a9b9Shx147065 dma_p = &sc->sc_dma_fw_data; 160184f7a9b9Shx147065 cookie = sc->sc_fw_data_cookie; 160284f7a9b9Shx147065 } 160384f7a9b9Shx147065 160484f7a9b9Shx147065 /* copy firmware image to DMA-safe memory */ 160584f7a9b9Shx147065 (void) memcpy(dma_p->mem_va, fw, size); 160684f7a9b9Shx147065 160784f7a9b9Shx147065 /* make sure the adapter will get up-to-date values */ 160884f7a9b9Shx147065 (void) ddi_dma_sync(dma_p->dma_hdl, 0, size, DDI_DMA_SYNC_FORDEV); 160984f7a9b9Shx147065 161084f7a9b9Shx147065 (void) memset(&desc, 0, sizeof (desc)); 161184f7a9b9Shx147065 desc.flags = LE_32(WPI_PAD32(size) << 28 | dma_p->ncookies << 24); 161284f7a9b9Shx147065 for (i = 0; i < dma_p->ncookies; i++) { 161384f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_DMA, "cookie%d addr:%x size:%x\n", 161484f7a9b9Shx147065 i, cookie[i].dmac_address, cookie[i].dmac_size)); 161584f7a9b9Shx147065 desc.segs[i].addr = cookie[i].dmac_address; 161684f7a9b9Shx147065 desc.segs[i].len = (uint32_t)cookie[i].dmac_size; 161784f7a9b9Shx147065 } 161884f7a9b9Shx147065 161984f7a9b9Shx147065 wpi_mem_lock(sc); 162084f7a9b9Shx147065 162184f7a9b9Shx147065 /* tell adapter where to copy image in its internal memory */ 162284f7a9b9Shx147065 WPI_WRITE(sc, WPI_FW_TARGET, target); 162384f7a9b9Shx147065 162484f7a9b9Shx147065 WPI_WRITE(sc, WPI_TX_CONFIG(6), 0); 162584f7a9b9Shx147065 162684f7a9b9Shx147065 /* copy firmware descriptor into NIC memory */ 162784f7a9b9Shx147065 WPI_WRITE_REGION_4(sc, WPI_TX_DESC(6), (uint32_t *)&desc, 162884f7a9b9Shx147065 sizeof desc / sizeof (uint32_t)); 162984f7a9b9Shx147065 163084f7a9b9Shx147065 WPI_WRITE(sc, WPI_TX_CREDIT(6), 0xfffff); 163184f7a9b9Shx147065 WPI_WRITE(sc, WPI_TX_STATE(6), 0x4001); 163284f7a9b9Shx147065 WPI_WRITE(sc, WPI_TX_CONFIG(6), 0x80000001); 163384f7a9b9Shx147065 163484f7a9b9Shx147065 /* wait while the adapter is busy copying the firmware */ 163584f7a9b9Shx147065 for (ntries = 0; ntries < 100; ntries++) { 163684f7a9b9Shx147065 if (WPI_READ(sc, WPI_TX_STATUS) & WPI_TX_IDLE(6)) 163784f7a9b9Shx147065 break; 163884f7a9b9Shx147065 DELAY(1000); 163984f7a9b9Shx147065 } 164084f7a9b9Shx147065 if (ntries == 100) { 164184f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_FW, "timeout transferring firmware\n")); 164284f7a9b9Shx147065 err = ETIMEDOUT; 164384f7a9b9Shx147065 } 164484f7a9b9Shx147065 164584f7a9b9Shx147065 WPI_WRITE(sc, WPI_TX_CREDIT(6), 0); 164684f7a9b9Shx147065 164784f7a9b9Shx147065 wpi_mem_unlock(sc); 164884f7a9b9Shx147065 164984f7a9b9Shx147065 return (err); 165084f7a9b9Shx147065 } 165184f7a9b9Shx147065 165284f7a9b9Shx147065 /*ARGSUSED*/ 165384f7a9b9Shx147065 static void 165484f7a9b9Shx147065 wpi_rx_intr(wpi_sc_t *sc, wpi_rx_desc_t *desc, wpi_rx_data_t *data) 165584f7a9b9Shx147065 { 165684f7a9b9Shx147065 ieee80211com_t *ic = &sc->sc_ic; 165784f7a9b9Shx147065 wpi_rx_ring_t *ring = &sc->sc_rxq; 165884f7a9b9Shx147065 wpi_rx_stat_t *stat; 165984f7a9b9Shx147065 wpi_rx_head_t *head; 166084f7a9b9Shx147065 wpi_rx_tail_t *tail; 166184f7a9b9Shx147065 ieee80211_node_t *in; 166284f7a9b9Shx147065 struct ieee80211_frame *wh; 166384f7a9b9Shx147065 mblk_t *mp; 166484f7a9b9Shx147065 uint16_t len; 166584f7a9b9Shx147065 166684f7a9b9Shx147065 stat = (wpi_rx_stat_t *)(desc + 1); 166784f7a9b9Shx147065 166884f7a9b9Shx147065 if (stat->len > WPI_STAT_MAXLEN) { 166984f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_RX, "invalid rx statistic header\n")); 167084f7a9b9Shx147065 return; 167184f7a9b9Shx147065 } 167284f7a9b9Shx147065 167384f7a9b9Shx147065 head = (wpi_rx_head_t *)((caddr_t)(stat + 1) + stat->len); 167484f7a9b9Shx147065 tail = (wpi_rx_tail_t *)((caddr_t)(head + 1) + LE_16(head->len)); 167584f7a9b9Shx147065 167684f7a9b9Shx147065 len = LE_16(head->len); 167784f7a9b9Shx147065 167884f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_RX, "rx intr: idx=%d len=%d stat len=%d rssi=%d " 167984f7a9b9Shx147065 "rate=%x chan=%d tstamp=%llu", ring->cur, LE_32(desc->len), 168084f7a9b9Shx147065 len, (int8_t)stat->rssi, head->rate, head->chan, 168184f7a9b9Shx147065 LE_64(tail->tstamp))); 168284f7a9b9Shx147065 168384f7a9b9Shx147065 if ((len < 20) || (len > sc->sc_dmabuf_sz)) { 168484f7a9b9Shx147065 sc->sc_rx_err++; 168584f7a9b9Shx147065 return; 168684f7a9b9Shx147065 } 168784f7a9b9Shx147065 168884f7a9b9Shx147065 /* 168984f7a9b9Shx147065 * Discard Rx frames with bad CRC early 169084f7a9b9Shx147065 */ 169184f7a9b9Shx147065 if ((LE_32(tail->flags) & WPI_RX_NOERROR) != WPI_RX_NOERROR) { 169284f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_RX, "rx tail flags error %x\n", 169384f7a9b9Shx147065 LE_32(tail->flags))); 169484f7a9b9Shx147065 sc->sc_rx_err++; 169584f7a9b9Shx147065 return; 169684f7a9b9Shx147065 } 169784f7a9b9Shx147065 169884f7a9b9Shx147065 /* update Rx descriptor */ 169984f7a9b9Shx147065 /* ring->desc[ring->cur] = LE_32(data->dma_data.cookie.dmac_address); */ 170084f7a9b9Shx147065 170184f7a9b9Shx147065 #ifdef WPI_BPF 170284f7a9b9Shx147065 #ifndef WPI_CURRENT 170384f7a9b9Shx147065 if (sc->sc_drvbpf != NULL) { 170484f7a9b9Shx147065 #else 170584f7a9b9Shx147065 if (bpf_peers_present(sc->sc_drvbpf)) { 170684f7a9b9Shx147065 #endif 170784f7a9b9Shx147065 struct wpi_rx_radiotap_header *tap = &sc->sc_rxtap; 170884f7a9b9Shx147065 170984f7a9b9Shx147065 tap->wr_flags = 0; 171084f7a9b9Shx147065 tap->wr_rate = head->rate; 171184f7a9b9Shx147065 tap->wr_chan_freq = 171284f7a9b9Shx147065 LE_16(ic->ic_channels[head->chan].ic_freq); 171384f7a9b9Shx147065 tap->wr_chan_flags = 171484f7a9b9Shx147065 LE_16(ic->ic_channels[head->chan].ic_flags); 171584f7a9b9Shx147065 tap->wr_dbm_antsignal = (int8_t)(stat->rssi - WPI_RSSI_OFFSET); 171684f7a9b9Shx147065 tap->wr_dbm_antnoise = (int8_t)LE_16(stat->noise); 171784f7a9b9Shx147065 tap->wr_tsft = tail->tstamp; 171884f7a9b9Shx147065 tap->wr_antenna = (LE_16(head->flags) >> 4) & 0xf; 171984f7a9b9Shx147065 switch (head->rate) { 172084f7a9b9Shx147065 /* CCK rates */ 172184f7a9b9Shx147065 case 10: tap->wr_rate = 2; break; 172284f7a9b9Shx147065 case 20: tap->wr_rate = 4; break; 172384f7a9b9Shx147065 case 55: tap->wr_rate = 11; break; 172484f7a9b9Shx147065 case 110: tap->wr_rate = 22; break; 172584f7a9b9Shx147065 /* OFDM rates */ 172684f7a9b9Shx147065 case 0xd: tap->wr_rate = 12; break; 172784f7a9b9Shx147065 case 0xf: tap->wr_rate = 18; break; 172884f7a9b9Shx147065 case 0x5: tap->wr_rate = 24; break; 172984f7a9b9Shx147065 case 0x7: tap->wr_rate = 36; break; 173084f7a9b9Shx147065 case 0x9: tap->wr_rate = 48; break; 173184f7a9b9Shx147065 case 0xb: tap->wr_rate = 72; break; 173284f7a9b9Shx147065 case 0x1: tap->wr_rate = 96; break; 173384f7a9b9Shx147065 case 0x3: tap->wr_rate = 108; break; 173484f7a9b9Shx147065 /* unknown rate: should not happen */ 173584f7a9b9Shx147065 default: tap->wr_rate = 0; 173684f7a9b9Shx147065 } 173784f7a9b9Shx147065 if (LE_16(head->flags) & 0x4) 173884f7a9b9Shx147065 tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; 173984f7a9b9Shx147065 174084f7a9b9Shx147065 bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m); 174184f7a9b9Shx147065 } 174284f7a9b9Shx147065 #endif 174384f7a9b9Shx147065 /* grab a reference to the source node */ 174484f7a9b9Shx147065 wh = (struct ieee80211_frame *)(head + 1); 174584f7a9b9Shx147065 174684f7a9b9Shx147065 #ifdef DEBUG 174784f7a9b9Shx147065 if (wpi_dbg_flags & WPI_DEBUG_RX) 174884f7a9b9Shx147065 ieee80211_dump_pkt((uint8_t *)wh, len, 0, 0); 174984f7a9b9Shx147065 #endif 175084f7a9b9Shx147065 175184f7a9b9Shx147065 in = ieee80211_find_rxnode(ic, wh); 175284f7a9b9Shx147065 mp = allocb(len, BPRI_MED); 175384f7a9b9Shx147065 if (mp) { 175484f7a9b9Shx147065 (void) memcpy(mp->b_wptr, wh, len); 175584f7a9b9Shx147065 mp->b_wptr += len; 175684f7a9b9Shx147065 175784f7a9b9Shx147065 /* send the frame to the 802.11 layer */ 175884f7a9b9Shx147065 (void) ieee80211_input(ic, mp, in, stat->rssi, 0); 175984f7a9b9Shx147065 } else { 176084f7a9b9Shx147065 sc->sc_rx_nobuf++; 176184f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_RX, 176284f7a9b9Shx147065 "wpi_rx_intr(): alloc rx buf failed\n")); 176384f7a9b9Shx147065 } 176484f7a9b9Shx147065 /* release node reference */ 176584f7a9b9Shx147065 ieee80211_free_node(in); 176684f7a9b9Shx147065 } 176784f7a9b9Shx147065 176884f7a9b9Shx147065 /*ARGSUSED*/ 176984f7a9b9Shx147065 static void 177084f7a9b9Shx147065 wpi_tx_intr(wpi_sc_t *sc, wpi_rx_desc_t *desc, wpi_rx_data_t *data) 177184f7a9b9Shx147065 { 177284f7a9b9Shx147065 ieee80211com_t *ic = &sc->sc_ic; 177384f7a9b9Shx147065 wpi_tx_ring_t *ring = &sc->sc_txq[desc->qid & 0x3]; 177484f7a9b9Shx147065 /* wpi_tx_data_t *txdata = &ring->data[desc->idx]; */ 177584f7a9b9Shx147065 wpi_tx_stat_t *stat = (wpi_tx_stat_t *)(desc + 1); 177684f7a9b9Shx147065 wpi_amrr_t *amrr = (wpi_amrr_t *)ic->ic_bss; 177784f7a9b9Shx147065 177884f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_TX, "tx done: qid=%d idx=%d retries=%d nkill=%d " 177984f7a9b9Shx147065 "rate=%x duration=%d status=%x\n", 178084f7a9b9Shx147065 desc->qid, desc->idx, stat->ntries, stat->nkill, stat->rate, 178184f7a9b9Shx147065 LE_32(stat->duration), LE_32(stat->status))); 178284f7a9b9Shx147065 178384f7a9b9Shx147065 amrr->txcnt++; 178484f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_RATECTL, "tx: %d cnt\n", amrr->txcnt)); 178584f7a9b9Shx147065 if (stat->ntries > 0) { 178684f7a9b9Shx147065 amrr->retrycnt++; 178784f7a9b9Shx147065 sc->sc_tx_retries++; 178884f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_RATECTL, "tx: %d retries\n", 178984f7a9b9Shx147065 amrr->retrycnt)); 179084f7a9b9Shx147065 } 179184f7a9b9Shx147065 179284f7a9b9Shx147065 sc->sc_tx_timer = 0; 179384f7a9b9Shx147065 179484f7a9b9Shx147065 mutex_enter(&sc->sc_tx_lock); 179584f7a9b9Shx147065 ring->queued--; 179684f7a9b9Shx147065 if (ring->queued < 0) 179784f7a9b9Shx147065 ring->queued = 0; 179884f7a9b9Shx147065 if ((sc->sc_need_reschedule) && (ring->queued <= (ring->count << 3))) { 179984f7a9b9Shx147065 sc->sc_need_reschedule = 0; 180084f7a9b9Shx147065 mutex_exit(&sc->sc_tx_lock); 180184f7a9b9Shx147065 mac_tx_update(ic->ic_mach); 180284f7a9b9Shx147065 mutex_enter(&sc->sc_tx_lock); 180384f7a9b9Shx147065 } 180484f7a9b9Shx147065 mutex_exit(&sc->sc_tx_lock); 180584f7a9b9Shx147065 } 180684f7a9b9Shx147065 180784f7a9b9Shx147065 static void 180884f7a9b9Shx147065 wpi_cmd_intr(wpi_sc_t *sc, wpi_rx_desc_t *desc) 180984f7a9b9Shx147065 { 181084f7a9b9Shx147065 if ((desc->qid & 7) != 4) { 181184f7a9b9Shx147065 return; /* not a command ack */ 181284f7a9b9Shx147065 } 181384f7a9b9Shx147065 mutex_enter(&sc->sc_glock); 181484f7a9b9Shx147065 sc->sc_flags |= WPI_F_CMD_DONE; 181584f7a9b9Shx147065 cv_signal(&sc->sc_cmd_cv); 181684f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 181784f7a9b9Shx147065 } 181884f7a9b9Shx147065 181984f7a9b9Shx147065 static uint_t 182084f7a9b9Shx147065 wpi_notif_softintr(caddr_t arg) 182184f7a9b9Shx147065 { 182284f7a9b9Shx147065 wpi_sc_t *sc = (wpi_sc_t *)arg; 182384f7a9b9Shx147065 wpi_rx_desc_t *desc; 182484f7a9b9Shx147065 wpi_rx_data_t *data; 182584f7a9b9Shx147065 uint32_t hw; 182684f7a9b9Shx147065 182784f7a9b9Shx147065 mutex_enter(&sc->sc_glock); 182884f7a9b9Shx147065 if (sc->sc_notif_softint_pending != 1) { 182984f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 183084f7a9b9Shx147065 return (DDI_INTR_UNCLAIMED); 183184f7a9b9Shx147065 } 183284f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 183384f7a9b9Shx147065 183484f7a9b9Shx147065 hw = LE_32(sc->sc_shared->next); 183584f7a9b9Shx147065 183684f7a9b9Shx147065 while (sc->sc_rxq.cur != hw) { 183784f7a9b9Shx147065 data = &sc->sc_rxq.data[sc->sc_rxq.cur]; 183884f7a9b9Shx147065 desc = (wpi_rx_desc_t *)data->dma_data.mem_va; 183984f7a9b9Shx147065 184084f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_INTR, "rx notification hw = %d cur = %d " 184184f7a9b9Shx147065 "qid=%x idx=%d flags=%x type=%d len=%d\n", 184284f7a9b9Shx147065 hw, sc->sc_rxq.cur, desc->qid, desc->idx, desc->flags, 184384f7a9b9Shx147065 desc->type, LE_32(desc->len))); 184484f7a9b9Shx147065 184584f7a9b9Shx147065 if (!(desc->qid & 0x80)) /* reply to a command */ 184684f7a9b9Shx147065 wpi_cmd_intr(sc, desc); 184784f7a9b9Shx147065 184884f7a9b9Shx147065 switch (desc->type) { 184984f7a9b9Shx147065 case WPI_RX_DONE: 185084f7a9b9Shx147065 /* a 802.11 frame was received */ 185184f7a9b9Shx147065 wpi_rx_intr(sc, desc, data); 185284f7a9b9Shx147065 break; 185384f7a9b9Shx147065 185484f7a9b9Shx147065 case WPI_TX_DONE: 185584f7a9b9Shx147065 /* a 802.11 frame has been transmitted */ 185684f7a9b9Shx147065 wpi_tx_intr(sc, desc, data); 185784f7a9b9Shx147065 break; 185884f7a9b9Shx147065 185984f7a9b9Shx147065 case WPI_UC_READY: 186084f7a9b9Shx147065 { 186184f7a9b9Shx147065 wpi_ucode_info_t *uc = 186284f7a9b9Shx147065 (wpi_ucode_info_t *)(desc + 1); 186384f7a9b9Shx147065 186484f7a9b9Shx147065 /* the microcontroller is ready */ 186584f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_FW, 186684f7a9b9Shx147065 "microcode alive notification version %x " 186784f7a9b9Shx147065 "alive %x\n", LE_32(uc->version), 186884f7a9b9Shx147065 LE_32(uc->valid))); 186984f7a9b9Shx147065 187084f7a9b9Shx147065 if (LE_32(uc->valid) != 1) { 187184f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_FW, 187284f7a9b9Shx147065 "microcontroller initialization failed\n")); 187384f7a9b9Shx147065 } 187484f7a9b9Shx147065 break; 187584f7a9b9Shx147065 } 187684f7a9b9Shx147065 case WPI_STATE_CHANGED: 187784f7a9b9Shx147065 { 187884f7a9b9Shx147065 uint32_t *status = (uint32_t *)(desc + 1); 187984f7a9b9Shx147065 188084f7a9b9Shx147065 /* enabled/disabled notification */ 188184f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_RADIO, "state changed to %x\n", 188284f7a9b9Shx147065 LE_32(*status))); 188384f7a9b9Shx147065 188484f7a9b9Shx147065 if (LE_32(*status) & 1) { 188519b23afcShx147065 /* 188619b23afcShx147065 * the radio button has to be pushed(OFF). It 188719b23afcShx147065 * is considered as a hw error, the 188819b23afcShx147065 * wpi_thread() tries to recover it after the 188919b23afcShx147065 * button is pushed again(ON) 189019b23afcShx147065 */ 189184f7a9b9Shx147065 cmn_err(CE_NOTE, 189284f7a9b9Shx147065 "wpi: Radio transmitter is off\n"); 189319b23afcShx147065 sc->sc_ostate = sc->sc_ic.ic_state; 189419b23afcShx147065 ieee80211_new_state(&sc->sc_ic, 189519b23afcShx147065 IEEE80211_S_INIT, -1); 189619b23afcShx147065 sc->sc_flags |= 189719b23afcShx147065 (WPI_F_HW_ERR_RECOVER | WPI_F_RADIO_OFF); 189884f7a9b9Shx147065 } 189984f7a9b9Shx147065 break; 190084f7a9b9Shx147065 } 190184f7a9b9Shx147065 case WPI_START_SCAN: 190284f7a9b9Shx147065 { 190384f7a9b9Shx147065 wpi_start_scan_t *scan = 190484f7a9b9Shx147065 (wpi_start_scan_t *)(desc + 1); 190584f7a9b9Shx147065 190684f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_SCAN, 190784f7a9b9Shx147065 "scanning channel %d status %x\n", 190884f7a9b9Shx147065 scan->chan, LE_32(scan->status))); 190984f7a9b9Shx147065 191084f7a9b9Shx147065 break; 191184f7a9b9Shx147065 } 191284f7a9b9Shx147065 case WPI_STOP_SCAN: 1913626dff79Spengcheng chen - Sun Microsystems - Beijing China { 1914626dff79Spengcheng chen - Sun Microsystems - Beijing China wpi_stop_scan_t *scan = 1915626dff79Spengcheng chen - Sun Microsystems - Beijing China (wpi_stop_scan_t *)(desc + 1); 1916626dff79Spengcheng chen - Sun Microsystems - Beijing China 1917626dff79Spengcheng chen - Sun Microsystems - Beijing China WPI_DBG((WPI_DEBUG_SCAN, 1918626dff79Spengcheng chen - Sun Microsystems - Beijing China "completed channel %d (burst of %d) status %02x\n", 1919626dff79Spengcheng chen - Sun Microsystems - Beijing China scan->chan, scan->nchan, scan->status)); 1920626dff79Spengcheng chen - Sun Microsystems - Beijing China 1921626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_scan_pending = 0; 1922626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_scan_next++; 1923626dff79Spengcheng chen - Sun Microsystems - Beijing China break; 1924626dff79Spengcheng chen - Sun Microsystems - Beijing China } 1925626dff79Spengcheng chen - Sun Microsystems - Beijing China default: 192684f7a9b9Shx147065 break; 192784f7a9b9Shx147065 } 192884f7a9b9Shx147065 192984f7a9b9Shx147065 sc->sc_rxq.cur = (sc->sc_rxq.cur + 1) % WPI_RX_RING_COUNT; 193084f7a9b9Shx147065 } 193184f7a9b9Shx147065 193284f7a9b9Shx147065 /* tell the firmware what we have processed */ 193384f7a9b9Shx147065 hw = (hw == 0) ? WPI_RX_RING_COUNT - 1 : hw - 1; 193484f7a9b9Shx147065 WPI_WRITE(sc, WPI_RX_WIDX, hw & (~7)); 193584f7a9b9Shx147065 mutex_enter(&sc->sc_glock); 193684f7a9b9Shx147065 sc->sc_notif_softint_pending = 0; 193784f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 193884f7a9b9Shx147065 193984f7a9b9Shx147065 return (DDI_INTR_CLAIMED); 194084f7a9b9Shx147065 } 194184f7a9b9Shx147065 194284f7a9b9Shx147065 static uint_t 194384f7a9b9Shx147065 wpi_intr(caddr_t arg) 194484f7a9b9Shx147065 { 194584f7a9b9Shx147065 wpi_sc_t *sc = (wpi_sc_t *)arg; 1946bb5141e1Shx147065 uint32_t r, rfh; 194784f7a9b9Shx147065 194884f7a9b9Shx147065 mutex_enter(&sc->sc_glock); 194919b23afcShx147065 if (sc->sc_flags & WPI_F_SUSPEND) { 195019b23afcShx147065 mutex_exit(&sc->sc_glock); 195119b23afcShx147065 return (DDI_INTR_UNCLAIMED); 195219b23afcShx147065 } 195319b23afcShx147065 195484f7a9b9Shx147065 r = WPI_READ(sc, WPI_INTR); 195584f7a9b9Shx147065 if (r == 0 || r == 0xffffffff) { 195684f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 195784f7a9b9Shx147065 return (DDI_INTR_UNCLAIMED); 195884f7a9b9Shx147065 } 195984f7a9b9Shx147065 196084f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_INTR, "interrupt reg %x\n", r)); 196184f7a9b9Shx147065 1962bb5141e1Shx147065 rfh = WPI_READ(sc, WPI_INTR_STATUS); 196384f7a9b9Shx147065 /* disable interrupts */ 196484f7a9b9Shx147065 WPI_WRITE(sc, WPI_MASK, 0); 196584f7a9b9Shx147065 /* ack interrupts */ 196684f7a9b9Shx147065 WPI_WRITE(sc, WPI_INTR, r); 1967bb5141e1Shx147065 WPI_WRITE(sc, WPI_INTR_STATUS, rfh); 196884f7a9b9Shx147065 196984f7a9b9Shx147065 if (sc->sc_notif_softint_id == NULL) { 197084f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 197184f7a9b9Shx147065 return (DDI_INTR_CLAIMED); 197284f7a9b9Shx147065 } 197384f7a9b9Shx147065 197484f7a9b9Shx147065 if (r & (WPI_SW_ERROR | WPI_HW_ERROR)) { 197584f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_FW, "fatal firmware error\n")); 197684f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 197784f7a9b9Shx147065 wpi_stop(sc); 1978626dff79Spengcheng chen - Sun Microsystems - Beijing China if (!(sc->sc_flags & WPI_F_HW_ERR_RECOVER)) { 197984f7a9b9Shx147065 sc->sc_ostate = sc->sc_ic.ic_state; 1980626dff79Spengcheng chen - Sun Microsystems - Beijing China } 19816f12def4Spengcheng chen - Sun Microsystems - Beijing China 19826f12def4Spengcheng chen - Sun Microsystems - Beijing China /* not capable of fast recovery */ 19836f12def4Spengcheng chen - Sun Microsystems - Beijing China if (!WPI_CHK_FAST_RECOVER(sc)) 198484f7a9b9Shx147065 ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1); 19856f12def4Spengcheng chen - Sun Microsystems - Beijing China 198684f7a9b9Shx147065 sc->sc_flags |= WPI_F_HW_ERR_RECOVER; 198784f7a9b9Shx147065 return (DDI_INTR_CLAIMED); 198884f7a9b9Shx147065 } 198984f7a9b9Shx147065 1990bb5141e1Shx147065 if ((r & (WPI_RX_INTR | WPI_RX_SWINT)) || 1991bb5141e1Shx147065 (rfh & 0x40070000)) { 199284f7a9b9Shx147065 sc->sc_notif_softint_pending = 1; 199384f7a9b9Shx147065 ddi_trigger_softintr(sc->sc_notif_softint_id); 199484f7a9b9Shx147065 } 199584f7a9b9Shx147065 199684f7a9b9Shx147065 if (r & WPI_ALIVE_INTR) { /* firmware initialized */ 199784f7a9b9Shx147065 sc->sc_flags |= WPI_F_FW_INIT; 199884f7a9b9Shx147065 cv_signal(&sc->sc_fw_cv); 199984f7a9b9Shx147065 } 200084f7a9b9Shx147065 200184f7a9b9Shx147065 /* re-enable interrupts */ 200284f7a9b9Shx147065 WPI_WRITE(sc, WPI_MASK, WPI_INTR_MASK); 200384f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 200484f7a9b9Shx147065 200584f7a9b9Shx147065 return (DDI_INTR_CLAIMED); 200684f7a9b9Shx147065 } 200784f7a9b9Shx147065 200884f7a9b9Shx147065 static uint8_t 200984f7a9b9Shx147065 wpi_plcp_signal(int rate) 201084f7a9b9Shx147065 { 201184f7a9b9Shx147065 switch (rate) { 201284f7a9b9Shx147065 /* CCK rates (returned values are device-dependent) */ 201384f7a9b9Shx147065 case 2: return (10); 201484f7a9b9Shx147065 case 4: return (20); 201584f7a9b9Shx147065 case 11: return (55); 201684f7a9b9Shx147065 case 22: return (110); 201784f7a9b9Shx147065 201884f7a9b9Shx147065 /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ 201984f7a9b9Shx147065 /* R1-R4 (ral/ural is R4-R1) */ 202084f7a9b9Shx147065 case 12: return (0xd); 202184f7a9b9Shx147065 case 18: return (0xf); 202284f7a9b9Shx147065 case 24: return (0x5); 202384f7a9b9Shx147065 case 36: return (0x7); 202484f7a9b9Shx147065 case 48: return (0x9); 202584f7a9b9Shx147065 case 72: return (0xb); 202684f7a9b9Shx147065 case 96: return (0x1); 202784f7a9b9Shx147065 case 108: return (0x3); 202884f7a9b9Shx147065 202984f7a9b9Shx147065 /* unsupported rates (should not get there) */ 203084f7a9b9Shx147065 default: return (0); 203184f7a9b9Shx147065 } 203284f7a9b9Shx147065 } 203384f7a9b9Shx147065 203484f7a9b9Shx147065 static mblk_t * 203584f7a9b9Shx147065 wpi_m_tx(void *arg, mblk_t *mp) 203684f7a9b9Shx147065 { 203784f7a9b9Shx147065 wpi_sc_t *sc = (wpi_sc_t *)arg; 203884f7a9b9Shx147065 ieee80211com_t *ic = &sc->sc_ic; 203984f7a9b9Shx147065 mblk_t *next; 204084f7a9b9Shx147065 204119b23afcShx147065 if (sc->sc_flags & WPI_F_SUSPEND) { 204219b23afcShx147065 freemsgchain(mp); 204319b23afcShx147065 return (NULL); 204419b23afcShx147065 } 204519b23afcShx147065 204684f7a9b9Shx147065 if (ic->ic_state != IEEE80211_S_RUN) { 204784f7a9b9Shx147065 freemsgchain(mp); 204884f7a9b9Shx147065 return (NULL); 204984f7a9b9Shx147065 } 205084f7a9b9Shx147065 20516f12def4Spengcheng chen - Sun Microsystems - Beijing China if ((sc->sc_flags & WPI_F_HW_ERR_RECOVER) && 20526f12def4Spengcheng chen - Sun Microsystems - Beijing China WPI_CHK_FAST_RECOVER(sc)) { 20536f12def4Spengcheng chen - Sun Microsystems - Beijing China WPI_DBG((WPI_DEBUG_FW, "wpi_m_tx(): hold queue\n")); 20546f12def4Spengcheng chen - Sun Microsystems - Beijing China return (mp); 20556f12def4Spengcheng chen - Sun Microsystems - Beijing China } 20566f12def4Spengcheng chen - Sun Microsystems - Beijing China 205784f7a9b9Shx147065 while (mp != NULL) { 205884f7a9b9Shx147065 next = mp->b_next; 205984f7a9b9Shx147065 mp->b_next = NULL; 206084f7a9b9Shx147065 if (wpi_send(ic, mp, IEEE80211_FC0_TYPE_DATA) != 0) { 206184f7a9b9Shx147065 mp->b_next = next; 206284f7a9b9Shx147065 break; 206384f7a9b9Shx147065 } 206484f7a9b9Shx147065 mp = next; 206584f7a9b9Shx147065 } 206684f7a9b9Shx147065 return (mp); 206784f7a9b9Shx147065 } 206884f7a9b9Shx147065 206984f7a9b9Shx147065 /* ARGSUSED */ 207084f7a9b9Shx147065 static int 207184f7a9b9Shx147065 wpi_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type) 207284f7a9b9Shx147065 { 207384f7a9b9Shx147065 wpi_sc_t *sc = (wpi_sc_t *)ic; 207484f7a9b9Shx147065 wpi_tx_ring_t *ring; 207584f7a9b9Shx147065 wpi_tx_desc_t *desc; 207684f7a9b9Shx147065 wpi_tx_data_t *data; 207784f7a9b9Shx147065 wpi_tx_cmd_t *cmd; 207884f7a9b9Shx147065 wpi_cmd_data_t *tx; 207984f7a9b9Shx147065 ieee80211_node_t *in; 208084f7a9b9Shx147065 struct ieee80211_frame *wh; 208184f7a9b9Shx147065 struct ieee80211_key *k; 208284f7a9b9Shx147065 mblk_t *m, *m0; 208384f7a9b9Shx147065 int rate, hdrlen, len, mblen, off, err = WPI_SUCCESS; 208484f7a9b9Shx147065 208584f7a9b9Shx147065 ring = ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA) ? 208684f7a9b9Shx147065 (&sc->sc_txq[0]) : (&sc->sc_txq[1]); 208784f7a9b9Shx147065 data = &ring->data[ring->cur]; 208884f7a9b9Shx147065 desc = data->desc; 208984f7a9b9Shx147065 cmd = data->cmd; 209084f7a9b9Shx147065 bzero(desc, sizeof (*desc)); 209184f7a9b9Shx147065 bzero(cmd, sizeof (*cmd)); 209284f7a9b9Shx147065 209384f7a9b9Shx147065 mutex_enter(&sc->sc_tx_lock); 209419b23afcShx147065 if (sc->sc_flags & WPI_F_SUSPEND) { 209519b23afcShx147065 mutex_exit(&sc->sc_tx_lock); 209619b23afcShx147065 if ((type & IEEE80211_FC0_TYPE_MASK) != 209719b23afcShx147065 IEEE80211_FC0_TYPE_DATA) { 209819b23afcShx147065 freemsg(mp); 209919b23afcShx147065 } 210040db2e2bSzf162725 err = ENXIO; 210119b23afcShx147065 goto exit; 210219b23afcShx147065 } 210319b23afcShx147065 210484f7a9b9Shx147065 if (ring->queued > ring->count - 64) { 210584f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_TX, "wpi_send(): no txbuf\n")); 210684f7a9b9Shx147065 sc->sc_need_reschedule = 1; 210784f7a9b9Shx147065 mutex_exit(&sc->sc_tx_lock); 210884f7a9b9Shx147065 if ((type & IEEE80211_FC0_TYPE_MASK) != 210984f7a9b9Shx147065 IEEE80211_FC0_TYPE_DATA) { 211084f7a9b9Shx147065 freemsg(mp); 211184f7a9b9Shx147065 } 211284f7a9b9Shx147065 sc->sc_tx_nobuf++; 211340db2e2bSzf162725 err = ENOMEM; 211484f7a9b9Shx147065 goto exit; 211584f7a9b9Shx147065 } 211684f7a9b9Shx147065 mutex_exit(&sc->sc_tx_lock); 211784f7a9b9Shx147065 211884f7a9b9Shx147065 hdrlen = sizeof (struct ieee80211_frame); 211984f7a9b9Shx147065 212084f7a9b9Shx147065 m = allocb(msgdsize(mp) + 32, BPRI_MED); 212184f7a9b9Shx147065 if (m == NULL) { /* can not alloc buf, drop this package */ 212284f7a9b9Shx147065 cmn_err(CE_WARN, 212384f7a9b9Shx147065 "wpi_send(): failed to allocate msgbuf\n"); 212484f7a9b9Shx147065 freemsg(mp); 212584f7a9b9Shx147065 err = WPI_SUCCESS; 212684f7a9b9Shx147065 goto exit; 212784f7a9b9Shx147065 } 212884f7a9b9Shx147065 for (off = 0, m0 = mp; m0 != NULL; m0 = m0->b_cont) { 212984f7a9b9Shx147065 mblen = MBLKL(m0); 213084f7a9b9Shx147065 (void) memcpy(m->b_rptr + off, m0->b_rptr, mblen); 213184f7a9b9Shx147065 off += mblen; 213284f7a9b9Shx147065 } 213384f7a9b9Shx147065 m->b_wptr += off; 213484f7a9b9Shx147065 freemsg(mp); 213584f7a9b9Shx147065 213684f7a9b9Shx147065 wh = (struct ieee80211_frame *)m->b_rptr; 213784f7a9b9Shx147065 213884f7a9b9Shx147065 in = ieee80211_find_txnode(ic, wh->i_addr1); 213984f7a9b9Shx147065 if (in == NULL) { 214084f7a9b9Shx147065 cmn_err(CE_WARN, "wpi_send(): failed to find tx node\n"); 214184f7a9b9Shx147065 freemsg(m); 214284f7a9b9Shx147065 sc->sc_tx_err++; 214384f7a9b9Shx147065 err = WPI_SUCCESS; 214484f7a9b9Shx147065 goto exit; 214584f7a9b9Shx147065 } 21463a1a8936Szf162725 21473a1a8936Szf162725 (void) ieee80211_encap(ic, m, in); 21483a1a8936Szf162725 21493a1a8936Szf162725 cmd->code = WPI_CMD_TX_DATA; 21503a1a8936Szf162725 cmd->flags = 0; 21513a1a8936Szf162725 cmd->qid = ring->qid; 21523a1a8936Szf162725 cmd->idx = ring->cur; 21533a1a8936Szf162725 21543a1a8936Szf162725 tx = (wpi_cmd_data_t *)cmd->data; 21553a1a8936Szf162725 tx->flags = 0; 21563a1a8936Szf162725 if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 21573a1a8936Szf162725 tx->flags |= LE_32(WPI_TX_NEED_ACK); 21583a1a8936Szf162725 } else { 21593a1a8936Szf162725 tx->flags &= ~(LE_32(WPI_TX_NEED_ACK)); 21603a1a8936Szf162725 } 21613a1a8936Szf162725 216284f7a9b9Shx147065 if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 216384f7a9b9Shx147065 k = ieee80211_crypto_encap(ic, m); 216484f7a9b9Shx147065 if (k == NULL) { 216584f7a9b9Shx147065 freemsg(m); 216684f7a9b9Shx147065 sc->sc_tx_err++; 216784f7a9b9Shx147065 err = WPI_SUCCESS; 216884f7a9b9Shx147065 goto exit; 216984f7a9b9Shx147065 } 217084f7a9b9Shx147065 21713a1a8936Szf162725 if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_AES_CCM) { 21723a1a8936Szf162725 tx->security = 2; /* for CCMP */ 21733a1a8936Szf162725 tx->flags |= LE_32(WPI_TX_NEED_ACK); 21743a1a8936Szf162725 (void) memcpy(&tx->key, k->wk_key, k->wk_keylen); 21753a1a8936Szf162725 } 21763a1a8936Szf162725 217784f7a9b9Shx147065 /* packet header may have moved, reset our local pointer */ 217884f7a9b9Shx147065 wh = (struct ieee80211_frame *)m->b_rptr; 217984f7a9b9Shx147065 } 218084f7a9b9Shx147065 218184f7a9b9Shx147065 len = msgdsize(m); 218284f7a9b9Shx147065 218384f7a9b9Shx147065 #ifdef DEBUG 218484f7a9b9Shx147065 if (wpi_dbg_flags & WPI_DEBUG_TX) 218584f7a9b9Shx147065 ieee80211_dump_pkt((uint8_t *)wh, hdrlen, 0, 0); 218684f7a9b9Shx147065 #endif 218784f7a9b9Shx147065 218884f7a9b9Shx147065 /* pickup a rate */ 218984f7a9b9Shx147065 if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == 219084f7a9b9Shx147065 IEEE80211_FC0_TYPE_MGT) { 219184f7a9b9Shx147065 /* mgmt frames are sent at the lowest available bit-rate */ 2192239e91abShx147065 rate = 2; 219384f7a9b9Shx147065 } else { 219484f7a9b9Shx147065 if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) { 219584f7a9b9Shx147065 rate = ic->ic_fixed_rate; 219684f7a9b9Shx147065 } else 219784f7a9b9Shx147065 rate = in->in_rates.ir_rates[in->in_txrate]; 219884f7a9b9Shx147065 } 219984f7a9b9Shx147065 rate &= IEEE80211_RATE_VAL; 220084f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_RATECTL, "tx rate[%d of %d] = %x", 220184f7a9b9Shx147065 in->in_txrate, in->in_rates.ir_nrates, rate)); 220284f7a9b9Shx147065 #ifdef WPI_BPF 220384f7a9b9Shx147065 #ifndef WPI_CURRENT 220484f7a9b9Shx147065 if (sc->sc_drvbpf != NULL) { 220584f7a9b9Shx147065 #else 220684f7a9b9Shx147065 if (bpf_peers_present(sc->sc_drvbpf)) { 220784f7a9b9Shx147065 #endif 220884f7a9b9Shx147065 struct wpi_tx_radiotap_header *tap = &sc->sc_txtap; 220984f7a9b9Shx147065 221084f7a9b9Shx147065 tap->wt_flags = 0; 221184f7a9b9Shx147065 tap->wt_chan_freq = LE_16(ic->ic_curchan->ic_freq); 221284f7a9b9Shx147065 tap->wt_chan_flags = LE_16(ic->ic_curchan->ic_flags); 221384f7a9b9Shx147065 tap->wt_rate = rate; 221484f7a9b9Shx147065 if (wh->i_fc[1] & IEEE80211_FC1_WEP) 221584f7a9b9Shx147065 tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; 221684f7a9b9Shx147065 221784f7a9b9Shx147065 bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0); 221884f7a9b9Shx147065 } 221984f7a9b9Shx147065 #endif 222084f7a9b9Shx147065 222184f7a9b9Shx147065 tx->flags |= (LE_32(WPI_TX_AUTO_SEQ)); 222284f7a9b9Shx147065 tx->flags |= LE_32(WPI_TX_BT_DISABLE | WPI_TX_CALIBRATION); 222384f7a9b9Shx147065 222484f7a9b9Shx147065 /* retrieve destination node's id */ 222584f7a9b9Shx147065 tx->id = IEEE80211_IS_MULTICAST(wh->i_addr1) ? WPI_ID_BROADCAST : 222684f7a9b9Shx147065 WPI_ID_BSS; 222784f7a9b9Shx147065 222884f7a9b9Shx147065 if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == 222984f7a9b9Shx147065 IEEE80211_FC0_TYPE_MGT) { 223084f7a9b9Shx147065 /* tell h/w to set timestamp in probe responses */ 223184f7a9b9Shx147065 if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == 223284f7a9b9Shx147065 IEEE80211_FC0_SUBTYPE_PROBE_RESP) 223384f7a9b9Shx147065 tx->flags |= LE_32(WPI_TX_INSERT_TSTAMP); 223484f7a9b9Shx147065 223584f7a9b9Shx147065 if (((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == 223684f7a9b9Shx147065 IEEE80211_FC0_SUBTYPE_ASSOC_REQ) || 223784f7a9b9Shx147065 ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == 223884f7a9b9Shx147065 IEEE80211_FC0_SUBTYPE_REASSOC_REQ)) 223984f7a9b9Shx147065 tx->timeout = 3; 224084f7a9b9Shx147065 else 224184f7a9b9Shx147065 tx->timeout = 2; 224284f7a9b9Shx147065 } else 224384f7a9b9Shx147065 tx->timeout = 0; 224484f7a9b9Shx147065 224584f7a9b9Shx147065 tx->rate = wpi_plcp_signal(rate); 224684f7a9b9Shx147065 224784f7a9b9Shx147065 /* be very persistant at sending frames out */ 224884f7a9b9Shx147065 tx->rts_ntries = 7; 224984f7a9b9Shx147065 tx->data_ntries = 15; 225084f7a9b9Shx147065 225184f7a9b9Shx147065 tx->cck_mask = 0x0f; 225284f7a9b9Shx147065 tx->ofdm_mask = 0xff; 225384f7a9b9Shx147065 tx->lifetime = LE_32(0xffffffff); 225484f7a9b9Shx147065 225584f7a9b9Shx147065 tx->len = LE_16(len); 225684f7a9b9Shx147065 225784f7a9b9Shx147065 /* save and trim IEEE802.11 header */ 225884f7a9b9Shx147065 (void) memcpy(tx + 1, m->b_rptr, hdrlen); 225984f7a9b9Shx147065 m->b_rptr += hdrlen; 226084f7a9b9Shx147065 (void) memcpy(data->dma_data.mem_va, m->b_rptr, len - hdrlen); 226184f7a9b9Shx147065 226284f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_TX, "sending data: qid=%d idx=%d len=%d", ring->qid, 226384f7a9b9Shx147065 ring->cur, len)); 226484f7a9b9Shx147065 226584f7a9b9Shx147065 /* first scatter/gather segment is used by the tx data command */ 226684f7a9b9Shx147065 desc->flags = LE_32(WPI_PAD32(len) << 28 | (2) << 24); 226784f7a9b9Shx147065 desc->segs[0].addr = LE_32(data->paddr_cmd); 226884f7a9b9Shx147065 desc->segs[0].len = LE_32( 226984f7a9b9Shx147065 roundup(4 + sizeof (wpi_cmd_data_t) + hdrlen, 4)); 227084f7a9b9Shx147065 desc->segs[1].addr = LE_32(data->dma_data.cookie.dmac_address); 227184f7a9b9Shx147065 desc->segs[1].len = LE_32(len - hdrlen); 227284f7a9b9Shx147065 227384f7a9b9Shx147065 WPI_DMA_SYNC(data->dma_data, DDI_DMA_SYNC_FORDEV); 227484f7a9b9Shx147065 WPI_DMA_SYNC(ring->dma_desc, DDI_DMA_SYNC_FORDEV); 227584f7a9b9Shx147065 227684f7a9b9Shx147065 mutex_enter(&sc->sc_tx_lock); 227784f7a9b9Shx147065 ring->queued++; 227884f7a9b9Shx147065 mutex_exit(&sc->sc_tx_lock); 227984f7a9b9Shx147065 228084f7a9b9Shx147065 /* kick ring */ 228184f7a9b9Shx147065 ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT; 228284f7a9b9Shx147065 WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); 228384f7a9b9Shx147065 freemsg(m); 228484f7a9b9Shx147065 /* release node reference */ 228584f7a9b9Shx147065 ieee80211_free_node(in); 228684f7a9b9Shx147065 228784f7a9b9Shx147065 ic->ic_stats.is_tx_bytes += len; 228884f7a9b9Shx147065 ic->ic_stats.is_tx_frags++; 228984f7a9b9Shx147065 229084f7a9b9Shx147065 if (sc->sc_tx_timer == 0) 229184f7a9b9Shx147065 sc->sc_tx_timer = 5; 229284f7a9b9Shx147065 exit: 229384f7a9b9Shx147065 return (err); 229484f7a9b9Shx147065 } 229584f7a9b9Shx147065 229684f7a9b9Shx147065 static void 229784f7a9b9Shx147065 wpi_m_ioctl(void* arg, queue_t *wq, mblk_t *mp) 229884f7a9b9Shx147065 { 229984f7a9b9Shx147065 wpi_sc_t *sc = (wpi_sc_t *)arg; 230084f7a9b9Shx147065 ieee80211com_t *ic = &sc->sc_ic; 230184f7a9b9Shx147065 int err; 230284f7a9b9Shx147065 230384f7a9b9Shx147065 err = ieee80211_ioctl(ic, wq, mp); 230484f7a9b9Shx147065 if (err == ENETRESET) { 2305367ecab4Shx147065 /* 2306367ecab4Shx147065 * This is special for the hidden AP connection. 2307367ecab4Shx147065 * In any case, we should make sure only one 'scan' 2308367ecab4Shx147065 * in the driver for a 'connect' CLI command. So 2309367ecab4Shx147065 * when connecting to a hidden AP, the scan is just 2310367ecab4Shx147065 * sent out to the air when we know the desired 2311367ecab4Shx147065 * essid of the AP we want to connect. 2312367ecab4Shx147065 */ 2313367ecab4Shx147065 if (ic->ic_des_esslen) { 2314626dff79Spengcheng chen - Sun Microsystems - Beijing China if (sc->sc_flags & WPI_F_RUNNING) { 2315626dff79Spengcheng chen - Sun Microsystems - Beijing China wpi_m_stop(sc); 2316626dff79Spengcheng chen - Sun Microsystems - Beijing China (void) wpi_m_start(sc); 231784f7a9b9Shx147065 (void) ieee80211_new_state(ic, 231884f7a9b9Shx147065 IEEE80211_S_SCAN, -1); 231984f7a9b9Shx147065 } 232084f7a9b9Shx147065 } 2321367ecab4Shx147065 } 2322626dff79Spengcheng chen - Sun Microsystems - Beijing China } 232384f7a9b9Shx147065 2324bcb5c89dSSowmini Varadhan /* 2325bcb5c89dSSowmini Varadhan * Callback functions for get/set properties 2326bcb5c89dSSowmini Varadhan */ 2327afdda45fSVasumathi Sundaram - Sun Microsystems /* ARGSUSED */ 2328bcb5c89dSSowmini Varadhan static int 2329bcb5c89dSSowmini Varadhan wpi_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_name, 2330*0dc2366fSVenugopal Iyer uint_t wldp_length, void *wldp_buf) 2331bcb5c89dSSowmini Varadhan { 2332bcb5c89dSSowmini Varadhan int err = 0; 2333bcb5c89dSSowmini Varadhan wpi_sc_t *sc = (wpi_sc_t *)arg; 2334bcb5c89dSSowmini Varadhan 2335bcb5c89dSSowmini Varadhan err = ieee80211_getprop(&sc->sc_ic, pr_name, wldp_pr_name, 2336*0dc2366fSVenugopal Iyer wldp_length, wldp_buf); 2337bcb5c89dSSowmini Varadhan 2338bcb5c89dSSowmini Varadhan return (err); 2339bcb5c89dSSowmini Varadhan } 2340*0dc2366fSVenugopal Iyer 2341*0dc2366fSVenugopal Iyer static void 2342*0dc2366fSVenugopal Iyer wpi_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 2343*0dc2366fSVenugopal Iyer mac_prop_info_handle_t mph) 2344*0dc2366fSVenugopal Iyer { 2345*0dc2366fSVenugopal Iyer wpi_sc_t *sc = (wpi_sc_t *)arg; 2346*0dc2366fSVenugopal Iyer 2347*0dc2366fSVenugopal Iyer ieee80211_propinfo(&sc->sc_ic, pr_name, wldp_pr_num, mph); 2348*0dc2366fSVenugopal Iyer } 2349*0dc2366fSVenugopal Iyer 2350bcb5c89dSSowmini Varadhan static int 2351bcb5c89dSSowmini Varadhan wpi_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_name, 2352bcb5c89dSSowmini Varadhan uint_t wldp_length, const void *wldp_buf) 2353bcb5c89dSSowmini Varadhan { 2354bcb5c89dSSowmini Varadhan int err; 2355bcb5c89dSSowmini Varadhan wpi_sc_t *sc = (wpi_sc_t *)arg; 2356bcb5c89dSSowmini Varadhan ieee80211com_t *ic = &sc->sc_ic; 2357bcb5c89dSSowmini Varadhan 2358bcb5c89dSSowmini Varadhan err = ieee80211_setprop(ic, pr_name, wldp_pr_name, 2359bcb5c89dSSowmini Varadhan wldp_length, wldp_buf); 2360bcb5c89dSSowmini Varadhan 2361bcb5c89dSSowmini Varadhan if (err == ENETRESET) { 2362bcb5c89dSSowmini Varadhan if (ic->ic_des_esslen) { 2363626dff79Spengcheng chen - Sun Microsystems - Beijing China if (sc->sc_flags & WPI_F_RUNNING) { 2364626dff79Spengcheng chen - Sun Microsystems - Beijing China wpi_m_stop(sc); 2365626dff79Spengcheng chen - Sun Microsystems - Beijing China (void) wpi_m_start(sc); 2366bcb5c89dSSowmini Varadhan (void) ieee80211_new_state(ic, 2367bcb5c89dSSowmini Varadhan IEEE80211_S_SCAN, -1); 2368bcb5c89dSSowmini Varadhan } 2369626dff79Spengcheng chen - Sun Microsystems - Beijing China } 2370bcb5c89dSSowmini Varadhan 2371bcb5c89dSSowmini Varadhan err = 0; 2372bcb5c89dSSowmini Varadhan } 2373bcb5c89dSSowmini Varadhan 2374bcb5c89dSSowmini Varadhan return (err); 2375bcb5c89dSSowmini Varadhan } 2376bcb5c89dSSowmini Varadhan 237784f7a9b9Shx147065 /*ARGSUSED*/ 237884f7a9b9Shx147065 static int 237984f7a9b9Shx147065 wpi_m_stat(void *arg, uint_t stat, uint64_t *val) 238084f7a9b9Shx147065 { 238184f7a9b9Shx147065 wpi_sc_t *sc = (wpi_sc_t *)arg; 238284f7a9b9Shx147065 ieee80211com_t *ic = &sc->sc_ic; 2383626dff79Spengcheng chen - Sun Microsystems - Beijing China ieee80211_node_t *in; 238484f7a9b9Shx147065 238584f7a9b9Shx147065 mutex_enter(&sc->sc_glock); 238684f7a9b9Shx147065 switch (stat) { 238784f7a9b9Shx147065 case MAC_STAT_IFSPEED: 2388626dff79Spengcheng chen - Sun Microsystems - Beijing China in = ic->ic_bss; 238984f7a9b9Shx147065 *val = ((ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) ? 2390626dff79Spengcheng chen - Sun Microsystems - Beijing China IEEE80211_RATE(in->in_txrate) : 2391626dff79Spengcheng chen - Sun Microsystems - Beijing China ic->ic_fixed_rate) / 2 * 1000000; 239284f7a9b9Shx147065 break; 239384f7a9b9Shx147065 case MAC_STAT_NOXMTBUF: 239484f7a9b9Shx147065 *val = sc->sc_tx_nobuf; 239584f7a9b9Shx147065 break; 239684f7a9b9Shx147065 case MAC_STAT_NORCVBUF: 239784f7a9b9Shx147065 *val = sc->sc_rx_nobuf; 239884f7a9b9Shx147065 break; 239984f7a9b9Shx147065 case MAC_STAT_IERRORS: 240084f7a9b9Shx147065 *val = sc->sc_rx_err; 240184f7a9b9Shx147065 break; 240284f7a9b9Shx147065 case MAC_STAT_RBYTES: 240384f7a9b9Shx147065 *val = ic->ic_stats.is_rx_bytes; 240484f7a9b9Shx147065 break; 240584f7a9b9Shx147065 case MAC_STAT_IPACKETS: 240684f7a9b9Shx147065 *val = ic->ic_stats.is_rx_frags; 240784f7a9b9Shx147065 break; 240884f7a9b9Shx147065 case MAC_STAT_OBYTES: 240984f7a9b9Shx147065 *val = ic->ic_stats.is_tx_bytes; 241084f7a9b9Shx147065 break; 241184f7a9b9Shx147065 case MAC_STAT_OPACKETS: 241284f7a9b9Shx147065 *val = ic->ic_stats.is_tx_frags; 241384f7a9b9Shx147065 break; 241484f7a9b9Shx147065 case MAC_STAT_OERRORS: 241584f7a9b9Shx147065 case WIFI_STAT_TX_FAILED: 241684f7a9b9Shx147065 *val = sc->sc_tx_err; 241784f7a9b9Shx147065 break; 241884f7a9b9Shx147065 case WIFI_STAT_TX_RETRANS: 241984f7a9b9Shx147065 *val = sc->sc_tx_retries; 242084f7a9b9Shx147065 break; 242184f7a9b9Shx147065 case WIFI_STAT_FCS_ERRORS: 242284f7a9b9Shx147065 case WIFI_STAT_WEP_ERRORS: 242384f7a9b9Shx147065 case WIFI_STAT_TX_FRAGS: 242484f7a9b9Shx147065 case WIFI_STAT_MCAST_TX: 242584f7a9b9Shx147065 case WIFI_STAT_RTS_SUCCESS: 242684f7a9b9Shx147065 case WIFI_STAT_RTS_FAILURE: 242784f7a9b9Shx147065 case WIFI_STAT_ACK_FAILURE: 242884f7a9b9Shx147065 case WIFI_STAT_RX_FRAGS: 242984f7a9b9Shx147065 case WIFI_STAT_MCAST_RX: 243084f7a9b9Shx147065 case WIFI_STAT_RX_DUPS: 243184f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 243284f7a9b9Shx147065 return (ieee80211_stat(ic, stat, val)); 243384f7a9b9Shx147065 default: 243484f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 243584f7a9b9Shx147065 return (ENOTSUP); 243684f7a9b9Shx147065 } 243784f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 243884f7a9b9Shx147065 243984f7a9b9Shx147065 return (WPI_SUCCESS); 244084f7a9b9Shx147065 244184f7a9b9Shx147065 } 244284f7a9b9Shx147065 244384f7a9b9Shx147065 static int 244484f7a9b9Shx147065 wpi_m_start(void *arg) 244584f7a9b9Shx147065 { 244684f7a9b9Shx147065 wpi_sc_t *sc = (wpi_sc_t *)arg; 244784f7a9b9Shx147065 ieee80211com_t *ic = &sc->sc_ic; 244884f7a9b9Shx147065 int err; 244984f7a9b9Shx147065 245084f7a9b9Shx147065 err = wpi_init(sc); 245184f7a9b9Shx147065 if (err != WPI_SUCCESS) { 245284f7a9b9Shx147065 wpi_stop(sc); 245384f7a9b9Shx147065 DELAY(1000000); 245484f7a9b9Shx147065 err = wpi_init(sc); 245584f7a9b9Shx147065 } 245684f7a9b9Shx147065 245719b23afcShx147065 if (err) { 245819b23afcShx147065 /* 245919b23afcShx147065 * The hw init err(eg. RF is OFF). Return Success to make 246019b23afcShx147065 * the 'plumb' succeed. The wpi_thread() tries to re-init 246119b23afcShx147065 * background. 246219b23afcShx147065 */ 246319b23afcShx147065 mutex_enter(&sc->sc_glock); 246419b23afcShx147065 sc->sc_flags |= WPI_F_HW_ERR_RECOVER; 246519b23afcShx147065 mutex_exit(&sc->sc_glock); 246619b23afcShx147065 return (WPI_SUCCESS); 246719b23afcShx147065 } 246819b23afcShx147065 ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 246919b23afcShx147065 mutex_enter(&sc->sc_glock); 247019b23afcShx147065 sc->sc_flags |= WPI_F_RUNNING; 247119b23afcShx147065 mutex_exit(&sc->sc_glock); 247219b23afcShx147065 247319b23afcShx147065 return (WPI_SUCCESS); 247484f7a9b9Shx147065 } 247584f7a9b9Shx147065 247684f7a9b9Shx147065 static void 247784f7a9b9Shx147065 wpi_m_stop(void *arg) 247884f7a9b9Shx147065 { 247984f7a9b9Shx147065 wpi_sc_t *sc = (wpi_sc_t *)arg; 248084f7a9b9Shx147065 ieee80211com_t *ic = &sc->sc_ic; 248184f7a9b9Shx147065 248284f7a9b9Shx147065 wpi_stop(sc); 248384f7a9b9Shx147065 ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 248484f7a9b9Shx147065 mutex_enter(&sc->sc_mt_lock); 248584f7a9b9Shx147065 sc->sc_flags &= ~WPI_F_HW_ERR_RECOVER; 248684f7a9b9Shx147065 sc->sc_flags &= ~WPI_F_RATE_AUTO_CTL; 248784f7a9b9Shx147065 mutex_exit(&sc->sc_mt_lock); 248819b23afcShx147065 mutex_enter(&sc->sc_glock); 248919b23afcShx147065 sc->sc_flags &= ~WPI_F_RUNNING; 249019b23afcShx147065 mutex_exit(&sc->sc_glock); 249184f7a9b9Shx147065 } 249284f7a9b9Shx147065 249384f7a9b9Shx147065 /*ARGSUSED*/ 249484f7a9b9Shx147065 static int 249584f7a9b9Shx147065 wpi_m_unicst(void *arg, const uint8_t *macaddr) 249684f7a9b9Shx147065 { 249784f7a9b9Shx147065 wpi_sc_t *sc = (wpi_sc_t *)arg; 249884f7a9b9Shx147065 ieee80211com_t *ic = &sc->sc_ic; 249984f7a9b9Shx147065 int err; 250084f7a9b9Shx147065 250184f7a9b9Shx147065 if (!IEEE80211_ADDR_EQ(ic->ic_macaddr, macaddr)) { 250284f7a9b9Shx147065 IEEE80211_ADDR_COPY(ic->ic_macaddr, macaddr); 250384f7a9b9Shx147065 mutex_enter(&sc->sc_glock); 250484f7a9b9Shx147065 err = wpi_config(sc); 250584f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 250684f7a9b9Shx147065 if (err != WPI_SUCCESS) { 250784f7a9b9Shx147065 cmn_err(CE_WARN, 250884f7a9b9Shx147065 "wpi_m_unicst(): " 250984f7a9b9Shx147065 "failed to configure device\n"); 251084f7a9b9Shx147065 goto fail; 251184f7a9b9Shx147065 } 251284f7a9b9Shx147065 } 251384f7a9b9Shx147065 return (WPI_SUCCESS); 251484f7a9b9Shx147065 fail: 251584f7a9b9Shx147065 return (err); 251684f7a9b9Shx147065 } 251784f7a9b9Shx147065 251884f7a9b9Shx147065 /*ARGSUSED*/ 251984f7a9b9Shx147065 static int 252084f7a9b9Shx147065 wpi_m_multicst(void *arg, boolean_t add, const uint8_t *m) 252184f7a9b9Shx147065 { 252284f7a9b9Shx147065 return (WPI_SUCCESS); 252384f7a9b9Shx147065 } 252484f7a9b9Shx147065 252584f7a9b9Shx147065 /*ARGSUSED*/ 252684f7a9b9Shx147065 static int 252784f7a9b9Shx147065 wpi_m_promisc(void *arg, boolean_t on) 252884f7a9b9Shx147065 { 252984f7a9b9Shx147065 return (WPI_SUCCESS); 253084f7a9b9Shx147065 } 253184f7a9b9Shx147065 253284f7a9b9Shx147065 static void 253384f7a9b9Shx147065 wpi_thread(wpi_sc_t *sc) 253484f7a9b9Shx147065 { 253584f7a9b9Shx147065 ieee80211com_t *ic = &sc->sc_ic; 253684f7a9b9Shx147065 clock_t clk; 253784f7a9b9Shx147065 int times = 0, err, n = 0, timeout = 0; 253819b23afcShx147065 uint32_t tmp; 253984f7a9b9Shx147065 254084f7a9b9Shx147065 mutex_enter(&sc->sc_mt_lock); 254184f7a9b9Shx147065 while (sc->sc_mf_thread_switch) { 254219b23afcShx147065 tmp = WPI_READ(sc, WPI_GPIO_CTL); 254319b23afcShx147065 if (tmp & WPI_GPIO_HW_RF_KILL) { 254419b23afcShx147065 sc->sc_flags &= ~WPI_F_RADIO_OFF; 254519b23afcShx147065 } else { 254619b23afcShx147065 sc->sc_flags |= WPI_F_RADIO_OFF; 254719b23afcShx147065 } 254819b23afcShx147065 /* 254919b23afcShx147065 * If in SUSPEND or the RF is OFF, do nothing 255019b23afcShx147065 */ 255119b23afcShx147065 if ((sc->sc_flags & WPI_F_SUSPEND) || 255219b23afcShx147065 (sc->sc_flags & WPI_F_RADIO_OFF)) { 255319b23afcShx147065 mutex_exit(&sc->sc_mt_lock); 255419b23afcShx147065 delay(drv_usectohz(100000)); 255519b23afcShx147065 mutex_enter(&sc->sc_mt_lock); 255619b23afcShx147065 continue; 255719b23afcShx147065 } 255819b23afcShx147065 255984f7a9b9Shx147065 /* 256084f7a9b9Shx147065 * recovery fatal error 256184f7a9b9Shx147065 */ 256284f7a9b9Shx147065 if (ic->ic_mach && 256384f7a9b9Shx147065 (sc->sc_flags & WPI_F_HW_ERR_RECOVER)) { 256484f7a9b9Shx147065 256584f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_FW, 256684f7a9b9Shx147065 "wpi_thread(): " 256784f7a9b9Shx147065 "try to recover fatal hw error: %d\n", times++)); 256884f7a9b9Shx147065 256984f7a9b9Shx147065 wpi_stop(sc); 2570626dff79Spengcheng chen - Sun Microsystems - Beijing China 25716f12def4Spengcheng chen - Sun Microsystems - Beijing China if (WPI_CHK_FAST_RECOVER(sc)) { 25726f12def4Spengcheng chen - Sun Microsystems - Beijing China /* save runtime configuration */ 25736f12def4Spengcheng chen - Sun Microsystems - Beijing China bcopy(&sc->sc_config, &sc->sc_config_save, 25746f12def4Spengcheng chen - Sun Microsystems - Beijing China sizeof (sc->sc_config)); 25756f12def4Spengcheng chen - Sun Microsystems - Beijing China } else { 25766f12def4Spengcheng chen - Sun Microsystems - Beijing China mutex_exit(&sc->sc_mt_lock); 2577626dff79Spengcheng chen - Sun Microsystems - Beijing China ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 257884f7a9b9Shx147065 delay(drv_usectohz(2000000)); 257984f7a9b9Shx147065 mutex_enter(&sc->sc_mt_lock); 25806f12def4Spengcheng chen - Sun Microsystems - Beijing China } 25816f12def4Spengcheng chen - Sun Microsystems - Beijing China 258284f7a9b9Shx147065 err = wpi_init(sc); 258384f7a9b9Shx147065 if (err != WPI_SUCCESS) { 258484f7a9b9Shx147065 n++; 258584f7a9b9Shx147065 if (n < 3) 258684f7a9b9Shx147065 continue; 258784f7a9b9Shx147065 } 258884f7a9b9Shx147065 n = 0; 258919b23afcShx147065 if (!err) 259019b23afcShx147065 sc->sc_flags |= WPI_F_RUNNING; 25916f12def4Spengcheng chen - Sun Microsystems - Beijing China 25926f12def4Spengcheng chen - Sun Microsystems - Beijing China if (!WPI_CHK_FAST_RECOVER(sc) || 25936f12def4Spengcheng chen - Sun Microsystems - Beijing China wpi_fast_recover(sc) != WPI_SUCCESS) { 259484f7a9b9Shx147065 sc->sc_flags &= ~WPI_F_HW_ERR_RECOVER; 25956f12def4Spengcheng chen - Sun Microsystems - Beijing China 259684f7a9b9Shx147065 mutex_exit(&sc->sc_mt_lock); 259784f7a9b9Shx147065 delay(drv_usectohz(2000000)); 259884f7a9b9Shx147065 if (sc->sc_ostate != IEEE80211_S_INIT) 25996f12def4Spengcheng chen - Sun Microsystems - Beijing China ieee80211_new_state(ic, 26006f12def4Spengcheng chen - Sun Microsystems - Beijing China IEEE80211_S_SCAN, 0); 260184f7a9b9Shx147065 mutex_enter(&sc->sc_mt_lock); 260284f7a9b9Shx147065 } 26036f12def4Spengcheng chen - Sun Microsystems - Beijing China } 260484f7a9b9Shx147065 2605d40f4da4Spengcheng chen - Sun Microsystems - Beijing China if (ic->ic_mach && (sc->sc_flags & WPI_F_LAZY_RESUME)) { 2606d40f4da4Spengcheng chen - Sun Microsystems - Beijing China WPI_DBG((WPI_DEBUG_RESUME, 2607d40f4da4Spengcheng chen - Sun Microsystems - Beijing China "wpi_thread(): " 2608d40f4da4Spengcheng chen - Sun Microsystems - Beijing China "lazy resume\n")); 2609d40f4da4Spengcheng chen - Sun Microsystems - Beijing China sc->sc_flags &= ~WPI_F_LAZY_RESUME; 2610d40f4da4Spengcheng chen - Sun Microsystems - Beijing China mutex_exit(&sc->sc_mt_lock); 2611d40f4da4Spengcheng chen - Sun Microsystems - Beijing China /* 2612d40f4da4Spengcheng chen - Sun Microsystems - Beijing China * NB: under WPA mode, this call hangs (door problem?) 2613d40f4da4Spengcheng chen - Sun Microsystems - Beijing China * when called in wpi_attach() and wpi_detach() while 2614d40f4da4Spengcheng chen - Sun Microsystems - Beijing China * system is in the procedure of CPR. To be safe, let 2615d40f4da4Spengcheng chen - Sun Microsystems - Beijing China * the thread do this. 2616d40f4da4Spengcheng chen - Sun Microsystems - Beijing China */ 2617d40f4da4Spengcheng chen - Sun Microsystems - Beijing China ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1); 2618d40f4da4Spengcheng chen - Sun Microsystems - Beijing China mutex_enter(&sc->sc_mt_lock); 2619d40f4da4Spengcheng chen - Sun Microsystems - Beijing China } 2620d40f4da4Spengcheng chen - Sun Microsystems - Beijing China 262184f7a9b9Shx147065 /* 2622626dff79Spengcheng chen - Sun Microsystems - Beijing China * scan next channel 2623626dff79Spengcheng chen - Sun Microsystems - Beijing China */ 2624626dff79Spengcheng chen - Sun Microsystems - Beijing China if (ic->ic_mach && 2625626dff79Spengcheng chen - Sun Microsystems - Beijing China (sc->sc_flags & WPI_F_SCANNING) && sc->sc_scan_next) { 2626626dff79Spengcheng chen - Sun Microsystems - Beijing China 2627626dff79Spengcheng chen - Sun Microsystems - Beijing China WPI_DBG((WPI_DEBUG_SCAN, 2628626dff79Spengcheng chen - Sun Microsystems - Beijing China "wpi_thread(): " 2629626dff79Spengcheng chen - Sun Microsystems - Beijing China "wait for probe response\n")); 2630626dff79Spengcheng chen - Sun Microsystems - Beijing China 2631626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_scan_next--; 2632626dff79Spengcheng chen - Sun Microsystems - Beijing China mutex_exit(&sc->sc_mt_lock); 2633626dff79Spengcheng chen - Sun Microsystems - Beijing China delay(drv_usectohz(200000)); 2634d40f4da4Spengcheng chen - Sun Microsystems - Beijing China if (sc->sc_flags & WPI_F_SCANNING) 2635626dff79Spengcheng chen - Sun Microsystems - Beijing China ieee80211_next_scan(ic); 2636626dff79Spengcheng chen - Sun Microsystems - Beijing China mutex_enter(&sc->sc_mt_lock); 2637626dff79Spengcheng chen - Sun Microsystems - Beijing China } 2638626dff79Spengcheng chen - Sun Microsystems - Beijing China 2639626dff79Spengcheng chen - Sun Microsystems - Beijing China /* 264084f7a9b9Shx147065 * rate ctl 264184f7a9b9Shx147065 */ 264284f7a9b9Shx147065 if (ic->ic_mach && 264384f7a9b9Shx147065 (sc->sc_flags & WPI_F_RATE_AUTO_CTL)) { 264484f7a9b9Shx147065 clk = ddi_get_lbolt(); 264584f7a9b9Shx147065 if (clk > sc->sc_clk + drv_usectohz(500000)) { 264684f7a9b9Shx147065 wpi_amrr_timeout(sc); 264784f7a9b9Shx147065 } 264884f7a9b9Shx147065 } 264984f7a9b9Shx147065 mutex_exit(&sc->sc_mt_lock); 265084f7a9b9Shx147065 delay(drv_usectohz(100000)); 265184f7a9b9Shx147065 mutex_enter(&sc->sc_mt_lock); 265284f7a9b9Shx147065 if (sc->sc_tx_timer) { 265384f7a9b9Shx147065 timeout++; 265484f7a9b9Shx147065 if (timeout == 10) { 265584f7a9b9Shx147065 sc->sc_tx_timer--; 265684f7a9b9Shx147065 if (sc->sc_tx_timer == 0) { 265784f7a9b9Shx147065 sc->sc_flags |= WPI_F_HW_ERR_RECOVER; 265884f7a9b9Shx147065 sc->sc_ostate = IEEE80211_S_RUN; 2659626dff79Spengcheng chen - Sun Microsystems - Beijing China WPI_DBG((WPI_DEBUG_FW, 2660626dff79Spengcheng chen - Sun Microsystems - Beijing China "wpi_thread(): send fail\n")); 266184f7a9b9Shx147065 } 266284f7a9b9Shx147065 timeout = 0; 266384f7a9b9Shx147065 } 266484f7a9b9Shx147065 } 266584f7a9b9Shx147065 } 266684f7a9b9Shx147065 sc->sc_mf_thread = NULL; 266784f7a9b9Shx147065 cv_signal(&sc->sc_mt_cv); 266884f7a9b9Shx147065 mutex_exit(&sc->sc_mt_lock); 266984f7a9b9Shx147065 } 267084f7a9b9Shx147065 267184f7a9b9Shx147065 /* 267284f7a9b9Shx147065 * Extract various information from EEPROM. 267384f7a9b9Shx147065 */ 267484f7a9b9Shx147065 static void 267584f7a9b9Shx147065 wpi_read_eeprom(wpi_sc_t *sc) 267684f7a9b9Shx147065 { 267784f7a9b9Shx147065 ieee80211com_t *ic = &sc->sc_ic; 267884f7a9b9Shx147065 uint16_t val; 267984f7a9b9Shx147065 int i; 268084f7a9b9Shx147065 268184f7a9b9Shx147065 /* read MAC address */ 268284f7a9b9Shx147065 val = wpi_read_prom_word(sc, WPI_EEPROM_MAC + 0); 268384f7a9b9Shx147065 ic->ic_macaddr[0] = val & 0xff; 268484f7a9b9Shx147065 ic->ic_macaddr[1] = val >> 8; 268584f7a9b9Shx147065 val = wpi_read_prom_word(sc, WPI_EEPROM_MAC + 1); 268684f7a9b9Shx147065 ic->ic_macaddr[2] = val & 0xff; 268784f7a9b9Shx147065 ic->ic_macaddr[3] = val >> 8; 268884f7a9b9Shx147065 val = wpi_read_prom_word(sc, WPI_EEPROM_MAC + 2); 268984f7a9b9Shx147065 ic->ic_macaddr[4] = val & 0xff; 269084f7a9b9Shx147065 ic->ic_macaddr[5] = val >> 8; 269184f7a9b9Shx147065 269284f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_EEPROM, 269384f7a9b9Shx147065 "mac:%2x:%2x:%2x:%2x:%2x:%2x\n", 269484f7a9b9Shx147065 ic->ic_macaddr[0], ic->ic_macaddr[1], 269584f7a9b9Shx147065 ic->ic_macaddr[2], ic->ic_macaddr[3], 269684f7a9b9Shx147065 ic->ic_macaddr[4], ic->ic_macaddr[5])); 269784f7a9b9Shx147065 /* read power settings for 2.4GHz channels */ 269884f7a9b9Shx147065 for (i = 0; i < 14; i++) { 269984f7a9b9Shx147065 sc->sc_pwr1[i] = wpi_read_prom_word(sc, WPI_EEPROM_PWR1 + i); 270084f7a9b9Shx147065 sc->sc_pwr2[i] = wpi_read_prom_word(sc, WPI_EEPROM_PWR2 + i); 270184f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_EEPROM, 270284f7a9b9Shx147065 "channel %d pwr1 0x%04x pwr2 0x%04x\n", i + 1, 270384f7a9b9Shx147065 sc->sc_pwr1[i], sc->sc_pwr2[i])); 270484f7a9b9Shx147065 } 270584f7a9b9Shx147065 } 270684f7a9b9Shx147065 270784f7a9b9Shx147065 /* 270884f7a9b9Shx147065 * Send a command to the firmware. 270984f7a9b9Shx147065 */ 271084f7a9b9Shx147065 static int 271184f7a9b9Shx147065 wpi_cmd(wpi_sc_t *sc, int code, const void *buf, int size, int async) 271284f7a9b9Shx147065 { 271384f7a9b9Shx147065 wpi_tx_ring_t *ring = &sc->sc_cmdq; 271484f7a9b9Shx147065 wpi_tx_desc_t *desc; 271584f7a9b9Shx147065 wpi_tx_cmd_t *cmd; 271684f7a9b9Shx147065 271784f7a9b9Shx147065 ASSERT(size <= sizeof (cmd->data)); 271884f7a9b9Shx147065 ASSERT(mutex_owned(&sc->sc_glock)); 271984f7a9b9Shx147065 272084f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_CMD, "wpi_cmd() # code[%d]", code)); 272184f7a9b9Shx147065 desc = ring->data[ring->cur].desc; 272284f7a9b9Shx147065 cmd = ring->data[ring->cur].cmd; 272384f7a9b9Shx147065 272484f7a9b9Shx147065 cmd->code = (uint8_t)code; 272584f7a9b9Shx147065 cmd->flags = 0; 272684f7a9b9Shx147065 cmd->qid = ring->qid; 272784f7a9b9Shx147065 cmd->idx = ring->cur; 272884f7a9b9Shx147065 (void) memcpy(cmd->data, buf, size); 272984f7a9b9Shx147065 273084f7a9b9Shx147065 desc->flags = LE_32(WPI_PAD32(size) << 28 | 1 << 24); 273184f7a9b9Shx147065 desc->segs[0].addr = ring->data[ring->cur].paddr_cmd; 273284f7a9b9Shx147065 desc->segs[0].len = 4 + size; 273384f7a9b9Shx147065 273484f7a9b9Shx147065 /* kick cmd ring */ 273584f7a9b9Shx147065 ring->cur = (ring->cur + 1) % WPI_CMD_RING_COUNT; 273684f7a9b9Shx147065 WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); 273784f7a9b9Shx147065 273884f7a9b9Shx147065 if (async) 273984f7a9b9Shx147065 return (WPI_SUCCESS); 274084f7a9b9Shx147065 else { 274184f7a9b9Shx147065 clock_t clk; 274284f7a9b9Shx147065 sc->sc_flags &= ~WPI_F_CMD_DONE; 274384f7a9b9Shx147065 clk = ddi_get_lbolt() + drv_usectohz(2000000); 274484f7a9b9Shx147065 while (!(sc->sc_flags & WPI_F_CMD_DONE)) { 274584f7a9b9Shx147065 if (cv_timedwait(&sc->sc_cmd_cv, &sc->sc_glock, clk) 274684f7a9b9Shx147065 < 0) 274784f7a9b9Shx147065 break; 274884f7a9b9Shx147065 } 274984f7a9b9Shx147065 if (sc->sc_flags & WPI_F_CMD_DONE) 275084f7a9b9Shx147065 return (WPI_SUCCESS); 275184f7a9b9Shx147065 else 275284f7a9b9Shx147065 return (WPI_FAIL); 275384f7a9b9Shx147065 } 275484f7a9b9Shx147065 } 275584f7a9b9Shx147065 275684f7a9b9Shx147065 /* 275784f7a9b9Shx147065 * Configure h/w multi-rate retries. 275884f7a9b9Shx147065 */ 275984f7a9b9Shx147065 static int 276084f7a9b9Shx147065 wpi_mrr_setup(wpi_sc_t *sc) 276184f7a9b9Shx147065 { 276284f7a9b9Shx147065 wpi_mrr_setup_t mrr; 276384f7a9b9Shx147065 int i, err; 276484f7a9b9Shx147065 276584f7a9b9Shx147065 /* CCK rates (not used with 802.11a) */ 276684f7a9b9Shx147065 for (i = WPI_CCK1; i <= WPI_CCK11; i++) { 276784f7a9b9Shx147065 mrr.rates[i].flags = 0; 276884f7a9b9Shx147065 mrr.rates[i].signal = wpi_ridx_to_signal[i]; 276984f7a9b9Shx147065 /* fallback to the immediate lower CCK rate (if any) */ 277084f7a9b9Shx147065 mrr.rates[i].next = (i == WPI_CCK1) ? WPI_CCK1 : i - 1; 277184f7a9b9Shx147065 /* try one time at this rate before falling back to "next" */ 277284f7a9b9Shx147065 mrr.rates[i].ntries = 1; 277384f7a9b9Shx147065 } 277484f7a9b9Shx147065 277584f7a9b9Shx147065 /* OFDM rates (not used with 802.11b) */ 277684f7a9b9Shx147065 for (i = WPI_OFDM6; i <= WPI_OFDM54; i++) { 277784f7a9b9Shx147065 mrr.rates[i].flags = 0; 277884f7a9b9Shx147065 mrr.rates[i].signal = wpi_ridx_to_signal[i]; 277984f7a9b9Shx147065 /* fallback to the immediate lower OFDM rate (if any) */ 278084f7a9b9Shx147065 mrr.rates[i].next = (i == WPI_OFDM6) ? WPI_OFDM6 : i - 1; 278184f7a9b9Shx147065 /* try one time at this rate before falling back to "next" */ 278284f7a9b9Shx147065 mrr.rates[i].ntries = 1; 278384f7a9b9Shx147065 } 278484f7a9b9Shx147065 278584f7a9b9Shx147065 /* setup MRR for control frames */ 278684f7a9b9Shx147065 mrr.which = LE_32(WPI_MRR_CTL); 278784f7a9b9Shx147065 err = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof (mrr), 1); 278884f7a9b9Shx147065 if (err != WPI_SUCCESS) { 278984f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_MRR, 279084f7a9b9Shx147065 "could not setup MRR for control frames\n")); 279184f7a9b9Shx147065 return (err); 279284f7a9b9Shx147065 } 279384f7a9b9Shx147065 279484f7a9b9Shx147065 /* setup MRR for data frames */ 279584f7a9b9Shx147065 mrr.which = LE_32(WPI_MRR_DATA); 279684f7a9b9Shx147065 err = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof (mrr), 1); 279784f7a9b9Shx147065 if (err != WPI_SUCCESS) { 279884f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_MRR, 279984f7a9b9Shx147065 "could not setup MRR for data frames\n")); 280084f7a9b9Shx147065 return (err); 280184f7a9b9Shx147065 } 280284f7a9b9Shx147065 280384f7a9b9Shx147065 return (WPI_SUCCESS); 280484f7a9b9Shx147065 } 280584f7a9b9Shx147065 280684f7a9b9Shx147065 static void 280784f7a9b9Shx147065 wpi_set_led(wpi_sc_t *sc, uint8_t which, uint8_t off, uint8_t on) 280884f7a9b9Shx147065 { 280984f7a9b9Shx147065 wpi_cmd_led_t led; 281084f7a9b9Shx147065 281184f7a9b9Shx147065 led.which = which; 281284f7a9b9Shx147065 led.unit = LE_32(100000); /* on/off in unit of 100ms */ 281384f7a9b9Shx147065 led.off = off; 281484f7a9b9Shx147065 led.on = on; 281584f7a9b9Shx147065 281684f7a9b9Shx147065 (void) wpi_cmd(sc, WPI_CMD_SET_LED, &led, sizeof (led), 1); 281784f7a9b9Shx147065 } 281884f7a9b9Shx147065 281984f7a9b9Shx147065 static int 282084f7a9b9Shx147065 wpi_auth(wpi_sc_t *sc) 282184f7a9b9Shx147065 { 282284f7a9b9Shx147065 ieee80211com_t *ic = &sc->sc_ic; 282384f7a9b9Shx147065 ieee80211_node_t *in = ic->ic_bss; 282484f7a9b9Shx147065 wpi_node_t node; 282584f7a9b9Shx147065 int err; 282684f7a9b9Shx147065 282784f7a9b9Shx147065 /* update adapter's configuration */ 282884f7a9b9Shx147065 IEEE80211_ADDR_COPY(sc->sc_config.bssid, in->in_bssid); 282984f7a9b9Shx147065 sc->sc_config.chan = ieee80211_chan2ieee(ic, in->in_chan); 283084f7a9b9Shx147065 if (ic->ic_curmode == IEEE80211_MODE_11B) { 283184f7a9b9Shx147065 sc->sc_config.cck_mask = 0x03; 283284f7a9b9Shx147065 sc->sc_config.ofdm_mask = 0; 283384f7a9b9Shx147065 } else if ((in->in_chan != IEEE80211_CHAN_ANYC) && 283484f7a9b9Shx147065 (IEEE80211_IS_CHAN_5GHZ(in->in_chan))) { 283584f7a9b9Shx147065 sc->sc_config.cck_mask = 0; 283684f7a9b9Shx147065 sc->sc_config.ofdm_mask = 0x15; 283784f7a9b9Shx147065 } else { /* assume 802.11b/g */ 283884f7a9b9Shx147065 sc->sc_config.cck_mask = 0x0f; 2839bb5141e1Shx147065 sc->sc_config.ofdm_mask = 0xff; 284084f7a9b9Shx147065 } 284184f7a9b9Shx147065 284284f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_80211, "config chan %d flags %x cck %x ofdm %x" 284384f7a9b9Shx147065 " bssid:%02x:%02x:%02x:%02x:%02x:%2x\n", 284484f7a9b9Shx147065 sc->sc_config.chan, sc->sc_config.flags, 284584f7a9b9Shx147065 sc->sc_config.cck_mask, sc->sc_config.ofdm_mask, 284684f7a9b9Shx147065 sc->sc_config.bssid[0], sc->sc_config.bssid[1], 284784f7a9b9Shx147065 sc->sc_config.bssid[2], sc->sc_config.bssid[3], 284884f7a9b9Shx147065 sc->sc_config.bssid[4], sc->sc_config.bssid[5])); 284984f7a9b9Shx147065 err = wpi_cmd(sc, WPI_CMD_CONFIGURE, &sc->sc_config, 285084f7a9b9Shx147065 sizeof (wpi_config_t), 1); 285184f7a9b9Shx147065 if (err != WPI_SUCCESS) { 285284f7a9b9Shx147065 cmn_err(CE_WARN, "wpi_auth(): failed to configurate chan%d\n", 285384f7a9b9Shx147065 sc->sc_config.chan); 285484f7a9b9Shx147065 return (err); 285584f7a9b9Shx147065 } 285684f7a9b9Shx147065 285784f7a9b9Shx147065 /* add default node */ 285884f7a9b9Shx147065 (void) memset(&node, 0, sizeof (node)); 285984f7a9b9Shx147065 IEEE80211_ADDR_COPY(node.bssid, in->in_bssid); 286084f7a9b9Shx147065 node.id = WPI_ID_BSS; 286184f7a9b9Shx147065 node.rate = wpi_plcp_signal(2); 286284f7a9b9Shx147065 err = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof (node), 1); 286384f7a9b9Shx147065 if (err != WPI_SUCCESS) { 286484f7a9b9Shx147065 cmn_err(CE_WARN, "wpi_auth(): failed to add BSS node\n"); 286584f7a9b9Shx147065 return (err); 286684f7a9b9Shx147065 } 286784f7a9b9Shx147065 286884f7a9b9Shx147065 err = wpi_mrr_setup(sc); 286984f7a9b9Shx147065 if (err != WPI_SUCCESS) { 287084f7a9b9Shx147065 cmn_err(CE_WARN, "wpi_auth(): failed to setup MRR\n"); 287184f7a9b9Shx147065 return (err); 287284f7a9b9Shx147065 } 287384f7a9b9Shx147065 287484f7a9b9Shx147065 return (WPI_SUCCESS); 287584f7a9b9Shx147065 } 287684f7a9b9Shx147065 287784f7a9b9Shx147065 /* 287884f7a9b9Shx147065 * Send a scan request to the firmware. 287984f7a9b9Shx147065 */ 288084f7a9b9Shx147065 static int 288184f7a9b9Shx147065 wpi_scan(wpi_sc_t *sc) 288284f7a9b9Shx147065 { 288384f7a9b9Shx147065 ieee80211com_t *ic = &sc->sc_ic; 288484f7a9b9Shx147065 wpi_tx_ring_t *ring = &sc->sc_cmdq; 288584f7a9b9Shx147065 wpi_tx_desc_t *desc; 288684f7a9b9Shx147065 wpi_tx_data_t *data; 288784f7a9b9Shx147065 wpi_tx_cmd_t *cmd; 288884f7a9b9Shx147065 wpi_scan_hdr_t *hdr; 288984f7a9b9Shx147065 wpi_scan_chan_t *chan; 289084f7a9b9Shx147065 struct ieee80211_frame *wh; 289184f7a9b9Shx147065 ieee80211_node_t *in = ic->ic_bss; 2892626dff79Spengcheng chen - Sun Microsystems - Beijing China uint8_t essid[IEEE80211_NWID_LEN+1]; 289384f7a9b9Shx147065 struct ieee80211_rateset *rs; 289484f7a9b9Shx147065 enum ieee80211_phymode mode; 289584f7a9b9Shx147065 uint8_t *frm; 289684f7a9b9Shx147065 int i, pktlen, nrates; 289784f7a9b9Shx147065 2898626dff79Spengcheng chen - Sun Microsystems - Beijing China /* previous scan not completed */ 2899626dff79Spengcheng chen - Sun Microsystems - Beijing China if (sc->sc_scan_pending) { 2900626dff79Spengcheng chen - Sun Microsystems - Beijing China WPI_DBG((WPI_DEBUG_SCAN, "previous scan not completed\n")); 2901626dff79Spengcheng chen - Sun Microsystems - Beijing China return (WPI_SUCCESS); 2902626dff79Spengcheng chen - Sun Microsystems - Beijing China } 2903626dff79Spengcheng chen - Sun Microsystems - Beijing China 290484f7a9b9Shx147065 data = &ring->data[ring->cur]; 290584f7a9b9Shx147065 desc = data->desc; 290684f7a9b9Shx147065 cmd = (wpi_tx_cmd_t *)data->dma_data.mem_va; 290784f7a9b9Shx147065 290884f7a9b9Shx147065 cmd->code = WPI_CMD_SCAN; 290984f7a9b9Shx147065 cmd->flags = 0; 291084f7a9b9Shx147065 cmd->qid = ring->qid; 291184f7a9b9Shx147065 cmd->idx = ring->cur; 291284f7a9b9Shx147065 291384f7a9b9Shx147065 hdr = (wpi_scan_hdr_t *)cmd->data; 291484f7a9b9Shx147065 (void) memset(hdr, 0, sizeof (wpi_scan_hdr_t)); 291584f7a9b9Shx147065 hdr->first = 1; 2916626dff79Spengcheng chen - Sun Microsystems - Beijing China hdr->nchan = 1; 291784f7a9b9Shx147065 hdr->len = hdr->nchan * sizeof (wpi_scan_chan_t); 2918626dff79Spengcheng chen - Sun Microsystems - Beijing China hdr->quiet = LE_16(50); 291984f7a9b9Shx147065 hdr->threshold = LE_16(1); 292084f7a9b9Shx147065 hdr->filter = LE_32(5); 292184f7a9b9Shx147065 hdr->rate = wpi_plcp_signal(2); 292284f7a9b9Shx147065 hdr->id = WPI_ID_BROADCAST; 292384f7a9b9Shx147065 hdr->mask = LE_32(0xffffffff); 292484f7a9b9Shx147065 hdr->esslen = ic->ic_des_esslen; 2925626dff79Spengcheng chen - Sun Microsystems - Beijing China 2926626dff79Spengcheng chen - Sun Microsystems - Beijing China if (ic->ic_des_esslen) { 2927626dff79Spengcheng chen - Sun Microsystems - Beijing China bcopy(ic->ic_des_essid, essid, ic->ic_des_esslen); 2928626dff79Spengcheng chen - Sun Microsystems - Beijing China essid[ic->ic_des_esslen] = '\0'; 2929626dff79Spengcheng chen - Sun Microsystems - Beijing China WPI_DBG((WPI_DEBUG_SCAN, "directed scan %s\n", essid)); 2930626dff79Spengcheng chen - Sun Microsystems - Beijing China 293184f7a9b9Shx147065 bcopy(ic->ic_des_essid, hdr->essid, ic->ic_des_esslen); 2932626dff79Spengcheng chen - Sun Microsystems - Beijing China } else { 293384f7a9b9Shx147065 bzero(hdr->essid, sizeof (hdr->essid)); 2934626dff79Spengcheng chen - Sun Microsystems - Beijing China } 2935626dff79Spengcheng chen - Sun Microsystems - Beijing China 293684f7a9b9Shx147065 /* 293784f7a9b9Shx147065 * Build a probe request frame. Most of the following code is a 293884f7a9b9Shx147065 * copy & paste of what is done in net80211. Unfortunately, the 293984f7a9b9Shx147065 * functions to add IEs are static and thus can't be reused here. 294084f7a9b9Shx147065 */ 294184f7a9b9Shx147065 wh = (struct ieee80211_frame *)(hdr + 1); 294284f7a9b9Shx147065 wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | 294384f7a9b9Shx147065 IEEE80211_FC0_SUBTYPE_PROBE_REQ; 294484f7a9b9Shx147065 wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 294584f7a9b9Shx147065 (void) memset(wh->i_addr1, 0xff, 6); 294684f7a9b9Shx147065 IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_macaddr); 294784f7a9b9Shx147065 (void) memset(wh->i_addr3, 0xff, 6); 294884f7a9b9Shx147065 *(uint16_t *)&wh->i_dur[0] = 0; /* filled by h/w */ 294984f7a9b9Shx147065 *(uint16_t *)&wh->i_seq[0] = 0; /* filled by h/w */ 295084f7a9b9Shx147065 295184f7a9b9Shx147065 frm = (uint8_t *)(wh + 1); 295284f7a9b9Shx147065 295384f7a9b9Shx147065 /* add essid IE */ 2954626dff79Spengcheng chen - Sun Microsystems - Beijing China if (in->in_esslen) { 2955626dff79Spengcheng chen - Sun Microsystems - Beijing China bcopy(in->in_essid, essid, in->in_esslen); 2956626dff79Spengcheng chen - Sun Microsystems - Beijing China essid[in->in_esslen] = '\0'; 2957626dff79Spengcheng chen - Sun Microsystems - Beijing China WPI_DBG((WPI_DEBUG_SCAN, "probe with ESSID %s\n", 2958626dff79Spengcheng chen - Sun Microsystems - Beijing China essid)); 2959626dff79Spengcheng chen - Sun Microsystems - Beijing China } 296084f7a9b9Shx147065 *frm++ = IEEE80211_ELEMID_SSID; 296184f7a9b9Shx147065 *frm++ = in->in_esslen; 296284f7a9b9Shx147065 (void) memcpy(frm, in->in_essid, in->in_esslen); 296384f7a9b9Shx147065 frm += in->in_esslen; 296484f7a9b9Shx147065 296584f7a9b9Shx147065 mode = ieee80211_chan2mode(ic, ic->ic_curchan); 296684f7a9b9Shx147065 rs = &ic->ic_sup_rates[mode]; 296784f7a9b9Shx147065 296884f7a9b9Shx147065 /* add supported rates IE */ 296984f7a9b9Shx147065 *frm++ = IEEE80211_ELEMID_RATES; 297084f7a9b9Shx147065 nrates = rs->ir_nrates; 297184f7a9b9Shx147065 if (nrates > IEEE80211_RATE_SIZE) 297284f7a9b9Shx147065 nrates = IEEE80211_RATE_SIZE; 297384f7a9b9Shx147065 *frm++ = (uint8_t)nrates; 297484f7a9b9Shx147065 (void) memcpy(frm, rs->ir_rates, nrates); 297584f7a9b9Shx147065 frm += nrates; 297684f7a9b9Shx147065 297784f7a9b9Shx147065 /* add supported xrates IE */ 297884f7a9b9Shx147065 if (rs->ir_nrates > IEEE80211_RATE_SIZE) { 297984f7a9b9Shx147065 nrates = rs->ir_nrates - IEEE80211_RATE_SIZE; 298084f7a9b9Shx147065 *frm++ = IEEE80211_ELEMID_XRATES; 298184f7a9b9Shx147065 *frm++ = (uint8_t)nrates; 298284f7a9b9Shx147065 (void) memcpy(frm, rs->ir_rates + IEEE80211_RATE_SIZE, nrates); 298384f7a9b9Shx147065 frm += nrates; 298484f7a9b9Shx147065 } 298584f7a9b9Shx147065 298684f7a9b9Shx147065 /* add optionnal IE (usually an RSN IE) */ 298784f7a9b9Shx147065 if (ic->ic_opt_ie != NULL) { 298884f7a9b9Shx147065 (void) memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len); 298984f7a9b9Shx147065 frm += ic->ic_opt_ie_len; 299084f7a9b9Shx147065 } 299184f7a9b9Shx147065 299284f7a9b9Shx147065 /* setup length of probe request */ 299322eb7cb5Sgd78059 hdr->pbrlen = LE_16((uintptr_t)frm - (uintptr_t)wh); 299484f7a9b9Shx147065 299584f7a9b9Shx147065 /* align on a 4-byte boundary */ 299684f7a9b9Shx147065 chan = (wpi_scan_chan_t *)frm; 299784f7a9b9Shx147065 for (i = 1; i <= hdr->nchan; i++, chan++) { 2998626dff79Spengcheng chen - Sun Microsystems - Beijing China if (ic->ic_des_esslen) { 2999626dff79Spengcheng chen - Sun Microsystems - Beijing China chan->flags = 0x3; 3000626dff79Spengcheng chen - Sun Microsystems - Beijing China } else { 3001626dff79Spengcheng chen - Sun Microsystems - Beijing China chan->flags = 0x1; 3002626dff79Spengcheng chen - Sun Microsystems - Beijing China } 3003626dff79Spengcheng chen - Sun Microsystems - Beijing China chan->chan = ieee80211_chan2ieee(ic, ic->ic_curchan); 300484f7a9b9Shx147065 chan->magic = LE_16(0x62ab); 3005626dff79Spengcheng chen - Sun Microsystems - Beijing China chan->active = LE_16(50); 300684f7a9b9Shx147065 chan->passive = LE_16(120); 300784f7a9b9Shx147065 300884f7a9b9Shx147065 frm += sizeof (wpi_scan_chan_t); 300984f7a9b9Shx147065 } 301084f7a9b9Shx147065 301122eb7cb5Sgd78059 pktlen = (uintptr_t)frm - (uintptr_t)cmd; 301284f7a9b9Shx147065 301384f7a9b9Shx147065 desc->flags = LE_32(WPI_PAD32(pktlen) << 28 | 1 << 24); 301484f7a9b9Shx147065 desc->segs[0].addr = LE_32(data->dma_data.cookie.dmac_address); 301584f7a9b9Shx147065 desc->segs[0].len = LE_32(pktlen); 301684f7a9b9Shx147065 301784f7a9b9Shx147065 WPI_DMA_SYNC(data->dma_data, DDI_DMA_SYNC_FORDEV); 301884f7a9b9Shx147065 WPI_DMA_SYNC(ring->dma_desc, DDI_DMA_SYNC_FORDEV); 301984f7a9b9Shx147065 302084f7a9b9Shx147065 /* kick cmd ring */ 302184f7a9b9Shx147065 ring->cur = (ring->cur + 1) % WPI_CMD_RING_COUNT; 302284f7a9b9Shx147065 WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); 302384f7a9b9Shx147065 3024626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_scan_pending = 1; 3025626dff79Spengcheng chen - Sun Microsystems - Beijing China 302684f7a9b9Shx147065 return (WPI_SUCCESS); /* will be notified async. of failure/success */ 302784f7a9b9Shx147065 } 302884f7a9b9Shx147065 302984f7a9b9Shx147065 static int 303084f7a9b9Shx147065 wpi_config(wpi_sc_t *sc) 303184f7a9b9Shx147065 { 303284f7a9b9Shx147065 ieee80211com_t *ic = &sc->sc_ic; 303384f7a9b9Shx147065 wpi_txpower_t txpower; 303484f7a9b9Shx147065 wpi_power_t power; 303584f7a9b9Shx147065 #ifdef WPI_BLUE_COEXISTENCE 303684f7a9b9Shx147065 wpi_bluetooth_t bluetooth; 303784f7a9b9Shx147065 #endif 303884f7a9b9Shx147065 wpi_node_t node; 303984f7a9b9Shx147065 int err; 304084f7a9b9Shx147065 304184f7a9b9Shx147065 /* Intel's binary only daemon is a joke.. */ 304284f7a9b9Shx147065 304384f7a9b9Shx147065 /* set Tx power for 2.4GHz channels (values read from EEPROM) */ 304484f7a9b9Shx147065 (void) memset(&txpower, 0, sizeof (txpower)); 304584f7a9b9Shx147065 (void) memcpy(txpower.pwr1, sc->sc_pwr1, 14 * sizeof (uint16_t)); 304684f7a9b9Shx147065 (void) memcpy(txpower.pwr2, sc->sc_pwr2, 14 * sizeof (uint16_t)); 304784f7a9b9Shx147065 err = wpi_cmd(sc, WPI_CMD_TXPOWER, &txpower, sizeof (txpower), 0); 304884f7a9b9Shx147065 if (err != WPI_SUCCESS) { 304984f7a9b9Shx147065 cmn_err(CE_WARN, "wpi_config(): failed to set txpower\n"); 305084f7a9b9Shx147065 return (err); 305184f7a9b9Shx147065 } 305284f7a9b9Shx147065 305384f7a9b9Shx147065 /* set power mode */ 305484f7a9b9Shx147065 (void) memset(&power, 0, sizeof (power)); 305584f7a9b9Shx147065 power.flags = LE_32(0x8); 305684f7a9b9Shx147065 err = wpi_cmd(sc, WPI_CMD_SET_POWER_MODE, &power, sizeof (power), 0); 305784f7a9b9Shx147065 if (err != WPI_SUCCESS) { 305884f7a9b9Shx147065 cmn_err(CE_WARN, "wpi_config(): failed to set power mode\n"); 305984f7a9b9Shx147065 return (err); 306084f7a9b9Shx147065 } 306184f7a9b9Shx147065 #ifdef WPI_BLUE_COEXISTENCE 306284f7a9b9Shx147065 /* configure bluetooth coexistence */ 306384f7a9b9Shx147065 (void) memset(&bluetooth, 0, sizeof (bluetooth)); 306484f7a9b9Shx147065 bluetooth.flags = 3; 306584f7a9b9Shx147065 bluetooth.lead = 0xaa; 306684f7a9b9Shx147065 bluetooth.kill = 1; 306784f7a9b9Shx147065 err = wpi_cmd(sc, WPI_CMD_BLUETOOTH, &bluetooth, 306884f7a9b9Shx147065 sizeof (bluetooth), 0); 306984f7a9b9Shx147065 if (err != WPI_SUCCESS) { 307084f7a9b9Shx147065 cmn_err(CE_WARN, 307184f7a9b9Shx147065 "wpi_config(): " 307284f7a9b9Shx147065 "failed to configurate bluetooth coexistence\n"); 307384f7a9b9Shx147065 return (err); 307484f7a9b9Shx147065 } 307584f7a9b9Shx147065 #endif 307684f7a9b9Shx147065 /* configure adapter */ 307784f7a9b9Shx147065 (void) memset(&sc->sc_config, 0, sizeof (wpi_config_t)); 307884f7a9b9Shx147065 IEEE80211_ADDR_COPY(sc->sc_config.myaddr, ic->ic_macaddr); 307984f7a9b9Shx147065 sc->sc_config.chan = ieee80211_chan2ieee(ic, ic->ic_curchan); 308084f7a9b9Shx147065 sc->sc_config.flags = LE_32(WPI_CONFIG_TSF | WPI_CONFIG_AUTO | 308184f7a9b9Shx147065 WPI_CONFIG_24GHZ); 308284f7a9b9Shx147065 sc->sc_config.filter = 0; 308384f7a9b9Shx147065 switch (ic->ic_opmode) { 308484f7a9b9Shx147065 case IEEE80211_M_STA: 308584f7a9b9Shx147065 sc->sc_config.mode = WPI_MODE_STA; 3086bb5141e1Shx147065 sc->sc_config.filter |= LE_32(WPI_FILTER_MULTICAST); 308784f7a9b9Shx147065 break; 308884f7a9b9Shx147065 case IEEE80211_M_IBSS: 308984f7a9b9Shx147065 case IEEE80211_M_AHDEMO: 309084f7a9b9Shx147065 sc->sc_config.mode = WPI_MODE_IBSS; 309184f7a9b9Shx147065 break; 309284f7a9b9Shx147065 case IEEE80211_M_HOSTAP: 309384f7a9b9Shx147065 sc->sc_config.mode = WPI_MODE_HOSTAP; 309484f7a9b9Shx147065 break; 309584f7a9b9Shx147065 case IEEE80211_M_MONITOR: 309684f7a9b9Shx147065 sc->sc_config.mode = WPI_MODE_MONITOR; 309784f7a9b9Shx147065 sc->sc_config.filter |= LE_32(WPI_FILTER_MULTICAST | 309884f7a9b9Shx147065 WPI_FILTER_CTL | WPI_FILTER_PROMISC); 309984f7a9b9Shx147065 break; 310084f7a9b9Shx147065 } 310184f7a9b9Shx147065 sc->sc_config.cck_mask = 0x0f; /* not yet negotiated */ 310284f7a9b9Shx147065 sc->sc_config.ofdm_mask = 0xff; /* not yet negotiated */ 310384f7a9b9Shx147065 err = wpi_cmd(sc, WPI_CMD_CONFIGURE, &sc->sc_config, 310484f7a9b9Shx147065 sizeof (wpi_config_t), 0); 310584f7a9b9Shx147065 if (err != WPI_SUCCESS) { 310684f7a9b9Shx147065 cmn_err(CE_WARN, "wpi_config(): " 310784f7a9b9Shx147065 "failed to set configure command\n"); 310884f7a9b9Shx147065 return (err); 310984f7a9b9Shx147065 } 311084f7a9b9Shx147065 311184f7a9b9Shx147065 /* add broadcast node */ 311284f7a9b9Shx147065 (void) memset(&node, 0, sizeof (node)); 311384f7a9b9Shx147065 (void) memset(node.bssid, 0xff, 6); 311484f7a9b9Shx147065 node.id = WPI_ID_BROADCAST; 311584f7a9b9Shx147065 node.rate = wpi_plcp_signal(2); 311684f7a9b9Shx147065 err = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof (node), 0); 311784f7a9b9Shx147065 if (err != WPI_SUCCESS) { 311884f7a9b9Shx147065 cmn_err(CE_WARN, "wpi_config(): " 311984f7a9b9Shx147065 "failed to add broadcast node\n"); 312084f7a9b9Shx147065 return (err); 312184f7a9b9Shx147065 } 312284f7a9b9Shx147065 312384f7a9b9Shx147065 return (WPI_SUCCESS); 312484f7a9b9Shx147065 } 312584f7a9b9Shx147065 312684f7a9b9Shx147065 static void 312784f7a9b9Shx147065 wpi_stop_master(wpi_sc_t *sc) 312884f7a9b9Shx147065 { 312984f7a9b9Shx147065 uint32_t tmp; 313084f7a9b9Shx147065 int ntries; 313184f7a9b9Shx147065 313284f7a9b9Shx147065 tmp = WPI_READ(sc, WPI_RESET); 313384f7a9b9Shx147065 WPI_WRITE(sc, WPI_RESET, tmp | WPI_STOP_MASTER); 313484f7a9b9Shx147065 313584f7a9b9Shx147065 tmp = WPI_READ(sc, WPI_GPIO_CTL); 313684f7a9b9Shx147065 if ((tmp & WPI_GPIO_PWR_STATUS) == WPI_GPIO_PWR_SLEEP) 313784f7a9b9Shx147065 return; /* already asleep */ 313884f7a9b9Shx147065 313984f7a9b9Shx147065 for (ntries = 0; ntries < 2000; ntries++) { 314084f7a9b9Shx147065 if (WPI_READ(sc, WPI_RESET) & WPI_MASTER_DISABLED) 314184f7a9b9Shx147065 break; 314284f7a9b9Shx147065 DELAY(1000); 314384f7a9b9Shx147065 } 314484f7a9b9Shx147065 if (ntries == 2000) 314584f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_HW, "timeout waiting for master\n")); 314684f7a9b9Shx147065 } 314784f7a9b9Shx147065 314884f7a9b9Shx147065 static int 314984f7a9b9Shx147065 wpi_power_up(wpi_sc_t *sc) 315084f7a9b9Shx147065 { 315184f7a9b9Shx147065 uint32_t tmp; 315284f7a9b9Shx147065 int ntries; 315384f7a9b9Shx147065 315484f7a9b9Shx147065 wpi_mem_lock(sc); 315584f7a9b9Shx147065 tmp = wpi_mem_read(sc, WPI_MEM_POWER); 315684f7a9b9Shx147065 wpi_mem_write(sc, WPI_MEM_POWER, tmp & ~0x03000000); 315784f7a9b9Shx147065 wpi_mem_unlock(sc); 315884f7a9b9Shx147065 315984f7a9b9Shx147065 for (ntries = 0; ntries < 5000; ntries++) { 316084f7a9b9Shx147065 if (WPI_READ(sc, WPI_GPIO_STATUS) & WPI_POWERED) 316184f7a9b9Shx147065 break; 316284f7a9b9Shx147065 DELAY(10); 316384f7a9b9Shx147065 } 316484f7a9b9Shx147065 if (ntries == 5000) { 316584f7a9b9Shx147065 cmn_err(CE_WARN, 316684f7a9b9Shx147065 "wpi_power_up(): timeout waiting for NIC to power up\n"); 316784f7a9b9Shx147065 return (ETIMEDOUT); 316884f7a9b9Shx147065 } 316984f7a9b9Shx147065 return (WPI_SUCCESS); 317084f7a9b9Shx147065 } 317184f7a9b9Shx147065 317284f7a9b9Shx147065 static int 317384f7a9b9Shx147065 wpi_reset(wpi_sc_t *sc) 317484f7a9b9Shx147065 { 317584f7a9b9Shx147065 uint32_t tmp; 317684f7a9b9Shx147065 int ntries; 317784f7a9b9Shx147065 317884f7a9b9Shx147065 /* clear any pending interrupts */ 317984f7a9b9Shx147065 WPI_WRITE(sc, WPI_INTR, 0xffffffff); 318084f7a9b9Shx147065 318184f7a9b9Shx147065 tmp = WPI_READ(sc, WPI_PLL_CTL); 318284f7a9b9Shx147065 WPI_WRITE(sc, WPI_PLL_CTL, tmp | WPI_PLL_INIT); 318384f7a9b9Shx147065 318484f7a9b9Shx147065 tmp = WPI_READ(sc, WPI_CHICKEN); 318584f7a9b9Shx147065 WPI_WRITE(sc, WPI_CHICKEN, tmp | WPI_CHICKEN_RXNOLOS); 318684f7a9b9Shx147065 318784f7a9b9Shx147065 tmp = WPI_READ(sc, WPI_GPIO_CTL); 318884f7a9b9Shx147065 WPI_WRITE(sc, WPI_GPIO_CTL, tmp | WPI_GPIO_INIT); 318984f7a9b9Shx147065 319084f7a9b9Shx147065 /* wait for clock stabilization */ 319184f7a9b9Shx147065 for (ntries = 0; ntries < 1000; ntries++) { 319284f7a9b9Shx147065 if (WPI_READ(sc, WPI_GPIO_CTL) & WPI_GPIO_CLOCK) 319384f7a9b9Shx147065 break; 319484f7a9b9Shx147065 DELAY(10); 319584f7a9b9Shx147065 } 319684f7a9b9Shx147065 if (ntries == 1000) { 319784f7a9b9Shx147065 cmn_err(CE_WARN, 319884f7a9b9Shx147065 "wpi_reset(): timeout waiting for clock stabilization\n"); 319984f7a9b9Shx147065 return (ETIMEDOUT); 320084f7a9b9Shx147065 } 320184f7a9b9Shx147065 320284f7a9b9Shx147065 /* initialize EEPROM */ 320384f7a9b9Shx147065 tmp = WPI_READ(sc, WPI_EEPROM_STATUS); 320484f7a9b9Shx147065 if ((tmp & WPI_EEPROM_VERSION) == 0) { 320584f7a9b9Shx147065 cmn_err(CE_WARN, "wpi_reset(): EEPROM not found\n"); 320684f7a9b9Shx147065 return (EIO); 320784f7a9b9Shx147065 } 320884f7a9b9Shx147065 WPI_WRITE(sc, WPI_EEPROM_STATUS, tmp & ~WPI_EEPROM_LOCKED); 320984f7a9b9Shx147065 321084f7a9b9Shx147065 return (WPI_SUCCESS); 321184f7a9b9Shx147065 } 321284f7a9b9Shx147065 321384f7a9b9Shx147065 static void 321484f7a9b9Shx147065 wpi_hw_config(wpi_sc_t *sc) 321584f7a9b9Shx147065 { 321684f7a9b9Shx147065 uint16_t val; 321784f7a9b9Shx147065 uint32_t hw; 321884f7a9b9Shx147065 321984f7a9b9Shx147065 /* voodoo from the Linux "driver".. */ 322084f7a9b9Shx147065 hw = WPI_READ(sc, WPI_HWCONFIG); 322184f7a9b9Shx147065 322284f7a9b9Shx147065 if ((sc->sc_rev & 0xc0) == 0x40) 322384f7a9b9Shx147065 hw |= WPI_HW_ALM_MB; 322484f7a9b9Shx147065 else if (!(sc->sc_rev & 0x80)) 322584f7a9b9Shx147065 hw |= WPI_HW_ALM_MM; 322684f7a9b9Shx147065 322784f7a9b9Shx147065 val = wpi_read_prom_word(sc, WPI_EEPROM_CAPABILITIES); 322884f7a9b9Shx147065 if ((val & 0xff) == 0x80) 322984f7a9b9Shx147065 hw |= WPI_HW_SKU_MRC; 323084f7a9b9Shx147065 323184f7a9b9Shx147065 val = wpi_read_prom_word(sc, WPI_EEPROM_REVISION); 323284f7a9b9Shx147065 hw &= ~WPI_HW_REV_D; 323384f7a9b9Shx147065 if ((val & 0xf0) == 0xd0) 323484f7a9b9Shx147065 hw |= WPI_HW_REV_D; 323584f7a9b9Shx147065 323684f7a9b9Shx147065 val = wpi_read_prom_word(sc, WPI_EEPROM_TYPE); 323784f7a9b9Shx147065 if ((val & 0xff) > 1) 323884f7a9b9Shx147065 hw |= WPI_HW_TYPE_B; 323984f7a9b9Shx147065 324084f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_HW, "setting h/w config %x\n", hw)); 324184f7a9b9Shx147065 WPI_WRITE(sc, WPI_HWCONFIG, hw); 324284f7a9b9Shx147065 } 324384f7a9b9Shx147065 324484f7a9b9Shx147065 static int 324584f7a9b9Shx147065 wpi_init(wpi_sc_t *sc) 324684f7a9b9Shx147065 { 324784f7a9b9Shx147065 uint32_t tmp; 324884f7a9b9Shx147065 int qid, ntries, err; 324984f7a9b9Shx147065 clock_t clk; 325084f7a9b9Shx147065 325184f7a9b9Shx147065 mutex_enter(&sc->sc_glock); 325284f7a9b9Shx147065 sc->sc_flags &= ~WPI_F_FW_INIT; 325384f7a9b9Shx147065 325484f7a9b9Shx147065 (void) wpi_reset(sc); 325584f7a9b9Shx147065 325684f7a9b9Shx147065 wpi_mem_lock(sc); 325784f7a9b9Shx147065 wpi_mem_write(sc, WPI_MEM_CLOCK1, 0xa00); 325884f7a9b9Shx147065 DELAY(20); 325984f7a9b9Shx147065 tmp = wpi_mem_read(sc, WPI_MEM_PCIDEV); 326084f7a9b9Shx147065 wpi_mem_write(sc, WPI_MEM_PCIDEV, tmp | 0x800); 326184f7a9b9Shx147065 wpi_mem_unlock(sc); 326284f7a9b9Shx147065 326384f7a9b9Shx147065 (void) wpi_power_up(sc); 326484f7a9b9Shx147065 wpi_hw_config(sc); 326584f7a9b9Shx147065 326619b23afcShx147065 tmp = WPI_READ(sc, WPI_GPIO_CTL); 326719b23afcShx147065 if (!(tmp & WPI_GPIO_HW_RF_KILL)) { 326819b23afcShx147065 cmn_err(CE_WARN, "wpi_init(): Radio transmitter is off\n"); 326919b23afcShx147065 goto fail1; 327019b23afcShx147065 } 327119b23afcShx147065 327284f7a9b9Shx147065 /* init Rx ring */ 327384f7a9b9Shx147065 wpi_mem_lock(sc); 327484f7a9b9Shx147065 WPI_WRITE(sc, WPI_RX_BASE, sc->sc_rxq.dma_desc.cookie.dmac_address); 327584f7a9b9Shx147065 WPI_WRITE(sc, WPI_RX_RIDX_PTR, 327684f7a9b9Shx147065 (uint32_t)(sc->sc_dma_sh.cookie.dmac_address + 327784f7a9b9Shx147065 offsetof(wpi_shared_t, next))); 327884f7a9b9Shx147065 WPI_WRITE(sc, WPI_RX_WIDX, (WPI_RX_RING_COUNT - 1) & (~7)); 327984f7a9b9Shx147065 WPI_WRITE(sc, WPI_RX_CONFIG, 0xa9601010); 328084f7a9b9Shx147065 wpi_mem_unlock(sc); 328184f7a9b9Shx147065 328284f7a9b9Shx147065 /* init Tx rings */ 328384f7a9b9Shx147065 wpi_mem_lock(sc); 328484f7a9b9Shx147065 wpi_mem_write(sc, WPI_MEM_MODE, 2); /* bypass mode */ 328584f7a9b9Shx147065 wpi_mem_write(sc, WPI_MEM_RA, 1); /* enable RA0 */ 328684f7a9b9Shx147065 wpi_mem_write(sc, WPI_MEM_TXCFG, 0x3f); /* enable all 6 Tx rings */ 328784f7a9b9Shx147065 wpi_mem_write(sc, WPI_MEM_BYPASS1, 0x10000); 328884f7a9b9Shx147065 wpi_mem_write(sc, WPI_MEM_BYPASS2, 0x30002); 328984f7a9b9Shx147065 wpi_mem_write(sc, WPI_MEM_MAGIC4, 4); 329084f7a9b9Shx147065 wpi_mem_write(sc, WPI_MEM_MAGIC5, 5); 329184f7a9b9Shx147065 329284f7a9b9Shx147065 WPI_WRITE(sc, WPI_TX_BASE_PTR, sc->sc_dma_sh.cookie.dmac_address); 329384f7a9b9Shx147065 WPI_WRITE(sc, WPI_MSG_CONFIG, 0xffff05a5); 329484f7a9b9Shx147065 329584f7a9b9Shx147065 for (qid = 0; qid < 6; qid++) { 329684f7a9b9Shx147065 WPI_WRITE(sc, WPI_TX_CTL(qid), 0); 329784f7a9b9Shx147065 WPI_WRITE(sc, WPI_TX_BASE(qid), 0); 329884f7a9b9Shx147065 WPI_WRITE(sc, WPI_TX_CONFIG(qid), 0x80200008); 329984f7a9b9Shx147065 } 330084f7a9b9Shx147065 wpi_mem_unlock(sc); 330184f7a9b9Shx147065 330284f7a9b9Shx147065 /* clear "radio off" and "disable command" bits (reversed logic) */ 330384f7a9b9Shx147065 WPI_WRITE(sc, WPI_UCODE_CLR, WPI_RADIO_OFF); 330484f7a9b9Shx147065 WPI_WRITE(sc, WPI_UCODE_CLR, WPI_DISABLE_CMD); 330584f7a9b9Shx147065 330684f7a9b9Shx147065 /* clear any pending interrupts */ 330784f7a9b9Shx147065 WPI_WRITE(sc, WPI_INTR, 0xffffffff); 330884f7a9b9Shx147065 330984f7a9b9Shx147065 /* enable interrupts */ 331084f7a9b9Shx147065 WPI_WRITE(sc, WPI_MASK, WPI_INTR_MASK); 331184f7a9b9Shx147065 331284f7a9b9Shx147065 /* load firmware boot code into NIC */ 331384f7a9b9Shx147065 err = wpi_load_microcode(sc); 331484f7a9b9Shx147065 if (err != WPI_SUCCESS) { 331584f7a9b9Shx147065 cmn_err(CE_WARN, "wpi_init(): failed to load microcode\n"); 331684f7a9b9Shx147065 goto fail1; 331784f7a9b9Shx147065 } 331884f7a9b9Shx147065 331984f7a9b9Shx147065 /* load firmware .text segment into NIC */ 332084f7a9b9Shx147065 err = wpi_load_firmware(sc, WPI_FW_TEXT); 332184f7a9b9Shx147065 if (err != WPI_SUCCESS) { 332284f7a9b9Shx147065 cmn_err(CE_WARN, "wpi_init(): " 332384f7a9b9Shx147065 "failed to load firmware(text)\n"); 332484f7a9b9Shx147065 goto fail1; 332584f7a9b9Shx147065 } 332684f7a9b9Shx147065 332784f7a9b9Shx147065 /* load firmware .data segment into NIC */ 332884f7a9b9Shx147065 err = wpi_load_firmware(sc, WPI_FW_DATA); 332984f7a9b9Shx147065 if (err != WPI_SUCCESS) { 333084f7a9b9Shx147065 cmn_err(CE_WARN, "wpi_init(): " 333184f7a9b9Shx147065 "failed to load firmware(data)\n"); 333284f7a9b9Shx147065 goto fail1; 333384f7a9b9Shx147065 } 333484f7a9b9Shx147065 333584f7a9b9Shx147065 /* now press "execute" ;-) */ 333684f7a9b9Shx147065 tmp = WPI_READ(sc, WPI_RESET); 333784f7a9b9Shx147065 tmp &= ~(WPI_MASTER_DISABLED | WPI_STOP_MASTER | WPI_NEVO_RESET); 333884f7a9b9Shx147065 WPI_WRITE(sc, WPI_RESET, tmp); 333984f7a9b9Shx147065 334084f7a9b9Shx147065 /* ..and wait at most one second for adapter to initialize */ 334184f7a9b9Shx147065 clk = ddi_get_lbolt() + drv_usectohz(2000000); 334284f7a9b9Shx147065 while (!(sc->sc_flags & WPI_F_FW_INIT)) { 334384f7a9b9Shx147065 if (cv_timedwait(&sc->sc_fw_cv, &sc->sc_glock, clk) < 0) 334484f7a9b9Shx147065 break; 334584f7a9b9Shx147065 } 334684f7a9b9Shx147065 if (!(sc->sc_flags & WPI_F_FW_INIT)) { 334784f7a9b9Shx147065 cmn_err(CE_WARN, 334884f7a9b9Shx147065 "wpi_init(): timeout waiting for firmware init\n"); 334984f7a9b9Shx147065 goto fail1; 335084f7a9b9Shx147065 } 335184f7a9b9Shx147065 335284f7a9b9Shx147065 /* wait for thermal sensors to calibrate */ 335384f7a9b9Shx147065 for (ntries = 0; ntries < 1000; ntries++) { 335484f7a9b9Shx147065 if (WPI_READ(sc, WPI_TEMPERATURE) != 0) 335584f7a9b9Shx147065 break; 335684f7a9b9Shx147065 DELAY(10); 335784f7a9b9Shx147065 } 335884f7a9b9Shx147065 335984f7a9b9Shx147065 if (ntries == 1000) { 336084f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_HW, 336184f7a9b9Shx147065 "wpi_init(): timeout waiting for thermal sensors " 336284f7a9b9Shx147065 "calibration\n")); 336384f7a9b9Shx147065 } 336484f7a9b9Shx147065 336584f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_HW, "temperature %d\n", 336684f7a9b9Shx147065 (int)WPI_READ(sc, WPI_TEMPERATURE))); 336784f7a9b9Shx147065 336884f7a9b9Shx147065 err = wpi_config(sc); 336984f7a9b9Shx147065 if (err) { 337084f7a9b9Shx147065 cmn_err(CE_WARN, "wpi_init(): failed to configure device\n"); 337184f7a9b9Shx147065 goto fail1; 337284f7a9b9Shx147065 } 337384f7a9b9Shx147065 337484f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 337584f7a9b9Shx147065 return (WPI_SUCCESS); 337684f7a9b9Shx147065 337784f7a9b9Shx147065 fail1: 337884f7a9b9Shx147065 err = WPI_FAIL; 337984f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 338084f7a9b9Shx147065 return (err); 338184f7a9b9Shx147065 } 338284f7a9b9Shx147065 33836f12def4Spengcheng chen - Sun Microsystems - Beijing China static int 33846f12def4Spengcheng chen - Sun Microsystems - Beijing China wpi_fast_recover(wpi_sc_t *sc) 33856f12def4Spengcheng chen - Sun Microsystems - Beijing China { 33866f12def4Spengcheng chen - Sun Microsystems - Beijing China ieee80211com_t *ic = &sc->sc_ic; 33876f12def4Spengcheng chen - Sun Microsystems - Beijing China int err; 33886f12def4Spengcheng chen - Sun Microsystems - Beijing China 33896f12def4Spengcheng chen - Sun Microsystems - Beijing China mutex_enter(&sc->sc_glock); 33906f12def4Spengcheng chen - Sun Microsystems - Beijing China 33916f12def4Spengcheng chen - Sun Microsystems - Beijing China /* restore runtime configuration */ 33926f12def4Spengcheng chen - Sun Microsystems - Beijing China bcopy(&sc->sc_config_save, &sc->sc_config, 33936f12def4Spengcheng chen - Sun Microsystems - Beijing China sizeof (sc->sc_config)); 33946f12def4Spengcheng chen - Sun Microsystems - Beijing China 33956f12def4Spengcheng chen - Sun Microsystems - Beijing China sc->sc_config.state = 0; 33966f12def4Spengcheng chen - Sun Microsystems - Beijing China sc->sc_config.filter &= ~LE_32(WPI_FILTER_BSS); 33976f12def4Spengcheng chen - Sun Microsystems - Beijing China 33986f12def4Spengcheng chen - Sun Microsystems - Beijing China if ((err = wpi_auth(sc)) != 0) { 33996f12def4Spengcheng chen - Sun Microsystems - Beijing China cmn_err(CE_WARN, "wpi_fast_recover(): " 34006f12def4Spengcheng chen - Sun Microsystems - Beijing China "failed to setup authentication\n"); 34016f12def4Spengcheng chen - Sun Microsystems - Beijing China mutex_exit(&sc->sc_glock); 34026f12def4Spengcheng chen - Sun Microsystems - Beijing China return (err); 34036f12def4Spengcheng chen - Sun Microsystems - Beijing China } 34046f12def4Spengcheng chen - Sun Microsystems - Beijing China 34056f12def4Spengcheng chen - Sun Microsystems - Beijing China sc->sc_config.state = LE_16(WPI_CONFIG_ASSOCIATED); 34066f12def4Spengcheng chen - Sun Microsystems - Beijing China sc->sc_config.flags &= ~LE_32(WPI_CONFIG_SHPREAMBLE | 34076f12def4Spengcheng chen - Sun Microsystems - Beijing China WPI_CONFIG_SHSLOT); 34086f12def4Spengcheng chen - Sun Microsystems - Beijing China if (ic->ic_flags & IEEE80211_F_SHSLOT) 34096f12def4Spengcheng chen - Sun Microsystems - Beijing China sc->sc_config.flags |= LE_32(WPI_CONFIG_SHSLOT); 34106f12def4Spengcheng chen - Sun Microsystems - Beijing China if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) 34116f12def4Spengcheng chen - Sun Microsystems - Beijing China sc->sc_config.flags |= LE_32(WPI_CONFIG_SHPREAMBLE); 34126f12def4Spengcheng chen - Sun Microsystems - Beijing China sc->sc_config.filter |= LE_32(WPI_FILTER_BSS); 34136f12def4Spengcheng chen - Sun Microsystems - Beijing China if (ic->ic_opmode != IEEE80211_M_STA) 34146f12def4Spengcheng chen - Sun Microsystems - Beijing China sc->sc_config.filter |= LE_32(WPI_FILTER_BEACON); 34156f12def4Spengcheng chen - Sun Microsystems - Beijing China 34166f12def4Spengcheng chen - Sun Microsystems - Beijing China WPI_DBG((WPI_DEBUG_80211, "config chan %d flags %x\n", 34176f12def4Spengcheng chen - Sun Microsystems - Beijing China sc->sc_config.chan, sc->sc_config.flags)); 34186f12def4Spengcheng chen - Sun Microsystems - Beijing China err = wpi_cmd(sc, WPI_CMD_CONFIGURE, &sc->sc_config, 34196f12def4Spengcheng chen - Sun Microsystems - Beijing China sizeof (wpi_config_t), 1); 34206f12def4Spengcheng chen - Sun Microsystems - Beijing China if (err != WPI_SUCCESS) { 34216f12def4Spengcheng chen - Sun Microsystems - Beijing China cmn_err(CE_WARN, "failed to setup association\n"); 34226f12def4Spengcheng chen - Sun Microsystems - Beijing China mutex_exit(&sc->sc_glock); 34236f12def4Spengcheng chen - Sun Microsystems - Beijing China return (err); 34246f12def4Spengcheng chen - Sun Microsystems - Beijing China } 34256f12def4Spengcheng chen - Sun Microsystems - Beijing China /* link LED on */ 34266f12def4Spengcheng chen - Sun Microsystems - Beijing China wpi_set_led(sc, WPI_LED_LINK, 0, 1); 34276f12def4Spengcheng chen - Sun Microsystems - Beijing China 34286f12def4Spengcheng chen - Sun Microsystems - Beijing China mutex_exit(&sc->sc_glock); 34296f12def4Spengcheng chen - Sun Microsystems - Beijing China 34306f12def4Spengcheng chen - Sun Microsystems - Beijing China /* update keys */ 34316f12def4Spengcheng chen - Sun Microsystems - Beijing China if (ic->ic_flags & IEEE80211_F_PRIVACY) { 34326f12def4Spengcheng chen - Sun Microsystems - Beijing China for (int i = 0; i < IEEE80211_KEY_MAX; i++) { 34336f12def4Spengcheng chen - Sun Microsystems - Beijing China if (ic->ic_nw_keys[i].wk_keyix == IEEE80211_KEYIX_NONE) 34346f12def4Spengcheng chen - Sun Microsystems - Beijing China continue; 34356f12def4Spengcheng chen - Sun Microsystems - Beijing China err = wpi_key_set(ic, &ic->ic_nw_keys[i], 34366f12def4Spengcheng chen - Sun Microsystems - Beijing China ic->ic_bss->in_macaddr); 34376f12def4Spengcheng chen - Sun Microsystems - Beijing China /* failure */ 34386f12def4Spengcheng chen - Sun Microsystems - Beijing China if (err == 0) { 34396f12def4Spengcheng chen - Sun Microsystems - Beijing China cmn_err(CE_WARN, "wpi_fast_recover(): " 34406f12def4Spengcheng chen - Sun Microsystems - Beijing China "failed to setup hardware keys\n"); 34416f12def4Spengcheng chen - Sun Microsystems - Beijing China return (WPI_FAIL); 34426f12def4Spengcheng chen - Sun Microsystems - Beijing China } 34436f12def4Spengcheng chen - Sun Microsystems - Beijing China } 34446f12def4Spengcheng chen - Sun Microsystems - Beijing China } 34456f12def4Spengcheng chen - Sun Microsystems - Beijing China 34466f12def4Spengcheng chen - Sun Microsystems - Beijing China sc->sc_flags &= ~WPI_F_HW_ERR_RECOVER; 34476f12def4Spengcheng chen - Sun Microsystems - Beijing China 34486f12def4Spengcheng chen - Sun Microsystems - Beijing China /* start queue */ 34496f12def4Spengcheng chen - Sun Microsystems - Beijing China WPI_DBG((WPI_DEBUG_FW, "wpi_fast_recover(): resume xmit\n")); 34506f12def4Spengcheng chen - Sun Microsystems - Beijing China mac_tx_update(ic->ic_mach); 34516f12def4Spengcheng chen - Sun Microsystems - Beijing China 34526f12def4Spengcheng chen - Sun Microsystems - Beijing China return (WPI_SUCCESS); 34536f12def4Spengcheng chen - Sun Microsystems - Beijing China } 34546f12def4Spengcheng chen - Sun Microsystems - Beijing China 3455faceed93Sfei feng - Sun Microsystems - Beijing China /* 3456faceed93Sfei feng - Sun Microsystems - Beijing China * quiesce(9E) entry point. 3457faceed93Sfei feng - Sun Microsystems - Beijing China * This function is called when the system is single-threaded at high 3458faceed93Sfei feng - Sun Microsystems - Beijing China * PIL with preemption disabled. Therefore, this function must not be 3459faceed93Sfei feng - Sun Microsystems - Beijing China * blocked. 3460faceed93Sfei feng - Sun Microsystems - Beijing China * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. 3461faceed93Sfei feng - Sun Microsystems - Beijing China * DDI_FAILURE indicates an error condition and should almost never happen. 3462faceed93Sfei feng - Sun Microsystems - Beijing China */ 3463faceed93Sfei feng - Sun Microsystems - Beijing China static int 3464faceed93Sfei feng - Sun Microsystems - Beijing China wpi_quiesce(dev_info_t *dip) 3465faceed93Sfei feng - Sun Microsystems - Beijing China { 3466faceed93Sfei feng - Sun Microsystems - Beijing China wpi_sc_t *sc; 3467faceed93Sfei feng - Sun Microsystems - Beijing China 3468faceed93Sfei feng - Sun Microsystems - Beijing China sc = ddi_get_soft_state(wpi_soft_state_p, ddi_get_instance(dip)); 3469faceed93Sfei feng - Sun Microsystems - Beijing China if (sc == NULL) 3470faceed93Sfei feng - Sun Microsystems - Beijing China return (DDI_FAILURE); 3471faceed93Sfei feng - Sun Microsystems - Beijing China 3472faceed93Sfei feng - Sun Microsystems - Beijing China #ifdef DEBUG 3473faceed93Sfei feng - Sun Microsystems - Beijing China /* by pass any messages, if it's quiesce */ 3474faceed93Sfei feng - Sun Microsystems - Beijing China wpi_dbg_flags = 0; 3475faceed93Sfei feng - Sun Microsystems - Beijing China #endif 3476faceed93Sfei feng - Sun Microsystems - Beijing China 3477faceed93Sfei feng - Sun Microsystems - Beijing China /* 3478faceed93Sfei feng - Sun Microsystems - Beijing China * No more blocking is allowed while we are in the 3479faceed93Sfei feng - Sun Microsystems - Beijing China * quiesce(9E) entry point. 3480faceed93Sfei feng - Sun Microsystems - Beijing China */ 3481faceed93Sfei feng - Sun Microsystems - Beijing China sc->sc_flags |= WPI_F_QUIESCED; 3482faceed93Sfei feng - Sun Microsystems - Beijing China 3483faceed93Sfei feng - Sun Microsystems - Beijing China /* 3484faceed93Sfei feng - Sun Microsystems - Beijing China * Disable and mask all interrupts. 3485faceed93Sfei feng - Sun Microsystems - Beijing China */ 3486faceed93Sfei feng - Sun Microsystems - Beijing China wpi_stop(sc); 3487faceed93Sfei feng - Sun Microsystems - Beijing China return (DDI_SUCCESS); 3488faceed93Sfei feng - Sun Microsystems - Beijing China } 3489faceed93Sfei feng - Sun Microsystems - Beijing China 349084f7a9b9Shx147065 static void 349184f7a9b9Shx147065 wpi_stop(wpi_sc_t *sc) 349284f7a9b9Shx147065 { 349384f7a9b9Shx147065 uint32_t tmp; 349484f7a9b9Shx147065 int ac; 349584f7a9b9Shx147065 3496faceed93Sfei feng - Sun Microsystems - Beijing China /* no mutex operation, if it's quiesced */ 3497faceed93Sfei feng - Sun Microsystems - Beijing China if (!(sc->sc_flags & WPI_F_QUIESCED)) 349884f7a9b9Shx147065 mutex_enter(&sc->sc_glock); 3499faceed93Sfei feng - Sun Microsystems - Beijing China 350084f7a9b9Shx147065 /* disable interrupts */ 350184f7a9b9Shx147065 WPI_WRITE(sc, WPI_MASK, 0); 350284f7a9b9Shx147065 WPI_WRITE(sc, WPI_INTR, WPI_INTR_MASK); 350384f7a9b9Shx147065 WPI_WRITE(sc, WPI_INTR_STATUS, 0xff); 350484f7a9b9Shx147065 WPI_WRITE(sc, WPI_INTR_STATUS, 0x00070000); 350584f7a9b9Shx147065 350684f7a9b9Shx147065 wpi_mem_lock(sc); 350784f7a9b9Shx147065 wpi_mem_write(sc, WPI_MEM_MODE, 0); 350884f7a9b9Shx147065 wpi_mem_unlock(sc); 350984f7a9b9Shx147065 351084f7a9b9Shx147065 /* reset all Tx rings */ 351184f7a9b9Shx147065 for (ac = 0; ac < 4; ac++) 351284f7a9b9Shx147065 wpi_reset_tx_ring(sc, &sc->sc_txq[ac]); 351384f7a9b9Shx147065 wpi_reset_tx_ring(sc, &sc->sc_cmdq); 351484f7a9b9Shx147065 wpi_reset_tx_ring(sc, &sc->sc_svcq); 351584f7a9b9Shx147065 351684f7a9b9Shx147065 /* reset Rx ring */ 351784f7a9b9Shx147065 wpi_reset_rx_ring(sc); 351884f7a9b9Shx147065 351984f7a9b9Shx147065 wpi_mem_lock(sc); 352084f7a9b9Shx147065 wpi_mem_write(sc, WPI_MEM_CLOCK2, 0x200); 352184f7a9b9Shx147065 wpi_mem_unlock(sc); 352284f7a9b9Shx147065 352384f7a9b9Shx147065 DELAY(5); 352484f7a9b9Shx147065 352584f7a9b9Shx147065 wpi_stop_master(sc); 352684f7a9b9Shx147065 352784f7a9b9Shx147065 sc->sc_tx_timer = 0; 3528626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_flags &= ~WPI_F_SCANNING; 3529626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_scan_pending = 0; 3530626dff79Spengcheng chen - Sun Microsystems - Beijing China sc->sc_scan_next = 0; 3531626dff79Spengcheng chen - Sun Microsystems - Beijing China 353284f7a9b9Shx147065 tmp = WPI_READ(sc, WPI_RESET); 353384f7a9b9Shx147065 WPI_WRITE(sc, WPI_RESET, tmp | WPI_SW_RESET); 3534626dff79Spengcheng chen - Sun Microsystems - Beijing China 3535faceed93Sfei feng - Sun Microsystems - Beijing China /* no mutex operation, if it's quiesced */ 3536faceed93Sfei feng - Sun Microsystems - Beijing China if (!(sc->sc_flags & WPI_F_QUIESCED)) 353784f7a9b9Shx147065 mutex_exit(&sc->sc_glock); 353884f7a9b9Shx147065 } 353984f7a9b9Shx147065 354084f7a9b9Shx147065 /* 354184f7a9b9Shx147065 * Naive implementation of the Adaptive Multi Rate Retry algorithm: 354284f7a9b9Shx147065 * "IEEE 802.11 Rate Adaptation: A Practical Approach" 354384f7a9b9Shx147065 * Mathieu Lacage, Hossein Manshaei, Thierry Turletti 354484f7a9b9Shx147065 * INRIA Sophia - Projet Planete 354584f7a9b9Shx147065 * http://www-sop.inria.fr/rapports/sophia/RR-5208.html 354684f7a9b9Shx147065 */ 354784f7a9b9Shx147065 #define is_success(amrr) \ 354884f7a9b9Shx147065 ((amrr)->retrycnt < (amrr)->txcnt / 10) 354984f7a9b9Shx147065 #define is_failure(amrr) \ 355084f7a9b9Shx147065 ((amrr)->retrycnt > (amrr)->txcnt / 3) 355184f7a9b9Shx147065 #define is_enough(amrr) \ 355284f7a9b9Shx147065 ((amrr)->txcnt > 100) 355384f7a9b9Shx147065 #define is_min_rate(in) \ 355484f7a9b9Shx147065 ((in)->in_txrate == 0) 355584f7a9b9Shx147065 #define is_max_rate(in) \ 355684f7a9b9Shx147065 ((in)->in_txrate == (in)->in_rates.ir_nrates - 1) 355784f7a9b9Shx147065 #define increase_rate(in) \ 355884f7a9b9Shx147065 ((in)->in_txrate++) 355984f7a9b9Shx147065 #define decrease_rate(in) \ 356084f7a9b9Shx147065 ((in)->in_txrate--) 356184f7a9b9Shx147065 #define reset_cnt(amrr) \ 356284f7a9b9Shx147065 { (amrr)->txcnt = (amrr)->retrycnt = 0; } 356384f7a9b9Shx147065 356484f7a9b9Shx147065 #define WPI_AMRR_MIN_SUCCESS_THRESHOLD 1 356584f7a9b9Shx147065 #define WPI_AMRR_MAX_SUCCESS_THRESHOLD 15 356684f7a9b9Shx147065 356784f7a9b9Shx147065 static void 356884f7a9b9Shx147065 wpi_amrr_init(wpi_amrr_t *amrr) 356984f7a9b9Shx147065 { 357084f7a9b9Shx147065 amrr->success = 0; 357184f7a9b9Shx147065 amrr->recovery = 0; 357284f7a9b9Shx147065 amrr->txcnt = amrr->retrycnt = 0; 357384f7a9b9Shx147065 amrr->success_threshold = WPI_AMRR_MIN_SUCCESS_THRESHOLD; 357484f7a9b9Shx147065 } 357584f7a9b9Shx147065 357684f7a9b9Shx147065 static void 357784f7a9b9Shx147065 wpi_amrr_timeout(wpi_sc_t *sc) 357884f7a9b9Shx147065 { 357984f7a9b9Shx147065 ieee80211com_t *ic = &sc->sc_ic; 358084f7a9b9Shx147065 358184f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_RATECTL, "wpi_amrr_timeout() enter\n")); 358284f7a9b9Shx147065 if (ic->ic_opmode == IEEE80211_M_STA) 358384f7a9b9Shx147065 wpi_amrr_ratectl(NULL, ic->ic_bss); 358484f7a9b9Shx147065 else 358584f7a9b9Shx147065 ieee80211_iterate_nodes(&ic->ic_sta, wpi_amrr_ratectl, NULL); 358684f7a9b9Shx147065 sc->sc_clk = ddi_get_lbolt(); 358784f7a9b9Shx147065 } 358884f7a9b9Shx147065 358984f7a9b9Shx147065 /* ARGSUSED */ 359084f7a9b9Shx147065 static void 359184f7a9b9Shx147065 wpi_amrr_ratectl(void *arg, ieee80211_node_t *in) 359284f7a9b9Shx147065 { 359384f7a9b9Shx147065 wpi_amrr_t *amrr = (wpi_amrr_t *)in; 359484f7a9b9Shx147065 int need_change = 0; 359584f7a9b9Shx147065 359684f7a9b9Shx147065 if (is_success(amrr) && is_enough(amrr)) { 359784f7a9b9Shx147065 amrr->success++; 359884f7a9b9Shx147065 if (amrr->success >= amrr->success_threshold && 359984f7a9b9Shx147065 !is_max_rate(in)) { 360084f7a9b9Shx147065 amrr->recovery = 1; 360184f7a9b9Shx147065 amrr->success = 0; 360284f7a9b9Shx147065 increase_rate(in); 360384f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_RATECTL, 360484f7a9b9Shx147065 "AMRR increasing rate %d (txcnt=%d retrycnt=%d)\n", 360584f7a9b9Shx147065 in->in_txrate, amrr->txcnt, amrr->retrycnt)); 360684f7a9b9Shx147065 need_change = 1; 360784f7a9b9Shx147065 } else { 360884f7a9b9Shx147065 amrr->recovery = 0; 360984f7a9b9Shx147065 } 361084f7a9b9Shx147065 } else if (is_failure(amrr)) { 361184f7a9b9Shx147065 amrr->success = 0; 361284f7a9b9Shx147065 if (!is_min_rate(in)) { 361384f7a9b9Shx147065 if (amrr->recovery) { 361484f7a9b9Shx147065 amrr->success_threshold++; 361584f7a9b9Shx147065 if (amrr->success_threshold > 361684f7a9b9Shx147065 WPI_AMRR_MAX_SUCCESS_THRESHOLD) 361784f7a9b9Shx147065 amrr->success_threshold = 361884f7a9b9Shx147065 WPI_AMRR_MAX_SUCCESS_THRESHOLD; 361984f7a9b9Shx147065 } else { 362084f7a9b9Shx147065 amrr->success_threshold = 362184f7a9b9Shx147065 WPI_AMRR_MIN_SUCCESS_THRESHOLD; 362284f7a9b9Shx147065 } 362384f7a9b9Shx147065 decrease_rate(in); 362484f7a9b9Shx147065 WPI_DBG((WPI_DEBUG_RATECTL, 362584f7a9b9Shx147065 "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)\n", 362684f7a9b9Shx147065 in->in_txrate, amrr->txcnt, amrr->retrycnt)); 362784f7a9b9Shx147065 need_change = 1; 362884f7a9b9Shx147065 } 362984f7a9b9Shx147065 amrr->recovery = 0; /* paper is incorrect */ 363084f7a9b9Shx147065 } 363184f7a9b9Shx147065 363284f7a9b9Shx147065 if (is_enough(amrr) || need_change) 363384f7a9b9Shx147065 reset_cnt(amrr); 363484f7a9b9Shx147065 } 3635