122a84b8dSQuaker Fang /* 2*0dc2366fSVenugopal Iyer * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 322a84b8dSQuaker Fang * Use is subject to license terms. 422a84b8dSQuaker Fang */ 522a84b8dSQuaker Fang 622a84b8dSQuaker Fang /* 722a84b8dSQuaker Fang * Copyright (c) 2009, Intel Corporation 822a84b8dSQuaker Fang * All rights reserved. 922a84b8dSQuaker Fang */ 1022a84b8dSQuaker Fang 1122a84b8dSQuaker Fang /* 1222a84b8dSQuaker Fang * Copyright (c) 2006 1322a84b8dSQuaker Fang * Copyright (c) 2007 1422a84b8dSQuaker Fang * Damien Bergamini <damien.bergamini@free.fr> 1522a84b8dSQuaker Fang * 1622a84b8dSQuaker Fang * Permission to use, copy, modify, and distribute this software for any 1722a84b8dSQuaker Fang * purpose with or without fee is hereby granted, provided that the above 1822a84b8dSQuaker Fang * copyright notice and this permission notice appear in all copies. 1922a84b8dSQuaker Fang * 2022a84b8dSQuaker Fang * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 2122a84b8dSQuaker Fang * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 2222a84b8dSQuaker Fang * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 2322a84b8dSQuaker Fang * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 2422a84b8dSQuaker Fang * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 2522a84b8dSQuaker Fang * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 2622a84b8dSQuaker Fang * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 2722a84b8dSQuaker Fang */ 2822a84b8dSQuaker Fang 2922a84b8dSQuaker Fang /* 3022a84b8dSQuaker Fang * Intel(R) WiFi Link 6000 Driver 3122a84b8dSQuaker Fang */ 3222a84b8dSQuaker Fang 3322a84b8dSQuaker Fang #include <sys/types.h> 3422a84b8dSQuaker Fang #include <sys/byteorder.h> 3522a84b8dSQuaker Fang #include <sys/conf.h> 3622a84b8dSQuaker Fang #include <sys/cmn_err.h> 3722a84b8dSQuaker Fang #include <sys/stat.h> 3822a84b8dSQuaker Fang #include <sys/ddi.h> 3922a84b8dSQuaker Fang #include <sys/sunddi.h> 4022a84b8dSQuaker Fang #include <sys/strsubr.h> 4122a84b8dSQuaker Fang #include <sys/ethernet.h> 4222a84b8dSQuaker Fang #include <inet/common.h> 4322a84b8dSQuaker Fang #include <inet/nd.h> 4422a84b8dSQuaker Fang #include <inet/mi.h> 4522a84b8dSQuaker Fang #include <sys/note.h> 4622a84b8dSQuaker Fang #include <sys/stream.h> 4722a84b8dSQuaker Fang #include <sys/strsun.h> 4822a84b8dSQuaker Fang #include <sys/modctl.h> 4922a84b8dSQuaker Fang #include <sys/devops.h> 5022a84b8dSQuaker Fang #include <sys/dlpi.h> 5122a84b8dSQuaker Fang #include <sys/mac_provider.h> 5222a84b8dSQuaker Fang #include <sys/mac_wifi.h> 5322a84b8dSQuaker Fang #include <sys/net80211.h> 5422a84b8dSQuaker Fang #include <sys/net80211_proto.h> 5522a84b8dSQuaker Fang #include <sys/varargs.h> 5622a84b8dSQuaker Fang #include <sys/policy.h> 5722a84b8dSQuaker Fang #include <sys/pci.h> 5822a84b8dSQuaker Fang 5922a84b8dSQuaker Fang #include "iwp_calibration.h" 6022a84b8dSQuaker Fang #include "iwp_hw.h" 6122a84b8dSQuaker Fang #include "iwp_eeprom.h" 6222a84b8dSQuaker Fang #include "iwp_var.h" 6322a84b8dSQuaker Fang #include <inet/wifi_ioctl.h> 6422a84b8dSQuaker Fang 6522a84b8dSQuaker Fang #ifdef DEBUG 6622a84b8dSQuaker Fang #define IWP_DEBUG_80211 (1 << 0) 6722a84b8dSQuaker Fang #define IWP_DEBUG_CMD (1 << 1) 6822a84b8dSQuaker Fang #define IWP_DEBUG_DMA (1 << 2) 6922a84b8dSQuaker Fang #define IWP_DEBUG_EEPROM (1 << 3) 7022a84b8dSQuaker Fang #define IWP_DEBUG_FW (1 << 4) 7122a84b8dSQuaker Fang #define IWP_DEBUG_HW (1 << 5) 7222a84b8dSQuaker Fang #define IWP_DEBUG_INTR (1 << 6) 7322a84b8dSQuaker Fang #define IWP_DEBUG_MRR (1 << 7) 7422a84b8dSQuaker Fang #define IWP_DEBUG_PIO (1 << 8) 7522a84b8dSQuaker Fang #define IWP_DEBUG_RX (1 << 9) 7622a84b8dSQuaker Fang #define IWP_DEBUG_SCAN (1 << 10) 7722a84b8dSQuaker Fang #define IWP_DEBUG_TX (1 << 11) 7822a84b8dSQuaker Fang #define IWP_DEBUG_RATECTL (1 << 12) 7922a84b8dSQuaker Fang #define IWP_DEBUG_RADIO (1 << 13) 8022a84b8dSQuaker Fang #define IWP_DEBUG_RESUME (1 << 14) 8122a84b8dSQuaker Fang #define IWP_DEBUG_CALIBRATION (1 << 15) 8222a84b8dSQuaker Fang /* 8322a84b8dSQuaker Fang * if want to see debug message of a given section, 8422a84b8dSQuaker Fang * please set this flag to one of above values 8522a84b8dSQuaker Fang */ 8622a84b8dSQuaker Fang uint32_t iwp_dbg_flags = 0; 8722a84b8dSQuaker Fang #define IWP_DBG(x) \ 8822a84b8dSQuaker Fang iwp_dbg x 8922a84b8dSQuaker Fang #else 9022a84b8dSQuaker Fang #define IWP_DBG(x) 9122a84b8dSQuaker Fang #endif 9222a84b8dSQuaker Fang 9322a84b8dSQuaker Fang static void *iwp_soft_state_p = NULL; 9422a84b8dSQuaker Fang 9522a84b8dSQuaker Fang /* 9622a84b8dSQuaker Fang * ucode will be compiled into driver image 9722a84b8dSQuaker Fang */ 9822a84b8dSQuaker Fang static uint8_t iwp_fw_bin [] = { 9922a84b8dSQuaker Fang #include "fw-iw/iwp.ucode" 10022a84b8dSQuaker Fang }; 10122a84b8dSQuaker Fang 10222a84b8dSQuaker Fang /* 10322a84b8dSQuaker Fang * DMA attributes for a shared page 10422a84b8dSQuaker Fang */ 10522a84b8dSQuaker Fang static ddi_dma_attr_t sh_dma_attr = { 10622a84b8dSQuaker Fang DMA_ATTR_V0, /* version of this structure */ 10722a84b8dSQuaker Fang 0, /* lowest usable address */ 10822a84b8dSQuaker Fang 0xffffffffU, /* highest usable address */ 10922a84b8dSQuaker Fang 0xffffffffU, /* maximum DMAable byte count */ 11022a84b8dSQuaker Fang 0x1000, /* alignment in bytes */ 11122a84b8dSQuaker Fang 0x1000, /* burst sizes (any?) */ 11222a84b8dSQuaker Fang 1, /* minimum transfer */ 11322a84b8dSQuaker Fang 0xffffffffU, /* maximum transfer */ 11422a84b8dSQuaker Fang 0xffffffffU, /* maximum segment length */ 11522a84b8dSQuaker Fang 1, /* maximum number of segments */ 11622a84b8dSQuaker Fang 1, /* granularity */ 11722a84b8dSQuaker Fang 0, /* flags (reserved) */ 11822a84b8dSQuaker Fang }; 11922a84b8dSQuaker Fang 12022a84b8dSQuaker Fang /* 12122a84b8dSQuaker Fang * DMA attributes for a keep warm DRAM descriptor 12222a84b8dSQuaker Fang */ 12322a84b8dSQuaker Fang static ddi_dma_attr_t kw_dma_attr = { 12422a84b8dSQuaker Fang DMA_ATTR_V0, /* version of this structure */ 12522a84b8dSQuaker Fang 0, /* lowest usable address */ 12622a84b8dSQuaker Fang 0xffffffffU, /* highest usable address */ 12722a84b8dSQuaker Fang 0xffffffffU, /* maximum DMAable byte count */ 12822a84b8dSQuaker Fang 0x1000, /* alignment in bytes */ 12922a84b8dSQuaker Fang 0x1000, /* burst sizes (any?) */ 13022a84b8dSQuaker Fang 1, /* minimum transfer */ 13122a84b8dSQuaker Fang 0xffffffffU, /* maximum transfer */ 13222a84b8dSQuaker Fang 0xffffffffU, /* maximum segment length */ 13322a84b8dSQuaker Fang 1, /* maximum number of segments */ 13422a84b8dSQuaker Fang 1, /* granularity */ 13522a84b8dSQuaker Fang 0, /* flags (reserved) */ 13622a84b8dSQuaker Fang }; 13722a84b8dSQuaker Fang 13822a84b8dSQuaker Fang /* 13922a84b8dSQuaker Fang * DMA attributes for a ring descriptor 14022a84b8dSQuaker Fang */ 14122a84b8dSQuaker Fang static ddi_dma_attr_t ring_desc_dma_attr = { 14222a84b8dSQuaker Fang DMA_ATTR_V0, /* version of this structure */ 14322a84b8dSQuaker Fang 0, /* lowest usable address */ 14422a84b8dSQuaker Fang 0xffffffffU, /* highest usable address */ 14522a84b8dSQuaker Fang 0xffffffffU, /* maximum DMAable byte count */ 14622a84b8dSQuaker Fang 0x100, /* alignment in bytes */ 14722a84b8dSQuaker Fang 0x100, /* burst sizes (any?) */ 14822a84b8dSQuaker Fang 1, /* minimum transfer */ 14922a84b8dSQuaker Fang 0xffffffffU, /* maximum transfer */ 15022a84b8dSQuaker Fang 0xffffffffU, /* maximum segment length */ 15122a84b8dSQuaker Fang 1, /* maximum number of segments */ 15222a84b8dSQuaker Fang 1, /* granularity */ 15322a84b8dSQuaker Fang 0, /* flags (reserved) */ 15422a84b8dSQuaker Fang }; 15522a84b8dSQuaker Fang 15622a84b8dSQuaker Fang /* 15722a84b8dSQuaker Fang * DMA attributes for a cmd 15822a84b8dSQuaker Fang */ 15922a84b8dSQuaker Fang static ddi_dma_attr_t cmd_dma_attr = { 16022a84b8dSQuaker Fang DMA_ATTR_V0, /* version of this structure */ 16122a84b8dSQuaker Fang 0, /* lowest usable address */ 16222a84b8dSQuaker Fang 0xffffffffU, /* highest usable address */ 16322a84b8dSQuaker Fang 0xffffffffU, /* maximum DMAable byte count */ 16422a84b8dSQuaker Fang 4, /* alignment in bytes */ 16522a84b8dSQuaker Fang 0x100, /* burst sizes (any?) */ 16622a84b8dSQuaker Fang 1, /* minimum transfer */ 16722a84b8dSQuaker Fang 0xffffffffU, /* maximum transfer */ 16822a84b8dSQuaker Fang 0xffffffffU, /* maximum segment length */ 16922a84b8dSQuaker Fang 1, /* maximum number of segments */ 17022a84b8dSQuaker Fang 1, /* granularity */ 17122a84b8dSQuaker Fang 0, /* flags (reserved) */ 17222a84b8dSQuaker Fang }; 17322a84b8dSQuaker Fang 17422a84b8dSQuaker Fang /* 17522a84b8dSQuaker Fang * DMA attributes for a rx buffer 17622a84b8dSQuaker Fang */ 17722a84b8dSQuaker Fang static ddi_dma_attr_t rx_buffer_dma_attr = { 17822a84b8dSQuaker Fang DMA_ATTR_V0, /* version of this structure */ 17922a84b8dSQuaker Fang 0, /* lowest usable address */ 18022a84b8dSQuaker Fang 0xffffffffU, /* highest usable address */ 18122a84b8dSQuaker Fang 0xffffffffU, /* maximum DMAable byte count */ 18222a84b8dSQuaker Fang 0x100, /* alignment in bytes */ 18322a84b8dSQuaker Fang 0x100, /* burst sizes (any?) */ 18422a84b8dSQuaker Fang 1, /* minimum transfer */ 18522a84b8dSQuaker Fang 0xffffffffU, /* maximum transfer */ 18622a84b8dSQuaker Fang 0xffffffffU, /* maximum segment length */ 18722a84b8dSQuaker Fang 1, /* maximum number of segments */ 18822a84b8dSQuaker Fang 1, /* granularity */ 18922a84b8dSQuaker Fang 0, /* flags (reserved) */ 19022a84b8dSQuaker Fang }; 19122a84b8dSQuaker Fang 19222a84b8dSQuaker Fang /* 19322a84b8dSQuaker Fang * DMA attributes for a tx buffer. 19422a84b8dSQuaker Fang * the maximum number of segments is 4 for the hardware. 19522a84b8dSQuaker Fang * now all the wifi drivers put the whole frame in a single 19622a84b8dSQuaker Fang * descriptor, so we define the maximum number of segments 1, 19722a84b8dSQuaker Fang * just the same as the rx_buffer. we consider leverage the HW 19822a84b8dSQuaker Fang * ability in the future, that is why we don't define rx and tx 19922a84b8dSQuaker Fang * buffer_dma_attr as the same. 20022a84b8dSQuaker Fang */ 20122a84b8dSQuaker Fang static ddi_dma_attr_t tx_buffer_dma_attr = { 20222a84b8dSQuaker Fang DMA_ATTR_V0, /* version of this structure */ 20322a84b8dSQuaker Fang 0, /* lowest usable address */ 20422a84b8dSQuaker Fang 0xffffffffU, /* highest usable address */ 20522a84b8dSQuaker Fang 0xffffffffU, /* maximum DMAable byte count */ 20622a84b8dSQuaker Fang 4, /* alignment in bytes */ 20722a84b8dSQuaker Fang 0x100, /* burst sizes (any?) */ 20822a84b8dSQuaker Fang 1, /* minimum transfer */ 20922a84b8dSQuaker Fang 0xffffffffU, /* maximum transfer */ 21022a84b8dSQuaker Fang 0xffffffffU, /* maximum segment length */ 21122a84b8dSQuaker Fang 1, /* maximum number of segments */ 21222a84b8dSQuaker Fang 1, /* granularity */ 21322a84b8dSQuaker Fang 0, /* flags (reserved) */ 21422a84b8dSQuaker Fang }; 21522a84b8dSQuaker Fang 21622a84b8dSQuaker Fang /* 21722a84b8dSQuaker Fang * DMA attributes for text and data part in the firmware 21822a84b8dSQuaker Fang */ 21922a84b8dSQuaker Fang static ddi_dma_attr_t fw_dma_attr = { 22022a84b8dSQuaker Fang DMA_ATTR_V0, /* version of this structure */ 22122a84b8dSQuaker Fang 0, /* lowest usable address */ 22222a84b8dSQuaker Fang 0xffffffffU, /* highest usable address */ 22322a84b8dSQuaker Fang 0x7fffffff, /* maximum DMAable byte count */ 22422a84b8dSQuaker Fang 0x10, /* alignment in bytes */ 22522a84b8dSQuaker Fang 0x100, /* burst sizes (any?) */ 22622a84b8dSQuaker Fang 1, /* minimum transfer */ 22722a84b8dSQuaker Fang 0xffffffffU, /* maximum transfer */ 22822a84b8dSQuaker Fang 0xffffffffU, /* maximum segment length */ 22922a84b8dSQuaker Fang 1, /* maximum number of segments */ 23022a84b8dSQuaker Fang 1, /* granularity */ 23122a84b8dSQuaker Fang 0, /* flags (reserved) */ 23222a84b8dSQuaker Fang }; 23322a84b8dSQuaker Fang 23422a84b8dSQuaker Fang /* 23522a84b8dSQuaker Fang * regs access attributes 23622a84b8dSQuaker Fang */ 23722a84b8dSQuaker Fang static ddi_device_acc_attr_t iwp_reg_accattr = { 23822a84b8dSQuaker Fang DDI_DEVICE_ATTR_V0, 23922a84b8dSQuaker Fang DDI_STRUCTURE_LE_ACC, 24022a84b8dSQuaker Fang DDI_STRICTORDER_ACC, 24122a84b8dSQuaker Fang DDI_DEFAULT_ACC 24222a84b8dSQuaker Fang }; 24322a84b8dSQuaker Fang 24422a84b8dSQuaker Fang /* 24522a84b8dSQuaker Fang * DMA access attributes for descriptor 24622a84b8dSQuaker Fang */ 24722a84b8dSQuaker Fang static ddi_device_acc_attr_t iwp_dma_descattr = { 24822a84b8dSQuaker Fang DDI_DEVICE_ATTR_V0, 24922a84b8dSQuaker Fang DDI_STRUCTURE_LE_ACC, 25022a84b8dSQuaker Fang DDI_STRICTORDER_ACC, 25122a84b8dSQuaker Fang DDI_DEFAULT_ACC 25222a84b8dSQuaker Fang }; 25322a84b8dSQuaker Fang 25422a84b8dSQuaker Fang /* 25522a84b8dSQuaker Fang * DMA access attributes 25622a84b8dSQuaker Fang */ 25722a84b8dSQuaker Fang static ddi_device_acc_attr_t iwp_dma_accattr = { 25822a84b8dSQuaker Fang DDI_DEVICE_ATTR_V0, 25922a84b8dSQuaker Fang DDI_NEVERSWAP_ACC, 26022a84b8dSQuaker Fang DDI_STRICTORDER_ACC, 26122a84b8dSQuaker Fang DDI_DEFAULT_ACC 26222a84b8dSQuaker Fang }; 26322a84b8dSQuaker Fang 26422a84b8dSQuaker Fang static int iwp_ring_init(iwp_sc_t *); 26522a84b8dSQuaker Fang static void iwp_ring_free(iwp_sc_t *); 26622a84b8dSQuaker Fang static int iwp_alloc_shared(iwp_sc_t *); 26722a84b8dSQuaker Fang static void iwp_free_shared(iwp_sc_t *); 26822a84b8dSQuaker Fang static int iwp_alloc_kw(iwp_sc_t *); 26922a84b8dSQuaker Fang static void iwp_free_kw(iwp_sc_t *); 27022a84b8dSQuaker Fang static int iwp_alloc_fw_dma(iwp_sc_t *); 27122a84b8dSQuaker Fang static void iwp_free_fw_dma(iwp_sc_t *); 27222a84b8dSQuaker Fang static int iwp_alloc_rx_ring(iwp_sc_t *); 27322a84b8dSQuaker Fang static void iwp_reset_rx_ring(iwp_sc_t *); 27422a84b8dSQuaker Fang static void iwp_free_rx_ring(iwp_sc_t *); 27522a84b8dSQuaker Fang static int iwp_alloc_tx_ring(iwp_sc_t *, iwp_tx_ring_t *, 27622a84b8dSQuaker Fang int, int); 27722a84b8dSQuaker Fang static void iwp_reset_tx_ring(iwp_sc_t *, iwp_tx_ring_t *); 27822a84b8dSQuaker Fang static void iwp_free_tx_ring(iwp_tx_ring_t *); 27922a84b8dSQuaker Fang static ieee80211_node_t *iwp_node_alloc(ieee80211com_t *); 28022a84b8dSQuaker Fang static void iwp_node_free(ieee80211_node_t *); 28122a84b8dSQuaker Fang static int iwp_newstate(ieee80211com_t *, enum ieee80211_state, int); 28222a84b8dSQuaker Fang static void iwp_mac_access_enter(iwp_sc_t *); 28322a84b8dSQuaker Fang static void iwp_mac_access_exit(iwp_sc_t *); 28422a84b8dSQuaker Fang static uint32_t iwp_reg_read(iwp_sc_t *, uint32_t); 28522a84b8dSQuaker Fang static void iwp_reg_write(iwp_sc_t *, uint32_t, uint32_t); 28622a84b8dSQuaker Fang static int iwp_load_init_firmware(iwp_sc_t *); 28722a84b8dSQuaker Fang static int iwp_load_run_firmware(iwp_sc_t *); 28822a84b8dSQuaker Fang static void iwp_tx_intr(iwp_sc_t *, iwp_rx_desc_t *); 28922a84b8dSQuaker Fang static void iwp_cmd_intr(iwp_sc_t *, iwp_rx_desc_t *); 29022a84b8dSQuaker Fang static uint_t iwp_intr(caddr_t, caddr_t); 29122a84b8dSQuaker Fang static int iwp_eep_load(iwp_sc_t *); 29222a84b8dSQuaker Fang static void iwp_get_mac_from_eep(iwp_sc_t *); 29322a84b8dSQuaker Fang static int iwp_eep_sem_down(iwp_sc_t *); 29422a84b8dSQuaker Fang static void iwp_eep_sem_up(iwp_sc_t *); 29522a84b8dSQuaker Fang static uint_t iwp_rx_softintr(caddr_t, caddr_t); 29622a84b8dSQuaker Fang static uint8_t iwp_rate_to_plcp(int); 29722a84b8dSQuaker Fang static int iwp_cmd(iwp_sc_t *, int, const void *, int, int); 29822a84b8dSQuaker Fang static void iwp_set_led(iwp_sc_t *, uint8_t, uint8_t, uint8_t); 29922a84b8dSQuaker Fang static int iwp_hw_set_before_auth(iwp_sc_t *); 30022a84b8dSQuaker Fang static int iwp_scan(iwp_sc_t *); 30122a84b8dSQuaker Fang static int iwp_config(iwp_sc_t *); 30222a84b8dSQuaker Fang static void iwp_stop_master(iwp_sc_t *); 30322a84b8dSQuaker Fang static int iwp_power_up(iwp_sc_t *); 30422a84b8dSQuaker Fang static int iwp_preinit(iwp_sc_t *); 30522a84b8dSQuaker Fang static int iwp_init(iwp_sc_t *); 30622a84b8dSQuaker Fang static void iwp_stop(iwp_sc_t *); 30722a84b8dSQuaker Fang static int iwp_quiesce(dev_info_t *t); 30822a84b8dSQuaker Fang static void iwp_amrr_init(iwp_amrr_t *); 30922a84b8dSQuaker Fang static void iwp_amrr_timeout(iwp_sc_t *); 31022a84b8dSQuaker Fang static void iwp_amrr_ratectl(void *, ieee80211_node_t *); 31122a84b8dSQuaker Fang static void iwp_ucode_alive(iwp_sc_t *, iwp_rx_desc_t *); 31222a84b8dSQuaker Fang static void iwp_rx_phy_intr(iwp_sc_t *, iwp_rx_desc_t *); 31322a84b8dSQuaker Fang static void iwp_rx_mpdu_intr(iwp_sc_t *, iwp_rx_desc_t *); 31422a84b8dSQuaker Fang static void iwp_release_calib_buffer(iwp_sc_t *); 31522a84b8dSQuaker Fang static int iwp_init_common(iwp_sc_t *); 31622a84b8dSQuaker Fang static uint8_t *iwp_eep_addr_trans(iwp_sc_t *, uint32_t); 31722a84b8dSQuaker Fang static int iwp_put_seg_fw(iwp_sc_t *, uint32_t, uint32_t, uint32_t); 31822a84b8dSQuaker Fang static int iwp_alive_common(iwp_sc_t *); 31922a84b8dSQuaker Fang static void iwp_save_calib_result(iwp_sc_t *, iwp_rx_desc_t *); 32022a84b8dSQuaker Fang static int iwp_attach(dev_info_t *, ddi_attach_cmd_t); 32122a84b8dSQuaker Fang static int iwp_detach(dev_info_t *, ddi_detach_cmd_t); 32222a84b8dSQuaker Fang static void iwp_destroy_locks(iwp_sc_t *); 32322a84b8dSQuaker Fang static int iwp_send(ieee80211com_t *, mblk_t *, uint8_t); 32422a84b8dSQuaker Fang static void iwp_thread(iwp_sc_t *); 32522a84b8dSQuaker Fang static int iwp_run_state_config(iwp_sc_t *); 32622a84b8dSQuaker Fang static int iwp_fast_recover(iwp_sc_t *); 32722a84b8dSQuaker Fang static void iwp_overwrite_ic_default(iwp_sc_t *); 32822a84b8dSQuaker Fang static int iwp_add_ap_sta(iwp_sc_t *); 32922a84b8dSQuaker Fang static int iwp_alloc_dma_mem(iwp_sc_t *, size_t, 33022a84b8dSQuaker Fang ddi_dma_attr_t *, ddi_device_acc_attr_t *, 33122a84b8dSQuaker Fang uint_t, iwp_dma_t *); 33222a84b8dSQuaker Fang static void iwp_free_dma_mem(iwp_dma_t *); 33322a84b8dSQuaker Fang static int iwp_eep_ver_chk(iwp_sc_t *); 33422a84b8dSQuaker Fang static void iwp_set_chip_param(iwp_sc_t *); 33522a84b8dSQuaker Fang 33622a84b8dSQuaker Fang /* 33722a84b8dSQuaker Fang * GLD specific operations 33822a84b8dSQuaker Fang */ 33922a84b8dSQuaker Fang static int iwp_m_stat(void *, uint_t, uint64_t *); 34022a84b8dSQuaker Fang static int iwp_m_start(void *); 34122a84b8dSQuaker Fang static void iwp_m_stop(void *); 34222a84b8dSQuaker Fang static int iwp_m_unicst(void *, const uint8_t *); 34322a84b8dSQuaker Fang static int iwp_m_multicst(void *, boolean_t, const uint8_t *); 34422a84b8dSQuaker Fang static int iwp_m_promisc(void *, boolean_t); 34522a84b8dSQuaker Fang static mblk_t *iwp_m_tx(void *, mblk_t *); 34622a84b8dSQuaker Fang static void iwp_m_ioctl(void *, queue_t *, mblk_t *); 34722a84b8dSQuaker Fang static int iwp_m_setprop(void *arg, const char *pr_name, 34822a84b8dSQuaker Fang mac_prop_id_t wldp_pr_num, uint_t wldp_length, const void *wldp_buf); 34922a84b8dSQuaker Fang static int iwp_m_getprop(void *arg, const char *pr_name, 350*0dc2366fSVenugopal Iyer mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf); 351*0dc2366fSVenugopal Iyer static void iwp_m_propinfo(void *, const char *, mac_prop_id_t, 352*0dc2366fSVenugopal Iyer mac_prop_info_handle_t); 35322a84b8dSQuaker Fang 35422a84b8dSQuaker Fang /* 35522a84b8dSQuaker Fang * Supported rates for 802.11b/g modes (in 500Kbps unit). 35622a84b8dSQuaker Fang */ 35722a84b8dSQuaker Fang static const struct ieee80211_rateset iwp_rateset_11b = 35822a84b8dSQuaker Fang { 4, { 2, 4, 11, 22 } }; 35922a84b8dSQuaker Fang 36022a84b8dSQuaker Fang static const struct ieee80211_rateset iwp_rateset_11g = 36122a84b8dSQuaker Fang { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } }; 36222a84b8dSQuaker Fang 36322a84b8dSQuaker Fang /* 36422a84b8dSQuaker Fang * For mfthread only 36522a84b8dSQuaker Fang */ 36622a84b8dSQuaker Fang extern pri_t minclsyspri; 36722a84b8dSQuaker Fang 36822a84b8dSQuaker Fang #define DRV_NAME_SP "iwp" 36922a84b8dSQuaker Fang 37022a84b8dSQuaker Fang /* 37122a84b8dSQuaker Fang * Module Loading Data & Entry Points 37222a84b8dSQuaker Fang */ 37322a84b8dSQuaker Fang DDI_DEFINE_STREAM_OPS(iwp_devops, nulldev, nulldev, iwp_attach, 37422a84b8dSQuaker Fang iwp_detach, nodev, NULL, D_MP, NULL, iwp_quiesce); 37522a84b8dSQuaker Fang 37622a84b8dSQuaker Fang static struct modldrv iwp_modldrv = { 37722a84b8dSQuaker Fang &mod_driverops, 37822a84b8dSQuaker Fang "Intel(R) PumaPeak driver(N)", 37922a84b8dSQuaker Fang &iwp_devops 38022a84b8dSQuaker Fang }; 38122a84b8dSQuaker Fang 38222a84b8dSQuaker Fang static struct modlinkage iwp_modlinkage = { 38322a84b8dSQuaker Fang MODREV_1, 38422a84b8dSQuaker Fang &iwp_modldrv, 38522a84b8dSQuaker Fang NULL 38622a84b8dSQuaker Fang }; 38722a84b8dSQuaker Fang 38822a84b8dSQuaker Fang int 38922a84b8dSQuaker Fang _init(void) 39022a84b8dSQuaker Fang { 39122a84b8dSQuaker Fang int status; 39222a84b8dSQuaker Fang 39322a84b8dSQuaker Fang status = ddi_soft_state_init(&iwp_soft_state_p, 39422a84b8dSQuaker Fang sizeof (iwp_sc_t), 1); 39522a84b8dSQuaker Fang if (status != DDI_SUCCESS) { 39622a84b8dSQuaker Fang return (status); 39722a84b8dSQuaker Fang } 39822a84b8dSQuaker Fang 39922a84b8dSQuaker Fang mac_init_ops(&iwp_devops, DRV_NAME_SP); 40022a84b8dSQuaker Fang status = mod_install(&iwp_modlinkage); 40122a84b8dSQuaker Fang if (status != DDI_SUCCESS) { 40222a84b8dSQuaker Fang mac_fini_ops(&iwp_devops); 40322a84b8dSQuaker Fang ddi_soft_state_fini(&iwp_soft_state_p); 40422a84b8dSQuaker Fang } 40522a84b8dSQuaker Fang 40622a84b8dSQuaker Fang return (status); 40722a84b8dSQuaker Fang } 40822a84b8dSQuaker Fang 40922a84b8dSQuaker Fang int 41022a84b8dSQuaker Fang _fini(void) 41122a84b8dSQuaker Fang { 41222a84b8dSQuaker Fang int status; 41322a84b8dSQuaker Fang 41422a84b8dSQuaker Fang status = mod_remove(&iwp_modlinkage); 41522a84b8dSQuaker Fang if (DDI_SUCCESS == status) { 41622a84b8dSQuaker Fang mac_fini_ops(&iwp_devops); 41722a84b8dSQuaker Fang ddi_soft_state_fini(&iwp_soft_state_p); 41822a84b8dSQuaker Fang } 41922a84b8dSQuaker Fang 42022a84b8dSQuaker Fang return (status); 42122a84b8dSQuaker Fang } 42222a84b8dSQuaker Fang 42322a84b8dSQuaker Fang int 42422a84b8dSQuaker Fang _info(struct modinfo *mip) 42522a84b8dSQuaker Fang { 42622a84b8dSQuaker Fang return (mod_info(&iwp_modlinkage, mip)); 42722a84b8dSQuaker Fang } 42822a84b8dSQuaker Fang 42922a84b8dSQuaker Fang /* 43022a84b8dSQuaker Fang * Mac Call Back entries 43122a84b8dSQuaker Fang */ 43222a84b8dSQuaker Fang mac_callbacks_t iwp_m_callbacks = { 433*0dc2366fSVenugopal Iyer MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO, 43422a84b8dSQuaker Fang iwp_m_stat, 43522a84b8dSQuaker Fang iwp_m_start, 43622a84b8dSQuaker Fang iwp_m_stop, 43722a84b8dSQuaker Fang iwp_m_promisc, 43822a84b8dSQuaker Fang iwp_m_multicst, 43922a84b8dSQuaker Fang iwp_m_unicst, 44022a84b8dSQuaker Fang iwp_m_tx, 441*0dc2366fSVenugopal Iyer NULL, 44222a84b8dSQuaker Fang iwp_m_ioctl, 44322a84b8dSQuaker Fang NULL, 44422a84b8dSQuaker Fang NULL, 44522a84b8dSQuaker Fang NULL, 44622a84b8dSQuaker Fang iwp_m_setprop, 447*0dc2366fSVenugopal Iyer iwp_m_getprop, 448*0dc2366fSVenugopal Iyer iwp_m_propinfo 44922a84b8dSQuaker Fang }; 45022a84b8dSQuaker Fang 45122a84b8dSQuaker Fang #ifdef DEBUG 45222a84b8dSQuaker Fang void 45322a84b8dSQuaker Fang iwp_dbg(uint32_t flags, const char *fmt, ...) 45422a84b8dSQuaker Fang { 45522a84b8dSQuaker Fang va_list ap; 45622a84b8dSQuaker Fang 45722a84b8dSQuaker Fang if (flags & iwp_dbg_flags) { 45822a84b8dSQuaker Fang va_start(ap, fmt); 45922a84b8dSQuaker Fang vcmn_err(CE_NOTE, fmt, ap); 46022a84b8dSQuaker Fang va_end(ap); 46122a84b8dSQuaker Fang } 46222a84b8dSQuaker Fang } 46322a84b8dSQuaker Fang #endif /* DEBUG */ 46422a84b8dSQuaker Fang 46522a84b8dSQuaker Fang /* 46622a84b8dSQuaker Fang * device operations 46722a84b8dSQuaker Fang */ 46822a84b8dSQuaker Fang int 46922a84b8dSQuaker Fang iwp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 47022a84b8dSQuaker Fang { 47122a84b8dSQuaker Fang iwp_sc_t *sc; 47222a84b8dSQuaker Fang ieee80211com_t *ic; 47322a84b8dSQuaker Fang int instance, i; 47422a84b8dSQuaker Fang char strbuf[32]; 47522a84b8dSQuaker Fang wifi_data_t wd = { 0 }; 47622a84b8dSQuaker Fang mac_register_t *macp; 47722a84b8dSQuaker Fang int intr_type; 47822a84b8dSQuaker Fang int intr_count; 47922a84b8dSQuaker Fang int intr_actual; 48022a84b8dSQuaker Fang int err = DDI_FAILURE; 48122a84b8dSQuaker Fang 48222a84b8dSQuaker Fang switch (cmd) { 48322a84b8dSQuaker Fang case DDI_ATTACH: 48422a84b8dSQuaker Fang break; 48522a84b8dSQuaker Fang case DDI_RESUME: 48622a84b8dSQuaker Fang instance = ddi_get_instance(dip); 48722a84b8dSQuaker Fang sc = ddi_get_soft_state(iwp_soft_state_p, 48822a84b8dSQuaker Fang instance); 48922a84b8dSQuaker Fang ASSERT(sc != NULL); 49022a84b8dSQuaker Fang 49122a84b8dSQuaker Fang if (sc->sc_flags & IWP_F_RUNNING) { 49222a84b8dSQuaker Fang (void) iwp_init(sc); 49322a84b8dSQuaker Fang } 49422a84b8dSQuaker Fang 49522a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_SUSPEND); 49622a84b8dSQuaker Fang 49722a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_RESUME, "iwp_attach(): " 49822a84b8dSQuaker Fang "resume\n")); 49922a84b8dSQuaker Fang return (DDI_SUCCESS); 50022a84b8dSQuaker Fang default: 50122a84b8dSQuaker Fang goto attach_fail1; 50222a84b8dSQuaker Fang } 50322a84b8dSQuaker Fang 50422a84b8dSQuaker Fang instance = ddi_get_instance(dip); 50522a84b8dSQuaker Fang err = ddi_soft_state_zalloc(iwp_soft_state_p, instance); 50622a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 50722a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_attach(): " 50822a84b8dSQuaker Fang "failed to allocate soft state\n"); 50922a84b8dSQuaker Fang goto attach_fail1; 51022a84b8dSQuaker Fang } 51122a84b8dSQuaker Fang 51222a84b8dSQuaker Fang sc = ddi_get_soft_state(iwp_soft_state_p, instance); 51322a84b8dSQuaker Fang ASSERT(sc != NULL); 51422a84b8dSQuaker Fang 51522a84b8dSQuaker Fang sc->sc_dip = dip; 51622a84b8dSQuaker Fang 51722a84b8dSQuaker Fang /* 51822a84b8dSQuaker Fang * map configure space 51922a84b8dSQuaker Fang */ 52022a84b8dSQuaker Fang err = ddi_regs_map_setup(dip, 0, &sc->sc_cfg_base, 0, 0, 52122a84b8dSQuaker Fang &iwp_reg_accattr, &sc->sc_cfg_handle); 52222a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 52322a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_attach(): " 52422a84b8dSQuaker Fang "failed to map config spaces regs\n"); 52522a84b8dSQuaker Fang goto attach_fail2; 52622a84b8dSQuaker Fang } 52722a84b8dSQuaker Fang 52822a84b8dSQuaker Fang sc->sc_dev_id = ddi_get16(sc->sc_cfg_handle, 52922a84b8dSQuaker Fang (uint16_t *)(sc->sc_cfg_base + PCI_CONF_DEVID)); 53022a84b8dSQuaker Fang if ((sc->sc_dev_id != 0x422B) && 53122a84b8dSQuaker Fang (sc->sc_dev_id != 0x422C) && 53222a84b8dSQuaker Fang (sc->sc_dev_id != 0x4238) && 53322a84b8dSQuaker Fang (sc->sc_dev_id != 0x4239) && 53422a84b8dSQuaker Fang (sc->sc_dev_id != 0x008d) && 53522a84b8dSQuaker Fang (sc->sc_dev_id != 0x008e)) { 53622a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_attach(): " 53722a84b8dSQuaker Fang "Do not support this device\n"); 53822a84b8dSQuaker Fang goto attach_fail3; 53922a84b8dSQuaker Fang } 54022a84b8dSQuaker Fang 54122a84b8dSQuaker Fang iwp_set_chip_param(sc); 54222a84b8dSQuaker Fang 54322a84b8dSQuaker Fang sc->sc_rev = ddi_get8(sc->sc_cfg_handle, 54422a84b8dSQuaker Fang (uint8_t *)(sc->sc_cfg_base + PCI_CONF_REVID)); 54522a84b8dSQuaker Fang 54622a84b8dSQuaker Fang /* 54722a84b8dSQuaker Fang * keep from disturbing C3 state of CPU 54822a84b8dSQuaker Fang */ 54922a84b8dSQuaker Fang ddi_put8(sc->sc_cfg_handle, (uint8_t *)(sc->sc_cfg_base + 55022a84b8dSQuaker Fang PCI_CFG_RETRY_TIMEOUT), 0); 55122a84b8dSQuaker Fang 55222a84b8dSQuaker Fang /* 55322a84b8dSQuaker Fang * determine the size of buffer for frame and command to ucode 55422a84b8dSQuaker Fang */ 55522a84b8dSQuaker Fang sc->sc_clsz = ddi_get16(sc->sc_cfg_handle, 55622a84b8dSQuaker Fang (uint16_t *)(sc->sc_cfg_base + PCI_CONF_CACHE_LINESZ)); 55722a84b8dSQuaker Fang if (!sc->sc_clsz) { 55822a84b8dSQuaker Fang sc->sc_clsz = 16; 55922a84b8dSQuaker Fang } 56022a84b8dSQuaker Fang sc->sc_clsz = (sc->sc_clsz << 2); 56122a84b8dSQuaker Fang 56222a84b8dSQuaker Fang sc->sc_dmabuf_sz = roundup(0x1000 + sizeof (struct ieee80211_frame) + 56322a84b8dSQuaker Fang IEEE80211_MTU + IEEE80211_CRC_LEN + 56422a84b8dSQuaker Fang (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + 56522a84b8dSQuaker Fang IEEE80211_WEP_CRCLEN), sc->sc_clsz); 56622a84b8dSQuaker Fang 56722a84b8dSQuaker Fang /* 56822a84b8dSQuaker Fang * Map operating registers 56922a84b8dSQuaker Fang */ 57022a84b8dSQuaker Fang err = ddi_regs_map_setup(dip, 1, &sc->sc_base, 57122a84b8dSQuaker Fang 0, 0, &iwp_reg_accattr, &sc->sc_handle); 57222a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 57322a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_attach(): " 57422a84b8dSQuaker Fang "failed to map device regs\n"); 57522a84b8dSQuaker Fang goto attach_fail3; 57622a84b8dSQuaker Fang } 57722a84b8dSQuaker Fang 57822a84b8dSQuaker Fang /* 57922a84b8dSQuaker Fang * this is used to differentiate type of hardware 58022a84b8dSQuaker Fang */ 58122a84b8dSQuaker Fang sc->sc_hw_rev = IWP_READ(sc, CSR_HW_REV); 58222a84b8dSQuaker Fang 58322a84b8dSQuaker Fang err = ddi_intr_get_supported_types(dip, &intr_type); 58422a84b8dSQuaker Fang if ((err != DDI_SUCCESS) || (!(intr_type & DDI_INTR_TYPE_FIXED))) { 58522a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_attach(): " 58622a84b8dSQuaker Fang "fixed type interrupt is not supported\n"); 58722a84b8dSQuaker Fang goto attach_fail4; 58822a84b8dSQuaker Fang } 58922a84b8dSQuaker Fang 59022a84b8dSQuaker Fang err = ddi_intr_get_nintrs(dip, DDI_INTR_TYPE_FIXED, &intr_count); 59122a84b8dSQuaker Fang if ((err != DDI_SUCCESS) || (intr_count != 1)) { 59222a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_attach(): " 59322a84b8dSQuaker Fang "no fixed interrupts\n"); 59422a84b8dSQuaker Fang goto attach_fail4; 59522a84b8dSQuaker Fang } 59622a84b8dSQuaker Fang 59722a84b8dSQuaker Fang sc->sc_intr_htable = kmem_zalloc(sizeof (ddi_intr_handle_t), KM_SLEEP); 59822a84b8dSQuaker Fang 59922a84b8dSQuaker Fang err = ddi_intr_alloc(dip, sc->sc_intr_htable, DDI_INTR_TYPE_FIXED, 0, 60022a84b8dSQuaker Fang intr_count, &intr_actual, 0); 60122a84b8dSQuaker Fang if ((err != DDI_SUCCESS) || (intr_actual != 1)) { 60222a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_attach(): " 60322a84b8dSQuaker Fang "ddi_intr_alloc() failed 0x%x\n", err); 60422a84b8dSQuaker Fang goto attach_fail5; 60522a84b8dSQuaker Fang } 60622a84b8dSQuaker Fang 60722a84b8dSQuaker Fang err = ddi_intr_get_pri(sc->sc_intr_htable[0], &sc->sc_intr_pri); 60822a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 60922a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_attach(): " 61022a84b8dSQuaker Fang "ddi_intr_get_pri() failed 0x%x\n", err); 61122a84b8dSQuaker Fang goto attach_fail6; 61222a84b8dSQuaker Fang } 61322a84b8dSQuaker Fang 61422a84b8dSQuaker Fang mutex_init(&sc->sc_glock, NULL, MUTEX_DRIVER, 61522a84b8dSQuaker Fang DDI_INTR_PRI(sc->sc_intr_pri)); 61622a84b8dSQuaker Fang mutex_init(&sc->sc_tx_lock, NULL, MUTEX_DRIVER, 61722a84b8dSQuaker Fang DDI_INTR_PRI(sc->sc_intr_pri)); 61822a84b8dSQuaker Fang mutex_init(&sc->sc_mt_lock, NULL, MUTEX_DRIVER, 61922a84b8dSQuaker Fang DDI_INTR_PRI(sc->sc_intr_pri)); 62022a84b8dSQuaker Fang 62122a84b8dSQuaker Fang cv_init(&sc->sc_cmd_cv, NULL, CV_DRIVER, NULL); 62222a84b8dSQuaker Fang cv_init(&sc->sc_put_seg_cv, NULL, CV_DRIVER, NULL); 62322a84b8dSQuaker Fang cv_init(&sc->sc_ucode_cv, NULL, CV_DRIVER, NULL); 62422a84b8dSQuaker Fang 62522a84b8dSQuaker Fang /* 62622a84b8dSQuaker Fang * initialize the mfthread 62722a84b8dSQuaker Fang */ 62822a84b8dSQuaker Fang cv_init(&sc->sc_mt_cv, NULL, CV_DRIVER, NULL); 62922a84b8dSQuaker Fang sc->sc_mf_thread = NULL; 63022a84b8dSQuaker Fang sc->sc_mf_thread_switch = 0; 63122a84b8dSQuaker Fang 63222a84b8dSQuaker Fang /* 63322a84b8dSQuaker Fang * Allocate shared buffer for communication between driver and ucode. 63422a84b8dSQuaker Fang */ 63522a84b8dSQuaker Fang err = iwp_alloc_shared(sc); 63622a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 63722a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_attach(): " 63822a84b8dSQuaker Fang "failed to allocate shared page\n"); 63922a84b8dSQuaker Fang goto attach_fail7; 64022a84b8dSQuaker Fang } 64122a84b8dSQuaker Fang 64222a84b8dSQuaker Fang (void) memset(sc->sc_shared, 0, sizeof (iwp_shared_t)); 64322a84b8dSQuaker Fang 64422a84b8dSQuaker Fang /* 64522a84b8dSQuaker Fang * Allocate keep warm page. 64622a84b8dSQuaker Fang */ 64722a84b8dSQuaker Fang err = iwp_alloc_kw(sc); 64822a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 64922a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_attach(): " 65022a84b8dSQuaker Fang "failed to allocate keep warm page\n"); 65122a84b8dSQuaker Fang goto attach_fail8; 65222a84b8dSQuaker Fang } 65322a84b8dSQuaker Fang 65422a84b8dSQuaker Fang /* 65522a84b8dSQuaker Fang * Do some necessary hardware initializations. 65622a84b8dSQuaker Fang */ 65722a84b8dSQuaker Fang err = iwp_preinit(sc); 65822a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 65922a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_attach(): " 66022a84b8dSQuaker Fang "failed to initialize hardware\n"); 66122a84b8dSQuaker Fang goto attach_fail9; 66222a84b8dSQuaker Fang } 66322a84b8dSQuaker Fang 66422a84b8dSQuaker Fang /* 66522a84b8dSQuaker Fang * get hardware configurations from eeprom 66622a84b8dSQuaker Fang */ 66722a84b8dSQuaker Fang err = iwp_eep_load(sc); 66822a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 66922a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_attach(): " 67022a84b8dSQuaker Fang "failed to load eeprom\n"); 67122a84b8dSQuaker Fang goto attach_fail9; 67222a84b8dSQuaker Fang } 67322a84b8dSQuaker Fang 67422a84b8dSQuaker Fang /* 67522a84b8dSQuaker Fang * calibration information from EEPROM 67622a84b8dSQuaker Fang */ 67722a84b8dSQuaker Fang sc->sc_eep_calib = (struct iwp_eep_calibration *) 67822a84b8dSQuaker Fang iwp_eep_addr_trans(sc, EEP_CALIBRATION); 67922a84b8dSQuaker Fang 68022a84b8dSQuaker Fang err = iwp_eep_ver_chk(sc); 68122a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 68222a84b8dSQuaker Fang goto attach_fail9; 68322a84b8dSQuaker Fang } 68422a84b8dSQuaker Fang 68522a84b8dSQuaker Fang /* 68622a84b8dSQuaker Fang * get MAC address of this chipset 68722a84b8dSQuaker Fang */ 68822a84b8dSQuaker Fang iwp_get_mac_from_eep(sc); 68922a84b8dSQuaker Fang 69022a84b8dSQuaker Fang 69122a84b8dSQuaker Fang /* 69222a84b8dSQuaker Fang * initialize TX and RX ring buffers 69322a84b8dSQuaker Fang */ 69422a84b8dSQuaker Fang err = iwp_ring_init(sc); 69522a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 69622a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_attach(): " 69722a84b8dSQuaker Fang "failed to allocate and initialize ring\n"); 69822a84b8dSQuaker Fang goto attach_fail9; 69922a84b8dSQuaker Fang } 70022a84b8dSQuaker Fang 70122a84b8dSQuaker Fang sc->sc_hdr = (iwp_firmware_hdr_t *)iwp_fw_bin; 70222a84b8dSQuaker Fang 70322a84b8dSQuaker Fang /* 70422a84b8dSQuaker Fang * copy ucode to dma buffer 70522a84b8dSQuaker Fang */ 70622a84b8dSQuaker Fang err = iwp_alloc_fw_dma(sc); 70722a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 70822a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_attach(): " 70922a84b8dSQuaker Fang "failed to allocate firmware dma\n"); 71022a84b8dSQuaker Fang goto attach_fail10; 71122a84b8dSQuaker Fang } 71222a84b8dSQuaker Fang 71322a84b8dSQuaker Fang /* 71422a84b8dSQuaker Fang * Initialize the wifi part, which will be used by 71522a84b8dSQuaker Fang * 802.11 module 71622a84b8dSQuaker Fang */ 71722a84b8dSQuaker Fang ic = &sc->sc_ic; 71822a84b8dSQuaker Fang ic->ic_phytype = IEEE80211_T_OFDM; 71922a84b8dSQuaker Fang ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ 72022a84b8dSQuaker Fang ic->ic_state = IEEE80211_S_INIT; 72122a84b8dSQuaker Fang ic->ic_maxrssi = 100; /* experimental number */ 72222a84b8dSQuaker Fang ic->ic_caps = IEEE80211_C_SHPREAMBLE | IEEE80211_C_TXPMGT | 72322a84b8dSQuaker Fang IEEE80211_C_PMGT | IEEE80211_C_SHSLOT; 72422a84b8dSQuaker Fang 72522a84b8dSQuaker Fang /* 72622a84b8dSQuaker Fang * Support WPA/WPA2 72722a84b8dSQuaker Fang */ 72822a84b8dSQuaker Fang ic->ic_caps |= IEEE80211_C_WPA; 72922a84b8dSQuaker Fang 73022a84b8dSQuaker Fang /* 73122a84b8dSQuaker Fang * set supported .11b and .11g rates 73222a84b8dSQuaker Fang */ 73322a84b8dSQuaker Fang ic->ic_sup_rates[IEEE80211_MODE_11B] = iwp_rateset_11b; 73422a84b8dSQuaker Fang ic->ic_sup_rates[IEEE80211_MODE_11G] = iwp_rateset_11g; 73522a84b8dSQuaker Fang 73622a84b8dSQuaker Fang /* 73722a84b8dSQuaker Fang * set supported .11b and .11g channels (1 through 11) 73822a84b8dSQuaker Fang */ 73922a84b8dSQuaker Fang for (i = 1; i <= 11; i++) { 74022a84b8dSQuaker Fang ic->ic_sup_channels[i].ich_freq = 74122a84b8dSQuaker Fang ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); 74222a84b8dSQuaker Fang ic->ic_sup_channels[i].ich_flags = 74322a84b8dSQuaker Fang IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | 74422a84b8dSQuaker Fang IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ | 74522a84b8dSQuaker Fang IEEE80211_CHAN_PASSIVE; 74622a84b8dSQuaker Fang } 74722a84b8dSQuaker Fang 74822a84b8dSQuaker Fang ic->ic_ibss_chan = &ic->ic_sup_channels[0]; 74922a84b8dSQuaker Fang ic->ic_xmit = iwp_send; 75022a84b8dSQuaker Fang 75122a84b8dSQuaker Fang /* 75222a84b8dSQuaker Fang * attach to 802.11 module 75322a84b8dSQuaker Fang */ 75422a84b8dSQuaker Fang ieee80211_attach(ic); 75522a84b8dSQuaker Fang 75622a84b8dSQuaker Fang /* 75722a84b8dSQuaker Fang * different instance has different WPA door 75822a84b8dSQuaker Fang */ 75922a84b8dSQuaker Fang (void) snprintf(ic->ic_wpadoor, MAX_IEEE80211STR, "%s_%s%d", WPA_DOOR, 76022a84b8dSQuaker Fang ddi_driver_name(dip), 76122a84b8dSQuaker Fang ddi_get_instance(dip)); 76222a84b8dSQuaker Fang 76322a84b8dSQuaker Fang /* 76422a84b8dSQuaker Fang * Overwrite 80211 default configurations. 76522a84b8dSQuaker Fang */ 76622a84b8dSQuaker Fang iwp_overwrite_ic_default(sc); 76722a84b8dSQuaker Fang 76822a84b8dSQuaker Fang /* 76922a84b8dSQuaker Fang * initialize 802.11 module 77022a84b8dSQuaker Fang */ 77122a84b8dSQuaker Fang ieee80211_media_init(ic); 77222a84b8dSQuaker Fang 77322a84b8dSQuaker Fang /* 77422a84b8dSQuaker Fang * initialize default tx key 77522a84b8dSQuaker Fang */ 77622a84b8dSQuaker Fang ic->ic_def_txkey = 0; 77722a84b8dSQuaker Fang 77822a84b8dSQuaker Fang err = ddi_intr_add_softint(dip, &sc->sc_soft_hdl, DDI_INTR_SOFTPRI_MAX, 77922a84b8dSQuaker Fang iwp_rx_softintr, (caddr_t)sc); 78022a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 78122a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_attach(): " 78222a84b8dSQuaker Fang "add soft interrupt failed\n"); 78322a84b8dSQuaker Fang goto attach_fail12; 78422a84b8dSQuaker Fang } 78522a84b8dSQuaker Fang 78622a84b8dSQuaker Fang err = ddi_intr_add_handler(sc->sc_intr_htable[0], iwp_intr, 78722a84b8dSQuaker Fang (caddr_t)sc, NULL); 78822a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 78922a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_attach(): " 79022a84b8dSQuaker Fang "ddi_intr_add_handle() failed\n"); 79122a84b8dSQuaker Fang goto attach_fail13; 79222a84b8dSQuaker Fang } 79322a84b8dSQuaker Fang 79422a84b8dSQuaker Fang err = ddi_intr_enable(sc->sc_intr_htable[0]); 79522a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 79622a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_attach(): " 79722a84b8dSQuaker Fang "ddi_intr_enable() failed\n"); 79822a84b8dSQuaker Fang goto attach_fail14; 79922a84b8dSQuaker Fang } 80022a84b8dSQuaker Fang 80122a84b8dSQuaker Fang /* 80222a84b8dSQuaker Fang * Initialize pointer to device specific functions 80322a84b8dSQuaker Fang */ 80422a84b8dSQuaker Fang wd.wd_secalloc = WIFI_SEC_NONE; 80522a84b8dSQuaker Fang wd.wd_opmode = ic->ic_opmode; 80622a84b8dSQuaker Fang IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_macaddr); 80722a84b8dSQuaker Fang 80822a84b8dSQuaker Fang /* 80922a84b8dSQuaker Fang * create relation to GLD 81022a84b8dSQuaker Fang */ 81122a84b8dSQuaker Fang macp = mac_alloc(MAC_VERSION); 81222a84b8dSQuaker Fang if (NULL == macp) { 81322a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_attach(): " 81422a84b8dSQuaker Fang "failed to do mac_alloc()\n"); 81522a84b8dSQuaker Fang goto attach_fail15; 81622a84b8dSQuaker Fang } 81722a84b8dSQuaker Fang 81822a84b8dSQuaker Fang macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI; 81922a84b8dSQuaker Fang macp->m_driver = sc; 82022a84b8dSQuaker Fang macp->m_dip = dip; 82122a84b8dSQuaker Fang macp->m_src_addr = ic->ic_macaddr; 82222a84b8dSQuaker Fang macp->m_callbacks = &iwp_m_callbacks; 82322a84b8dSQuaker Fang macp->m_min_sdu = 0; 82422a84b8dSQuaker Fang macp->m_max_sdu = IEEE80211_MTU; 82522a84b8dSQuaker Fang macp->m_pdata = &wd; 82622a84b8dSQuaker Fang macp->m_pdata_size = sizeof (wd); 82722a84b8dSQuaker Fang 82822a84b8dSQuaker Fang /* 82922a84b8dSQuaker Fang * Register the macp to mac 83022a84b8dSQuaker Fang */ 83122a84b8dSQuaker Fang err = mac_register(macp, &ic->ic_mach); 83222a84b8dSQuaker Fang mac_free(macp); 83322a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 83422a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_attach(): " 83522a84b8dSQuaker Fang "failed to do mac_register()\n"); 83622a84b8dSQuaker Fang goto attach_fail15; 83722a84b8dSQuaker Fang } 83822a84b8dSQuaker Fang 83922a84b8dSQuaker Fang /* 84022a84b8dSQuaker Fang * Create minor node of type DDI_NT_NET_WIFI 84122a84b8dSQuaker Fang */ 84222a84b8dSQuaker Fang (void) snprintf(strbuf, sizeof (strbuf), DRV_NAME_SP"%d", instance); 84322a84b8dSQuaker Fang err = ddi_create_minor_node(dip, strbuf, S_IFCHR, 84422a84b8dSQuaker Fang instance + 1, DDI_NT_NET_WIFI, 0); 84522a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 84622a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_attach(): " 84722a84b8dSQuaker Fang "failed to do ddi_create_minor_node()\n"); 84822a84b8dSQuaker Fang } 84922a84b8dSQuaker Fang 85022a84b8dSQuaker Fang /* 85122a84b8dSQuaker Fang * Notify link is down now 85222a84b8dSQuaker Fang */ 85322a84b8dSQuaker Fang mac_link_update(ic->ic_mach, LINK_STATE_DOWN); 85422a84b8dSQuaker Fang 85522a84b8dSQuaker Fang /* 85622a84b8dSQuaker Fang * create the mf thread to handle the link status, 85722a84b8dSQuaker Fang * recovery fatal error, etc. 85822a84b8dSQuaker Fang */ 85922a84b8dSQuaker Fang sc->sc_mf_thread_switch = 1; 86022a84b8dSQuaker Fang if (NULL == sc->sc_mf_thread) { 86122a84b8dSQuaker Fang sc->sc_mf_thread = thread_create((caddr_t)NULL, 0, 86222a84b8dSQuaker Fang iwp_thread, sc, 0, &p0, TS_RUN, minclsyspri); 86322a84b8dSQuaker Fang } 86422a84b8dSQuaker Fang 86522a84b8dSQuaker Fang atomic_or_32(&sc->sc_flags, IWP_F_ATTACHED); 86622a84b8dSQuaker Fang 86722a84b8dSQuaker Fang return (DDI_SUCCESS); 86822a84b8dSQuaker Fang 86922a84b8dSQuaker Fang attach_fail15: 87022a84b8dSQuaker Fang (void) ddi_intr_disable(sc->sc_intr_htable[0]); 87122a84b8dSQuaker Fang attach_fail14: 87222a84b8dSQuaker Fang (void) ddi_intr_remove_handler(sc->sc_intr_htable[0]); 87322a84b8dSQuaker Fang attach_fail13: 87422a84b8dSQuaker Fang (void) ddi_intr_remove_softint(sc->sc_soft_hdl); 87522a84b8dSQuaker Fang sc->sc_soft_hdl = NULL; 87622a84b8dSQuaker Fang attach_fail12: 87722a84b8dSQuaker Fang ieee80211_detach(ic); 87822a84b8dSQuaker Fang attach_fail11: 87922a84b8dSQuaker Fang iwp_free_fw_dma(sc); 88022a84b8dSQuaker Fang attach_fail10: 88122a84b8dSQuaker Fang iwp_ring_free(sc); 88222a84b8dSQuaker Fang attach_fail9: 88322a84b8dSQuaker Fang iwp_free_kw(sc); 88422a84b8dSQuaker Fang attach_fail8: 88522a84b8dSQuaker Fang iwp_free_shared(sc); 88622a84b8dSQuaker Fang attach_fail7: 88722a84b8dSQuaker Fang iwp_destroy_locks(sc); 88822a84b8dSQuaker Fang attach_fail6: 88922a84b8dSQuaker Fang (void) ddi_intr_free(sc->sc_intr_htable[0]); 89022a84b8dSQuaker Fang attach_fail5: 89122a84b8dSQuaker Fang kmem_free(sc->sc_intr_htable, sizeof (ddi_intr_handle_t)); 89222a84b8dSQuaker Fang attach_fail4: 89322a84b8dSQuaker Fang ddi_regs_map_free(&sc->sc_handle); 89422a84b8dSQuaker Fang attach_fail3: 89522a84b8dSQuaker Fang ddi_regs_map_free(&sc->sc_cfg_handle); 89622a84b8dSQuaker Fang attach_fail2: 89722a84b8dSQuaker Fang ddi_soft_state_free(iwp_soft_state_p, instance); 89822a84b8dSQuaker Fang attach_fail1: 89922a84b8dSQuaker Fang return (DDI_FAILURE); 90022a84b8dSQuaker Fang } 90122a84b8dSQuaker Fang 90222a84b8dSQuaker Fang int 90322a84b8dSQuaker Fang iwp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 90422a84b8dSQuaker Fang { 90522a84b8dSQuaker Fang iwp_sc_t *sc; 90622a84b8dSQuaker Fang ieee80211com_t *ic; 90722a84b8dSQuaker Fang int err; 90822a84b8dSQuaker Fang 90922a84b8dSQuaker Fang sc = ddi_get_soft_state(iwp_soft_state_p, ddi_get_instance(dip)); 91022a84b8dSQuaker Fang ASSERT(sc != NULL); 91122a84b8dSQuaker Fang ic = &sc->sc_ic; 91222a84b8dSQuaker Fang 91322a84b8dSQuaker Fang switch (cmd) { 91422a84b8dSQuaker Fang case DDI_DETACH: 91522a84b8dSQuaker Fang break; 91622a84b8dSQuaker Fang case DDI_SUSPEND: 91722a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_HW_ERR_RECOVER); 91822a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_RATE_AUTO_CTL); 91922a84b8dSQuaker Fang 92022a84b8dSQuaker Fang atomic_or_32(&sc->sc_flags, IWP_F_SUSPEND); 92122a84b8dSQuaker Fang 92222a84b8dSQuaker Fang if (sc->sc_flags & IWP_F_RUNNING) { 92322a84b8dSQuaker Fang iwp_stop(sc); 92422a84b8dSQuaker Fang ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 92522a84b8dSQuaker Fang 92622a84b8dSQuaker Fang } 92722a84b8dSQuaker Fang 92822a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_RESUME, "iwp_detach(): " 92922a84b8dSQuaker Fang "suspend\n")); 93022a84b8dSQuaker Fang return (DDI_SUCCESS); 93122a84b8dSQuaker Fang default: 93222a84b8dSQuaker Fang return (DDI_FAILURE); 93322a84b8dSQuaker Fang } 93422a84b8dSQuaker Fang 93522a84b8dSQuaker Fang if (!(sc->sc_flags & IWP_F_ATTACHED)) { 93622a84b8dSQuaker Fang return (DDI_FAILURE); 93722a84b8dSQuaker Fang } 93822a84b8dSQuaker Fang 93922a84b8dSQuaker Fang /* 94022a84b8dSQuaker Fang * Destroy the mf_thread 94122a84b8dSQuaker Fang */ 94222a84b8dSQuaker Fang sc->sc_mf_thread_switch = 0; 94322a84b8dSQuaker Fang 94422a84b8dSQuaker Fang mutex_enter(&sc->sc_mt_lock); 94522a84b8dSQuaker Fang while (sc->sc_mf_thread != NULL) { 94622a84b8dSQuaker Fang if (cv_wait_sig(&sc->sc_mt_cv, &sc->sc_mt_lock) == 0) { 94722a84b8dSQuaker Fang break; 94822a84b8dSQuaker Fang } 94922a84b8dSQuaker Fang } 95022a84b8dSQuaker Fang mutex_exit(&sc->sc_mt_lock); 95122a84b8dSQuaker Fang 95222a84b8dSQuaker Fang err = mac_disable(sc->sc_ic.ic_mach); 95322a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 95422a84b8dSQuaker Fang return (err); 95522a84b8dSQuaker Fang } 95622a84b8dSQuaker Fang 95722a84b8dSQuaker Fang /* 95822a84b8dSQuaker Fang * stop chipset 95922a84b8dSQuaker Fang */ 96022a84b8dSQuaker Fang iwp_stop(sc); 96122a84b8dSQuaker Fang 96222a84b8dSQuaker Fang DELAY(500000); 96322a84b8dSQuaker Fang 96422a84b8dSQuaker Fang /* 96522a84b8dSQuaker Fang * release buffer for calibration 96622a84b8dSQuaker Fang */ 96722a84b8dSQuaker Fang iwp_release_calib_buffer(sc); 96822a84b8dSQuaker Fang 96922a84b8dSQuaker Fang /* 97022a84b8dSQuaker Fang * Unregiste from GLD 97122a84b8dSQuaker Fang */ 97222a84b8dSQuaker Fang (void) mac_unregister(sc->sc_ic.ic_mach); 97322a84b8dSQuaker Fang 97422a84b8dSQuaker Fang mutex_enter(&sc->sc_glock); 97522a84b8dSQuaker Fang iwp_free_fw_dma(sc); 97622a84b8dSQuaker Fang iwp_ring_free(sc); 97722a84b8dSQuaker Fang iwp_free_kw(sc); 97822a84b8dSQuaker Fang iwp_free_shared(sc); 97922a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 98022a84b8dSQuaker Fang 98122a84b8dSQuaker Fang (void) ddi_intr_disable(sc->sc_intr_htable[0]); 98222a84b8dSQuaker Fang (void) ddi_intr_remove_handler(sc->sc_intr_htable[0]); 98322a84b8dSQuaker Fang (void) ddi_intr_free(sc->sc_intr_htable[0]); 98422a84b8dSQuaker Fang kmem_free(sc->sc_intr_htable, sizeof (ddi_intr_handle_t)); 98522a84b8dSQuaker Fang 98622a84b8dSQuaker Fang (void) ddi_intr_remove_softint(sc->sc_soft_hdl); 98722a84b8dSQuaker Fang sc->sc_soft_hdl = NULL; 98822a84b8dSQuaker Fang 98922a84b8dSQuaker Fang /* 99022a84b8dSQuaker Fang * detach from 80211 module 99122a84b8dSQuaker Fang */ 99222a84b8dSQuaker Fang ieee80211_detach(&sc->sc_ic); 99322a84b8dSQuaker Fang 99422a84b8dSQuaker Fang iwp_destroy_locks(sc); 99522a84b8dSQuaker Fang 99622a84b8dSQuaker Fang ddi_regs_map_free(&sc->sc_handle); 99722a84b8dSQuaker Fang ddi_regs_map_free(&sc->sc_cfg_handle); 99822a84b8dSQuaker Fang ddi_remove_minor_node(dip, NULL); 99922a84b8dSQuaker Fang ddi_soft_state_free(iwp_soft_state_p, ddi_get_instance(dip)); 100022a84b8dSQuaker Fang 100122a84b8dSQuaker Fang return (DDI_SUCCESS); 100222a84b8dSQuaker Fang } 100322a84b8dSQuaker Fang 100422a84b8dSQuaker Fang /* 100522a84b8dSQuaker Fang * destroy all locks 100622a84b8dSQuaker Fang */ 100722a84b8dSQuaker Fang static void 100822a84b8dSQuaker Fang iwp_destroy_locks(iwp_sc_t *sc) 100922a84b8dSQuaker Fang { 101022a84b8dSQuaker Fang cv_destroy(&sc->sc_mt_cv); 101122a84b8dSQuaker Fang cv_destroy(&sc->sc_cmd_cv); 101222a84b8dSQuaker Fang cv_destroy(&sc->sc_put_seg_cv); 101322a84b8dSQuaker Fang cv_destroy(&sc->sc_ucode_cv); 101422a84b8dSQuaker Fang mutex_destroy(&sc->sc_mt_lock); 101522a84b8dSQuaker Fang mutex_destroy(&sc->sc_tx_lock); 101622a84b8dSQuaker Fang mutex_destroy(&sc->sc_glock); 101722a84b8dSQuaker Fang } 101822a84b8dSQuaker Fang 101922a84b8dSQuaker Fang /* 102022a84b8dSQuaker Fang * Allocate an area of memory and a DMA handle for accessing it 102122a84b8dSQuaker Fang */ 102222a84b8dSQuaker Fang static int 102322a84b8dSQuaker Fang iwp_alloc_dma_mem(iwp_sc_t *sc, size_t memsize, 102422a84b8dSQuaker Fang ddi_dma_attr_t *dma_attr_p, ddi_device_acc_attr_t *acc_attr_p, 102522a84b8dSQuaker Fang uint_t dma_flags, iwp_dma_t *dma_p) 102622a84b8dSQuaker Fang { 102722a84b8dSQuaker Fang caddr_t vaddr; 102822a84b8dSQuaker Fang int err = DDI_FAILURE; 102922a84b8dSQuaker Fang 103022a84b8dSQuaker Fang /* 103122a84b8dSQuaker Fang * Allocate handle 103222a84b8dSQuaker Fang */ 103322a84b8dSQuaker Fang err = ddi_dma_alloc_handle(sc->sc_dip, dma_attr_p, 103422a84b8dSQuaker Fang DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl); 103522a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 103622a84b8dSQuaker Fang dma_p->dma_hdl = NULL; 103722a84b8dSQuaker Fang return (DDI_FAILURE); 103822a84b8dSQuaker Fang } 103922a84b8dSQuaker Fang 104022a84b8dSQuaker Fang /* 104122a84b8dSQuaker Fang * Allocate memory 104222a84b8dSQuaker Fang */ 104322a84b8dSQuaker Fang err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize, acc_attr_p, 104422a84b8dSQuaker Fang dma_flags & (DDI_DMA_CONSISTENT | DDI_DMA_STREAMING), 104522a84b8dSQuaker Fang DDI_DMA_SLEEP, NULL, &vaddr, &dma_p->alength, &dma_p->acc_hdl); 104622a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 104722a84b8dSQuaker Fang ddi_dma_free_handle(&dma_p->dma_hdl); 104822a84b8dSQuaker Fang dma_p->dma_hdl = NULL; 104922a84b8dSQuaker Fang dma_p->acc_hdl = NULL; 105022a84b8dSQuaker Fang return (DDI_FAILURE); 105122a84b8dSQuaker Fang } 105222a84b8dSQuaker Fang 105322a84b8dSQuaker Fang /* 105422a84b8dSQuaker Fang * Bind the two together 105522a84b8dSQuaker Fang */ 105622a84b8dSQuaker Fang dma_p->mem_va = vaddr; 105722a84b8dSQuaker Fang err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL, 105822a84b8dSQuaker Fang vaddr, dma_p->alength, dma_flags, DDI_DMA_SLEEP, NULL, 105922a84b8dSQuaker Fang &dma_p->cookie, &dma_p->ncookies); 106022a84b8dSQuaker Fang if (err != DDI_DMA_MAPPED) { 106122a84b8dSQuaker Fang ddi_dma_mem_free(&dma_p->acc_hdl); 106222a84b8dSQuaker Fang ddi_dma_free_handle(&dma_p->dma_hdl); 106322a84b8dSQuaker Fang dma_p->acc_hdl = NULL; 106422a84b8dSQuaker Fang dma_p->dma_hdl = NULL; 106522a84b8dSQuaker Fang return (DDI_FAILURE); 106622a84b8dSQuaker Fang } 106722a84b8dSQuaker Fang 106822a84b8dSQuaker Fang dma_p->nslots = ~0U; 106922a84b8dSQuaker Fang dma_p->size = ~0U; 107022a84b8dSQuaker Fang dma_p->token = ~0U; 107122a84b8dSQuaker Fang dma_p->offset = 0; 107222a84b8dSQuaker Fang return (DDI_SUCCESS); 107322a84b8dSQuaker Fang } 107422a84b8dSQuaker Fang 107522a84b8dSQuaker Fang /* 107622a84b8dSQuaker Fang * Free one allocated area of DMAable memory 107722a84b8dSQuaker Fang */ 107822a84b8dSQuaker Fang static void 107922a84b8dSQuaker Fang iwp_free_dma_mem(iwp_dma_t *dma_p) 108022a84b8dSQuaker Fang { 108122a84b8dSQuaker Fang if (dma_p->dma_hdl != NULL) { 108222a84b8dSQuaker Fang if (dma_p->ncookies) { 108322a84b8dSQuaker Fang (void) ddi_dma_unbind_handle(dma_p->dma_hdl); 108422a84b8dSQuaker Fang dma_p->ncookies = 0; 108522a84b8dSQuaker Fang } 108622a84b8dSQuaker Fang ddi_dma_free_handle(&dma_p->dma_hdl); 108722a84b8dSQuaker Fang dma_p->dma_hdl = NULL; 108822a84b8dSQuaker Fang } 108922a84b8dSQuaker Fang 109022a84b8dSQuaker Fang if (dma_p->acc_hdl != NULL) { 109122a84b8dSQuaker Fang ddi_dma_mem_free(&dma_p->acc_hdl); 109222a84b8dSQuaker Fang dma_p->acc_hdl = NULL; 109322a84b8dSQuaker Fang } 109422a84b8dSQuaker Fang } 109522a84b8dSQuaker Fang 109622a84b8dSQuaker Fang /* 109722a84b8dSQuaker Fang * copy ucode into dma buffers 109822a84b8dSQuaker Fang */ 109922a84b8dSQuaker Fang static int 110022a84b8dSQuaker Fang iwp_alloc_fw_dma(iwp_sc_t *sc) 110122a84b8dSQuaker Fang { 110222a84b8dSQuaker Fang int err = DDI_FAILURE; 110322a84b8dSQuaker Fang iwp_dma_t *dma_p; 110422a84b8dSQuaker Fang char *t; 110522a84b8dSQuaker Fang 110622a84b8dSQuaker Fang /* 110722a84b8dSQuaker Fang * firmware image layout: 110822a84b8dSQuaker Fang * |HDR|<-TEXT->|<-DATA->|<-INIT_TEXT->|<-INIT_DATA->|<-BOOT->| 110922a84b8dSQuaker Fang */ 111022a84b8dSQuaker Fang 111122a84b8dSQuaker Fang /* 111222a84b8dSQuaker Fang * Check firmware image size. 111322a84b8dSQuaker Fang */ 111422a84b8dSQuaker Fang if (LE_32(sc->sc_hdr->init_textsz) > RTC_INST_SIZE) { 111522a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_alloc_fw_dma(): " 111622a84b8dSQuaker Fang "firmware init text size 0x%x is too large\n", 111722a84b8dSQuaker Fang LE_32(sc->sc_hdr->init_textsz)); 111822a84b8dSQuaker Fang 111922a84b8dSQuaker Fang goto fail; 112022a84b8dSQuaker Fang } 112122a84b8dSQuaker Fang 112222a84b8dSQuaker Fang if (LE_32(sc->sc_hdr->init_datasz) > RTC_DATA_SIZE) { 112322a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_alloc_fw_dma(): " 112422a84b8dSQuaker Fang "firmware init data size 0x%x is too large\n", 112522a84b8dSQuaker Fang LE_32(sc->sc_hdr->init_datasz)); 112622a84b8dSQuaker Fang 112722a84b8dSQuaker Fang goto fail; 112822a84b8dSQuaker Fang } 112922a84b8dSQuaker Fang 113022a84b8dSQuaker Fang if (LE_32(sc->sc_hdr->textsz) > RTC_INST_SIZE) { 113122a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_alloc_fw_dma(): " 113222a84b8dSQuaker Fang "firmware text size 0x%x is too large\n", 113322a84b8dSQuaker Fang LE_32(sc->sc_hdr->textsz)); 113422a84b8dSQuaker Fang 113522a84b8dSQuaker Fang goto fail; 113622a84b8dSQuaker Fang } 113722a84b8dSQuaker Fang 113822a84b8dSQuaker Fang if (LE_32(sc->sc_hdr->datasz) > RTC_DATA_SIZE) { 113922a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_alloc_fw_dma(): " 114022a84b8dSQuaker Fang "firmware data size 0x%x is too large\n", 114122a84b8dSQuaker Fang LE_32(sc->sc_hdr->datasz)); 114222a84b8dSQuaker Fang 114322a84b8dSQuaker Fang goto fail; 114422a84b8dSQuaker Fang } 114522a84b8dSQuaker Fang 114622a84b8dSQuaker Fang /* 114722a84b8dSQuaker Fang * copy text of runtime ucode 114822a84b8dSQuaker Fang */ 114922a84b8dSQuaker Fang t = (char *)(sc->sc_hdr + 1); 115022a84b8dSQuaker Fang err = iwp_alloc_dma_mem(sc, LE_32(sc->sc_hdr->textsz), 115122a84b8dSQuaker Fang &fw_dma_attr, &iwp_dma_accattr, 115222a84b8dSQuaker Fang DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 115322a84b8dSQuaker Fang &sc->sc_dma_fw_text); 115422a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 115522a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_alloc_fw_dma(): " 115622a84b8dSQuaker Fang "failed to allocate text dma memory.\n"); 115722a84b8dSQuaker Fang goto fail; 115822a84b8dSQuaker Fang } 115922a84b8dSQuaker Fang 116022a84b8dSQuaker Fang dma_p = &sc->sc_dma_fw_text; 116122a84b8dSQuaker Fang 116222a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_DMA, "iwp_alloc_fw_dma(): " 116322a84b8dSQuaker Fang "text[ncookies:%d addr:%lx size:%lx]\n", 116422a84b8dSQuaker Fang dma_p->ncookies, dma_p->cookie.dmac_address, 116522a84b8dSQuaker Fang dma_p->cookie.dmac_size)); 116622a84b8dSQuaker Fang 116722a84b8dSQuaker Fang (void) memcpy(dma_p->mem_va, t, LE_32(sc->sc_hdr->textsz)); 116822a84b8dSQuaker Fang 116922a84b8dSQuaker Fang /* 117022a84b8dSQuaker Fang * copy data and bak-data of runtime ucode 117122a84b8dSQuaker Fang */ 117222a84b8dSQuaker Fang t += LE_32(sc->sc_hdr->textsz); 117322a84b8dSQuaker Fang err = iwp_alloc_dma_mem(sc, LE_32(sc->sc_hdr->datasz), 117422a84b8dSQuaker Fang &fw_dma_attr, &iwp_dma_accattr, 117522a84b8dSQuaker Fang DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 117622a84b8dSQuaker Fang &sc->sc_dma_fw_data); 117722a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 117822a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_alloc_fw_dma(): " 117922a84b8dSQuaker Fang "failed to allocate data dma memory\n"); 118022a84b8dSQuaker Fang goto fail; 118122a84b8dSQuaker Fang } 118222a84b8dSQuaker Fang 118322a84b8dSQuaker Fang dma_p = &sc->sc_dma_fw_data; 118422a84b8dSQuaker Fang 118522a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_DMA, "iwp_alloc_fw_dma(): " 118622a84b8dSQuaker Fang "data[ncookies:%d addr:%lx size:%lx]\n", 118722a84b8dSQuaker Fang dma_p->ncookies, dma_p->cookie.dmac_address, 118822a84b8dSQuaker Fang dma_p->cookie.dmac_size)); 118922a84b8dSQuaker Fang 119022a84b8dSQuaker Fang (void) memcpy(dma_p->mem_va, t, LE_32(sc->sc_hdr->datasz)); 119122a84b8dSQuaker Fang 119222a84b8dSQuaker Fang err = iwp_alloc_dma_mem(sc, LE_32(sc->sc_hdr->datasz), 119322a84b8dSQuaker Fang &fw_dma_attr, &iwp_dma_accattr, 119422a84b8dSQuaker Fang DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 119522a84b8dSQuaker Fang &sc->sc_dma_fw_data_bak); 119622a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 119722a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_alloc_fw_dma(): " 119822a84b8dSQuaker Fang "failed to allocate data bakup dma memory\n"); 119922a84b8dSQuaker Fang goto fail; 120022a84b8dSQuaker Fang } 120122a84b8dSQuaker Fang 120222a84b8dSQuaker Fang dma_p = &sc->sc_dma_fw_data_bak; 120322a84b8dSQuaker Fang 120422a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_DMA, "iwp_alloc_fw_dma(): " 120522a84b8dSQuaker Fang "data_bak[ncookies:%d addr:%lx " 120622a84b8dSQuaker Fang "size:%lx]\n", 120722a84b8dSQuaker Fang dma_p->ncookies, dma_p->cookie.dmac_address, 120822a84b8dSQuaker Fang dma_p->cookie.dmac_size)); 120922a84b8dSQuaker Fang 121022a84b8dSQuaker Fang (void) memcpy(dma_p->mem_va, t, LE_32(sc->sc_hdr->datasz)); 121122a84b8dSQuaker Fang 121222a84b8dSQuaker Fang /* 121322a84b8dSQuaker Fang * copy text of init ucode 121422a84b8dSQuaker Fang */ 121522a84b8dSQuaker Fang t += LE_32(sc->sc_hdr->datasz); 121622a84b8dSQuaker Fang err = iwp_alloc_dma_mem(sc, LE_32(sc->sc_hdr->init_textsz), 121722a84b8dSQuaker Fang &fw_dma_attr, &iwp_dma_accattr, 121822a84b8dSQuaker Fang DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 121922a84b8dSQuaker Fang &sc->sc_dma_fw_init_text); 122022a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 122122a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_alloc_fw_dma(): " 122222a84b8dSQuaker Fang "failed to allocate init text dma memory\n"); 122322a84b8dSQuaker Fang goto fail; 122422a84b8dSQuaker Fang } 122522a84b8dSQuaker Fang 122622a84b8dSQuaker Fang dma_p = &sc->sc_dma_fw_init_text; 122722a84b8dSQuaker Fang 122822a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_DMA, "iwp_alloc_fw_dma(): " 122922a84b8dSQuaker Fang "init_text[ncookies:%d addr:%lx " 123022a84b8dSQuaker Fang "size:%lx]\n", 123122a84b8dSQuaker Fang dma_p->ncookies, dma_p->cookie.dmac_address, 123222a84b8dSQuaker Fang dma_p->cookie.dmac_size)); 123322a84b8dSQuaker Fang 123422a84b8dSQuaker Fang (void) memcpy(dma_p->mem_va, t, LE_32(sc->sc_hdr->init_textsz)); 123522a84b8dSQuaker Fang 123622a84b8dSQuaker Fang /* 123722a84b8dSQuaker Fang * copy data of init ucode 123822a84b8dSQuaker Fang */ 123922a84b8dSQuaker Fang t += LE_32(sc->sc_hdr->init_textsz); 124022a84b8dSQuaker Fang err = iwp_alloc_dma_mem(sc, LE_32(sc->sc_hdr->init_datasz), 124122a84b8dSQuaker Fang &fw_dma_attr, &iwp_dma_accattr, 124222a84b8dSQuaker Fang DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 124322a84b8dSQuaker Fang &sc->sc_dma_fw_init_data); 124422a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 124522a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_alloc_fw_dma(): " 124622a84b8dSQuaker Fang "failed to allocate init data dma memory\n"); 124722a84b8dSQuaker Fang goto fail; 124822a84b8dSQuaker Fang } 124922a84b8dSQuaker Fang 125022a84b8dSQuaker Fang dma_p = &sc->sc_dma_fw_init_data; 125122a84b8dSQuaker Fang 125222a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_DMA, "iwp_alloc_fw_dma(): " 125322a84b8dSQuaker Fang "init_data[ncookies:%d addr:%lx " 125422a84b8dSQuaker Fang "size:%lx]\n", 125522a84b8dSQuaker Fang dma_p->ncookies, dma_p->cookie.dmac_address, 125622a84b8dSQuaker Fang dma_p->cookie.dmac_size)); 125722a84b8dSQuaker Fang 125822a84b8dSQuaker Fang (void) memcpy(dma_p->mem_va, t, LE_32(sc->sc_hdr->init_datasz)); 125922a84b8dSQuaker Fang 126022a84b8dSQuaker Fang sc->sc_boot = t + LE_32(sc->sc_hdr->init_datasz); 126122a84b8dSQuaker Fang fail: 126222a84b8dSQuaker Fang return (err); 126322a84b8dSQuaker Fang } 126422a84b8dSQuaker Fang 126522a84b8dSQuaker Fang static void 126622a84b8dSQuaker Fang iwp_free_fw_dma(iwp_sc_t *sc) 126722a84b8dSQuaker Fang { 126822a84b8dSQuaker Fang iwp_free_dma_mem(&sc->sc_dma_fw_text); 126922a84b8dSQuaker Fang iwp_free_dma_mem(&sc->sc_dma_fw_data); 127022a84b8dSQuaker Fang iwp_free_dma_mem(&sc->sc_dma_fw_data_bak); 127122a84b8dSQuaker Fang iwp_free_dma_mem(&sc->sc_dma_fw_init_text); 127222a84b8dSQuaker Fang iwp_free_dma_mem(&sc->sc_dma_fw_init_data); 127322a84b8dSQuaker Fang } 127422a84b8dSQuaker Fang 127522a84b8dSQuaker Fang /* 127622a84b8dSQuaker Fang * Allocate a shared buffer between host and NIC. 127722a84b8dSQuaker Fang */ 127822a84b8dSQuaker Fang static int 127922a84b8dSQuaker Fang iwp_alloc_shared(iwp_sc_t *sc) 128022a84b8dSQuaker Fang { 128122a84b8dSQuaker Fang #ifdef DEBUG 128222a84b8dSQuaker Fang iwp_dma_t *dma_p; 128322a84b8dSQuaker Fang #endif 128422a84b8dSQuaker Fang int err = DDI_FAILURE; 128522a84b8dSQuaker Fang 128622a84b8dSQuaker Fang /* 128722a84b8dSQuaker Fang * must be aligned on a 4K-page boundary 128822a84b8dSQuaker Fang */ 128922a84b8dSQuaker Fang err = iwp_alloc_dma_mem(sc, sizeof (iwp_shared_t), 129022a84b8dSQuaker Fang &sh_dma_attr, &iwp_dma_descattr, 129122a84b8dSQuaker Fang DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 129222a84b8dSQuaker Fang &sc->sc_dma_sh); 129322a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 129422a84b8dSQuaker Fang goto fail; 129522a84b8dSQuaker Fang } 129622a84b8dSQuaker Fang 129722a84b8dSQuaker Fang sc->sc_shared = (iwp_shared_t *)sc->sc_dma_sh.mem_va; 129822a84b8dSQuaker Fang 129922a84b8dSQuaker Fang #ifdef DEBUG 130022a84b8dSQuaker Fang dma_p = &sc->sc_dma_sh; 130122a84b8dSQuaker Fang #endif 130222a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_DMA, "iwp_alloc_shared(): " 130322a84b8dSQuaker Fang "sh[ncookies:%d addr:%lx size:%lx]\n", 130422a84b8dSQuaker Fang dma_p->ncookies, dma_p->cookie.dmac_address, 130522a84b8dSQuaker Fang dma_p->cookie.dmac_size)); 130622a84b8dSQuaker Fang 130722a84b8dSQuaker Fang return (err); 130822a84b8dSQuaker Fang fail: 130922a84b8dSQuaker Fang iwp_free_shared(sc); 131022a84b8dSQuaker Fang return (err); 131122a84b8dSQuaker Fang } 131222a84b8dSQuaker Fang 131322a84b8dSQuaker Fang static void 131422a84b8dSQuaker Fang iwp_free_shared(iwp_sc_t *sc) 131522a84b8dSQuaker Fang { 131622a84b8dSQuaker Fang iwp_free_dma_mem(&sc->sc_dma_sh); 131722a84b8dSQuaker Fang } 131822a84b8dSQuaker Fang 131922a84b8dSQuaker Fang /* 132022a84b8dSQuaker Fang * Allocate a keep warm page. 132122a84b8dSQuaker Fang */ 132222a84b8dSQuaker Fang static int 132322a84b8dSQuaker Fang iwp_alloc_kw(iwp_sc_t *sc) 132422a84b8dSQuaker Fang { 132522a84b8dSQuaker Fang #ifdef DEBUG 132622a84b8dSQuaker Fang iwp_dma_t *dma_p; 132722a84b8dSQuaker Fang #endif 132822a84b8dSQuaker Fang int err = DDI_FAILURE; 132922a84b8dSQuaker Fang 133022a84b8dSQuaker Fang /* 133122a84b8dSQuaker Fang * must be aligned on a 4K-page boundary 133222a84b8dSQuaker Fang */ 133322a84b8dSQuaker Fang err = iwp_alloc_dma_mem(sc, IWP_KW_SIZE, 133422a84b8dSQuaker Fang &kw_dma_attr, &iwp_dma_descattr, 133522a84b8dSQuaker Fang DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 133622a84b8dSQuaker Fang &sc->sc_dma_kw); 133722a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 133822a84b8dSQuaker Fang goto fail; 133922a84b8dSQuaker Fang } 134022a84b8dSQuaker Fang 134122a84b8dSQuaker Fang #ifdef DEBUG 134222a84b8dSQuaker Fang dma_p = &sc->sc_dma_kw; 134322a84b8dSQuaker Fang #endif 134422a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_DMA, "iwp_alloc_kw(): " 134522a84b8dSQuaker Fang "kw[ncookies:%d addr:%lx size:%lx]\n", 134622a84b8dSQuaker Fang dma_p->ncookies, dma_p->cookie.dmac_address, 134722a84b8dSQuaker Fang dma_p->cookie.dmac_size)); 134822a84b8dSQuaker Fang 134922a84b8dSQuaker Fang return (err); 135022a84b8dSQuaker Fang fail: 135122a84b8dSQuaker Fang iwp_free_kw(sc); 135222a84b8dSQuaker Fang return (err); 135322a84b8dSQuaker Fang } 135422a84b8dSQuaker Fang 135522a84b8dSQuaker Fang static void 135622a84b8dSQuaker Fang iwp_free_kw(iwp_sc_t *sc) 135722a84b8dSQuaker Fang { 135822a84b8dSQuaker Fang iwp_free_dma_mem(&sc->sc_dma_kw); 135922a84b8dSQuaker Fang } 136022a84b8dSQuaker Fang 136122a84b8dSQuaker Fang /* 136222a84b8dSQuaker Fang * initialize RX ring buffers 136322a84b8dSQuaker Fang */ 136422a84b8dSQuaker Fang static int 136522a84b8dSQuaker Fang iwp_alloc_rx_ring(iwp_sc_t *sc) 136622a84b8dSQuaker Fang { 136722a84b8dSQuaker Fang iwp_rx_ring_t *ring; 136822a84b8dSQuaker Fang iwp_rx_data_t *data; 136922a84b8dSQuaker Fang #ifdef DEBUG 137022a84b8dSQuaker Fang iwp_dma_t *dma_p; 137122a84b8dSQuaker Fang #endif 137222a84b8dSQuaker Fang int i, err = DDI_FAILURE; 137322a84b8dSQuaker Fang 137422a84b8dSQuaker Fang ring = &sc->sc_rxq; 137522a84b8dSQuaker Fang ring->cur = 0; 137622a84b8dSQuaker Fang 137722a84b8dSQuaker Fang /* 137822a84b8dSQuaker Fang * allocate RX description ring buffer 137922a84b8dSQuaker Fang */ 138022a84b8dSQuaker Fang err = iwp_alloc_dma_mem(sc, RX_QUEUE_SIZE * sizeof (uint32_t), 138122a84b8dSQuaker Fang &ring_desc_dma_attr, &iwp_dma_descattr, 138222a84b8dSQuaker Fang DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 138322a84b8dSQuaker Fang &ring->dma_desc); 138422a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 138522a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_DMA, "iwp_alloc_rx_ring(): " 138622a84b8dSQuaker Fang "dma alloc rx ring desc " 138722a84b8dSQuaker Fang "failed\n")); 138822a84b8dSQuaker Fang goto fail; 138922a84b8dSQuaker Fang } 139022a84b8dSQuaker Fang 139122a84b8dSQuaker Fang ring->desc = (uint32_t *)ring->dma_desc.mem_va; 139222a84b8dSQuaker Fang #ifdef DEBUG 139322a84b8dSQuaker Fang dma_p = &ring->dma_desc; 139422a84b8dSQuaker Fang #endif 139522a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_DMA, "iwp_alloc_rx_ring(): " 139622a84b8dSQuaker Fang "rx bd[ncookies:%d addr:%lx size:%lx]\n", 139722a84b8dSQuaker Fang dma_p->ncookies, dma_p->cookie.dmac_address, 139822a84b8dSQuaker Fang dma_p->cookie.dmac_size)); 139922a84b8dSQuaker Fang 140022a84b8dSQuaker Fang /* 140122a84b8dSQuaker Fang * Allocate Rx frame buffers. 140222a84b8dSQuaker Fang */ 140322a84b8dSQuaker Fang for (i = 0; i < RX_QUEUE_SIZE; i++) { 140422a84b8dSQuaker Fang data = &ring->data[i]; 140522a84b8dSQuaker Fang err = iwp_alloc_dma_mem(sc, sc->sc_dmabuf_sz, 140622a84b8dSQuaker Fang &rx_buffer_dma_attr, &iwp_dma_accattr, 140722a84b8dSQuaker Fang DDI_DMA_READ | DDI_DMA_STREAMING, 140822a84b8dSQuaker Fang &data->dma_data); 140922a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 141022a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_DMA, "iwp_alloc_rx_ring(): " 141122a84b8dSQuaker Fang "dma alloc rx ring " 141222a84b8dSQuaker Fang "buf[%d] failed\n", i)); 141322a84b8dSQuaker Fang goto fail; 141422a84b8dSQuaker Fang } 141522a84b8dSQuaker Fang /* 141622a84b8dSQuaker Fang * the physical address bit [8-36] are used, 141722a84b8dSQuaker Fang * instead of bit [0-31] in 3945. 141822a84b8dSQuaker Fang */ 141922a84b8dSQuaker Fang ring->desc[i] = (uint32_t) 142022a84b8dSQuaker Fang (data->dma_data.cookie.dmac_address >> 8); 142122a84b8dSQuaker Fang } 142222a84b8dSQuaker Fang 142322a84b8dSQuaker Fang #ifdef DEBUG 142422a84b8dSQuaker Fang dma_p = &ring->data[0].dma_data; 142522a84b8dSQuaker Fang #endif 142622a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_DMA, "iwp_alloc_rx_ring(): " 142722a84b8dSQuaker Fang "rx buffer[0][ncookies:%d addr:%lx " 142822a84b8dSQuaker Fang "size:%lx]\n", 142922a84b8dSQuaker Fang dma_p->ncookies, dma_p->cookie.dmac_address, 143022a84b8dSQuaker Fang dma_p->cookie.dmac_size)); 143122a84b8dSQuaker Fang 143222a84b8dSQuaker Fang IWP_DMA_SYNC(ring->dma_desc, DDI_DMA_SYNC_FORDEV); 143322a84b8dSQuaker Fang 143422a84b8dSQuaker Fang return (err); 143522a84b8dSQuaker Fang 143622a84b8dSQuaker Fang fail: 143722a84b8dSQuaker Fang iwp_free_rx_ring(sc); 143822a84b8dSQuaker Fang return (err); 143922a84b8dSQuaker Fang } 144022a84b8dSQuaker Fang 144122a84b8dSQuaker Fang /* 144222a84b8dSQuaker Fang * disable RX ring 144322a84b8dSQuaker Fang */ 144422a84b8dSQuaker Fang static void 144522a84b8dSQuaker Fang iwp_reset_rx_ring(iwp_sc_t *sc) 144622a84b8dSQuaker Fang { 144722a84b8dSQuaker Fang int n; 144822a84b8dSQuaker Fang 144922a84b8dSQuaker Fang iwp_mac_access_enter(sc); 145022a84b8dSQuaker Fang IWP_WRITE(sc, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); 145122a84b8dSQuaker Fang for (n = 0; n < 2000; n++) { 145222a84b8dSQuaker Fang if (IWP_READ(sc, FH_MEM_RSSR_RX_STATUS_REG) & (1 << 24)) { 145322a84b8dSQuaker Fang break; 145422a84b8dSQuaker Fang } 145522a84b8dSQuaker Fang DELAY(1000); 145622a84b8dSQuaker Fang } 145722a84b8dSQuaker Fang #ifdef DEBUG 145822a84b8dSQuaker Fang if (2000 == n) { 145922a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_DMA, "iwp_reset_rx_ring(): " 146022a84b8dSQuaker Fang "timeout resetting Rx ring\n")); 146122a84b8dSQuaker Fang } 146222a84b8dSQuaker Fang #endif 146322a84b8dSQuaker Fang iwp_mac_access_exit(sc); 146422a84b8dSQuaker Fang 146522a84b8dSQuaker Fang sc->sc_rxq.cur = 0; 146622a84b8dSQuaker Fang } 146722a84b8dSQuaker Fang 146822a84b8dSQuaker Fang static void 146922a84b8dSQuaker Fang iwp_free_rx_ring(iwp_sc_t *sc) 147022a84b8dSQuaker Fang { 147122a84b8dSQuaker Fang int i; 147222a84b8dSQuaker Fang 147322a84b8dSQuaker Fang for (i = 0; i < RX_QUEUE_SIZE; i++) { 147422a84b8dSQuaker Fang if (sc->sc_rxq.data[i].dma_data.dma_hdl) { 147522a84b8dSQuaker Fang IWP_DMA_SYNC(sc->sc_rxq.data[i].dma_data, 147622a84b8dSQuaker Fang DDI_DMA_SYNC_FORCPU); 147722a84b8dSQuaker Fang } 147822a84b8dSQuaker Fang 147922a84b8dSQuaker Fang iwp_free_dma_mem(&sc->sc_rxq.data[i].dma_data); 148022a84b8dSQuaker Fang } 148122a84b8dSQuaker Fang 148222a84b8dSQuaker Fang if (sc->sc_rxq.dma_desc.dma_hdl) { 148322a84b8dSQuaker Fang IWP_DMA_SYNC(sc->sc_rxq.dma_desc, DDI_DMA_SYNC_FORDEV); 148422a84b8dSQuaker Fang } 148522a84b8dSQuaker Fang 148622a84b8dSQuaker Fang iwp_free_dma_mem(&sc->sc_rxq.dma_desc); 148722a84b8dSQuaker Fang } 148822a84b8dSQuaker Fang 148922a84b8dSQuaker Fang /* 149022a84b8dSQuaker Fang * initialize TX ring buffers 149122a84b8dSQuaker Fang */ 149222a84b8dSQuaker Fang static int 149322a84b8dSQuaker Fang iwp_alloc_tx_ring(iwp_sc_t *sc, iwp_tx_ring_t *ring, 149422a84b8dSQuaker Fang int slots, int qid) 149522a84b8dSQuaker Fang { 149622a84b8dSQuaker Fang iwp_tx_data_t *data; 149722a84b8dSQuaker Fang iwp_tx_desc_t *desc_h; 149822a84b8dSQuaker Fang uint32_t paddr_desc_h; 149922a84b8dSQuaker Fang iwp_cmd_t *cmd_h; 150022a84b8dSQuaker Fang uint32_t paddr_cmd_h; 150122a84b8dSQuaker Fang #ifdef DEBUG 150222a84b8dSQuaker Fang iwp_dma_t *dma_p; 150322a84b8dSQuaker Fang #endif 150422a84b8dSQuaker Fang int i, err = DDI_FAILURE; 150522a84b8dSQuaker Fang ring->qid = qid; 150622a84b8dSQuaker Fang ring->count = TFD_QUEUE_SIZE_MAX; 150722a84b8dSQuaker Fang ring->window = slots; 150822a84b8dSQuaker Fang ring->queued = 0; 150922a84b8dSQuaker Fang ring->cur = 0; 151022a84b8dSQuaker Fang ring->desc_cur = 0; 151122a84b8dSQuaker Fang 151222a84b8dSQuaker Fang /* 151322a84b8dSQuaker Fang * allocate buffer for TX descriptor ring 151422a84b8dSQuaker Fang */ 151522a84b8dSQuaker Fang err = iwp_alloc_dma_mem(sc, 151622a84b8dSQuaker Fang TFD_QUEUE_SIZE_MAX * sizeof (iwp_tx_desc_t), 151722a84b8dSQuaker Fang &ring_desc_dma_attr, &iwp_dma_descattr, 151822a84b8dSQuaker Fang DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 151922a84b8dSQuaker Fang &ring->dma_desc); 152022a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 152122a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_DMA, "iwp_alloc_tx_ring(): " 152222a84b8dSQuaker Fang "dma alloc tx ring desc[%d] " 152322a84b8dSQuaker Fang "failed\n", qid)); 152422a84b8dSQuaker Fang goto fail; 152522a84b8dSQuaker Fang } 152622a84b8dSQuaker Fang 152722a84b8dSQuaker Fang #ifdef DEBUG 152822a84b8dSQuaker Fang dma_p = &ring->dma_desc; 152922a84b8dSQuaker Fang #endif 153022a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_DMA, "iwp_alloc_tx_ring(): " 153122a84b8dSQuaker Fang "tx bd[ncookies:%d addr:%lx size:%lx]\n", 153222a84b8dSQuaker Fang dma_p->ncookies, dma_p->cookie.dmac_address, 153322a84b8dSQuaker Fang dma_p->cookie.dmac_size)); 153422a84b8dSQuaker Fang 153522a84b8dSQuaker Fang desc_h = (iwp_tx_desc_t *)ring->dma_desc.mem_va; 153622a84b8dSQuaker Fang paddr_desc_h = ring->dma_desc.cookie.dmac_address; 153722a84b8dSQuaker Fang 153822a84b8dSQuaker Fang /* 153922a84b8dSQuaker Fang * allocate buffer for ucode command 154022a84b8dSQuaker Fang */ 154122a84b8dSQuaker Fang err = iwp_alloc_dma_mem(sc, 154222a84b8dSQuaker Fang TFD_QUEUE_SIZE_MAX * sizeof (iwp_cmd_t), 154322a84b8dSQuaker Fang &cmd_dma_attr, &iwp_dma_accattr, 154422a84b8dSQuaker Fang DDI_DMA_RDWR | DDI_DMA_CONSISTENT, 154522a84b8dSQuaker Fang &ring->dma_cmd); 154622a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 154722a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_DMA, "iwp_alloc_tx_ring(): " 154822a84b8dSQuaker Fang "dma alloc tx ring cmd[%d]" 154922a84b8dSQuaker Fang " failed\n", qid)); 155022a84b8dSQuaker Fang goto fail; 155122a84b8dSQuaker Fang } 155222a84b8dSQuaker Fang 155322a84b8dSQuaker Fang #ifdef DEBUG 155422a84b8dSQuaker Fang dma_p = &ring->dma_cmd; 155522a84b8dSQuaker Fang #endif 155622a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_DMA, "iwp_alloc_tx_ring(): " 155722a84b8dSQuaker Fang "tx cmd[ncookies:%d addr:%lx size:%lx]\n", 155822a84b8dSQuaker Fang dma_p->ncookies, dma_p->cookie.dmac_address, 155922a84b8dSQuaker Fang dma_p->cookie.dmac_size)); 156022a84b8dSQuaker Fang 156122a84b8dSQuaker Fang cmd_h = (iwp_cmd_t *)ring->dma_cmd.mem_va; 156222a84b8dSQuaker Fang paddr_cmd_h = ring->dma_cmd.cookie.dmac_address; 156322a84b8dSQuaker Fang 156422a84b8dSQuaker Fang /* 156522a84b8dSQuaker Fang * Allocate Tx frame buffers. 156622a84b8dSQuaker Fang */ 156722a84b8dSQuaker Fang ring->data = kmem_zalloc(sizeof (iwp_tx_data_t) * TFD_QUEUE_SIZE_MAX, 156822a84b8dSQuaker Fang KM_NOSLEEP); 156922a84b8dSQuaker Fang if (NULL == ring->data) { 157022a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_DMA, "iwp_alloc_tx_ring(): " 157122a84b8dSQuaker Fang "could not allocate " 157222a84b8dSQuaker Fang "tx data slots\n")); 157322a84b8dSQuaker Fang goto fail; 157422a84b8dSQuaker Fang } 157522a84b8dSQuaker Fang 157622a84b8dSQuaker Fang for (i = 0; i < TFD_QUEUE_SIZE_MAX; i++) { 157722a84b8dSQuaker Fang data = &ring->data[i]; 157822a84b8dSQuaker Fang err = iwp_alloc_dma_mem(sc, sc->sc_dmabuf_sz, 157922a84b8dSQuaker Fang &tx_buffer_dma_attr, &iwp_dma_accattr, 158022a84b8dSQuaker Fang DDI_DMA_WRITE | DDI_DMA_STREAMING, 158122a84b8dSQuaker Fang &data->dma_data); 158222a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 158322a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_DMA, "iwp_alloc_tx_ring(): " 158422a84b8dSQuaker Fang "dma alloc tx " 158522a84b8dSQuaker Fang "ring buf[%d] failed\n", i)); 158622a84b8dSQuaker Fang goto fail; 158722a84b8dSQuaker Fang } 158822a84b8dSQuaker Fang 158922a84b8dSQuaker Fang data->desc = desc_h + i; 159022a84b8dSQuaker Fang data->paddr_desc = paddr_desc_h + 159122a84b8dSQuaker Fang _PTRDIFF(data->desc, desc_h); 159222a84b8dSQuaker Fang data->cmd = cmd_h + i; 159322a84b8dSQuaker Fang data->paddr_cmd = paddr_cmd_h + 159422a84b8dSQuaker Fang _PTRDIFF(data->cmd, cmd_h); 159522a84b8dSQuaker Fang } 159622a84b8dSQuaker Fang #ifdef DEBUG 159722a84b8dSQuaker Fang dma_p = &ring->data[0].dma_data; 159822a84b8dSQuaker Fang #endif 159922a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_DMA, "iwp_alloc_tx_ring(): " 160022a84b8dSQuaker Fang "tx buffer[0][ncookies:%d addr:%lx " 160122a84b8dSQuaker Fang "size:%lx]\n", 160222a84b8dSQuaker Fang dma_p->ncookies, dma_p->cookie.dmac_address, 160322a84b8dSQuaker Fang dma_p->cookie.dmac_size)); 160422a84b8dSQuaker Fang 160522a84b8dSQuaker Fang return (err); 160622a84b8dSQuaker Fang 160722a84b8dSQuaker Fang fail: 160822a84b8dSQuaker Fang iwp_free_tx_ring(ring); 160922a84b8dSQuaker Fang 161022a84b8dSQuaker Fang return (err); 161122a84b8dSQuaker Fang } 161222a84b8dSQuaker Fang 161322a84b8dSQuaker Fang /* 161422a84b8dSQuaker Fang * disable TX ring 161522a84b8dSQuaker Fang */ 161622a84b8dSQuaker Fang static void 161722a84b8dSQuaker Fang iwp_reset_tx_ring(iwp_sc_t *sc, iwp_tx_ring_t *ring) 161822a84b8dSQuaker Fang { 161922a84b8dSQuaker Fang iwp_tx_data_t *data; 162022a84b8dSQuaker Fang int i, n; 162122a84b8dSQuaker Fang 162222a84b8dSQuaker Fang iwp_mac_access_enter(sc); 162322a84b8dSQuaker Fang 162422a84b8dSQuaker Fang IWP_WRITE(sc, IWP_FH_TCSR_CHNL_TX_CONFIG_REG(ring->qid), 0); 162522a84b8dSQuaker Fang for (n = 0; n < 200; n++) { 162622a84b8dSQuaker Fang if (IWP_READ(sc, IWP_FH_TSSR_TX_STATUS_REG) & 162722a84b8dSQuaker Fang IWP_FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ring->qid)) { 162822a84b8dSQuaker Fang break; 162922a84b8dSQuaker Fang } 163022a84b8dSQuaker Fang DELAY(10); 163122a84b8dSQuaker Fang } 163222a84b8dSQuaker Fang 163322a84b8dSQuaker Fang #ifdef DEBUG 163422a84b8dSQuaker Fang if (200 == n) { 163522a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_DMA, "iwp_reset_tx_ring(): " 163622a84b8dSQuaker Fang "timeout reset tx ring %d\n", 163722a84b8dSQuaker Fang ring->qid)); 163822a84b8dSQuaker Fang } 163922a84b8dSQuaker Fang #endif 164022a84b8dSQuaker Fang 164122a84b8dSQuaker Fang iwp_mac_access_exit(sc); 164222a84b8dSQuaker Fang 164322a84b8dSQuaker Fang /* by pass, if it's quiesce */ 164422a84b8dSQuaker Fang if (!(sc->sc_flags & IWP_F_QUIESCED)) { 164522a84b8dSQuaker Fang for (i = 0; i < ring->count; i++) { 164622a84b8dSQuaker Fang data = &ring->data[i]; 164722a84b8dSQuaker Fang IWP_DMA_SYNC(data->dma_data, DDI_DMA_SYNC_FORDEV); 164822a84b8dSQuaker Fang } 164922a84b8dSQuaker Fang } 165022a84b8dSQuaker Fang 165122a84b8dSQuaker Fang ring->queued = 0; 165222a84b8dSQuaker Fang ring->cur = 0; 165322a84b8dSQuaker Fang ring->desc_cur = 0; 165422a84b8dSQuaker Fang } 165522a84b8dSQuaker Fang 165622a84b8dSQuaker Fang static void 165722a84b8dSQuaker Fang iwp_free_tx_ring(iwp_tx_ring_t *ring) 165822a84b8dSQuaker Fang { 165922a84b8dSQuaker Fang int i; 166022a84b8dSQuaker Fang 166122a84b8dSQuaker Fang if (ring->dma_desc.dma_hdl != NULL) { 166222a84b8dSQuaker Fang IWP_DMA_SYNC(ring->dma_desc, DDI_DMA_SYNC_FORDEV); 166322a84b8dSQuaker Fang } 166422a84b8dSQuaker Fang iwp_free_dma_mem(&ring->dma_desc); 166522a84b8dSQuaker Fang 166622a84b8dSQuaker Fang if (ring->dma_cmd.dma_hdl != NULL) { 166722a84b8dSQuaker Fang IWP_DMA_SYNC(ring->dma_cmd, DDI_DMA_SYNC_FORDEV); 166822a84b8dSQuaker Fang } 166922a84b8dSQuaker Fang iwp_free_dma_mem(&ring->dma_cmd); 167022a84b8dSQuaker Fang 167122a84b8dSQuaker Fang if (ring->data != NULL) { 167222a84b8dSQuaker Fang for (i = 0; i < ring->count; i++) { 167322a84b8dSQuaker Fang if (ring->data[i].dma_data.dma_hdl) { 167422a84b8dSQuaker Fang IWP_DMA_SYNC(ring->data[i].dma_data, 167522a84b8dSQuaker Fang DDI_DMA_SYNC_FORDEV); 167622a84b8dSQuaker Fang } 167722a84b8dSQuaker Fang iwp_free_dma_mem(&ring->data[i].dma_data); 167822a84b8dSQuaker Fang } 167922a84b8dSQuaker Fang kmem_free(ring->data, ring->count * sizeof (iwp_tx_data_t)); 168022a84b8dSQuaker Fang } 168122a84b8dSQuaker Fang } 168222a84b8dSQuaker Fang 168322a84b8dSQuaker Fang /* 168422a84b8dSQuaker Fang * initialize TX and RX ring 168522a84b8dSQuaker Fang */ 168622a84b8dSQuaker Fang static int 168722a84b8dSQuaker Fang iwp_ring_init(iwp_sc_t *sc) 168822a84b8dSQuaker Fang { 168922a84b8dSQuaker Fang int i, err = DDI_FAILURE; 169022a84b8dSQuaker Fang 169122a84b8dSQuaker Fang for (i = 0; i < IWP_NUM_QUEUES; i++) { 169222a84b8dSQuaker Fang if (IWP_CMD_QUEUE_NUM == i) { 169322a84b8dSQuaker Fang continue; 169422a84b8dSQuaker Fang } 169522a84b8dSQuaker Fang 169622a84b8dSQuaker Fang err = iwp_alloc_tx_ring(sc, &sc->sc_txq[i], TFD_TX_CMD_SLOTS, 169722a84b8dSQuaker Fang i); 169822a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 169922a84b8dSQuaker Fang goto fail; 170022a84b8dSQuaker Fang } 170122a84b8dSQuaker Fang } 170222a84b8dSQuaker Fang 170322a84b8dSQuaker Fang /* 170422a84b8dSQuaker Fang * initialize command queue 170522a84b8dSQuaker Fang */ 170622a84b8dSQuaker Fang err = iwp_alloc_tx_ring(sc, &sc->sc_txq[IWP_CMD_QUEUE_NUM], 170722a84b8dSQuaker Fang TFD_CMD_SLOTS, IWP_CMD_QUEUE_NUM); 170822a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 170922a84b8dSQuaker Fang goto fail; 171022a84b8dSQuaker Fang } 171122a84b8dSQuaker Fang 171222a84b8dSQuaker Fang err = iwp_alloc_rx_ring(sc); 171322a84b8dSQuaker Fang if (err != DDI_SUCCESS) { 171422a84b8dSQuaker Fang goto fail; 171522a84b8dSQuaker Fang } 171622a84b8dSQuaker Fang 171722a84b8dSQuaker Fang fail: 171822a84b8dSQuaker Fang return (err); 171922a84b8dSQuaker Fang } 172022a84b8dSQuaker Fang 172122a84b8dSQuaker Fang static void 172222a84b8dSQuaker Fang iwp_ring_free(iwp_sc_t *sc) 172322a84b8dSQuaker Fang { 172422a84b8dSQuaker Fang int i = IWP_NUM_QUEUES; 172522a84b8dSQuaker Fang 172622a84b8dSQuaker Fang iwp_free_rx_ring(sc); 172722a84b8dSQuaker Fang while (--i >= 0) { 172822a84b8dSQuaker Fang iwp_free_tx_ring(&sc->sc_txq[i]); 172922a84b8dSQuaker Fang } 173022a84b8dSQuaker Fang } 173122a84b8dSQuaker Fang 173222a84b8dSQuaker Fang /* ARGSUSED */ 173322a84b8dSQuaker Fang static ieee80211_node_t * 173422a84b8dSQuaker Fang iwp_node_alloc(ieee80211com_t *ic) 173522a84b8dSQuaker Fang { 173622a84b8dSQuaker Fang iwp_amrr_t *amrr; 173722a84b8dSQuaker Fang 173822a84b8dSQuaker Fang amrr = kmem_zalloc(sizeof (iwp_amrr_t), KM_SLEEP); 173922a84b8dSQuaker Fang if (NULL == amrr) { 174022a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_node_alloc(): " 174122a84b8dSQuaker Fang "failed to allocate memory for amrr structure\n"); 174222a84b8dSQuaker Fang return (NULL); 174322a84b8dSQuaker Fang } 174422a84b8dSQuaker Fang 174522a84b8dSQuaker Fang iwp_amrr_init(amrr); 174622a84b8dSQuaker Fang 174722a84b8dSQuaker Fang return (&amrr->in); 174822a84b8dSQuaker Fang } 174922a84b8dSQuaker Fang 175022a84b8dSQuaker Fang static void 175122a84b8dSQuaker Fang iwp_node_free(ieee80211_node_t *in) 175222a84b8dSQuaker Fang { 175322a84b8dSQuaker Fang ieee80211com_t *ic; 175422a84b8dSQuaker Fang 175522a84b8dSQuaker Fang if ((NULL == in) || 175622a84b8dSQuaker Fang (NULL == in->in_ic)) { 175722a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_node_free() " 175822a84b8dSQuaker Fang "Got a NULL point from Net80211 module\n"); 175922a84b8dSQuaker Fang return; 176022a84b8dSQuaker Fang } 176122a84b8dSQuaker Fang ic = in->in_ic; 176222a84b8dSQuaker Fang 176322a84b8dSQuaker Fang if (ic->ic_node_cleanup != NULL) { 176422a84b8dSQuaker Fang ic->ic_node_cleanup(in); 176522a84b8dSQuaker Fang } 176622a84b8dSQuaker Fang 176722a84b8dSQuaker Fang if (in->in_wpa_ie != NULL) { 176822a84b8dSQuaker Fang ieee80211_free(in->in_wpa_ie); 176922a84b8dSQuaker Fang } 177022a84b8dSQuaker Fang 177122a84b8dSQuaker Fang if (in->in_wme_ie != NULL) { 177222a84b8dSQuaker Fang ieee80211_free(in->in_wme_ie); 177322a84b8dSQuaker Fang } 177422a84b8dSQuaker Fang 177522a84b8dSQuaker Fang if (in->in_htcap_ie != NULL) { 177622a84b8dSQuaker Fang ieee80211_free(in->in_htcap_ie); 177722a84b8dSQuaker Fang } 177822a84b8dSQuaker Fang 177922a84b8dSQuaker Fang kmem_free(in, sizeof (iwp_amrr_t)); 178022a84b8dSQuaker Fang } 178122a84b8dSQuaker Fang 178222a84b8dSQuaker Fang 178322a84b8dSQuaker Fang /* 178422a84b8dSQuaker Fang * change station's state. this function will be invoked by 80211 module 178522a84b8dSQuaker Fang * when need to change staton's state. 178622a84b8dSQuaker Fang */ 178722a84b8dSQuaker Fang static int 178822a84b8dSQuaker Fang iwp_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg) 178922a84b8dSQuaker Fang { 179022a84b8dSQuaker Fang iwp_sc_t *sc; 179122a84b8dSQuaker Fang ieee80211_node_t *in; 179222a84b8dSQuaker Fang enum ieee80211_state ostate; 179322a84b8dSQuaker Fang iwp_add_sta_t node; 179422a84b8dSQuaker Fang int i, err = IWP_FAIL; 179522a84b8dSQuaker Fang 179622a84b8dSQuaker Fang if (NULL == ic) { 179722a84b8dSQuaker Fang return (err); 179822a84b8dSQuaker Fang } 179922a84b8dSQuaker Fang sc = (iwp_sc_t *)ic; 180022a84b8dSQuaker Fang in = ic->ic_bss; 180122a84b8dSQuaker Fang ostate = ic->ic_state; 180222a84b8dSQuaker Fang 180322a84b8dSQuaker Fang mutex_enter(&sc->sc_glock); 180422a84b8dSQuaker Fang 180522a84b8dSQuaker Fang switch (nstate) { 180622a84b8dSQuaker Fang case IEEE80211_S_SCAN: 180722a84b8dSQuaker Fang switch (ostate) { 180822a84b8dSQuaker Fang case IEEE80211_S_INIT: 180922a84b8dSQuaker Fang atomic_or_32(&sc->sc_flags, IWP_F_SCANNING); 181022a84b8dSQuaker Fang iwp_set_led(sc, 2, 10, 2); 181122a84b8dSQuaker Fang 181222a84b8dSQuaker Fang /* 181322a84b8dSQuaker Fang * clear association to receive beacons from 181422a84b8dSQuaker Fang * all BSS'es 181522a84b8dSQuaker Fang */ 181622a84b8dSQuaker Fang sc->sc_config.assoc_id = 0; 181722a84b8dSQuaker Fang sc->sc_config.filter_flags &= 181822a84b8dSQuaker Fang ~LE_32(RXON_FILTER_ASSOC_MSK); 181922a84b8dSQuaker Fang 182022a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_80211, "iwp_newstate(): " 182122a84b8dSQuaker Fang "config chan %d " 182222a84b8dSQuaker Fang "flags %x filter_flags %x\n", 182322a84b8dSQuaker Fang LE_16(sc->sc_config.chan), 182422a84b8dSQuaker Fang LE_32(sc->sc_config.flags), 182522a84b8dSQuaker Fang LE_32(sc->sc_config.filter_flags))); 182622a84b8dSQuaker Fang 182722a84b8dSQuaker Fang err = iwp_cmd(sc, REPLY_RXON, &sc->sc_config, 182822a84b8dSQuaker Fang sizeof (iwp_rxon_cmd_t), 1); 182922a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 183022a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_newstate(): " 183122a84b8dSQuaker Fang "could not clear association\n"); 183222a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_SCANNING); 183322a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 183422a84b8dSQuaker Fang return (err); 183522a84b8dSQuaker Fang } 183622a84b8dSQuaker Fang 183722a84b8dSQuaker Fang /* add broadcast node to send probe request */ 183822a84b8dSQuaker Fang (void) memset(&node, 0, sizeof (node)); 183922a84b8dSQuaker Fang (void) memset(&node.sta.addr, 0xff, IEEE80211_ADDR_LEN); 184022a84b8dSQuaker Fang node.sta.sta_id = IWP_BROADCAST_ID; 184122a84b8dSQuaker Fang err = iwp_cmd(sc, REPLY_ADD_STA, &node, 184222a84b8dSQuaker Fang sizeof (node), 1); 184322a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 184422a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_newstate(): " 184522a84b8dSQuaker Fang "could not add broadcast node\n"); 184622a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_SCANNING); 184722a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 184822a84b8dSQuaker Fang return (err); 184922a84b8dSQuaker Fang } 185022a84b8dSQuaker Fang break; 185122a84b8dSQuaker Fang case IEEE80211_S_SCAN: 185222a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 185322a84b8dSQuaker Fang /* step to next channel before actual FW scan */ 185422a84b8dSQuaker Fang err = sc->sc_newstate(ic, nstate, arg); 185522a84b8dSQuaker Fang mutex_enter(&sc->sc_glock); 185622a84b8dSQuaker Fang if ((err != 0) || ((err = iwp_scan(sc)) != 0)) { 185722a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_newstate(): " 185822a84b8dSQuaker Fang "could not initiate scan\n"); 185922a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_SCANNING); 186022a84b8dSQuaker Fang ieee80211_cancel_scan(ic); 186122a84b8dSQuaker Fang } 186222a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 186322a84b8dSQuaker Fang return (err); 186422a84b8dSQuaker Fang default: 186522a84b8dSQuaker Fang break; 186622a84b8dSQuaker Fang } 186722a84b8dSQuaker Fang sc->sc_clk = 0; 186822a84b8dSQuaker Fang break; 186922a84b8dSQuaker Fang 187022a84b8dSQuaker Fang case IEEE80211_S_AUTH: 187122a84b8dSQuaker Fang if (ostate == IEEE80211_S_SCAN) { 187222a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_SCANNING); 187322a84b8dSQuaker Fang } 187422a84b8dSQuaker Fang 187522a84b8dSQuaker Fang /* 187622a84b8dSQuaker Fang * reset state to handle reassociations correctly 187722a84b8dSQuaker Fang */ 187822a84b8dSQuaker Fang sc->sc_config.assoc_id = 0; 187922a84b8dSQuaker Fang sc->sc_config.filter_flags &= ~LE_32(RXON_FILTER_ASSOC_MSK); 188022a84b8dSQuaker Fang 188122a84b8dSQuaker Fang /* 188222a84b8dSQuaker Fang * before sending authentication and association request frame, 188322a84b8dSQuaker Fang * we need do something in the hardware, such as setting the 188422a84b8dSQuaker Fang * channel same to the target AP... 188522a84b8dSQuaker Fang */ 188622a84b8dSQuaker Fang if ((err = iwp_hw_set_before_auth(sc)) != 0) { 188722a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_80211, "iwp_newstate(): " 188822a84b8dSQuaker Fang "could not send authentication request\n")); 188922a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 189022a84b8dSQuaker Fang return (err); 189122a84b8dSQuaker Fang } 189222a84b8dSQuaker Fang break; 189322a84b8dSQuaker Fang 189422a84b8dSQuaker Fang case IEEE80211_S_RUN: 189522a84b8dSQuaker Fang if (ostate == IEEE80211_S_SCAN) { 189622a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_SCANNING); 189722a84b8dSQuaker Fang } 189822a84b8dSQuaker Fang 189922a84b8dSQuaker Fang if (IEEE80211_M_MONITOR == ic->ic_opmode) { 190022a84b8dSQuaker Fang /* let LED blink when monitoring */ 190122a84b8dSQuaker Fang iwp_set_led(sc, 2, 10, 10); 190222a84b8dSQuaker Fang break; 190322a84b8dSQuaker Fang } 190422a84b8dSQuaker Fang 190522a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_80211, "iwp_newstate(): " 190622a84b8dSQuaker Fang "associated.\n")); 190722a84b8dSQuaker Fang 190822a84b8dSQuaker Fang err = iwp_run_state_config(sc); 190922a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 191022a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_newstate(): " 191122a84b8dSQuaker Fang "failed to set up association\n"); 191222a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 191322a84b8dSQuaker Fang return (err); 191422a84b8dSQuaker Fang } 191522a84b8dSQuaker Fang 191622a84b8dSQuaker Fang /* 191722a84b8dSQuaker Fang * start automatic rate control 191822a84b8dSQuaker Fang */ 191922a84b8dSQuaker Fang if (IEEE80211_FIXED_RATE_NONE == ic->ic_fixed_rate) { 192022a84b8dSQuaker Fang atomic_or_32(&sc->sc_flags, IWP_F_RATE_AUTO_CTL); 192122a84b8dSQuaker Fang 192222a84b8dSQuaker Fang /* 192322a84b8dSQuaker Fang * set rate to some reasonable initial value 192422a84b8dSQuaker Fang */ 192522a84b8dSQuaker Fang i = in->in_rates.ir_nrates - 1; 192622a84b8dSQuaker Fang while (i > 0 && IEEE80211_RATE(i) > 72) { 192722a84b8dSQuaker Fang i--; 192822a84b8dSQuaker Fang } 192922a84b8dSQuaker Fang in->in_txrate = i; 193022a84b8dSQuaker Fang 193122a84b8dSQuaker Fang } else { 193222a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_RATE_AUTO_CTL); 193322a84b8dSQuaker Fang } 193422a84b8dSQuaker Fang 193522a84b8dSQuaker Fang /* 193622a84b8dSQuaker Fang * set LED on after associated 193722a84b8dSQuaker Fang */ 193822a84b8dSQuaker Fang iwp_set_led(sc, 2, 0, 1); 193922a84b8dSQuaker Fang break; 194022a84b8dSQuaker Fang 194122a84b8dSQuaker Fang case IEEE80211_S_INIT: 194222a84b8dSQuaker Fang if (ostate == IEEE80211_S_SCAN) { 194322a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_SCANNING); 194422a84b8dSQuaker Fang } 194522a84b8dSQuaker Fang /* 194622a84b8dSQuaker Fang * set LED off after init 194722a84b8dSQuaker Fang */ 194822a84b8dSQuaker Fang iwp_set_led(sc, 2, 1, 0); 194922a84b8dSQuaker Fang break; 195022a84b8dSQuaker Fang 195122a84b8dSQuaker Fang case IEEE80211_S_ASSOC: 195222a84b8dSQuaker Fang if (ostate == IEEE80211_S_SCAN) { 195322a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_SCANNING); 195422a84b8dSQuaker Fang } 195522a84b8dSQuaker Fang break; 195622a84b8dSQuaker Fang } 195722a84b8dSQuaker Fang 195822a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 195922a84b8dSQuaker Fang 196022a84b8dSQuaker Fang return (sc->sc_newstate(ic, nstate, arg)); 196122a84b8dSQuaker Fang } 196222a84b8dSQuaker Fang 196322a84b8dSQuaker Fang /* 196422a84b8dSQuaker Fang * exclusive access to mac begin. 196522a84b8dSQuaker Fang */ 196622a84b8dSQuaker Fang static void 196722a84b8dSQuaker Fang iwp_mac_access_enter(iwp_sc_t *sc) 196822a84b8dSQuaker Fang { 196922a84b8dSQuaker Fang uint32_t tmp; 197022a84b8dSQuaker Fang int n; 197122a84b8dSQuaker Fang 197222a84b8dSQuaker Fang tmp = IWP_READ(sc, CSR_GP_CNTRL); 197322a84b8dSQuaker Fang IWP_WRITE(sc, CSR_GP_CNTRL, 197422a84b8dSQuaker Fang tmp | CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 197522a84b8dSQuaker Fang 197622a84b8dSQuaker Fang /* wait until we succeed */ 197722a84b8dSQuaker Fang for (n = 0; n < 1000; n++) { 197822a84b8dSQuaker Fang if ((IWP_READ(sc, CSR_GP_CNTRL) & 197922a84b8dSQuaker Fang (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | 198022a84b8dSQuaker Fang CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP)) == 198122a84b8dSQuaker Fang CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN) { 198222a84b8dSQuaker Fang break; 198322a84b8dSQuaker Fang } 198422a84b8dSQuaker Fang DELAY(10); 198522a84b8dSQuaker Fang } 198622a84b8dSQuaker Fang 198722a84b8dSQuaker Fang #ifdef DEBUG 198822a84b8dSQuaker Fang if (1000 == n) { 198922a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_PIO, "iwp_mac_access_enter(): " 199022a84b8dSQuaker Fang "could not lock memory\n")); 199122a84b8dSQuaker Fang } 199222a84b8dSQuaker Fang #endif 199322a84b8dSQuaker Fang } 199422a84b8dSQuaker Fang 199522a84b8dSQuaker Fang /* 199622a84b8dSQuaker Fang * exclusive access to mac end. 199722a84b8dSQuaker Fang */ 199822a84b8dSQuaker Fang static void 199922a84b8dSQuaker Fang iwp_mac_access_exit(iwp_sc_t *sc) 200022a84b8dSQuaker Fang { 200122a84b8dSQuaker Fang uint32_t tmp = IWP_READ(sc, CSR_GP_CNTRL); 200222a84b8dSQuaker Fang IWP_WRITE(sc, CSR_GP_CNTRL, 200322a84b8dSQuaker Fang tmp & ~CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 200422a84b8dSQuaker Fang } 200522a84b8dSQuaker Fang 200622a84b8dSQuaker Fang /* 200722a84b8dSQuaker Fang * this function defined here for future use. 200822a84b8dSQuaker Fang * static uint32_t 200922a84b8dSQuaker Fang * iwp_mem_read(iwp_sc_t *sc, uint32_t addr) 201022a84b8dSQuaker Fang * { 201122a84b8dSQuaker Fang * IWP_WRITE(sc, HBUS_TARG_MEM_RADDR, addr); 201222a84b8dSQuaker Fang * return (IWP_READ(sc, HBUS_TARG_MEM_RDAT)); 201322a84b8dSQuaker Fang * } 201422a84b8dSQuaker Fang */ 201522a84b8dSQuaker Fang 201622a84b8dSQuaker Fang /* 201722a84b8dSQuaker Fang * write mac memory 201822a84b8dSQuaker Fang */ 201922a84b8dSQuaker Fang static void 202022a84b8dSQuaker Fang iwp_mem_write(iwp_sc_t *sc, uint32_t addr, uint32_t data) 202122a84b8dSQuaker Fang { 202222a84b8dSQuaker Fang IWP_WRITE(sc, HBUS_TARG_MEM_WADDR, addr); 202322a84b8dSQuaker Fang IWP_WRITE(sc, HBUS_TARG_MEM_WDAT, data); 202422a84b8dSQuaker Fang } 202522a84b8dSQuaker Fang 202622a84b8dSQuaker Fang /* 202722a84b8dSQuaker Fang * read mac register 202822a84b8dSQuaker Fang */ 202922a84b8dSQuaker Fang static uint32_t 203022a84b8dSQuaker Fang iwp_reg_read(iwp_sc_t *sc, uint32_t addr) 203122a84b8dSQuaker Fang { 203222a84b8dSQuaker Fang IWP_WRITE(sc, HBUS_TARG_PRPH_RADDR, addr | (3 << 24)); 203322a84b8dSQuaker Fang return (IWP_READ(sc, HBUS_TARG_PRPH_RDAT)); 203422a84b8dSQuaker Fang } 203522a84b8dSQuaker Fang 203622a84b8dSQuaker Fang /* 203722a84b8dSQuaker Fang * write mac register 203822a84b8dSQuaker Fang */ 203922a84b8dSQuaker Fang static void 204022a84b8dSQuaker Fang iwp_reg_write(iwp_sc_t *sc, uint32_t addr, uint32_t data) 204122a84b8dSQuaker Fang { 204222a84b8dSQuaker Fang IWP_WRITE(sc, HBUS_TARG_PRPH_WADDR, addr | (3 << 24)); 204322a84b8dSQuaker Fang IWP_WRITE(sc, HBUS_TARG_PRPH_WDAT, data); 204422a84b8dSQuaker Fang } 204522a84b8dSQuaker Fang 204622a84b8dSQuaker Fang 204722a84b8dSQuaker Fang /* 204822a84b8dSQuaker Fang * steps of loading ucode: 204922a84b8dSQuaker Fang * load init ucode=>init alive=>calibrate=> 205022a84b8dSQuaker Fang * receive calibration result=>reinitialize NIC=> 205122a84b8dSQuaker Fang * load runtime ucode=>runtime alive=> 205222a84b8dSQuaker Fang * send calibration result=>running. 205322a84b8dSQuaker Fang */ 205422a84b8dSQuaker Fang static int 205522a84b8dSQuaker Fang iwp_load_init_firmware(iwp_sc_t *sc) 205622a84b8dSQuaker Fang { 205722a84b8dSQuaker Fang int err = IWP_FAIL; 205822a84b8dSQuaker Fang clock_t clk; 205922a84b8dSQuaker Fang 206022a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_PUT_SEG); 206122a84b8dSQuaker Fang 206222a84b8dSQuaker Fang /* 206322a84b8dSQuaker Fang * load init_text section of uCode to hardware 206422a84b8dSQuaker Fang */ 206522a84b8dSQuaker Fang err = iwp_put_seg_fw(sc, sc->sc_dma_fw_init_text.cookie.dmac_address, 206622a84b8dSQuaker Fang RTC_INST_LOWER_BOUND, sc->sc_dma_fw_init_text.cookie.dmac_size); 206722a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 206822a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_load_init_firmware(): " 206922a84b8dSQuaker Fang "failed to write init uCode.\n"); 207022a84b8dSQuaker Fang return (err); 207122a84b8dSQuaker Fang } 207222a84b8dSQuaker Fang 207322a84b8dSQuaker Fang clk = ddi_get_lbolt() + drv_usectohz(1000000); 207422a84b8dSQuaker Fang 207522a84b8dSQuaker Fang /* wait loading init_text until completed or timeout */ 207622a84b8dSQuaker Fang while (!(sc->sc_flags & IWP_F_PUT_SEG)) { 207722a84b8dSQuaker Fang if (cv_timedwait(&sc->sc_put_seg_cv, &sc->sc_glock, clk) < 0) { 207822a84b8dSQuaker Fang break; 207922a84b8dSQuaker Fang } 208022a84b8dSQuaker Fang } 208122a84b8dSQuaker Fang 208222a84b8dSQuaker Fang if (!(sc->sc_flags & IWP_F_PUT_SEG)) { 208322a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_load_init_firmware(): " 208422a84b8dSQuaker Fang "timeout waiting for init uCode load.\n"); 208522a84b8dSQuaker Fang return (IWP_FAIL); 208622a84b8dSQuaker Fang } 208722a84b8dSQuaker Fang 208822a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_PUT_SEG); 208922a84b8dSQuaker Fang 209022a84b8dSQuaker Fang /* 209122a84b8dSQuaker Fang * load init_data section of uCode to hardware 209222a84b8dSQuaker Fang */ 209322a84b8dSQuaker Fang err = iwp_put_seg_fw(sc, sc->sc_dma_fw_init_data.cookie.dmac_address, 209422a84b8dSQuaker Fang RTC_DATA_LOWER_BOUND, sc->sc_dma_fw_init_data.cookie.dmac_size); 209522a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 209622a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_load_init_firmware(): " 209722a84b8dSQuaker Fang "failed to write init_data uCode.\n"); 209822a84b8dSQuaker Fang return (err); 209922a84b8dSQuaker Fang } 210022a84b8dSQuaker Fang 210122a84b8dSQuaker Fang clk = ddi_get_lbolt() + drv_usectohz(1000000); 210222a84b8dSQuaker Fang 210322a84b8dSQuaker Fang /* 210422a84b8dSQuaker Fang * wait loading init_data until completed or timeout 210522a84b8dSQuaker Fang */ 210622a84b8dSQuaker Fang while (!(sc->sc_flags & IWP_F_PUT_SEG)) { 210722a84b8dSQuaker Fang if (cv_timedwait(&sc->sc_put_seg_cv, &sc->sc_glock, clk) < 0) { 210822a84b8dSQuaker Fang break; 210922a84b8dSQuaker Fang } 211022a84b8dSQuaker Fang } 211122a84b8dSQuaker Fang 211222a84b8dSQuaker Fang if (!(sc->sc_flags & IWP_F_PUT_SEG)) { 211322a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_load_init_firmware(): " 211422a84b8dSQuaker Fang "timeout waiting for init_data uCode load.\n"); 211522a84b8dSQuaker Fang return (IWP_FAIL); 211622a84b8dSQuaker Fang } 211722a84b8dSQuaker Fang 211822a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_PUT_SEG); 211922a84b8dSQuaker Fang 212022a84b8dSQuaker Fang return (err); 212122a84b8dSQuaker Fang } 212222a84b8dSQuaker Fang 212322a84b8dSQuaker Fang static int 212422a84b8dSQuaker Fang iwp_load_run_firmware(iwp_sc_t *sc) 212522a84b8dSQuaker Fang { 212622a84b8dSQuaker Fang int err = IWP_FAIL; 212722a84b8dSQuaker Fang clock_t clk; 212822a84b8dSQuaker Fang 212922a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_PUT_SEG); 213022a84b8dSQuaker Fang 213122a84b8dSQuaker Fang /* 213222a84b8dSQuaker Fang * load init_text section of uCode to hardware 213322a84b8dSQuaker Fang */ 213422a84b8dSQuaker Fang err = iwp_put_seg_fw(sc, sc->sc_dma_fw_text.cookie.dmac_address, 213522a84b8dSQuaker Fang RTC_INST_LOWER_BOUND, sc->sc_dma_fw_text.cookie.dmac_size); 213622a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 213722a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_load_run_firmware(): " 213822a84b8dSQuaker Fang "failed to write run uCode.\n"); 213922a84b8dSQuaker Fang return (err); 214022a84b8dSQuaker Fang } 214122a84b8dSQuaker Fang 214222a84b8dSQuaker Fang clk = ddi_get_lbolt() + drv_usectohz(1000000); 214322a84b8dSQuaker Fang 214422a84b8dSQuaker Fang /* wait loading run_text until completed or timeout */ 214522a84b8dSQuaker Fang while (!(sc->sc_flags & IWP_F_PUT_SEG)) { 214622a84b8dSQuaker Fang if (cv_timedwait(&sc->sc_put_seg_cv, &sc->sc_glock, clk) < 0) { 214722a84b8dSQuaker Fang break; 214822a84b8dSQuaker Fang } 214922a84b8dSQuaker Fang } 215022a84b8dSQuaker Fang 215122a84b8dSQuaker Fang if (!(sc->sc_flags & IWP_F_PUT_SEG)) { 215222a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_load_run_firmware(): " 215322a84b8dSQuaker Fang "timeout waiting for run uCode load.\n"); 215422a84b8dSQuaker Fang return (IWP_FAIL); 215522a84b8dSQuaker Fang } 215622a84b8dSQuaker Fang 215722a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_PUT_SEG); 215822a84b8dSQuaker Fang 215922a84b8dSQuaker Fang /* 216022a84b8dSQuaker Fang * load run_data section of uCode to hardware 216122a84b8dSQuaker Fang */ 216222a84b8dSQuaker Fang err = iwp_put_seg_fw(sc, sc->sc_dma_fw_data_bak.cookie.dmac_address, 216322a84b8dSQuaker Fang RTC_DATA_LOWER_BOUND, sc->sc_dma_fw_data.cookie.dmac_size); 216422a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 216522a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_load_run_firmware(): " 216622a84b8dSQuaker Fang "failed to write run_data uCode.\n"); 216722a84b8dSQuaker Fang return (err); 216822a84b8dSQuaker Fang } 216922a84b8dSQuaker Fang 217022a84b8dSQuaker Fang clk = ddi_get_lbolt() + drv_usectohz(1000000); 217122a84b8dSQuaker Fang 217222a84b8dSQuaker Fang /* 217322a84b8dSQuaker Fang * wait loading run_data until completed or timeout 217422a84b8dSQuaker Fang */ 217522a84b8dSQuaker Fang while (!(sc->sc_flags & IWP_F_PUT_SEG)) { 217622a84b8dSQuaker Fang if (cv_timedwait(&sc->sc_put_seg_cv, &sc->sc_glock, clk) < 0) { 217722a84b8dSQuaker Fang break; 217822a84b8dSQuaker Fang } 217922a84b8dSQuaker Fang } 218022a84b8dSQuaker Fang 218122a84b8dSQuaker Fang if (!(sc->sc_flags & IWP_F_PUT_SEG)) { 218222a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_load_run_firmware(): " 218322a84b8dSQuaker Fang "timeout waiting for run_data uCode load.\n"); 218422a84b8dSQuaker Fang return (IWP_FAIL); 218522a84b8dSQuaker Fang } 218622a84b8dSQuaker Fang 218722a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_PUT_SEG); 218822a84b8dSQuaker Fang 218922a84b8dSQuaker Fang return (err); 219022a84b8dSQuaker Fang } 219122a84b8dSQuaker Fang 219222a84b8dSQuaker Fang /* 219322a84b8dSQuaker Fang * this function will be invoked to receive phy information 219422a84b8dSQuaker Fang * when a frame is received. 219522a84b8dSQuaker Fang */ 219622a84b8dSQuaker Fang static void 219722a84b8dSQuaker Fang iwp_rx_phy_intr(iwp_sc_t *sc, iwp_rx_desc_t *desc) 219822a84b8dSQuaker Fang { 219922a84b8dSQuaker Fang 220022a84b8dSQuaker Fang sc->sc_rx_phy_res.flag = 1; 220122a84b8dSQuaker Fang 220222a84b8dSQuaker Fang (void) memcpy(sc->sc_rx_phy_res.buf, (uint8_t *)(desc + 1), 220322a84b8dSQuaker Fang sizeof (iwp_rx_phy_res_t)); 220422a84b8dSQuaker Fang } 220522a84b8dSQuaker Fang 220622a84b8dSQuaker Fang /* 220722a84b8dSQuaker Fang * this function will be invoked to receive body of frame when 220822a84b8dSQuaker Fang * a frame is received. 220922a84b8dSQuaker Fang */ 221022a84b8dSQuaker Fang static void 221122a84b8dSQuaker Fang iwp_rx_mpdu_intr(iwp_sc_t *sc, iwp_rx_desc_t *desc) 221222a84b8dSQuaker Fang { 221322a84b8dSQuaker Fang ieee80211com_t *ic = &sc->sc_ic; 221422a84b8dSQuaker Fang #ifdef DEBUG 221522a84b8dSQuaker Fang iwp_rx_ring_t *ring = &sc->sc_rxq; 221622a84b8dSQuaker Fang #endif 221722a84b8dSQuaker Fang struct ieee80211_frame *wh; 221822a84b8dSQuaker Fang struct iwp_rx_non_cfg_phy *phyinfo; 221922a84b8dSQuaker Fang struct iwp_rx_mpdu_body_size *mpdu_size; 222022a84b8dSQuaker Fang 222122a84b8dSQuaker Fang mblk_t *mp; 222222a84b8dSQuaker Fang int16_t t; 222322a84b8dSQuaker Fang uint16_t len, rssi, agc; 222422a84b8dSQuaker Fang uint32_t temp, crc, *tail; 222522a84b8dSQuaker Fang uint32_t arssi, brssi, crssi, mrssi; 222622a84b8dSQuaker Fang iwp_rx_phy_res_t *stat; 222722a84b8dSQuaker Fang ieee80211_node_t *in; 222822a84b8dSQuaker Fang 222922a84b8dSQuaker Fang /* 223022a84b8dSQuaker Fang * assuming not 11n here. cope with 11n in phase-II 223122a84b8dSQuaker Fang */ 223222a84b8dSQuaker Fang mpdu_size = (struct iwp_rx_mpdu_body_size *)(desc + 1); 223322a84b8dSQuaker Fang stat = (iwp_rx_phy_res_t *)sc->sc_rx_phy_res.buf; 223422a84b8dSQuaker Fang if (stat->cfg_phy_cnt > 20) { 223522a84b8dSQuaker Fang return; 223622a84b8dSQuaker Fang } 223722a84b8dSQuaker Fang 223822a84b8dSQuaker Fang phyinfo = (struct iwp_rx_non_cfg_phy *)stat->non_cfg_phy; 223922a84b8dSQuaker Fang temp = LE_32(phyinfo->non_cfg_phy[IWP_RX_RES_AGC_IDX]); 224022a84b8dSQuaker Fang agc = (temp & IWP_OFDM_AGC_MSK) >> IWP_OFDM_AGC_BIT_POS; 224122a84b8dSQuaker Fang 224222a84b8dSQuaker Fang temp = LE_32(phyinfo->non_cfg_phy[IWP_RX_RES_RSSI_AB_IDX]); 224322a84b8dSQuaker Fang arssi = (temp & IWP_OFDM_RSSI_A_MSK) >> IWP_OFDM_RSSI_A_BIT_POS; 224422a84b8dSQuaker Fang brssi = (temp & IWP_OFDM_RSSI_B_MSK) >> IWP_OFDM_RSSI_B_BIT_POS; 224522a84b8dSQuaker Fang 224622a84b8dSQuaker Fang temp = LE_32(phyinfo->non_cfg_phy[IWP_RX_RES_RSSI_C_IDX]); 224722a84b8dSQuaker Fang crssi = (temp & IWP_OFDM_RSSI_C_MSK) >> IWP_OFDM_RSSI_C_BIT_POS; 224822a84b8dSQuaker Fang 224922a84b8dSQuaker Fang mrssi = MAX(arssi, brssi); 225022a84b8dSQuaker Fang mrssi = MAX(mrssi, crssi); 225122a84b8dSQuaker Fang 225222a84b8dSQuaker Fang t = mrssi - agc - IWP_RSSI_OFFSET; 225322a84b8dSQuaker Fang /* 225422a84b8dSQuaker Fang * convert dBm to percentage 225522a84b8dSQuaker Fang */ 225622a84b8dSQuaker Fang rssi = (100 * 75 * 75 - (-20 - t) * (15 * 75 + 62 * (-20 - t))) 225722a84b8dSQuaker Fang / (75 * 75); 225822a84b8dSQuaker Fang if (rssi > 100) { 225922a84b8dSQuaker Fang rssi = 100; 226022a84b8dSQuaker Fang } 226122a84b8dSQuaker Fang if (rssi < 1) { 226222a84b8dSQuaker Fang rssi = 1; 226322a84b8dSQuaker Fang } 226422a84b8dSQuaker Fang 226522a84b8dSQuaker Fang /* 226622a84b8dSQuaker Fang * size of frame, not include FCS 226722a84b8dSQuaker Fang */ 226822a84b8dSQuaker Fang len = LE_16(mpdu_size->byte_count); 226922a84b8dSQuaker Fang tail = (uint32_t *)((uint8_t *)(desc + 1) + 227022a84b8dSQuaker Fang sizeof (struct iwp_rx_mpdu_body_size) + len); 227122a84b8dSQuaker Fang bcopy(tail, &crc, 4); 227222a84b8dSQuaker Fang 227322a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_RX, "iwp_rx_mpdu_intr(): " 227422a84b8dSQuaker Fang "rx intr: idx=%d phy_len=%x len=%d " 227522a84b8dSQuaker Fang "rate=%x chan=%d tstamp=%x non_cfg_phy_count=%x " 227622a84b8dSQuaker Fang "cfg_phy_count=%x tail=%x", ring->cur, sizeof (*stat), 227722a84b8dSQuaker Fang len, stat->rate.r.s.rate, stat->channel, 227822a84b8dSQuaker Fang LE_32(stat->timestampl), stat->non_cfg_phy_cnt, 227922a84b8dSQuaker Fang stat->cfg_phy_cnt, LE_32(crc))); 228022a84b8dSQuaker Fang 228122a84b8dSQuaker Fang if ((len < 16) || (len > sc->sc_dmabuf_sz)) { 228222a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_RX, "iwp_rx_mpdu_intr(): " 228322a84b8dSQuaker Fang "rx frame oversize\n")); 228422a84b8dSQuaker Fang return; 228522a84b8dSQuaker Fang } 228622a84b8dSQuaker Fang 228722a84b8dSQuaker Fang /* 228822a84b8dSQuaker Fang * discard Rx frames with bad CRC 228922a84b8dSQuaker Fang */ 229022a84b8dSQuaker Fang if ((LE_32(crc) & 229122a84b8dSQuaker Fang (RX_RES_STATUS_NO_CRC32_ERROR | RX_RES_STATUS_NO_RXE_OVERFLOW)) != 229222a84b8dSQuaker Fang (RX_RES_STATUS_NO_CRC32_ERROR | RX_RES_STATUS_NO_RXE_OVERFLOW)) { 229322a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_RX, "iwp_rx_mpdu_intr(): " 229422a84b8dSQuaker Fang "rx crc error tail: %x\n", 229522a84b8dSQuaker Fang LE_32(crc))); 229622a84b8dSQuaker Fang sc->sc_rx_err++; 229722a84b8dSQuaker Fang return; 229822a84b8dSQuaker Fang } 229922a84b8dSQuaker Fang 230022a84b8dSQuaker Fang wh = (struct ieee80211_frame *) 230122a84b8dSQuaker Fang ((uint8_t *)(desc + 1)+ sizeof (struct iwp_rx_mpdu_body_size)); 230222a84b8dSQuaker Fang 230322a84b8dSQuaker Fang if (IEEE80211_FC0_SUBTYPE_ASSOC_RESP == *(uint8_t *)wh) { 230422a84b8dSQuaker Fang sc->sc_assoc_id = *((uint16_t *)(wh + 1) + 2); 230522a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_RX, "iwp_rx_mpdu_intr(): " 230622a84b8dSQuaker Fang "rx : association id = %x\n", 230722a84b8dSQuaker Fang sc->sc_assoc_id)); 230822a84b8dSQuaker Fang } 230922a84b8dSQuaker Fang 231022a84b8dSQuaker Fang #ifdef DEBUG 231122a84b8dSQuaker Fang if (iwp_dbg_flags & IWP_DEBUG_RX) { 231222a84b8dSQuaker Fang ieee80211_dump_pkt((uint8_t *)wh, len, 0, 0); 231322a84b8dSQuaker Fang } 231422a84b8dSQuaker Fang #endif 231522a84b8dSQuaker Fang 231622a84b8dSQuaker Fang in = ieee80211_find_rxnode(ic, wh); 231722a84b8dSQuaker Fang mp = allocb(len, BPRI_MED); 231822a84b8dSQuaker Fang if (mp) { 231922a84b8dSQuaker Fang (void) memcpy(mp->b_wptr, wh, len); 232022a84b8dSQuaker Fang mp->b_wptr += len; 232122a84b8dSQuaker Fang 232222a84b8dSQuaker Fang /* 232322a84b8dSQuaker Fang * send the frame to the 802.11 layer 232422a84b8dSQuaker Fang */ 232522a84b8dSQuaker Fang (void) ieee80211_input(ic, mp, in, rssi, 0); 232622a84b8dSQuaker Fang } else { 232722a84b8dSQuaker Fang sc->sc_rx_nobuf++; 232822a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_RX, "iwp_rx_mpdu_intr(): " 232922a84b8dSQuaker Fang "alloc rx buf failed\n")); 233022a84b8dSQuaker Fang } 233122a84b8dSQuaker Fang 233222a84b8dSQuaker Fang /* 233322a84b8dSQuaker Fang * release node reference 233422a84b8dSQuaker Fang */ 233522a84b8dSQuaker Fang ieee80211_free_node(in); 233622a84b8dSQuaker Fang } 233722a84b8dSQuaker Fang 233822a84b8dSQuaker Fang /* 233922a84b8dSQuaker Fang * process correlative affairs after a frame is sent. 234022a84b8dSQuaker Fang */ 234122a84b8dSQuaker Fang static void 234222a84b8dSQuaker Fang iwp_tx_intr(iwp_sc_t *sc, iwp_rx_desc_t *desc) 234322a84b8dSQuaker Fang { 234422a84b8dSQuaker Fang ieee80211com_t *ic = &sc->sc_ic; 234522a84b8dSQuaker Fang iwp_tx_ring_t *ring = &sc->sc_txq[desc->hdr.qid & 0x3]; 234622a84b8dSQuaker Fang iwp_tx_stat_t *stat = (iwp_tx_stat_t *)(desc + 1); 234722a84b8dSQuaker Fang iwp_amrr_t *amrr; 234822a84b8dSQuaker Fang 234922a84b8dSQuaker Fang if (NULL == ic->ic_bss) { 235022a84b8dSQuaker Fang return; 235122a84b8dSQuaker Fang } 235222a84b8dSQuaker Fang 235322a84b8dSQuaker Fang amrr = (iwp_amrr_t *)ic->ic_bss; 235422a84b8dSQuaker Fang 235522a84b8dSQuaker Fang amrr->txcnt++; 235622a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_RATECTL, "iwp_tx_intr(): " 235722a84b8dSQuaker Fang "tx: %d cnt\n", amrr->txcnt)); 235822a84b8dSQuaker Fang 235922a84b8dSQuaker Fang if (stat->ntries > 0) { 236022a84b8dSQuaker Fang amrr->retrycnt++; 236122a84b8dSQuaker Fang sc->sc_tx_retries++; 236222a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_TX, "iwp_tx_intr(): " 236322a84b8dSQuaker Fang "tx: %d retries\n", 236422a84b8dSQuaker Fang sc->sc_tx_retries)); 236522a84b8dSQuaker Fang } 236622a84b8dSQuaker Fang 236722a84b8dSQuaker Fang mutex_enter(&sc->sc_mt_lock); 236822a84b8dSQuaker Fang sc->sc_tx_timer = 0; 236922a84b8dSQuaker Fang mutex_exit(&sc->sc_mt_lock); 237022a84b8dSQuaker Fang 237122a84b8dSQuaker Fang mutex_enter(&sc->sc_tx_lock); 237222a84b8dSQuaker Fang 237322a84b8dSQuaker Fang ring->queued--; 237422a84b8dSQuaker Fang if (ring->queued < 0) { 237522a84b8dSQuaker Fang ring->queued = 0; 237622a84b8dSQuaker Fang } 237722a84b8dSQuaker Fang 237822a84b8dSQuaker Fang if ((sc->sc_need_reschedule) && (ring->queued <= (ring->count >> 3))) { 237922a84b8dSQuaker Fang sc->sc_need_reschedule = 0; 238022a84b8dSQuaker Fang mutex_exit(&sc->sc_tx_lock); 238122a84b8dSQuaker Fang mac_tx_update(ic->ic_mach); 238222a84b8dSQuaker Fang mutex_enter(&sc->sc_tx_lock); 238322a84b8dSQuaker Fang } 238422a84b8dSQuaker Fang 238522a84b8dSQuaker Fang mutex_exit(&sc->sc_tx_lock); 238622a84b8dSQuaker Fang } 238722a84b8dSQuaker Fang 238822a84b8dSQuaker Fang /* 238922a84b8dSQuaker Fang * inform a given command has been executed 239022a84b8dSQuaker Fang */ 239122a84b8dSQuaker Fang static void 239222a84b8dSQuaker Fang iwp_cmd_intr(iwp_sc_t *sc, iwp_rx_desc_t *desc) 239322a84b8dSQuaker Fang { 239422a84b8dSQuaker Fang if ((desc->hdr.qid & 7) != 4) { 239522a84b8dSQuaker Fang return; 239622a84b8dSQuaker Fang } 239722a84b8dSQuaker Fang 239822a84b8dSQuaker Fang if (sc->sc_cmd_accum > 0) { 239922a84b8dSQuaker Fang sc->sc_cmd_accum--; 240022a84b8dSQuaker Fang return; 240122a84b8dSQuaker Fang } 240222a84b8dSQuaker Fang 240322a84b8dSQuaker Fang mutex_enter(&sc->sc_glock); 240422a84b8dSQuaker Fang 240522a84b8dSQuaker Fang sc->sc_cmd_flag = SC_CMD_FLG_DONE; 240622a84b8dSQuaker Fang 240722a84b8dSQuaker Fang cv_signal(&sc->sc_cmd_cv); 240822a84b8dSQuaker Fang 240922a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 241022a84b8dSQuaker Fang 241122a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_CMD, "iwp_cmd_intr(): " 241222a84b8dSQuaker Fang "qid=%x idx=%d flags=%x type=0x%x\n", 241322a84b8dSQuaker Fang desc->hdr.qid, desc->hdr.idx, desc->hdr.flags, 241422a84b8dSQuaker Fang desc->hdr.type)); 241522a84b8dSQuaker Fang } 241622a84b8dSQuaker Fang 241722a84b8dSQuaker Fang /* 241822a84b8dSQuaker Fang * this function will be invoked when alive notification occur. 241922a84b8dSQuaker Fang */ 242022a84b8dSQuaker Fang static void 242122a84b8dSQuaker Fang iwp_ucode_alive(iwp_sc_t *sc, iwp_rx_desc_t *desc) 242222a84b8dSQuaker Fang { 242322a84b8dSQuaker Fang uint32_t rv; 242422a84b8dSQuaker Fang struct iwp_calib_cfg_cmd cmd; 242522a84b8dSQuaker Fang struct iwp_alive_resp *ar = 242622a84b8dSQuaker Fang (struct iwp_alive_resp *)(desc + 1); 242722a84b8dSQuaker Fang struct iwp_calib_results *res_p = &sc->sc_calib_results; 242822a84b8dSQuaker Fang 242922a84b8dSQuaker Fang /* 243022a84b8dSQuaker Fang * the microcontroller is ready 243122a84b8dSQuaker Fang */ 243222a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_FW, "iwp_ucode_alive(): " 243322a84b8dSQuaker Fang "microcode alive notification minor: %x major: %x type: " 243422a84b8dSQuaker Fang "%x subtype: %x\n", 243522a84b8dSQuaker Fang ar->ucode_minor, ar->ucode_minor, ar->ver_type, ar->ver_subtype)); 243622a84b8dSQuaker Fang 243722a84b8dSQuaker Fang #ifdef DEBUG 243822a84b8dSQuaker Fang if (LE_32(ar->is_valid) != UCODE_VALID_OK) { 243922a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_FW, "iwp_ucode_alive(): " 244022a84b8dSQuaker Fang "microcontroller initialization failed\n")); 244122a84b8dSQuaker Fang } 244222a84b8dSQuaker Fang #endif 244322a84b8dSQuaker Fang 244422a84b8dSQuaker Fang /* 244522a84b8dSQuaker Fang * determine if init alive or runtime alive. 244622a84b8dSQuaker Fang */ 244722a84b8dSQuaker Fang if (INITIALIZE_SUBTYPE == ar->ver_subtype) { 244822a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_FW, "iwp_ucode_alive(): " 244922a84b8dSQuaker Fang "initialization alive received.\n")); 245022a84b8dSQuaker Fang 245122a84b8dSQuaker Fang (void) memcpy(&sc->sc_card_alive_init, ar, 245222a84b8dSQuaker Fang sizeof (struct iwp_init_alive_resp)); 245322a84b8dSQuaker Fang 245422a84b8dSQuaker Fang /* 245522a84b8dSQuaker Fang * necessary configuration to NIC 245622a84b8dSQuaker Fang */ 245722a84b8dSQuaker Fang mutex_enter(&sc->sc_glock); 245822a84b8dSQuaker Fang 245922a84b8dSQuaker Fang rv = iwp_alive_common(sc); 246022a84b8dSQuaker Fang if (rv != IWP_SUCCESS) { 246122a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_ucode_alive(): " 246222a84b8dSQuaker Fang "common alive process failed in init alive.\n"); 246322a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 246422a84b8dSQuaker Fang return; 246522a84b8dSQuaker Fang } 246622a84b8dSQuaker Fang 246722a84b8dSQuaker Fang (void) memset(&cmd, 0, sizeof (cmd)); 246822a84b8dSQuaker Fang 246922a84b8dSQuaker Fang cmd.ucd_calib_cfg.once.is_enable = IWP_CALIB_INIT_CFG_ALL; 247022a84b8dSQuaker Fang cmd.ucd_calib_cfg.once.start = IWP_CALIB_INIT_CFG_ALL; 247122a84b8dSQuaker Fang cmd.ucd_calib_cfg.once.send_res = IWP_CALIB_INIT_CFG_ALL; 247222a84b8dSQuaker Fang cmd.ucd_calib_cfg.flags = IWP_CALIB_INIT_CFG_ALL; 247322a84b8dSQuaker Fang 247422a84b8dSQuaker Fang /* 247522a84b8dSQuaker Fang * require ucode execute calibration 247622a84b8dSQuaker Fang */ 247722a84b8dSQuaker Fang rv = iwp_cmd(sc, CALIBRATION_CFG_CMD, &cmd, sizeof (cmd), 1); 247822a84b8dSQuaker Fang if (rv != IWP_SUCCESS) { 247922a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_ucode_alive(): " 248022a84b8dSQuaker Fang "failed to send calibration configure command.\n"); 248122a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 248222a84b8dSQuaker Fang return; 248322a84b8dSQuaker Fang } 248422a84b8dSQuaker Fang 248522a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 248622a84b8dSQuaker Fang 248722a84b8dSQuaker Fang } else { /* runtime alive */ 248822a84b8dSQuaker Fang 248922a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_FW, "iwp_ucode_alive(): " 249022a84b8dSQuaker Fang "runtime alive received.\n")); 249122a84b8dSQuaker Fang 249222a84b8dSQuaker Fang (void) memcpy(&sc->sc_card_alive_run, ar, 249322a84b8dSQuaker Fang sizeof (struct iwp_alive_resp)); 249422a84b8dSQuaker Fang 249522a84b8dSQuaker Fang mutex_enter(&sc->sc_glock); 249622a84b8dSQuaker Fang 249722a84b8dSQuaker Fang /* 249822a84b8dSQuaker Fang * necessary configuration to NIC 249922a84b8dSQuaker Fang */ 250022a84b8dSQuaker Fang rv = iwp_alive_common(sc); 250122a84b8dSQuaker Fang if (rv != IWP_SUCCESS) { 250222a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_ucode_alive(): " 250322a84b8dSQuaker Fang "common alive process failed in run alive.\n"); 250422a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 250522a84b8dSQuaker Fang return; 250622a84b8dSQuaker Fang } 250722a84b8dSQuaker Fang 250822a84b8dSQuaker Fang /* 250922a84b8dSQuaker Fang * send the result of local oscilator calibration to uCode. 251022a84b8dSQuaker Fang */ 251122a84b8dSQuaker Fang if (res_p->lo_res != NULL) { 251222a84b8dSQuaker Fang rv = iwp_cmd(sc, REPLY_PHY_CALIBRATION_CMD, 251322a84b8dSQuaker Fang res_p->lo_res, res_p->lo_res_len, 1); 251422a84b8dSQuaker Fang if (rv != IWP_SUCCESS) { 251522a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_ucode_alive(): " 251622a84b8dSQuaker Fang "failed to send local" 251722a84b8dSQuaker Fang "oscilator calibration command.\n"); 251822a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 251922a84b8dSQuaker Fang return; 252022a84b8dSQuaker Fang } 252122a84b8dSQuaker Fang 252222a84b8dSQuaker Fang DELAY(1000); 252322a84b8dSQuaker Fang } 252422a84b8dSQuaker Fang 252522a84b8dSQuaker Fang /* 252622a84b8dSQuaker Fang * send the result of TX IQ calibration to uCode. 252722a84b8dSQuaker Fang */ 252822a84b8dSQuaker Fang if (res_p->tx_iq_res != NULL) { 252922a84b8dSQuaker Fang rv = iwp_cmd(sc, REPLY_PHY_CALIBRATION_CMD, 253022a84b8dSQuaker Fang res_p->tx_iq_res, res_p->tx_iq_res_len, 1); 253122a84b8dSQuaker Fang if (rv != IWP_SUCCESS) { 253222a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_ucode_alive(): " 253322a84b8dSQuaker Fang "failed to send TX IQ" 253422a84b8dSQuaker Fang "calibration command.\n"); 253522a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 253622a84b8dSQuaker Fang return; 253722a84b8dSQuaker Fang } 253822a84b8dSQuaker Fang 253922a84b8dSQuaker Fang DELAY(1000); 254022a84b8dSQuaker Fang } 254122a84b8dSQuaker Fang 254222a84b8dSQuaker Fang /* 254322a84b8dSQuaker Fang * send the result of TX IQ perd calibration to uCode. 254422a84b8dSQuaker Fang */ 254522a84b8dSQuaker Fang if (res_p->tx_iq_perd_res != NULL) { 254622a84b8dSQuaker Fang rv = iwp_cmd(sc, REPLY_PHY_CALIBRATION_CMD, 254722a84b8dSQuaker Fang res_p->tx_iq_perd_res, 254822a84b8dSQuaker Fang res_p->tx_iq_perd_res_len, 1); 254922a84b8dSQuaker Fang if (rv != IWP_SUCCESS) { 255022a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_ucode_alive(): " 255122a84b8dSQuaker Fang "failed to send TX IQ perd" 255222a84b8dSQuaker Fang "calibration command.\n"); 255322a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 255422a84b8dSQuaker Fang return; 255522a84b8dSQuaker Fang } 255622a84b8dSQuaker Fang 255722a84b8dSQuaker Fang DELAY(1000); 255822a84b8dSQuaker Fang } 255922a84b8dSQuaker Fang 256022a84b8dSQuaker Fang /* 256122a84b8dSQuaker Fang * send the result of Base Band calibration to uCode. 256222a84b8dSQuaker Fang */ 256322a84b8dSQuaker Fang if (res_p->base_band_res != NULL) { 256422a84b8dSQuaker Fang rv = iwp_cmd(sc, REPLY_PHY_CALIBRATION_CMD, 256522a84b8dSQuaker Fang res_p->base_band_res, 256622a84b8dSQuaker Fang res_p->base_band_res_len, 1); 256722a84b8dSQuaker Fang if (rv != IWP_SUCCESS) { 256822a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_ucode_alive(): " 256922a84b8dSQuaker Fang "failed to send Base Band" 257022a84b8dSQuaker Fang "calibration command.\n"); 257122a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 257222a84b8dSQuaker Fang return; 257322a84b8dSQuaker Fang } 257422a84b8dSQuaker Fang 257522a84b8dSQuaker Fang DELAY(1000); 257622a84b8dSQuaker Fang } 257722a84b8dSQuaker Fang 257822a84b8dSQuaker Fang atomic_or_32(&sc->sc_flags, IWP_F_FW_INIT); 257922a84b8dSQuaker Fang cv_signal(&sc->sc_ucode_cv); 258022a84b8dSQuaker Fang 258122a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 258222a84b8dSQuaker Fang } 258322a84b8dSQuaker Fang 258422a84b8dSQuaker Fang } 258522a84b8dSQuaker Fang 258622a84b8dSQuaker Fang /* 258722a84b8dSQuaker Fang * deal with receiving frames, command response 258822a84b8dSQuaker Fang * and all notifications from ucode. 258922a84b8dSQuaker Fang */ 259022a84b8dSQuaker Fang /* ARGSUSED */ 259122a84b8dSQuaker Fang static uint_t 259222a84b8dSQuaker Fang iwp_rx_softintr(caddr_t arg, caddr_t unused) 259322a84b8dSQuaker Fang { 259422a84b8dSQuaker Fang iwp_sc_t *sc; 259522a84b8dSQuaker Fang ieee80211com_t *ic; 259622a84b8dSQuaker Fang iwp_rx_desc_t *desc; 259722a84b8dSQuaker Fang iwp_rx_data_t *data; 259822a84b8dSQuaker Fang uint32_t index; 259922a84b8dSQuaker Fang 260022a84b8dSQuaker Fang if (NULL == arg) { 260122a84b8dSQuaker Fang return (DDI_INTR_UNCLAIMED); 260222a84b8dSQuaker Fang } 260322a84b8dSQuaker Fang sc = (iwp_sc_t *)arg; 260422a84b8dSQuaker Fang ic = &sc->sc_ic; 260522a84b8dSQuaker Fang 260622a84b8dSQuaker Fang /* 260722a84b8dSQuaker Fang * firmware has moved the index of the rx queue, driver get it, 260822a84b8dSQuaker Fang * and deal with it. 260922a84b8dSQuaker Fang */ 261022a84b8dSQuaker Fang index = (sc->sc_shared->val0) & 0xfff; 261122a84b8dSQuaker Fang 261222a84b8dSQuaker Fang while (sc->sc_rxq.cur != index) { 261322a84b8dSQuaker Fang data = &sc->sc_rxq.data[sc->sc_rxq.cur]; 261422a84b8dSQuaker Fang desc = (iwp_rx_desc_t *)data->dma_data.mem_va; 261522a84b8dSQuaker Fang 261622a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_INTR, "iwp_rx_softintr(): " 261722a84b8dSQuaker Fang "rx notification index = %d" 261822a84b8dSQuaker Fang " cur = %d qid=%x idx=%d flags=%x type=%x len=%d\n", 261922a84b8dSQuaker Fang index, sc->sc_rxq.cur, desc->hdr.qid, desc->hdr.idx, 262022a84b8dSQuaker Fang desc->hdr.flags, desc->hdr.type, LE_32(desc->len))); 262122a84b8dSQuaker Fang 262222a84b8dSQuaker Fang /* 262322a84b8dSQuaker Fang * a command other than a tx need to be replied 262422a84b8dSQuaker Fang */ 262522a84b8dSQuaker Fang if (!(desc->hdr.qid & 0x80) && 262622a84b8dSQuaker Fang (desc->hdr.type != REPLY_SCAN_CMD) && 262722a84b8dSQuaker Fang (desc->hdr.type != REPLY_TX)) { 262822a84b8dSQuaker Fang iwp_cmd_intr(sc, desc); 262922a84b8dSQuaker Fang } 263022a84b8dSQuaker Fang 263122a84b8dSQuaker Fang switch (desc->hdr.type) { 263222a84b8dSQuaker Fang case REPLY_RX_PHY_CMD: 263322a84b8dSQuaker Fang iwp_rx_phy_intr(sc, desc); 263422a84b8dSQuaker Fang break; 263522a84b8dSQuaker Fang 263622a84b8dSQuaker Fang case REPLY_RX_MPDU_CMD: 263722a84b8dSQuaker Fang iwp_rx_mpdu_intr(sc, desc); 263822a84b8dSQuaker Fang break; 263922a84b8dSQuaker Fang 264022a84b8dSQuaker Fang case REPLY_TX: 264122a84b8dSQuaker Fang iwp_tx_intr(sc, desc); 264222a84b8dSQuaker Fang break; 264322a84b8dSQuaker Fang 264422a84b8dSQuaker Fang case REPLY_ALIVE: 264522a84b8dSQuaker Fang iwp_ucode_alive(sc, desc); 264622a84b8dSQuaker Fang break; 264722a84b8dSQuaker Fang 264822a84b8dSQuaker Fang case CARD_STATE_NOTIFICATION: 264922a84b8dSQuaker Fang { 265022a84b8dSQuaker Fang uint32_t *status = (uint32_t *)(desc + 1); 265122a84b8dSQuaker Fang 265222a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_RADIO, "iwp_rx_softintr(): " 265322a84b8dSQuaker Fang "state changed to %x\n", 265422a84b8dSQuaker Fang LE_32(*status))); 265522a84b8dSQuaker Fang 265622a84b8dSQuaker Fang if (LE_32(*status) & 1) { 265722a84b8dSQuaker Fang /* 265822a84b8dSQuaker Fang * the radio button has to be pushed(OFF). It 265922a84b8dSQuaker Fang * is considered as a hw error, the 266022a84b8dSQuaker Fang * iwp_thread() tries to recover it after the 266122a84b8dSQuaker Fang * button is pushed again(ON) 266222a84b8dSQuaker Fang */ 266322a84b8dSQuaker Fang cmn_err(CE_NOTE, "iwp_rx_softintr(): " 266422a84b8dSQuaker Fang "radio transmitter is off\n"); 266522a84b8dSQuaker Fang sc->sc_ostate = sc->sc_ic.ic_state; 266622a84b8dSQuaker Fang ieee80211_new_state(&sc->sc_ic, 266722a84b8dSQuaker Fang IEEE80211_S_INIT, -1); 266822a84b8dSQuaker Fang atomic_or_32(&sc->sc_flags, 266922a84b8dSQuaker Fang IWP_F_HW_ERR_RECOVER | IWP_F_RADIO_OFF); 267022a84b8dSQuaker Fang } 267122a84b8dSQuaker Fang 267222a84b8dSQuaker Fang break; 267322a84b8dSQuaker Fang } 267422a84b8dSQuaker Fang 267522a84b8dSQuaker Fang case SCAN_START_NOTIFICATION: 267622a84b8dSQuaker Fang { 267722a84b8dSQuaker Fang iwp_start_scan_t *scan = 267822a84b8dSQuaker Fang (iwp_start_scan_t *)(desc + 1); 267922a84b8dSQuaker Fang 268022a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_SCAN, "iwp_rx_softintr(): " 268122a84b8dSQuaker Fang "scanning channel %d status %x\n", 268222a84b8dSQuaker Fang scan->chan, LE_32(scan->status))); 268322a84b8dSQuaker Fang 268422a84b8dSQuaker Fang ic->ic_curchan = &ic->ic_sup_channels[scan->chan]; 268522a84b8dSQuaker Fang break; 268622a84b8dSQuaker Fang } 268722a84b8dSQuaker Fang 268822a84b8dSQuaker Fang case SCAN_COMPLETE_NOTIFICATION: 268922a84b8dSQuaker Fang { 269022a84b8dSQuaker Fang #ifdef DEBUG 269122a84b8dSQuaker Fang iwp_stop_scan_t *scan = 269222a84b8dSQuaker Fang (iwp_stop_scan_t *)(desc + 1); 269322a84b8dSQuaker Fang 269422a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_SCAN, "iwp_rx_softintr(): " 269522a84b8dSQuaker Fang "completed channel %d (burst of %d) status %02x\n", 269622a84b8dSQuaker Fang scan->chan, scan->nchan, scan->status)); 269722a84b8dSQuaker Fang #endif 269822a84b8dSQuaker Fang 269922a84b8dSQuaker Fang sc->sc_scan_pending++; 270022a84b8dSQuaker Fang break; 270122a84b8dSQuaker Fang } 270222a84b8dSQuaker Fang 270322a84b8dSQuaker Fang case STATISTICS_NOTIFICATION: 270422a84b8dSQuaker Fang { 270522a84b8dSQuaker Fang /* 270622a84b8dSQuaker Fang * handle statistics notification 270722a84b8dSQuaker Fang */ 270822a84b8dSQuaker Fang break; 270922a84b8dSQuaker Fang } 271022a84b8dSQuaker Fang 271122a84b8dSQuaker Fang case CALIBRATION_RES_NOTIFICATION: 271222a84b8dSQuaker Fang iwp_save_calib_result(sc, desc); 271322a84b8dSQuaker Fang break; 271422a84b8dSQuaker Fang 271522a84b8dSQuaker Fang case CALIBRATION_COMPLETE_NOTIFICATION: 271622a84b8dSQuaker Fang mutex_enter(&sc->sc_glock); 271722a84b8dSQuaker Fang atomic_or_32(&sc->sc_flags, IWP_F_FW_INIT); 271822a84b8dSQuaker Fang cv_signal(&sc->sc_ucode_cv); 271922a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 272022a84b8dSQuaker Fang break; 272122a84b8dSQuaker Fang 272222a84b8dSQuaker Fang case MISSED_BEACONS_NOTIFICATION: 272322a84b8dSQuaker Fang { 272422a84b8dSQuaker Fang struct iwp_beacon_missed *miss = 272522a84b8dSQuaker Fang (struct iwp_beacon_missed *)(desc + 1); 272622a84b8dSQuaker Fang 272722a84b8dSQuaker Fang if ((ic->ic_state == IEEE80211_S_RUN) && 272822a84b8dSQuaker Fang (LE_32(miss->consecutive) > 50)) { 272922a84b8dSQuaker Fang cmn_err(CE_NOTE, "iwp: iwp_rx_softintr(): " 273022a84b8dSQuaker Fang "beacon missed %d/%d\n", 273122a84b8dSQuaker Fang LE_32(miss->consecutive), 273222a84b8dSQuaker Fang LE_32(miss->total)); 273322a84b8dSQuaker Fang (void) ieee80211_new_state(ic, 273422a84b8dSQuaker Fang IEEE80211_S_INIT, -1); 273522a84b8dSQuaker Fang } 273622a84b8dSQuaker Fang break; 273722a84b8dSQuaker Fang } 273822a84b8dSQuaker Fang } 273922a84b8dSQuaker Fang 274022a84b8dSQuaker Fang sc->sc_rxq.cur = (sc->sc_rxq.cur + 1) % RX_QUEUE_SIZE; 274122a84b8dSQuaker Fang } 274222a84b8dSQuaker Fang 274322a84b8dSQuaker Fang /* 274422a84b8dSQuaker Fang * driver dealt with what received in rx queue and tell the information 274522a84b8dSQuaker Fang * to the firmware. 274622a84b8dSQuaker Fang */ 274722a84b8dSQuaker Fang index = (0 == index) ? RX_QUEUE_SIZE - 1 : index - 1; 274822a84b8dSQuaker Fang IWP_WRITE(sc, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, index & (~7)); 274922a84b8dSQuaker Fang 275022a84b8dSQuaker Fang /* 275122a84b8dSQuaker Fang * re-enable interrupts 275222a84b8dSQuaker Fang */ 275322a84b8dSQuaker Fang IWP_WRITE(sc, CSR_INT_MASK, CSR_INI_SET_MASK); 275422a84b8dSQuaker Fang 275522a84b8dSQuaker Fang return (DDI_INTR_CLAIMED); 275622a84b8dSQuaker Fang } 275722a84b8dSQuaker Fang 275822a84b8dSQuaker Fang /* 275922a84b8dSQuaker Fang * the handle of interrupt 276022a84b8dSQuaker Fang */ 276122a84b8dSQuaker Fang /* ARGSUSED */ 276222a84b8dSQuaker Fang static uint_t 276322a84b8dSQuaker Fang iwp_intr(caddr_t arg, caddr_t unused) 276422a84b8dSQuaker Fang { 276522a84b8dSQuaker Fang iwp_sc_t *sc; 276622a84b8dSQuaker Fang uint32_t r, rfh; 276722a84b8dSQuaker Fang 276822a84b8dSQuaker Fang if (NULL == arg) { 276922a84b8dSQuaker Fang return (DDI_INTR_UNCLAIMED); 277022a84b8dSQuaker Fang } 277122a84b8dSQuaker Fang sc = (iwp_sc_t *)arg; 277222a84b8dSQuaker Fang 277322a84b8dSQuaker Fang r = IWP_READ(sc, CSR_INT); 277422a84b8dSQuaker Fang if (0 == r || 0xffffffff == r) { 277522a84b8dSQuaker Fang return (DDI_INTR_UNCLAIMED); 277622a84b8dSQuaker Fang } 277722a84b8dSQuaker Fang 277822a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_INTR, "iwp_intr(): " 277922a84b8dSQuaker Fang "interrupt reg %x\n", r)); 278022a84b8dSQuaker Fang 278122a84b8dSQuaker Fang rfh = IWP_READ(sc, CSR_FH_INT_STATUS); 278222a84b8dSQuaker Fang 278322a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_INTR, "iwp_intr(): " 278422a84b8dSQuaker Fang "FH interrupt reg %x\n", rfh)); 278522a84b8dSQuaker Fang 278622a84b8dSQuaker Fang /* 278722a84b8dSQuaker Fang * disable interrupts 278822a84b8dSQuaker Fang */ 278922a84b8dSQuaker Fang IWP_WRITE(sc, CSR_INT_MASK, 0); 279022a84b8dSQuaker Fang 279122a84b8dSQuaker Fang /* 279222a84b8dSQuaker Fang * ack interrupts 279322a84b8dSQuaker Fang */ 279422a84b8dSQuaker Fang IWP_WRITE(sc, CSR_INT, r); 279522a84b8dSQuaker Fang IWP_WRITE(sc, CSR_FH_INT_STATUS, rfh); 279622a84b8dSQuaker Fang 279722a84b8dSQuaker Fang if (r & (BIT_INT_SWERROR | BIT_INT_ERR)) { 279822a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_FW, "iwp_intr(): " 279922a84b8dSQuaker Fang "fatal firmware error\n")); 280022a84b8dSQuaker Fang iwp_stop(sc); 280122a84b8dSQuaker Fang sc->sc_ostate = sc->sc_ic.ic_state; 280222a84b8dSQuaker Fang 280322a84b8dSQuaker Fang /* notify upper layer */ 280422a84b8dSQuaker Fang if (!IWP_CHK_FAST_RECOVER(sc)) { 280522a84b8dSQuaker Fang ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1); 280622a84b8dSQuaker Fang } 280722a84b8dSQuaker Fang 280822a84b8dSQuaker Fang atomic_or_32(&sc->sc_flags, IWP_F_HW_ERR_RECOVER); 280922a84b8dSQuaker Fang return (DDI_INTR_CLAIMED); 281022a84b8dSQuaker Fang } 281122a84b8dSQuaker Fang 281222a84b8dSQuaker Fang if (r & BIT_INT_RF_KILL) { 281322a84b8dSQuaker Fang uint32_t tmp = IWP_READ(sc, CSR_GP_CNTRL); 281422a84b8dSQuaker Fang if (tmp & (1 << 27)) { 281522a84b8dSQuaker Fang cmn_err(CE_NOTE, "RF switch: radio on\n"); 281622a84b8dSQuaker Fang } 281722a84b8dSQuaker Fang } 281822a84b8dSQuaker Fang 281922a84b8dSQuaker Fang if ((r & (BIT_INT_FH_RX | BIT_INT_SW_RX)) || 282022a84b8dSQuaker Fang (rfh & FH_INT_RX_MASK)) { 282122a84b8dSQuaker Fang (void) ddi_intr_trigger_softint(sc->sc_soft_hdl, NULL); 282222a84b8dSQuaker Fang return (DDI_INTR_CLAIMED); 282322a84b8dSQuaker Fang } 282422a84b8dSQuaker Fang 282522a84b8dSQuaker Fang if (r & BIT_INT_FH_TX) { 282622a84b8dSQuaker Fang mutex_enter(&sc->sc_glock); 282722a84b8dSQuaker Fang atomic_or_32(&sc->sc_flags, IWP_F_PUT_SEG); 282822a84b8dSQuaker Fang cv_signal(&sc->sc_put_seg_cv); 282922a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 283022a84b8dSQuaker Fang } 283122a84b8dSQuaker Fang 283222a84b8dSQuaker Fang #ifdef DEBUG 283322a84b8dSQuaker Fang if (r & BIT_INT_ALIVE) { 283422a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_FW, "iwp_intr(): " 283522a84b8dSQuaker Fang "firmware initialized.\n")); 283622a84b8dSQuaker Fang } 283722a84b8dSQuaker Fang #endif 283822a84b8dSQuaker Fang 283922a84b8dSQuaker Fang /* 284022a84b8dSQuaker Fang * re-enable interrupts 284122a84b8dSQuaker Fang */ 284222a84b8dSQuaker Fang IWP_WRITE(sc, CSR_INT_MASK, CSR_INI_SET_MASK); 284322a84b8dSQuaker Fang 284422a84b8dSQuaker Fang return (DDI_INTR_CLAIMED); 284522a84b8dSQuaker Fang } 284622a84b8dSQuaker Fang 284722a84b8dSQuaker Fang static uint8_t 284822a84b8dSQuaker Fang iwp_rate_to_plcp(int rate) 284922a84b8dSQuaker Fang { 285022a84b8dSQuaker Fang uint8_t ret; 285122a84b8dSQuaker Fang 285222a84b8dSQuaker Fang switch (rate) { 285322a84b8dSQuaker Fang /* 285422a84b8dSQuaker Fang * CCK rates 285522a84b8dSQuaker Fang */ 285622a84b8dSQuaker Fang case 2: 285722a84b8dSQuaker Fang ret = 0xa; 285822a84b8dSQuaker Fang break; 285922a84b8dSQuaker Fang 286022a84b8dSQuaker Fang case 4: 286122a84b8dSQuaker Fang ret = 0x14; 286222a84b8dSQuaker Fang break; 286322a84b8dSQuaker Fang 286422a84b8dSQuaker Fang case 11: 286522a84b8dSQuaker Fang ret = 0x37; 286622a84b8dSQuaker Fang break; 286722a84b8dSQuaker Fang 286822a84b8dSQuaker Fang case 22: 286922a84b8dSQuaker Fang ret = 0x6e; 287022a84b8dSQuaker Fang break; 287122a84b8dSQuaker Fang 287222a84b8dSQuaker Fang /* 287322a84b8dSQuaker Fang * OFDM rates 287422a84b8dSQuaker Fang */ 287522a84b8dSQuaker Fang case 12: 287622a84b8dSQuaker Fang ret = 0xd; 287722a84b8dSQuaker Fang break; 287822a84b8dSQuaker Fang 287922a84b8dSQuaker Fang case 18: 288022a84b8dSQuaker Fang ret = 0xf; 288122a84b8dSQuaker Fang break; 288222a84b8dSQuaker Fang 288322a84b8dSQuaker Fang case 24: 288422a84b8dSQuaker Fang ret = 0x5; 288522a84b8dSQuaker Fang break; 288622a84b8dSQuaker Fang 288722a84b8dSQuaker Fang case 36: 288822a84b8dSQuaker Fang ret = 0x7; 288922a84b8dSQuaker Fang break; 289022a84b8dSQuaker Fang 289122a84b8dSQuaker Fang case 48: 289222a84b8dSQuaker Fang ret = 0x9; 289322a84b8dSQuaker Fang break; 289422a84b8dSQuaker Fang 289522a84b8dSQuaker Fang case 72: 289622a84b8dSQuaker Fang ret = 0xb; 289722a84b8dSQuaker Fang break; 289822a84b8dSQuaker Fang 289922a84b8dSQuaker Fang case 96: 290022a84b8dSQuaker Fang ret = 0x1; 290122a84b8dSQuaker Fang break; 290222a84b8dSQuaker Fang 290322a84b8dSQuaker Fang case 108: 290422a84b8dSQuaker Fang ret = 0x3; 290522a84b8dSQuaker Fang break; 290622a84b8dSQuaker Fang 290722a84b8dSQuaker Fang default: 290822a84b8dSQuaker Fang ret = 0; 290922a84b8dSQuaker Fang break; 291022a84b8dSQuaker Fang } 291122a84b8dSQuaker Fang 291222a84b8dSQuaker Fang return (ret); 291322a84b8dSQuaker Fang } 291422a84b8dSQuaker Fang 291522a84b8dSQuaker Fang /* 291622a84b8dSQuaker Fang * invoked by GLD send frames 291722a84b8dSQuaker Fang */ 291822a84b8dSQuaker Fang static mblk_t * 291922a84b8dSQuaker Fang iwp_m_tx(void *arg, mblk_t *mp) 292022a84b8dSQuaker Fang { 292122a84b8dSQuaker Fang iwp_sc_t *sc; 292222a84b8dSQuaker Fang ieee80211com_t *ic; 292322a84b8dSQuaker Fang mblk_t *next; 292422a84b8dSQuaker Fang 292522a84b8dSQuaker Fang if (NULL == arg) { 292622a84b8dSQuaker Fang return (NULL); 292722a84b8dSQuaker Fang } 292822a84b8dSQuaker Fang sc = (iwp_sc_t *)arg; 292922a84b8dSQuaker Fang ic = &sc->sc_ic; 293022a84b8dSQuaker Fang 293122a84b8dSQuaker Fang if (sc->sc_flags & IWP_F_SUSPEND) { 293222a84b8dSQuaker Fang freemsgchain(mp); 293322a84b8dSQuaker Fang return (NULL); 293422a84b8dSQuaker Fang } 293522a84b8dSQuaker Fang 293622a84b8dSQuaker Fang if (ic->ic_state != IEEE80211_S_RUN) { 293722a84b8dSQuaker Fang freemsgchain(mp); 293822a84b8dSQuaker Fang return (NULL); 293922a84b8dSQuaker Fang } 294022a84b8dSQuaker Fang 294122a84b8dSQuaker Fang if ((sc->sc_flags & IWP_F_HW_ERR_RECOVER) && 294222a84b8dSQuaker Fang IWP_CHK_FAST_RECOVER(sc)) { 294322a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_FW, "iwp_m_tx(): " 294422a84b8dSQuaker Fang "hold queue\n")); 294522a84b8dSQuaker Fang return (mp); 294622a84b8dSQuaker Fang } 294722a84b8dSQuaker Fang 294822a84b8dSQuaker Fang 294922a84b8dSQuaker Fang while (mp != NULL) { 295022a84b8dSQuaker Fang next = mp->b_next; 295122a84b8dSQuaker Fang mp->b_next = NULL; 295222a84b8dSQuaker Fang if (iwp_send(ic, mp, IEEE80211_FC0_TYPE_DATA) != 0) { 295322a84b8dSQuaker Fang mp->b_next = next; 295422a84b8dSQuaker Fang break; 295522a84b8dSQuaker Fang } 295622a84b8dSQuaker Fang mp = next; 295722a84b8dSQuaker Fang } 295822a84b8dSQuaker Fang 295922a84b8dSQuaker Fang return (mp); 296022a84b8dSQuaker Fang } 296122a84b8dSQuaker Fang 296222a84b8dSQuaker Fang /* 296322a84b8dSQuaker Fang * send frames 296422a84b8dSQuaker Fang */ 296522a84b8dSQuaker Fang static int 296622a84b8dSQuaker Fang iwp_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type) 296722a84b8dSQuaker Fang { 296822a84b8dSQuaker Fang iwp_sc_t *sc; 296922a84b8dSQuaker Fang iwp_tx_ring_t *ring; 297022a84b8dSQuaker Fang iwp_tx_desc_t *desc; 297122a84b8dSQuaker Fang iwp_tx_data_t *data; 297222a84b8dSQuaker Fang iwp_tx_data_t *desc_data; 297322a84b8dSQuaker Fang iwp_cmd_t *cmd; 297422a84b8dSQuaker Fang iwp_tx_cmd_t *tx; 297522a84b8dSQuaker Fang ieee80211_node_t *in; 297622a84b8dSQuaker Fang struct ieee80211_frame *wh; 297722a84b8dSQuaker Fang struct ieee80211_key *k = NULL; 297822a84b8dSQuaker Fang mblk_t *m, *m0; 297922a84b8dSQuaker Fang int hdrlen, len, len0, mblen, off, err = IWP_SUCCESS; 298022a84b8dSQuaker Fang uint16_t masks = 0; 298122a84b8dSQuaker Fang uint32_t rate, s_id = 0; 298222a84b8dSQuaker Fang 298322a84b8dSQuaker Fang if (NULL == ic) { 298422a84b8dSQuaker Fang return (IWP_FAIL); 298522a84b8dSQuaker Fang } 298622a84b8dSQuaker Fang sc = (iwp_sc_t *)ic; 298722a84b8dSQuaker Fang 298822a84b8dSQuaker Fang if (sc->sc_flags & IWP_F_SUSPEND) { 298922a84b8dSQuaker Fang if ((type & IEEE80211_FC0_TYPE_MASK) != 299022a84b8dSQuaker Fang IEEE80211_FC0_TYPE_DATA) { 299122a84b8dSQuaker Fang freemsg(mp); 299222a84b8dSQuaker Fang } 299322a84b8dSQuaker Fang err = IWP_FAIL; 299422a84b8dSQuaker Fang goto exit; 299522a84b8dSQuaker Fang } 299622a84b8dSQuaker Fang 299722a84b8dSQuaker Fang mutex_enter(&sc->sc_tx_lock); 299822a84b8dSQuaker Fang ring = &sc->sc_txq[0]; 299922a84b8dSQuaker Fang data = &ring->data[ring->cur]; 300022a84b8dSQuaker Fang cmd = data->cmd; 300122a84b8dSQuaker Fang bzero(cmd, sizeof (*cmd)); 300222a84b8dSQuaker Fang 300322a84b8dSQuaker Fang ring->cur = (ring->cur + 1) % ring->count; 300422a84b8dSQuaker Fang 300522a84b8dSQuaker Fang /* 300622a84b8dSQuaker Fang * Need reschedule TX if TX buffer is full. 300722a84b8dSQuaker Fang */ 300822a84b8dSQuaker Fang if (ring->queued > ring->count - IWP_MAX_WIN_SIZE) { 300922a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_TX, "iwp_send(): " 301022a84b8dSQuaker Fang "no txbuf\n")); 301122a84b8dSQuaker Fang 301222a84b8dSQuaker Fang sc->sc_need_reschedule = 1; 301322a84b8dSQuaker Fang mutex_exit(&sc->sc_tx_lock); 301422a84b8dSQuaker Fang 301522a84b8dSQuaker Fang if ((type & IEEE80211_FC0_TYPE_MASK) != 301622a84b8dSQuaker Fang IEEE80211_FC0_TYPE_DATA) { 301722a84b8dSQuaker Fang freemsg(mp); 301822a84b8dSQuaker Fang } 301922a84b8dSQuaker Fang sc->sc_tx_nobuf++; 302022a84b8dSQuaker Fang err = IWP_FAIL; 302122a84b8dSQuaker Fang goto exit; 302222a84b8dSQuaker Fang } 302322a84b8dSQuaker Fang 302422a84b8dSQuaker Fang ring->queued++; 302522a84b8dSQuaker Fang 302622a84b8dSQuaker Fang mutex_exit(&sc->sc_tx_lock); 302722a84b8dSQuaker Fang 302822a84b8dSQuaker Fang hdrlen = ieee80211_hdrspace(ic, mp->b_rptr); 302922a84b8dSQuaker Fang 303022a84b8dSQuaker Fang m = allocb(msgdsize(mp) + 32, BPRI_MED); 303122a84b8dSQuaker Fang if (NULL == m) { /* can not alloc buf, drop this package */ 303222a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_send(): " 303322a84b8dSQuaker Fang "failed to allocate msgbuf\n"); 303422a84b8dSQuaker Fang freemsg(mp); 303522a84b8dSQuaker Fang 303622a84b8dSQuaker Fang mutex_enter(&sc->sc_tx_lock); 303722a84b8dSQuaker Fang ring->queued--; 303822a84b8dSQuaker Fang if ((sc->sc_need_reschedule) && (ring->queued <= 0)) { 303922a84b8dSQuaker Fang sc->sc_need_reschedule = 0; 304022a84b8dSQuaker Fang mutex_exit(&sc->sc_tx_lock); 304122a84b8dSQuaker Fang mac_tx_update(ic->ic_mach); 304222a84b8dSQuaker Fang mutex_enter(&sc->sc_tx_lock); 304322a84b8dSQuaker Fang } 304422a84b8dSQuaker Fang mutex_exit(&sc->sc_tx_lock); 304522a84b8dSQuaker Fang 304622a84b8dSQuaker Fang err = IWP_SUCCESS; 304722a84b8dSQuaker Fang goto exit; 304822a84b8dSQuaker Fang } 304922a84b8dSQuaker Fang 305022a84b8dSQuaker Fang for (off = 0, m0 = mp; m0 != NULL; m0 = m0->b_cont) { 305122a84b8dSQuaker Fang mblen = MBLKL(m0); 305222a84b8dSQuaker Fang (void) memcpy(m->b_rptr + off, m0->b_rptr, mblen); 305322a84b8dSQuaker Fang off += mblen; 305422a84b8dSQuaker Fang } 305522a84b8dSQuaker Fang 305622a84b8dSQuaker Fang m->b_wptr += off; 305722a84b8dSQuaker Fang 305822a84b8dSQuaker Fang wh = (struct ieee80211_frame *)m->b_rptr; 305922a84b8dSQuaker Fang 306022a84b8dSQuaker Fang /* 306122a84b8dSQuaker Fang * determine send which AP or station in IBSS 306222a84b8dSQuaker Fang */ 306322a84b8dSQuaker Fang in = ieee80211_find_txnode(ic, wh->i_addr1); 306422a84b8dSQuaker Fang if (NULL == in) { 306522a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_send(): " 306622a84b8dSQuaker Fang "failed to find tx node\n"); 306722a84b8dSQuaker Fang freemsg(mp); 306822a84b8dSQuaker Fang freemsg(m); 306922a84b8dSQuaker Fang sc->sc_tx_err++; 307022a84b8dSQuaker Fang 307122a84b8dSQuaker Fang mutex_enter(&sc->sc_tx_lock); 307222a84b8dSQuaker Fang ring->queued--; 307322a84b8dSQuaker Fang if ((sc->sc_need_reschedule) && (ring->queued <= 0)) { 307422a84b8dSQuaker Fang sc->sc_need_reschedule = 0; 307522a84b8dSQuaker Fang mutex_exit(&sc->sc_tx_lock); 307622a84b8dSQuaker Fang mac_tx_update(ic->ic_mach); 307722a84b8dSQuaker Fang mutex_enter(&sc->sc_tx_lock); 307822a84b8dSQuaker Fang } 307922a84b8dSQuaker Fang mutex_exit(&sc->sc_tx_lock); 308022a84b8dSQuaker Fang 308122a84b8dSQuaker Fang err = IWP_SUCCESS; 308222a84b8dSQuaker Fang goto exit; 308322a84b8dSQuaker Fang } 308422a84b8dSQuaker Fang 308522a84b8dSQuaker Fang /* 308622a84b8dSQuaker Fang * Net80211 module encapsulate outbound data frames. 308722a84b8dSQuaker Fang * Add some feilds of 80211 frame. 308822a84b8dSQuaker Fang */ 308922a84b8dSQuaker Fang if ((type & IEEE80211_FC0_TYPE_MASK) == 309022a84b8dSQuaker Fang IEEE80211_FC0_TYPE_DATA) { 309122a84b8dSQuaker Fang (void) ieee80211_encap(ic, m, in); 309222a84b8dSQuaker Fang } 309322a84b8dSQuaker Fang 309422a84b8dSQuaker Fang freemsg(mp); 309522a84b8dSQuaker Fang 309622a84b8dSQuaker Fang cmd->hdr.type = REPLY_TX; 309722a84b8dSQuaker Fang cmd->hdr.flags = 0; 309822a84b8dSQuaker Fang cmd->hdr.qid = ring->qid; 309922a84b8dSQuaker Fang 310022a84b8dSQuaker Fang tx = (iwp_tx_cmd_t *)cmd->data; 310122a84b8dSQuaker Fang tx->tx_flags = 0; 310222a84b8dSQuaker Fang 310322a84b8dSQuaker Fang if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { 310422a84b8dSQuaker Fang tx->tx_flags &= ~(LE_32(TX_CMD_FLG_ACK_MSK)); 310522a84b8dSQuaker Fang } else { 310622a84b8dSQuaker Fang tx->tx_flags |= LE_32(TX_CMD_FLG_ACK_MSK); 310722a84b8dSQuaker Fang } 310822a84b8dSQuaker Fang 310922a84b8dSQuaker Fang if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 311022a84b8dSQuaker Fang k = ieee80211_crypto_encap(ic, m); 311122a84b8dSQuaker Fang if (NULL == k) { 311222a84b8dSQuaker Fang freemsg(m); 311322a84b8dSQuaker Fang sc->sc_tx_err++; 311422a84b8dSQuaker Fang 311522a84b8dSQuaker Fang mutex_enter(&sc->sc_tx_lock); 311622a84b8dSQuaker Fang ring->queued--; 311722a84b8dSQuaker Fang if ((sc->sc_need_reschedule) && (ring->queued <= 0)) { 311822a84b8dSQuaker Fang sc->sc_need_reschedule = 0; 311922a84b8dSQuaker Fang mutex_exit(&sc->sc_tx_lock); 312022a84b8dSQuaker Fang mac_tx_update(ic->ic_mach); 312122a84b8dSQuaker Fang mutex_enter(&sc->sc_tx_lock); 312222a84b8dSQuaker Fang } 312322a84b8dSQuaker Fang mutex_exit(&sc->sc_tx_lock); 312422a84b8dSQuaker Fang 312522a84b8dSQuaker Fang err = IWP_SUCCESS; 312622a84b8dSQuaker Fang goto exit; 312722a84b8dSQuaker Fang } 312822a84b8dSQuaker Fang 312922a84b8dSQuaker Fang /* packet header may have moved, reset our local pointer */ 313022a84b8dSQuaker Fang wh = (struct ieee80211_frame *)m->b_rptr; 313122a84b8dSQuaker Fang } 313222a84b8dSQuaker Fang 313322a84b8dSQuaker Fang len = msgdsize(m); 313422a84b8dSQuaker Fang 313522a84b8dSQuaker Fang #ifdef DEBUG 313622a84b8dSQuaker Fang if (iwp_dbg_flags & IWP_DEBUG_TX) { 313722a84b8dSQuaker Fang ieee80211_dump_pkt((uint8_t *)wh, hdrlen, 0, 0); 313822a84b8dSQuaker Fang } 313922a84b8dSQuaker Fang #endif 314022a84b8dSQuaker Fang 314122a84b8dSQuaker Fang tx->rts_retry_limit = IWP_TX_RTS_RETRY_LIMIT; 314222a84b8dSQuaker Fang tx->data_retry_limit = IWP_TX_DATA_RETRY_LIMIT; 314322a84b8dSQuaker Fang 314422a84b8dSQuaker Fang /* 314522a84b8dSQuaker Fang * specific TX parameters for management frames 314622a84b8dSQuaker Fang */ 314722a84b8dSQuaker Fang if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == 314822a84b8dSQuaker Fang IEEE80211_FC0_TYPE_MGT) { 314922a84b8dSQuaker Fang /* 315022a84b8dSQuaker Fang * mgmt frames are sent at 1M 315122a84b8dSQuaker Fang */ 315222a84b8dSQuaker Fang if ((in->in_rates.ir_rates[0] & 315322a84b8dSQuaker Fang IEEE80211_RATE_VAL) != 0) { 315422a84b8dSQuaker Fang rate = in->in_rates.ir_rates[0] & IEEE80211_RATE_VAL; 315522a84b8dSQuaker Fang } else { 315622a84b8dSQuaker Fang rate = 2; 315722a84b8dSQuaker Fang } 315822a84b8dSQuaker Fang 315922a84b8dSQuaker Fang tx->tx_flags |= LE_32(TX_CMD_FLG_SEQ_CTL_MSK); 316022a84b8dSQuaker Fang 316122a84b8dSQuaker Fang /* 316222a84b8dSQuaker Fang * tell h/w to set timestamp in probe responses 316322a84b8dSQuaker Fang */ 316422a84b8dSQuaker Fang if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == 316522a84b8dSQuaker Fang IEEE80211_FC0_SUBTYPE_PROBE_RESP) { 316622a84b8dSQuaker Fang tx->tx_flags |= LE_32(TX_CMD_FLG_TSF_MSK); 316722a84b8dSQuaker Fang 316822a84b8dSQuaker Fang tx->data_retry_limit = 3; 316922a84b8dSQuaker Fang if (tx->data_retry_limit < tx->rts_retry_limit) { 317022a84b8dSQuaker Fang tx->rts_retry_limit = tx->data_retry_limit; 317122a84b8dSQuaker Fang } 317222a84b8dSQuaker Fang } 317322a84b8dSQuaker Fang 317422a84b8dSQuaker Fang if (((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == 317522a84b8dSQuaker Fang IEEE80211_FC0_SUBTYPE_ASSOC_REQ) || 317622a84b8dSQuaker Fang ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == 317722a84b8dSQuaker Fang IEEE80211_FC0_SUBTYPE_REASSOC_REQ)) { 317822a84b8dSQuaker Fang tx->timeout.pm_frame_timeout = LE_16(3); 317922a84b8dSQuaker Fang } else { 318022a84b8dSQuaker Fang tx->timeout.pm_frame_timeout = LE_16(2); 318122a84b8dSQuaker Fang } 318222a84b8dSQuaker Fang 318322a84b8dSQuaker Fang } else { 318422a84b8dSQuaker Fang /* 318522a84b8dSQuaker Fang * do it here for the software way rate scaling. 318622a84b8dSQuaker Fang * later for rate scaling in hardware. 318722a84b8dSQuaker Fang * 318822a84b8dSQuaker Fang * now the txrate is determined in tx cmd flags, set to the 318922a84b8dSQuaker Fang * max value 54M for 11g and 11M for 11b originally. 319022a84b8dSQuaker Fang */ 319122a84b8dSQuaker Fang if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) { 319222a84b8dSQuaker Fang rate = ic->ic_fixed_rate; 319322a84b8dSQuaker Fang } else { 319422a84b8dSQuaker Fang if ((in->in_rates.ir_rates[in->in_txrate] & 319522a84b8dSQuaker Fang IEEE80211_RATE_VAL) != 0) { 319622a84b8dSQuaker Fang rate = in->in_rates. 319722a84b8dSQuaker Fang ir_rates[in->in_txrate] & 319822a84b8dSQuaker Fang IEEE80211_RATE_VAL; 319922a84b8dSQuaker Fang } 320022a84b8dSQuaker Fang } 320122a84b8dSQuaker Fang 320222a84b8dSQuaker Fang tx->tx_flags |= LE_32(TX_CMD_FLG_SEQ_CTL_MSK); 320322a84b8dSQuaker Fang 320422a84b8dSQuaker Fang tx->timeout.pm_frame_timeout = 0; 320522a84b8dSQuaker Fang } 320622a84b8dSQuaker Fang 320722a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_TX, "iwp_send(): " 320822a84b8dSQuaker Fang "tx rate[%d of %d] = %x", 320922a84b8dSQuaker Fang in->in_txrate, in->in_rates.ir_nrates, rate)); 321022a84b8dSQuaker Fang 321122a84b8dSQuaker Fang len0 = roundup(4 + sizeof (iwp_tx_cmd_t) + hdrlen, 4); 321222a84b8dSQuaker Fang if (len0 != (4 + sizeof (iwp_tx_cmd_t) + hdrlen)) { 321322a84b8dSQuaker Fang tx->tx_flags |= LE_32(TX_CMD_FLG_MH_PAD_MSK); 321422a84b8dSQuaker Fang } 321522a84b8dSQuaker Fang 321622a84b8dSQuaker Fang /* 321722a84b8dSQuaker Fang * retrieve destination node's id 321822a84b8dSQuaker Fang */ 321922a84b8dSQuaker Fang if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { 322022a84b8dSQuaker Fang tx->sta_id = IWP_BROADCAST_ID; 322122a84b8dSQuaker Fang } else { 322222a84b8dSQuaker Fang tx->sta_id = IWP_AP_ID; 322322a84b8dSQuaker Fang } 322422a84b8dSQuaker Fang 322522a84b8dSQuaker Fang if (2 == rate || 4 == rate || 11 == rate || 22 == rate) { 322622a84b8dSQuaker Fang masks |= RATE_MCS_CCK_MSK; 322722a84b8dSQuaker Fang } 322822a84b8dSQuaker Fang 322922a84b8dSQuaker Fang masks |= RATE_MCS_ANT_B_MSK; 323022a84b8dSQuaker Fang tx->rate.r.rate_n_flags = LE_32(iwp_rate_to_plcp(rate) | masks); 323122a84b8dSQuaker Fang 323222a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_TX, "iwp_send(): " 323322a84b8dSQuaker Fang "tx flag = %x", 323422a84b8dSQuaker Fang tx->tx_flags)); 323522a84b8dSQuaker Fang 323622a84b8dSQuaker Fang tx->stop_time.life_time = LE_32(0xffffffff); 323722a84b8dSQuaker Fang 323822a84b8dSQuaker Fang tx->len = LE_16(len); 323922a84b8dSQuaker Fang 324022a84b8dSQuaker Fang tx->dram_lsb_ptr = 324122a84b8dSQuaker Fang LE_32(data->paddr_cmd + 4 + offsetof(iwp_tx_cmd_t, scratch)); 324222a84b8dSQuaker Fang tx->dram_msb_ptr = 0; 324322a84b8dSQuaker Fang tx->driver_txop = 0; 324422a84b8dSQuaker Fang tx->next_frame_len = 0; 324522a84b8dSQuaker Fang 324622a84b8dSQuaker Fang (void) memcpy(tx + 1, m->b_rptr, hdrlen); 324722a84b8dSQuaker Fang m->b_rptr += hdrlen; 324822a84b8dSQuaker Fang (void) memcpy(data->dma_data.mem_va, m->b_rptr, len - hdrlen); 324922a84b8dSQuaker Fang 325022a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_TX, "iwp_send(): " 325122a84b8dSQuaker Fang "sending data: qid=%d idx=%d len=%d", 325222a84b8dSQuaker Fang ring->qid, ring->cur, len)); 325322a84b8dSQuaker Fang 325422a84b8dSQuaker Fang /* 325522a84b8dSQuaker Fang * first segment includes the tx cmd plus the 802.11 header, 325622a84b8dSQuaker Fang * the second includes the remaining of the 802.11 frame. 325722a84b8dSQuaker Fang */ 325822a84b8dSQuaker Fang mutex_enter(&sc->sc_tx_lock); 325922a84b8dSQuaker Fang 326022a84b8dSQuaker Fang cmd->hdr.idx = ring->desc_cur; 326122a84b8dSQuaker Fang 326222a84b8dSQuaker Fang desc_data = &ring->data[ring->desc_cur]; 326322a84b8dSQuaker Fang desc = desc_data->desc; 326422a84b8dSQuaker Fang bzero(desc, sizeof (*desc)); 326522a84b8dSQuaker Fang desc->val0 = 2 << 24; 326622a84b8dSQuaker Fang desc->pa[0].tb1_addr = data->paddr_cmd; 326722a84b8dSQuaker Fang desc->pa[0].val1 = ((len0 << 4) & 0xfff0) | 326822a84b8dSQuaker Fang ((data->dma_data.cookie.dmac_address & 0xffff) << 16); 326922a84b8dSQuaker Fang desc->pa[0].val2 = 327022a84b8dSQuaker Fang ((data->dma_data.cookie.dmac_address & 0xffff0000) >> 16) | 327122a84b8dSQuaker Fang ((len - hdrlen) << 20); 327222a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_TX, "iwp_send(): " 327322a84b8dSQuaker Fang "phy addr1 = 0x%x phy addr2 = 0x%x " 327422a84b8dSQuaker Fang "len1 = 0x%x, len2 = 0x%x val1 = 0x%x val2 = 0x%x", 327522a84b8dSQuaker Fang data->paddr_cmd, data->dma_data.cookie.dmac_address, 327622a84b8dSQuaker Fang len0, len - hdrlen, desc->pa[0].val1, desc->pa[0].val2)); 327722a84b8dSQuaker Fang 327822a84b8dSQuaker Fang /* 327922a84b8dSQuaker Fang * kick ring 328022a84b8dSQuaker Fang */ 328122a84b8dSQuaker Fang s_id = tx->sta_id; 328222a84b8dSQuaker Fang 328322a84b8dSQuaker Fang sc->sc_shared->queues_byte_cnt_tbls[ring->qid]. 328422a84b8dSQuaker Fang tfd_offset[ring->desc_cur].val = 328522a84b8dSQuaker Fang (8 + len) | (s_id << 12); 328622a84b8dSQuaker Fang if (ring->desc_cur < IWP_MAX_WIN_SIZE) { 328722a84b8dSQuaker Fang sc->sc_shared->queues_byte_cnt_tbls[ring->qid]. 328822a84b8dSQuaker Fang tfd_offset[IWP_QUEUE_SIZE + ring->desc_cur].val = 328922a84b8dSQuaker Fang (8 + len) | (s_id << 12); 329022a84b8dSQuaker Fang } 329122a84b8dSQuaker Fang 329222a84b8dSQuaker Fang IWP_DMA_SYNC(data->dma_data, DDI_DMA_SYNC_FORDEV); 329322a84b8dSQuaker Fang IWP_DMA_SYNC(ring->dma_desc, DDI_DMA_SYNC_FORDEV); 329422a84b8dSQuaker Fang 329522a84b8dSQuaker Fang ring->desc_cur = (ring->desc_cur + 1) % ring->count; 329622a84b8dSQuaker Fang IWP_WRITE(sc, HBUS_TARG_WRPTR, ring->qid << 8 | ring->desc_cur); 329722a84b8dSQuaker Fang 329822a84b8dSQuaker Fang mutex_exit(&sc->sc_tx_lock); 329922a84b8dSQuaker Fang freemsg(m); 330022a84b8dSQuaker Fang 330122a84b8dSQuaker Fang /* 330222a84b8dSQuaker Fang * release node reference 330322a84b8dSQuaker Fang */ 330422a84b8dSQuaker Fang ieee80211_free_node(in); 330522a84b8dSQuaker Fang 330622a84b8dSQuaker Fang ic->ic_stats.is_tx_bytes += len; 330722a84b8dSQuaker Fang ic->ic_stats.is_tx_frags++; 330822a84b8dSQuaker Fang 330922a84b8dSQuaker Fang mutex_enter(&sc->sc_mt_lock); 331022a84b8dSQuaker Fang if (0 == sc->sc_tx_timer) { 331122a84b8dSQuaker Fang sc->sc_tx_timer = 4; 331222a84b8dSQuaker Fang } 331322a84b8dSQuaker Fang mutex_exit(&sc->sc_mt_lock); 331422a84b8dSQuaker Fang 331522a84b8dSQuaker Fang exit: 331622a84b8dSQuaker Fang return (err); 331722a84b8dSQuaker Fang } 331822a84b8dSQuaker Fang 331922a84b8dSQuaker Fang /* 332022a84b8dSQuaker Fang * invoked by GLD to deal with IOCTL affaires 332122a84b8dSQuaker Fang */ 332222a84b8dSQuaker Fang static void 332322a84b8dSQuaker Fang iwp_m_ioctl(void* arg, queue_t *wq, mblk_t *mp) 332422a84b8dSQuaker Fang { 332522a84b8dSQuaker Fang iwp_sc_t *sc; 332622a84b8dSQuaker Fang ieee80211com_t *ic; 332722a84b8dSQuaker Fang int err = EINVAL; 332822a84b8dSQuaker Fang 332922a84b8dSQuaker Fang if (NULL == arg) { 333022a84b8dSQuaker Fang return; 333122a84b8dSQuaker Fang } 333222a84b8dSQuaker Fang sc = (iwp_sc_t *)arg; 333322a84b8dSQuaker Fang ic = &sc->sc_ic; 333422a84b8dSQuaker Fang 333522a84b8dSQuaker Fang err = ieee80211_ioctl(ic, wq, mp); 333622a84b8dSQuaker Fang if (ENETRESET == err) { 333722a84b8dSQuaker Fang /* 333822a84b8dSQuaker Fang * This is special for the hidden AP connection. 333922a84b8dSQuaker Fang * In any case, we should make sure only one 'scan' 334022a84b8dSQuaker Fang * in the driver for a 'connect' CLI command. So 334122a84b8dSQuaker Fang * when connecting to a hidden AP, the scan is just 334222a84b8dSQuaker Fang * sent out to the air when we know the desired 334322a84b8dSQuaker Fang * essid of the AP we want to connect. 334422a84b8dSQuaker Fang */ 334522a84b8dSQuaker Fang if (ic->ic_des_esslen) { 334622a84b8dSQuaker Fang if (sc->sc_flags & IWP_F_RUNNING) { 334722a84b8dSQuaker Fang iwp_m_stop(sc); 334822a84b8dSQuaker Fang (void) iwp_m_start(sc); 334922a84b8dSQuaker Fang (void) ieee80211_new_state(ic, 335022a84b8dSQuaker Fang IEEE80211_S_SCAN, -1); 335122a84b8dSQuaker Fang } 335222a84b8dSQuaker Fang } 335322a84b8dSQuaker Fang } 335422a84b8dSQuaker Fang } 335522a84b8dSQuaker Fang 335622a84b8dSQuaker Fang /* 335722a84b8dSQuaker Fang * Call back functions for get/set proporty 335822a84b8dSQuaker Fang */ 335922a84b8dSQuaker Fang static int 336022a84b8dSQuaker Fang iwp_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 3361*0dc2366fSVenugopal Iyer uint_t wldp_length, void *wldp_buf) 336222a84b8dSQuaker Fang { 336322a84b8dSQuaker Fang iwp_sc_t *sc; 336422a84b8dSQuaker Fang int err = EINVAL; 336522a84b8dSQuaker Fang 336622a84b8dSQuaker Fang if (NULL == arg) { 336722a84b8dSQuaker Fang return (EINVAL); 336822a84b8dSQuaker Fang } 336922a84b8dSQuaker Fang sc = (iwp_sc_t *)arg; 337022a84b8dSQuaker Fang 337122a84b8dSQuaker Fang err = ieee80211_getprop(&sc->sc_ic, pr_name, wldp_pr_num, 3372*0dc2366fSVenugopal Iyer wldp_length, wldp_buf); 337322a84b8dSQuaker Fang 337422a84b8dSQuaker Fang return (err); 337522a84b8dSQuaker Fang } 337622a84b8dSQuaker Fang 3377*0dc2366fSVenugopal Iyer static void 3378*0dc2366fSVenugopal Iyer iwp_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 3379*0dc2366fSVenugopal Iyer mac_prop_info_handle_t prh) 3380*0dc2366fSVenugopal Iyer { 3381*0dc2366fSVenugopal Iyer iwp_sc_t *sc; 3382*0dc2366fSVenugopal Iyer 3383*0dc2366fSVenugopal Iyer sc = (iwp_sc_t *)arg; 3384*0dc2366fSVenugopal Iyer ieee80211_propinfo(&sc->sc_ic, pr_name, wldp_pr_num, prh); 3385*0dc2366fSVenugopal Iyer } 3386*0dc2366fSVenugopal Iyer 338722a84b8dSQuaker Fang static int 338822a84b8dSQuaker Fang iwp_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 338922a84b8dSQuaker Fang uint_t wldp_length, const void *wldp_buf) 339022a84b8dSQuaker Fang { 339122a84b8dSQuaker Fang iwp_sc_t *sc; 339222a84b8dSQuaker Fang ieee80211com_t *ic; 339322a84b8dSQuaker Fang int err = EINVAL; 339422a84b8dSQuaker Fang 339522a84b8dSQuaker Fang if (NULL == arg) { 339622a84b8dSQuaker Fang return (EINVAL); 339722a84b8dSQuaker Fang } 339822a84b8dSQuaker Fang sc = (iwp_sc_t *)arg; 339922a84b8dSQuaker Fang ic = &sc->sc_ic; 340022a84b8dSQuaker Fang 340122a84b8dSQuaker Fang err = ieee80211_setprop(ic, pr_name, wldp_pr_num, wldp_length, 340222a84b8dSQuaker Fang wldp_buf); 340322a84b8dSQuaker Fang 340422a84b8dSQuaker Fang if (err == ENETRESET) { 340522a84b8dSQuaker Fang if (ic->ic_des_esslen) { 340622a84b8dSQuaker Fang if (sc->sc_flags & IWP_F_RUNNING) { 340722a84b8dSQuaker Fang iwp_m_stop(sc); 340822a84b8dSQuaker Fang (void) iwp_m_start(sc); 340922a84b8dSQuaker Fang (void) ieee80211_new_state(ic, 341022a84b8dSQuaker Fang IEEE80211_S_SCAN, -1); 341122a84b8dSQuaker Fang } 341222a84b8dSQuaker Fang } 341322a84b8dSQuaker Fang err = 0; 341422a84b8dSQuaker Fang } 341522a84b8dSQuaker Fang return (err); 341622a84b8dSQuaker Fang } 341722a84b8dSQuaker Fang 341822a84b8dSQuaker Fang /* 341922a84b8dSQuaker Fang * invoked by GLD supply statistics NIC and driver 342022a84b8dSQuaker Fang */ 342122a84b8dSQuaker Fang static int 342222a84b8dSQuaker Fang iwp_m_stat(void *arg, uint_t stat, uint64_t *val) 342322a84b8dSQuaker Fang { 342422a84b8dSQuaker Fang iwp_sc_t *sc; 342522a84b8dSQuaker Fang ieee80211com_t *ic; 342622a84b8dSQuaker Fang ieee80211_node_t *in; 342722a84b8dSQuaker Fang 342822a84b8dSQuaker Fang if (NULL == arg) { 342922a84b8dSQuaker Fang return (EINVAL); 343022a84b8dSQuaker Fang } 343122a84b8dSQuaker Fang sc = (iwp_sc_t *)arg; 343222a84b8dSQuaker Fang ic = &sc->sc_ic; 343322a84b8dSQuaker Fang 343422a84b8dSQuaker Fang mutex_enter(&sc->sc_glock); 343522a84b8dSQuaker Fang 343622a84b8dSQuaker Fang switch (stat) { 343722a84b8dSQuaker Fang case MAC_STAT_IFSPEED: 343822a84b8dSQuaker Fang in = ic->ic_bss; 343922a84b8dSQuaker Fang *val = ((IEEE80211_FIXED_RATE_NONE == ic->ic_fixed_rate) ? 344022a84b8dSQuaker Fang IEEE80211_RATE(in->in_txrate) : 344122a84b8dSQuaker Fang ic->ic_fixed_rate) / 2 * 1000000; 344222a84b8dSQuaker Fang break; 344322a84b8dSQuaker Fang case MAC_STAT_NOXMTBUF: 344422a84b8dSQuaker Fang *val = sc->sc_tx_nobuf; 344522a84b8dSQuaker Fang break; 344622a84b8dSQuaker Fang case MAC_STAT_NORCVBUF: 344722a84b8dSQuaker Fang *val = sc->sc_rx_nobuf; 344822a84b8dSQuaker Fang break; 344922a84b8dSQuaker Fang case MAC_STAT_IERRORS: 345022a84b8dSQuaker Fang *val = sc->sc_rx_err; 345122a84b8dSQuaker Fang break; 345222a84b8dSQuaker Fang case MAC_STAT_RBYTES: 345322a84b8dSQuaker Fang *val = ic->ic_stats.is_rx_bytes; 345422a84b8dSQuaker Fang break; 345522a84b8dSQuaker Fang case MAC_STAT_IPACKETS: 345622a84b8dSQuaker Fang *val = ic->ic_stats.is_rx_frags; 345722a84b8dSQuaker Fang break; 345822a84b8dSQuaker Fang case MAC_STAT_OBYTES: 345922a84b8dSQuaker Fang *val = ic->ic_stats.is_tx_bytes; 346022a84b8dSQuaker Fang break; 346122a84b8dSQuaker Fang case MAC_STAT_OPACKETS: 346222a84b8dSQuaker Fang *val = ic->ic_stats.is_tx_frags; 346322a84b8dSQuaker Fang break; 346422a84b8dSQuaker Fang case MAC_STAT_OERRORS: 346522a84b8dSQuaker Fang case WIFI_STAT_TX_FAILED: 346622a84b8dSQuaker Fang *val = sc->sc_tx_err; 346722a84b8dSQuaker Fang break; 346822a84b8dSQuaker Fang case WIFI_STAT_TX_RETRANS: 346922a84b8dSQuaker Fang *val = sc->sc_tx_retries; 347022a84b8dSQuaker Fang break; 347122a84b8dSQuaker Fang case WIFI_STAT_FCS_ERRORS: 347222a84b8dSQuaker Fang case WIFI_STAT_WEP_ERRORS: 347322a84b8dSQuaker Fang case WIFI_STAT_TX_FRAGS: 347422a84b8dSQuaker Fang case WIFI_STAT_MCAST_TX: 347522a84b8dSQuaker Fang case WIFI_STAT_RTS_SUCCESS: 347622a84b8dSQuaker Fang case WIFI_STAT_RTS_FAILURE: 347722a84b8dSQuaker Fang case WIFI_STAT_ACK_FAILURE: 347822a84b8dSQuaker Fang case WIFI_STAT_RX_FRAGS: 347922a84b8dSQuaker Fang case WIFI_STAT_MCAST_RX: 348022a84b8dSQuaker Fang case WIFI_STAT_RX_DUPS: 348122a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 348222a84b8dSQuaker Fang return (ieee80211_stat(ic, stat, val)); 348322a84b8dSQuaker Fang default: 348422a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 348522a84b8dSQuaker Fang return (ENOTSUP); 348622a84b8dSQuaker Fang } 348722a84b8dSQuaker Fang 348822a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 348922a84b8dSQuaker Fang 349022a84b8dSQuaker Fang return (IWP_SUCCESS); 349122a84b8dSQuaker Fang 349222a84b8dSQuaker Fang } 349322a84b8dSQuaker Fang 349422a84b8dSQuaker Fang /* 349522a84b8dSQuaker Fang * invoked by GLD to start or open NIC 349622a84b8dSQuaker Fang */ 349722a84b8dSQuaker Fang static int 349822a84b8dSQuaker Fang iwp_m_start(void *arg) 349922a84b8dSQuaker Fang { 350022a84b8dSQuaker Fang iwp_sc_t *sc; 350122a84b8dSQuaker Fang ieee80211com_t *ic; 350222a84b8dSQuaker Fang int err = IWP_FAIL; 350322a84b8dSQuaker Fang 350422a84b8dSQuaker Fang if (NULL == arg) { 350522a84b8dSQuaker Fang return (EINVAL); 350622a84b8dSQuaker Fang } 350722a84b8dSQuaker Fang sc = (iwp_sc_t *)arg; 350822a84b8dSQuaker Fang ic = &sc->sc_ic; 350922a84b8dSQuaker Fang 351022a84b8dSQuaker Fang err = iwp_init(sc); 351122a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 351222a84b8dSQuaker Fang /* 351322a84b8dSQuaker Fang * The hw init err(eg. RF is OFF). Return Success to make 351422a84b8dSQuaker Fang * the 'plumb' succeed. The iwp_thread() tries to re-init 351522a84b8dSQuaker Fang * background. 351622a84b8dSQuaker Fang */ 351722a84b8dSQuaker Fang atomic_or_32(&sc->sc_flags, IWP_F_HW_ERR_RECOVER); 351822a84b8dSQuaker Fang return (IWP_SUCCESS); 351922a84b8dSQuaker Fang } 352022a84b8dSQuaker Fang 352122a84b8dSQuaker Fang ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 352222a84b8dSQuaker Fang 352322a84b8dSQuaker Fang atomic_or_32(&sc->sc_flags, IWP_F_RUNNING); 352422a84b8dSQuaker Fang 352522a84b8dSQuaker Fang return (IWP_SUCCESS); 352622a84b8dSQuaker Fang } 352722a84b8dSQuaker Fang 352822a84b8dSQuaker Fang /* 352922a84b8dSQuaker Fang * invoked by GLD to stop or down NIC 353022a84b8dSQuaker Fang */ 353122a84b8dSQuaker Fang static void 353222a84b8dSQuaker Fang iwp_m_stop(void *arg) 353322a84b8dSQuaker Fang { 353422a84b8dSQuaker Fang iwp_sc_t *sc; 353522a84b8dSQuaker Fang ieee80211com_t *ic; 353622a84b8dSQuaker Fang 353722a84b8dSQuaker Fang if (NULL == arg) { 353822a84b8dSQuaker Fang return; 353922a84b8dSQuaker Fang } 354022a84b8dSQuaker Fang sc = (iwp_sc_t *)arg; 354122a84b8dSQuaker Fang ic = &sc->sc_ic; 354222a84b8dSQuaker Fang 354322a84b8dSQuaker Fang iwp_stop(sc); 354422a84b8dSQuaker Fang 354522a84b8dSQuaker Fang /* 354622a84b8dSQuaker Fang * release buffer for calibration 354722a84b8dSQuaker Fang */ 354822a84b8dSQuaker Fang iwp_release_calib_buffer(sc); 354922a84b8dSQuaker Fang 355022a84b8dSQuaker Fang ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 355122a84b8dSQuaker Fang 355222a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_HW_ERR_RECOVER); 355322a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_RATE_AUTO_CTL); 355422a84b8dSQuaker Fang 355522a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_RUNNING); 355622a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_SCANNING); 355722a84b8dSQuaker Fang } 355822a84b8dSQuaker Fang 355922a84b8dSQuaker Fang /* 356022a84b8dSQuaker Fang * invoked by GLD to configure NIC 356122a84b8dSQuaker Fang */ 356222a84b8dSQuaker Fang static int 356322a84b8dSQuaker Fang iwp_m_unicst(void *arg, const uint8_t *macaddr) 356422a84b8dSQuaker Fang { 356522a84b8dSQuaker Fang iwp_sc_t *sc; 356622a84b8dSQuaker Fang ieee80211com_t *ic; 356722a84b8dSQuaker Fang int err = IWP_SUCCESS; 356822a84b8dSQuaker Fang 356922a84b8dSQuaker Fang if (NULL == arg) { 357022a84b8dSQuaker Fang return (EINVAL); 357122a84b8dSQuaker Fang } 357222a84b8dSQuaker Fang sc = (iwp_sc_t *)arg; 357322a84b8dSQuaker Fang ic = &sc->sc_ic; 357422a84b8dSQuaker Fang 357522a84b8dSQuaker Fang if (!IEEE80211_ADDR_EQ(ic->ic_macaddr, macaddr)) { 357622a84b8dSQuaker Fang IEEE80211_ADDR_COPY(ic->ic_macaddr, macaddr); 357722a84b8dSQuaker Fang mutex_enter(&sc->sc_glock); 357822a84b8dSQuaker Fang err = iwp_config(sc); 357922a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 358022a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 358122a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_m_unicst(): " 358222a84b8dSQuaker Fang "failed to configure device\n"); 358322a84b8dSQuaker Fang goto fail; 358422a84b8dSQuaker Fang } 358522a84b8dSQuaker Fang } 358622a84b8dSQuaker Fang 358722a84b8dSQuaker Fang return (err); 358822a84b8dSQuaker Fang 358922a84b8dSQuaker Fang fail: 359022a84b8dSQuaker Fang return (err); 359122a84b8dSQuaker Fang } 359222a84b8dSQuaker Fang 359322a84b8dSQuaker Fang /* ARGSUSED */ 359422a84b8dSQuaker Fang static int 359522a84b8dSQuaker Fang iwp_m_multicst(void *arg, boolean_t add, const uint8_t *m) 359622a84b8dSQuaker Fang { 359722a84b8dSQuaker Fang return (IWP_SUCCESS); 359822a84b8dSQuaker Fang } 359922a84b8dSQuaker Fang 360022a84b8dSQuaker Fang /* ARGSUSED */ 360122a84b8dSQuaker Fang static int 360222a84b8dSQuaker Fang iwp_m_promisc(void *arg, boolean_t on) 360322a84b8dSQuaker Fang { 360422a84b8dSQuaker Fang return (IWP_SUCCESS); 360522a84b8dSQuaker Fang } 360622a84b8dSQuaker Fang 360722a84b8dSQuaker Fang /* 360822a84b8dSQuaker Fang * kernel thread to deal with exceptional situation 360922a84b8dSQuaker Fang */ 361022a84b8dSQuaker Fang static void 361122a84b8dSQuaker Fang iwp_thread(iwp_sc_t *sc) 361222a84b8dSQuaker Fang { 361322a84b8dSQuaker Fang ieee80211com_t *ic = &sc->sc_ic; 361422a84b8dSQuaker Fang clock_t clk; 361522a84b8dSQuaker Fang int err, n = 0, timeout = 0; 361622a84b8dSQuaker Fang uint32_t tmp; 361722a84b8dSQuaker Fang #ifdef DEBUG 361822a84b8dSQuaker Fang int times = 0; 361922a84b8dSQuaker Fang #endif 362022a84b8dSQuaker Fang 362122a84b8dSQuaker Fang while (sc->sc_mf_thread_switch) { 362222a84b8dSQuaker Fang tmp = IWP_READ(sc, CSR_GP_CNTRL); 362322a84b8dSQuaker Fang if (tmp & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) { 362422a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_RADIO_OFF); 362522a84b8dSQuaker Fang } else { 362622a84b8dSQuaker Fang atomic_or_32(&sc->sc_flags, IWP_F_RADIO_OFF); 362722a84b8dSQuaker Fang } 362822a84b8dSQuaker Fang 362922a84b8dSQuaker Fang /* 363022a84b8dSQuaker Fang * If in SUSPEND or the RF is OFF, do nothing. 363122a84b8dSQuaker Fang */ 363222a84b8dSQuaker Fang if (sc->sc_flags & IWP_F_RADIO_OFF) { 363322a84b8dSQuaker Fang delay(drv_usectohz(100000)); 363422a84b8dSQuaker Fang continue; 363522a84b8dSQuaker Fang } 363622a84b8dSQuaker Fang 363722a84b8dSQuaker Fang /* 363822a84b8dSQuaker Fang * recovery fatal error 363922a84b8dSQuaker Fang */ 364022a84b8dSQuaker Fang if (ic->ic_mach && 364122a84b8dSQuaker Fang (sc->sc_flags & IWP_F_HW_ERR_RECOVER)) { 364222a84b8dSQuaker Fang 364322a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_FW, "iwp_thread(): " 364422a84b8dSQuaker Fang "try to recover fatal hw error: %d\n", times++)); 364522a84b8dSQuaker Fang 364622a84b8dSQuaker Fang iwp_stop(sc); 364722a84b8dSQuaker Fang 364822a84b8dSQuaker Fang if (IWP_CHK_FAST_RECOVER(sc)) { 364922a84b8dSQuaker Fang /* save runtime configuration */ 365022a84b8dSQuaker Fang bcopy(&sc->sc_config, &sc->sc_config_save, 365122a84b8dSQuaker Fang sizeof (sc->sc_config)); 365222a84b8dSQuaker Fang } else { 365322a84b8dSQuaker Fang ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 365422a84b8dSQuaker Fang delay(drv_usectohz(2000000 + n*500000)); 365522a84b8dSQuaker Fang } 365622a84b8dSQuaker Fang 365722a84b8dSQuaker Fang err = iwp_init(sc); 365822a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 365922a84b8dSQuaker Fang n++; 366022a84b8dSQuaker Fang if (n < 20) { 366122a84b8dSQuaker Fang continue; 366222a84b8dSQuaker Fang } 366322a84b8dSQuaker Fang } 366422a84b8dSQuaker Fang 366522a84b8dSQuaker Fang n = 0; 366622a84b8dSQuaker Fang if (!err) { 366722a84b8dSQuaker Fang atomic_or_32(&sc->sc_flags, IWP_F_RUNNING); 366822a84b8dSQuaker Fang } 366922a84b8dSQuaker Fang 367022a84b8dSQuaker Fang 367122a84b8dSQuaker Fang if (!IWP_CHK_FAST_RECOVER(sc) || 367222a84b8dSQuaker Fang iwp_fast_recover(sc) != IWP_SUCCESS) { 367322a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, 367422a84b8dSQuaker Fang ~IWP_F_HW_ERR_RECOVER); 367522a84b8dSQuaker Fang 367622a84b8dSQuaker Fang delay(drv_usectohz(2000000)); 367722a84b8dSQuaker Fang if (sc->sc_ostate != IEEE80211_S_INIT) { 367822a84b8dSQuaker Fang ieee80211_new_state(ic, 367922a84b8dSQuaker Fang IEEE80211_S_SCAN, 0); 368022a84b8dSQuaker Fang } 368122a84b8dSQuaker Fang } 368222a84b8dSQuaker Fang } 368322a84b8dSQuaker Fang 368422a84b8dSQuaker Fang if (ic->ic_mach && 368522a84b8dSQuaker Fang (sc->sc_flags & IWP_F_SCANNING) && sc->sc_scan_pending) { 368622a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_SCAN, "iwp_thread(): " 368722a84b8dSQuaker Fang "wait for probe response\n")); 368822a84b8dSQuaker Fang 368922a84b8dSQuaker Fang sc->sc_scan_pending--; 369022a84b8dSQuaker Fang delay(drv_usectohz(200000)); 369122a84b8dSQuaker Fang ieee80211_next_scan(ic); 369222a84b8dSQuaker Fang } 369322a84b8dSQuaker Fang 369422a84b8dSQuaker Fang /* 369522a84b8dSQuaker Fang * rate ctl 369622a84b8dSQuaker Fang */ 369722a84b8dSQuaker Fang if (ic->ic_mach && 369822a84b8dSQuaker Fang (sc->sc_flags & IWP_F_RATE_AUTO_CTL)) { 369922a84b8dSQuaker Fang clk = ddi_get_lbolt(); 370022a84b8dSQuaker Fang if (clk > sc->sc_clk + drv_usectohz(1000000)) { 370122a84b8dSQuaker Fang iwp_amrr_timeout(sc); 370222a84b8dSQuaker Fang } 370322a84b8dSQuaker Fang } 370422a84b8dSQuaker Fang 370522a84b8dSQuaker Fang delay(drv_usectohz(100000)); 370622a84b8dSQuaker Fang 370722a84b8dSQuaker Fang mutex_enter(&sc->sc_mt_lock); 370822a84b8dSQuaker Fang if (sc->sc_tx_timer) { 370922a84b8dSQuaker Fang timeout++; 371022a84b8dSQuaker Fang if (10 == timeout) { 371122a84b8dSQuaker Fang sc->sc_tx_timer--; 371222a84b8dSQuaker Fang if (0 == sc->sc_tx_timer) { 371322a84b8dSQuaker Fang atomic_or_32(&sc->sc_flags, 371422a84b8dSQuaker Fang IWP_F_HW_ERR_RECOVER); 371522a84b8dSQuaker Fang sc->sc_ostate = IEEE80211_S_RUN; 371622a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_FW, "iwp_thread(): " 371722a84b8dSQuaker Fang "try to recover from " 371822a84b8dSQuaker Fang "send fail\n")); 371922a84b8dSQuaker Fang } 372022a84b8dSQuaker Fang timeout = 0; 372122a84b8dSQuaker Fang } 372222a84b8dSQuaker Fang } 372322a84b8dSQuaker Fang mutex_exit(&sc->sc_mt_lock); 372422a84b8dSQuaker Fang } 372522a84b8dSQuaker Fang 372622a84b8dSQuaker Fang mutex_enter(&sc->sc_mt_lock); 372722a84b8dSQuaker Fang sc->sc_mf_thread = NULL; 372822a84b8dSQuaker Fang cv_signal(&sc->sc_mt_cv); 372922a84b8dSQuaker Fang mutex_exit(&sc->sc_mt_lock); 373022a84b8dSQuaker Fang } 373122a84b8dSQuaker Fang 373222a84b8dSQuaker Fang 373322a84b8dSQuaker Fang /* 373422a84b8dSQuaker Fang * Send a command to the ucode. 373522a84b8dSQuaker Fang */ 373622a84b8dSQuaker Fang static int 373722a84b8dSQuaker Fang iwp_cmd(iwp_sc_t *sc, int code, const void *buf, int size, int async) 373822a84b8dSQuaker Fang { 373922a84b8dSQuaker Fang iwp_tx_ring_t *ring = &sc->sc_txq[IWP_CMD_QUEUE_NUM]; 374022a84b8dSQuaker Fang iwp_tx_desc_t *desc; 374122a84b8dSQuaker Fang iwp_cmd_t *cmd; 374222a84b8dSQuaker Fang 374322a84b8dSQuaker Fang ASSERT(size <= sizeof (cmd->data)); 374422a84b8dSQuaker Fang ASSERT(mutex_owned(&sc->sc_glock)); 374522a84b8dSQuaker Fang 374622a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_CMD, "iwp_cmd() " 374722a84b8dSQuaker Fang "code[%d]", code)); 374822a84b8dSQuaker Fang desc = ring->data[ring->cur].desc; 374922a84b8dSQuaker Fang cmd = ring->data[ring->cur].cmd; 375022a84b8dSQuaker Fang 375122a84b8dSQuaker Fang cmd->hdr.type = (uint8_t)code; 375222a84b8dSQuaker Fang cmd->hdr.flags = 0; 375322a84b8dSQuaker Fang cmd->hdr.qid = ring->qid; 375422a84b8dSQuaker Fang cmd->hdr.idx = ring->cur; 375522a84b8dSQuaker Fang (void) memcpy(cmd->data, buf, size); 375622a84b8dSQuaker Fang (void) memset(desc, 0, sizeof (*desc)); 375722a84b8dSQuaker Fang 375822a84b8dSQuaker Fang desc->val0 = 1 << 24; 375922a84b8dSQuaker Fang desc->pa[0].tb1_addr = 376022a84b8dSQuaker Fang (uint32_t)(ring->data[ring->cur].paddr_cmd & 0xffffffff); 376122a84b8dSQuaker Fang desc->pa[0].val1 = ((4 + size) << 4) & 0xfff0; 376222a84b8dSQuaker Fang 376322a84b8dSQuaker Fang if (async) { 376422a84b8dSQuaker Fang sc->sc_cmd_accum++; 376522a84b8dSQuaker Fang } 376622a84b8dSQuaker Fang 376722a84b8dSQuaker Fang /* 376822a84b8dSQuaker Fang * kick cmd ring XXX 376922a84b8dSQuaker Fang */ 377022a84b8dSQuaker Fang sc->sc_shared->queues_byte_cnt_tbls[ring->qid]. 377122a84b8dSQuaker Fang tfd_offset[ring->cur].val = 8; 377222a84b8dSQuaker Fang if (ring->cur < IWP_MAX_WIN_SIZE) { 377322a84b8dSQuaker Fang sc->sc_shared->queues_byte_cnt_tbls[ring->qid]. 377422a84b8dSQuaker Fang tfd_offset[IWP_QUEUE_SIZE + ring->cur].val = 8; 377522a84b8dSQuaker Fang } 377622a84b8dSQuaker Fang ring->cur = (ring->cur + 1) % ring->count; 377722a84b8dSQuaker Fang IWP_WRITE(sc, HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); 377822a84b8dSQuaker Fang 377922a84b8dSQuaker Fang if (async) { 378022a84b8dSQuaker Fang return (IWP_SUCCESS); 378122a84b8dSQuaker Fang } else { 378222a84b8dSQuaker Fang clock_t clk; 378322a84b8dSQuaker Fang 378422a84b8dSQuaker Fang clk = ddi_get_lbolt() + drv_usectohz(2000000); 378522a84b8dSQuaker Fang while (sc->sc_cmd_flag != SC_CMD_FLG_DONE) { 378622a84b8dSQuaker Fang if (cv_timedwait(&sc->sc_cmd_cv, 378722a84b8dSQuaker Fang &sc->sc_glock, clk) < 0) { 378822a84b8dSQuaker Fang break; 378922a84b8dSQuaker Fang } 379022a84b8dSQuaker Fang } 379122a84b8dSQuaker Fang 379222a84b8dSQuaker Fang if (SC_CMD_FLG_DONE == sc->sc_cmd_flag) { 379322a84b8dSQuaker Fang sc->sc_cmd_flag = SC_CMD_FLG_NONE; 379422a84b8dSQuaker Fang return (IWP_SUCCESS); 379522a84b8dSQuaker Fang } else { 379622a84b8dSQuaker Fang sc->sc_cmd_flag = SC_CMD_FLG_NONE; 379722a84b8dSQuaker Fang return (IWP_FAIL); 379822a84b8dSQuaker Fang } 379922a84b8dSQuaker Fang } 380022a84b8dSQuaker Fang } 380122a84b8dSQuaker Fang 380222a84b8dSQuaker Fang /* 380322a84b8dSQuaker Fang * require ucode seting led of NIC 380422a84b8dSQuaker Fang */ 380522a84b8dSQuaker Fang static void 380622a84b8dSQuaker Fang iwp_set_led(iwp_sc_t *sc, uint8_t id, uint8_t off, uint8_t on) 380722a84b8dSQuaker Fang { 380822a84b8dSQuaker Fang iwp_led_cmd_t led; 380922a84b8dSQuaker Fang 381022a84b8dSQuaker Fang led.interval = LE_32(100000); /* unit: 100ms */ 381122a84b8dSQuaker Fang led.id = id; 381222a84b8dSQuaker Fang led.off = off; 381322a84b8dSQuaker Fang led.on = on; 381422a84b8dSQuaker Fang 381522a84b8dSQuaker Fang (void) iwp_cmd(sc, REPLY_LEDS_CMD, &led, sizeof (led), 1); 381622a84b8dSQuaker Fang } 381722a84b8dSQuaker Fang 381822a84b8dSQuaker Fang /* 381922a84b8dSQuaker Fang * necessary setting to NIC before authentication 382022a84b8dSQuaker Fang */ 382122a84b8dSQuaker Fang static int 382222a84b8dSQuaker Fang iwp_hw_set_before_auth(iwp_sc_t *sc) 382322a84b8dSQuaker Fang { 382422a84b8dSQuaker Fang ieee80211com_t *ic = &sc->sc_ic; 382522a84b8dSQuaker Fang ieee80211_node_t *in = ic->ic_bss; 382622a84b8dSQuaker Fang int err = IWP_FAIL; 382722a84b8dSQuaker Fang 382822a84b8dSQuaker Fang /* 382922a84b8dSQuaker Fang * update adapter's configuration according 383022a84b8dSQuaker Fang * the info of target AP 383122a84b8dSQuaker Fang */ 383222a84b8dSQuaker Fang IEEE80211_ADDR_COPY(sc->sc_config.bssid, in->in_bssid); 383322a84b8dSQuaker Fang sc->sc_config.chan = LE_16(ieee80211_chan2ieee(ic, in->in_chan)); 383422a84b8dSQuaker Fang 383522a84b8dSQuaker Fang sc->sc_config.ofdm_ht_triple_stream_basic_rates = 0; 383622a84b8dSQuaker Fang sc->sc_config.ofdm_ht_dual_stream_basic_rates = 0; 383722a84b8dSQuaker Fang sc->sc_config.ofdm_ht_single_stream_basic_rates = 0; 383822a84b8dSQuaker Fang 383922a84b8dSQuaker Fang if (IEEE80211_MODE_11B == ic->ic_curmode) { 384022a84b8dSQuaker Fang sc->sc_config.cck_basic_rates = 0x03; 384122a84b8dSQuaker Fang sc->sc_config.ofdm_basic_rates = 0; 384222a84b8dSQuaker Fang } else if ((in->in_chan != IEEE80211_CHAN_ANYC) && 384322a84b8dSQuaker Fang (IEEE80211_IS_CHAN_5GHZ(in->in_chan))) { 384422a84b8dSQuaker Fang sc->sc_config.cck_basic_rates = 0; 384522a84b8dSQuaker Fang sc->sc_config.ofdm_basic_rates = 0x15; 384622a84b8dSQuaker Fang } else { /* assume 802.11b/g */ 384722a84b8dSQuaker Fang sc->sc_config.cck_basic_rates = 0x0f; 384822a84b8dSQuaker Fang sc->sc_config.ofdm_basic_rates = 0xff; 384922a84b8dSQuaker Fang } 385022a84b8dSQuaker Fang 385122a84b8dSQuaker Fang sc->sc_config.flags &= ~LE_32(RXON_FLG_SHORT_PREAMBLE_MSK | 385222a84b8dSQuaker Fang RXON_FLG_SHORT_SLOT_MSK); 385322a84b8dSQuaker Fang 385422a84b8dSQuaker Fang if (ic->ic_flags & IEEE80211_F_SHSLOT) { 385522a84b8dSQuaker Fang sc->sc_config.flags |= LE_32(RXON_FLG_SHORT_SLOT_MSK); 385622a84b8dSQuaker Fang } else { 385722a84b8dSQuaker Fang sc->sc_config.flags &= LE_32(~RXON_FLG_SHORT_SLOT_MSK); 385822a84b8dSQuaker Fang } 385922a84b8dSQuaker Fang 386022a84b8dSQuaker Fang if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) { 386122a84b8dSQuaker Fang sc->sc_config.flags |= LE_32(RXON_FLG_SHORT_PREAMBLE_MSK); 386222a84b8dSQuaker Fang } else { 386322a84b8dSQuaker Fang sc->sc_config.flags &= LE_32(~RXON_FLG_SHORT_PREAMBLE_MSK); 386422a84b8dSQuaker Fang } 386522a84b8dSQuaker Fang 386622a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_80211, "iwp_hw_set_before_auth(): " 386722a84b8dSQuaker Fang "config chan %d flags %x " 386822a84b8dSQuaker Fang "filter_flags %x cck %x ofdm %x" 386922a84b8dSQuaker Fang " bssid:%02x:%02x:%02x:%02x:%02x:%2x\n", 387022a84b8dSQuaker Fang LE_16(sc->sc_config.chan), LE_32(sc->sc_config.flags), 387122a84b8dSQuaker Fang LE_32(sc->sc_config.filter_flags), 387222a84b8dSQuaker Fang sc->sc_config.cck_basic_rates, sc->sc_config.ofdm_basic_rates, 387322a84b8dSQuaker Fang sc->sc_config.bssid[0], sc->sc_config.bssid[1], 387422a84b8dSQuaker Fang sc->sc_config.bssid[2], sc->sc_config.bssid[3], 387522a84b8dSQuaker Fang sc->sc_config.bssid[4], sc->sc_config.bssid[5])); 387622a84b8dSQuaker Fang 387722a84b8dSQuaker Fang err = iwp_cmd(sc, REPLY_RXON, &sc->sc_config, 387822a84b8dSQuaker Fang sizeof (iwp_rxon_cmd_t), 1); 387922a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 388022a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_hw_set_before_auth(): " 388122a84b8dSQuaker Fang "failed to config chan%d\n", sc->sc_config.chan); 388222a84b8dSQuaker Fang return (err); 388322a84b8dSQuaker Fang } 388422a84b8dSQuaker Fang 388522a84b8dSQuaker Fang /* 388622a84b8dSQuaker Fang * add default AP node 388722a84b8dSQuaker Fang */ 388822a84b8dSQuaker Fang err = iwp_add_ap_sta(sc); 388922a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 389022a84b8dSQuaker Fang return (err); 389122a84b8dSQuaker Fang } 389222a84b8dSQuaker Fang 389322a84b8dSQuaker Fang 389422a84b8dSQuaker Fang return (err); 389522a84b8dSQuaker Fang } 389622a84b8dSQuaker Fang 389722a84b8dSQuaker Fang /* 389822a84b8dSQuaker Fang * Send a scan request(assembly scan cmd) to the firmware. 389922a84b8dSQuaker Fang */ 390022a84b8dSQuaker Fang static int 390122a84b8dSQuaker Fang iwp_scan(iwp_sc_t *sc) 390222a84b8dSQuaker Fang { 390322a84b8dSQuaker Fang ieee80211com_t *ic = &sc->sc_ic; 390422a84b8dSQuaker Fang iwp_tx_ring_t *ring = &sc->sc_txq[IWP_CMD_QUEUE_NUM]; 390522a84b8dSQuaker Fang iwp_tx_desc_t *desc; 390622a84b8dSQuaker Fang iwp_tx_data_t *data; 390722a84b8dSQuaker Fang iwp_cmd_t *cmd; 390822a84b8dSQuaker Fang iwp_scan_hdr_t *hdr; 390922a84b8dSQuaker Fang iwp_scan_chan_t chan; 391022a84b8dSQuaker Fang struct ieee80211_frame *wh; 391122a84b8dSQuaker Fang ieee80211_node_t *in = ic->ic_bss; 391222a84b8dSQuaker Fang uint8_t essid[IEEE80211_NWID_LEN+1]; 391322a84b8dSQuaker Fang struct ieee80211_rateset *rs; 391422a84b8dSQuaker Fang enum ieee80211_phymode mode; 391522a84b8dSQuaker Fang uint8_t *frm; 391622a84b8dSQuaker Fang int i, pktlen, nrates; 391722a84b8dSQuaker Fang 391822a84b8dSQuaker Fang data = &ring->data[ring->cur]; 391922a84b8dSQuaker Fang desc = data->desc; 392022a84b8dSQuaker Fang cmd = (iwp_cmd_t *)data->dma_data.mem_va; 392122a84b8dSQuaker Fang 392222a84b8dSQuaker Fang cmd->hdr.type = REPLY_SCAN_CMD; 392322a84b8dSQuaker Fang cmd->hdr.flags = 0; 392422a84b8dSQuaker Fang cmd->hdr.qid = ring->qid; 392522a84b8dSQuaker Fang cmd->hdr.idx = ring->cur | 0x40; 392622a84b8dSQuaker Fang 392722a84b8dSQuaker Fang hdr = (iwp_scan_hdr_t *)cmd->data; 392822a84b8dSQuaker Fang (void) memset(hdr, 0, sizeof (iwp_scan_hdr_t)); 392922a84b8dSQuaker Fang hdr->nchan = 1; 393022a84b8dSQuaker Fang hdr->quiet_time = LE_16(50); 393122a84b8dSQuaker Fang hdr->quiet_plcp_th = LE_16(1); 393222a84b8dSQuaker Fang 393322a84b8dSQuaker Fang hdr->flags = LE_32(RXON_FLG_BAND_24G_MSK); 393422a84b8dSQuaker Fang hdr->rx_chain = LE_16(RXON_RX_CHAIN_DRIVER_FORCE_MSK | 393522a84b8dSQuaker Fang (0x7 << RXON_RX_CHAIN_VALID_POS) | 393622a84b8dSQuaker Fang (0x2 << RXON_RX_CHAIN_FORCE_SEL_POS) | 393722a84b8dSQuaker Fang (0x2 << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS)); 393822a84b8dSQuaker Fang 393922a84b8dSQuaker Fang hdr->tx_cmd.tx_flags = LE_32(TX_CMD_FLG_SEQ_CTL_MSK); 394022a84b8dSQuaker Fang hdr->tx_cmd.sta_id = IWP_BROADCAST_ID; 394122a84b8dSQuaker Fang hdr->tx_cmd.stop_time.life_time = LE_32(0xffffffff); 394222a84b8dSQuaker Fang hdr->tx_cmd.rate.r.rate_n_flags = LE_32(iwp_rate_to_plcp(2)); 394322a84b8dSQuaker Fang hdr->tx_cmd.rate.r.rate_n_flags |= 394422a84b8dSQuaker Fang LE_32(RATE_MCS_ANT_B_MSK |RATE_MCS_CCK_MSK); 394522a84b8dSQuaker Fang hdr->direct_scan[0].len = ic->ic_des_esslen; 394622a84b8dSQuaker Fang hdr->direct_scan[0].id = IEEE80211_ELEMID_SSID; 394722a84b8dSQuaker Fang 394822a84b8dSQuaker Fang hdr->filter_flags = LE_32(RXON_FILTER_ACCEPT_GRP_MSK | 394922a84b8dSQuaker Fang RXON_FILTER_BCON_AWARE_MSK); 395022a84b8dSQuaker Fang 395122a84b8dSQuaker Fang if (ic->ic_des_esslen) { 395222a84b8dSQuaker Fang bcopy(ic->ic_des_essid, essid, ic->ic_des_esslen); 395322a84b8dSQuaker Fang essid[ic->ic_des_esslen] = '\0'; 395422a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_SCAN, "iwp_scan(): " 395522a84b8dSQuaker Fang "directed scan %s\n", essid)); 395622a84b8dSQuaker Fang 395722a84b8dSQuaker Fang bcopy(ic->ic_des_essid, hdr->direct_scan[0].ssid, 395822a84b8dSQuaker Fang ic->ic_des_esslen); 395922a84b8dSQuaker Fang } else { 396022a84b8dSQuaker Fang bzero(hdr->direct_scan[0].ssid, 396122a84b8dSQuaker Fang sizeof (hdr->direct_scan[0].ssid)); 396222a84b8dSQuaker Fang } 396322a84b8dSQuaker Fang 396422a84b8dSQuaker Fang /* 396522a84b8dSQuaker Fang * a probe request frame is required after the REPLY_SCAN_CMD 396622a84b8dSQuaker Fang */ 396722a84b8dSQuaker Fang wh = (struct ieee80211_frame *)(hdr + 1); 396822a84b8dSQuaker Fang wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | 396922a84b8dSQuaker Fang IEEE80211_FC0_SUBTYPE_PROBE_REQ; 397022a84b8dSQuaker Fang wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; 397122a84b8dSQuaker Fang (void) memset(wh->i_addr1, 0xff, 6); 397222a84b8dSQuaker Fang IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_macaddr); 397322a84b8dSQuaker Fang (void) memset(wh->i_addr3, 0xff, 6); 397422a84b8dSQuaker Fang *(uint16_t *)&wh->i_dur[0] = 0; 397522a84b8dSQuaker Fang *(uint16_t *)&wh->i_seq[0] = 0; 397622a84b8dSQuaker Fang 397722a84b8dSQuaker Fang frm = (uint8_t *)(wh + 1); 397822a84b8dSQuaker Fang 397922a84b8dSQuaker Fang /* 398022a84b8dSQuaker Fang * essid IE 398122a84b8dSQuaker Fang */ 398222a84b8dSQuaker Fang if (in->in_esslen) { 398322a84b8dSQuaker Fang bcopy(in->in_essid, essid, in->in_esslen); 398422a84b8dSQuaker Fang essid[in->in_esslen] = '\0'; 398522a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_SCAN, "iwp_scan(): " 398622a84b8dSQuaker Fang "probe with ESSID %s\n", 398722a84b8dSQuaker Fang essid)); 398822a84b8dSQuaker Fang } 398922a84b8dSQuaker Fang *frm++ = IEEE80211_ELEMID_SSID; 399022a84b8dSQuaker Fang *frm++ = in->in_esslen; 399122a84b8dSQuaker Fang (void) memcpy(frm, in->in_essid, in->in_esslen); 399222a84b8dSQuaker Fang frm += in->in_esslen; 399322a84b8dSQuaker Fang 399422a84b8dSQuaker Fang mode = ieee80211_chan2mode(ic, ic->ic_curchan); 399522a84b8dSQuaker Fang rs = &ic->ic_sup_rates[mode]; 399622a84b8dSQuaker Fang 399722a84b8dSQuaker Fang /* 399822a84b8dSQuaker Fang * supported rates IE 399922a84b8dSQuaker Fang */ 400022a84b8dSQuaker Fang *frm++ = IEEE80211_ELEMID_RATES; 400122a84b8dSQuaker Fang nrates = rs->ir_nrates; 400222a84b8dSQuaker Fang if (nrates > IEEE80211_RATE_SIZE) { 400322a84b8dSQuaker Fang nrates = IEEE80211_RATE_SIZE; 400422a84b8dSQuaker Fang } 400522a84b8dSQuaker Fang 400622a84b8dSQuaker Fang *frm++ = (uint8_t)nrates; 400722a84b8dSQuaker Fang (void) memcpy(frm, rs->ir_rates, nrates); 400822a84b8dSQuaker Fang frm += nrates; 400922a84b8dSQuaker Fang 401022a84b8dSQuaker Fang /* 401122a84b8dSQuaker Fang * supported xrates IE 401222a84b8dSQuaker Fang */ 401322a84b8dSQuaker Fang if (rs->ir_nrates > IEEE80211_RATE_SIZE) { 401422a84b8dSQuaker Fang nrates = rs->ir_nrates - IEEE80211_RATE_SIZE; 401522a84b8dSQuaker Fang *frm++ = IEEE80211_ELEMID_XRATES; 401622a84b8dSQuaker Fang *frm++ = (uint8_t)nrates; 401722a84b8dSQuaker Fang (void) memcpy(frm, rs->ir_rates + IEEE80211_RATE_SIZE, nrates); 401822a84b8dSQuaker Fang frm += nrates; 401922a84b8dSQuaker Fang } 402022a84b8dSQuaker Fang 402122a84b8dSQuaker Fang /* 402222a84b8dSQuaker Fang * optionnal IE (usually for wpa) 402322a84b8dSQuaker Fang */ 402422a84b8dSQuaker Fang if (ic->ic_opt_ie != NULL) { 402522a84b8dSQuaker Fang (void) memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len); 402622a84b8dSQuaker Fang frm += ic->ic_opt_ie_len; 402722a84b8dSQuaker Fang } 402822a84b8dSQuaker Fang 402922a84b8dSQuaker Fang /* setup length of probe request */ 403022a84b8dSQuaker Fang hdr->tx_cmd.len = LE_16(_PTRDIFF(frm, wh)); 403122a84b8dSQuaker Fang hdr->len = LE_16(hdr->nchan * sizeof (iwp_scan_chan_t) + 403222a84b8dSQuaker Fang LE_16(hdr->tx_cmd.len) + sizeof (iwp_scan_hdr_t)); 403322a84b8dSQuaker Fang 403422a84b8dSQuaker Fang /* 403522a84b8dSQuaker Fang * the attribute of the scan channels are required after the probe 403622a84b8dSQuaker Fang * request frame. 403722a84b8dSQuaker Fang */ 403822a84b8dSQuaker Fang for (i = 1; i <= hdr->nchan; i++) { 403922a84b8dSQuaker Fang if (ic->ic_des_esslen) { 404022a84b8dSQuaker Fang chan.type = LE_32(3); 404122a84b8dSQuaker Fang } else { 404222a84b8dSQuaker Fang chan.type = LE_32(1); 404322a84b8dSQuaker Fang } 404422a84b8dSQuaker Fang 404522a84b8dSQuaker Fang chan.chan = LE_16(ieee80211_chan2ieee(ic, ic->ic_curchan)); 404622a84b8dSQuaker Fang chan.tpc.tx_gain = 0x28; 404722a84b8dSQuaker Fang chan.tpc.dsp_atten = 110; 404822a84b8dSQuaker Fang chan.active_dwell = LE_16(50); 404922a84b8dSQuaker Fang chan.passive_dwell = LE_16(120); 405022a84b8dSQuaker Fang 405122a84b8dSQuaker Fang bcopy(&chan, frm, sizeof (iwp_scan_chan_t)); 405222a84b8dSQuaker Fang frm += sizeof (iwp_scan_chan_t); 405322a84b8dSQuaker Fang } 405422a84b8dSQuaker Fang 405522a84b8dSQuaker Fang pktlen = _PTRDIFF(frm, cmd); 405622a84b8dSQuaker Fang 405722a84b8dSQuaker Fang (void) memset(desc, 0, sizeof (*desc)); 405822a84b8dSQuaker Fang desc->val0 = 1 << 24; 405922a84b8dSQuaker Fang desc->pa[0].tb1_addr = 406022a84b8dSQuaker Fang (uint32_t)(data->dma_data.cookie.dmac_address & 0xffffffff); 406122a84b8dSQuaker Fang desc->pa[0].val1 = (pktlen << 4) & 0xfff0; 406222a84b8dSQuaker Fang 406322a84b8dSQuaker Fang /* 406422a84b8dSQuaker Fang * maybe for cmd, filling the byte cnt table is not necessary. 406522a84b8dSQuaker Fang * anyway, we fill it here. 406622a84b8dSQuaker Fang */ 406722a84b8dSQuaker Fang sc->sc_shared->queues_byte_cnt_tbls[ring->qid] 406822a84b8dSQuaker Fang .tfd_offset[ring->cur].val = 8; 406922a84b8dSQuaker Fang if (ring->cur < IWP_MAX_WIN_SIZE) { 407022a84b8dSQuaker Fang sc->sc_shared->queues_byte_cnt_tbls[ring->qid]. 407122a84b8dSQuaker Fang tfd_offset[IWP_QUEUE_SIZE + ring->cur].val = 8; 407222a84b8dSQuaker Fang } 407322a84b8dSQuaker Fang 407422a84b8dSQuaker Fang /* 407522a84b8dSQuaker Fang * kick cmd ring 407622a84b8dSQuaker Fang */ 407722a84b8dSQuaker Fang ring->cur = (ring->cur + 1) % ring->count; 407822a84b8dSQuaker Fang IWP_WRITE(sc, HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); 407922a84b8dSQuaker Fang 408022a84b8dSQuaker Fang return (IWP_SUCCESS); 408122a84b8dSQuaker Fang } 408222a84b8dSQuaker Fang 408322a84b8dSQuaker Fang /* 408422a84b8dSQuaker Fang * configure NIC by using ucode commands after loading ucode. 408522a84b8dSQuaker Fang */ 408622a84b8dSQuaker Fang static int 408722a84b8dSQuaker Fang iwp_config(iwp_sc_t *sc) 408822a84b8dSQuaker Fang { 408922a84b8dSQuaker Fang ieee80211com_t *ic = &sc->sc_ic; 409022a84b8dSQuaker Fang iwp_powertable_cmd_t powertable; 409122a84b8dSQuaker Fang iwp_bt_cmd_t bt; 409222a84b8dSQuaker Fang iwp_add_sta_t node; 409322a84b8dSQuaker Fang iwp_rem_sta_t rm_sta; 409422a84b8dSQuaker Fang const uint8_t bcast[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 409522a84b8dSQuaker Fang int err = IWP_FAIL; 409622a84b8dSQuaker Fang 409722a84b8dSQuaker Fang /* 409822a84b8dSQuaker Fang * set power mode. Disable power management at present, do it later 409922a84b8dSQuaker Fang */ 410022a84b8dSQuaker Fang (void) memset(&powertable, 0, sizeof (powertable)); 410122a84b8dSQuaker Fang powertable.flags = LE_16(0x8); 410222a84b8dSQuaker Fang err = iwp_cmd(sc, POWER_TABLE_CMD, &powertable, 410322a84b8dSQuaker Fang sizeof (powertable), 0); 410422a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 410522a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_config(): " 410622a84b8dSQuaker Fang "failed to set power mode\n"); 410722a84b8dSQuaker Fang return (err); 410822a84b8dSQuaker Fang } 410922a84b8dSQuaker Fang 411022a84b8dSQuaker Fang /* 411122a84b8dSQuaker Fang * configure bt coexistence 411222a84b8dSQuaker Fang */ 411322a84b8dSQuaker Fang (void) memset(&bt, 0, sizeof (bt)); 411422a84b8dSQuaker Fang bt.flags = 3; 411522a84b8dSQuaker Fang bt.lead_time = 0xaa; 411622a84b8dSQuaker Fang bt.max_kill = 1; 411722a84b8dSQuaker Fang err = iwp_cmd(sc, REPLY_BT_CONFIG, &bt, 411822a84b8dSQuaker Fang sizeof (bt), 0); 411922a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 412022a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_config(): " 412122a84b8dSQuaker Fang "failed to configurate bt coexistence\n"); 412222a84b8dSQuaker Fang return (err); 412322a84b8dSQuaker Fang } 412422a84b8dSQuaker Fang 412522a84b8dSQuaker Fang /* 412622a84b8dSQuaker Fang * configure rxon 412722a84b8dSQuaker Fang */ 412822a84b8dSQuaker Fang (void) memset(&sc->sc_config, 0, sizeof (iwp_rxon_cmd_t)); 412922a84b8dSQuaker Fang IEEE80211_ADDR_COPY(sc->sc_config.node_addr, ic->ic_macaddr); 413022a84b8dSQuaker Fang IEEE80211_ADDR_COPY(sc->sc_config.wlap_bssid, ic->ic_macaddr); 413122a84b8dSQuaker Fang sc->sc_config.chan = LE_16(ieee80211_chan2ieee(ic, ic->ic_curchan)); 413222a84b8dSQuaker Fang sc->sc_config.flags = LE_32(RXON_FLG_BAND_24G_MSK); 413322a84b8dSQuaker Fang sc->sc_config.flags &= LE_32(~(RXON_FLG_CHANNEL_MODE_MIXED_MSK | 413422a84b8dSQuaker Fang RXON_FLG_CHANNEL_MODE_PURE_40_MSK)); 413522a84b8dSQuaker Fang 413622a84b8dSQuaker Fang switch (ic->ic_opmode) { 413722a84b8dSQuaker Fang case IEEE80211_M_STA: 413822a84b8dSQuaker Fang sc->sc_config.dev_type = RXON_DEV_TYPE_ESS; 413922a84b8dSQuaker Fang sc->sc_config.filter_flags |= LE_32(RXON_FILTER_ACCEPT_GRP_MSK | 414022a84b8dSQuaker Fang RXON_FILTER_DIS_DECRYPT_MSK | 414122a84b8dSQuaker Fang RXON_FILTER_DIS_GRP_DECRYPT_MSK); 414222a84b8dSQuaker Fang break; 414322a84b8dSQuaker Fang case IEEE80211_M_IBSS: 414422a84b8dSQuaker Fang case IEEE80211_M_AHDEMO: 414522a84b8dSQuaker Fang sc->sc_config.dev_type = RXON_DEV_TYPE_IBSS; 414622a84b8dSQuaker Fang 414722a84b8dSQuaker Fang sc->sc_config.flags |= LE_32(RXON_FLG_SHORT_PREAMBLE_MSK); 414822a84b8dSQuaker Fang sc->sc_config.filter_flags = LE_32(RXON_FILTER_ACCEPT_GRP_MSK | 414922a84b8dSQuaker Fang RXON_FILTER_DIS_DECRYPT_MSK | 415022a84b8dSQuaker Fang RXON_FILTER_DIS_GRP_DECRYPT_MSK); 415122a84b8dSQuaker Fang break; 415222a84b8dSQuaker Fang case IEEE80211_M_HOSTAP: 415322a84b8dSQuaker Fang sc->sc_config.dev_type = RXON_DEV_TYPE_AP; 415422a84b8dSQuaker Fang break; 415522a84b8dSQuaker Fang case IEEE80211_M_MONITOR: 415622a84b8dSQuaker Fang sc->sc_config.dev_type = RXON_DEV_TYPE_SNIFFER; 415722a84b8dSQuaker Fang sc->sc_config.filter_flags |= LE_32(RXON_FILTER_ACCEPT_GRP_MSK | 415822a84b8dSQuaker Fang RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_PROMISC_MSK); 415922a84b8dSQuaker Fang break; 416022a84b8dSQuaker Fang } 416122a84b8dSQuaker Fang 416222a84b8dSQuaker Fang /* 416322a84b8dSQuaker Fang * Support all CCK rates. 416422a84b8dSQuaker Fang */ 416522a84b8dSQuaker Fang sc->sc_config.cck_basic_rates = 0x0f; 416622a84b8dSQuaker Fang 416722a84b8dSQuaker Fang /* 416822a84b8dSQuaker Fang * Support all OFDM rates. 416922a84b8dSQuaker Fang */ 417022a84b8dSQuaker Fang sc->sc_config.ofdm_basic_rates = 0xff; 417122a84b8dSQuaker Fang 417222a84b8dSQuaker Fang sc->sc_config.rx_chain = LE_16(RXON_RX_CHAIN_DRIVER_FORCE_MSK | 417322a84b8dSQuaker Fang (0x7 << RXON_RX_CHAIN_VALID_POS) | 417422a84b8dSQuaker Fang (0x2 << RXON_RX_CHAIN_FORCE_SEL_POS) | 417522a84b8dSQuaker Fang (0x2 << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS)); 417622a84b8dSQuaker Fang 417722a84b8dSQuaker Fang err = iwp_cmd(sc, REPLY_RXON, &sc->sc_config, 417822a84b8dSQuaker Fang sizeof (iwp_rxon_cmd_t), 0); 417922a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 418022a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_config(): " 418122a84b8dSQuaker Fang "failed to set configure command\n"); 418222a84b8dSQuaker Fang return (err); 418322a84b8dSQuaker Fang } 418422a84b8dSQuaker Fang 418522a84b8dSQuaker Fang /* 418622a84b8dSQuaker Fang * remove all nodes in NIC 418722a84b8dSQuaker Fang */ 418822a84b8dSQuaker Fang (void) memset(&rm_sta, 0, sizeof (rm_sta)); 418922a84b8dSQuaker Fang rm_sta.num_sta = 1; 419022a84b8dSQuaker Fang (void) memcpy(rm_sta.addr, bcast, 6); 419122a84b8dSQuaker Fang 419222a84b8dSQuaker Fang err = iwp_cmd(sc, REPLY_REMOVE_STA, &rm_sta, sizeof (iwp_rem_sta_t), 0); 419322a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 419422a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_config(): " 419522a84b8dSQuaker Fang "failed to remove broadcast node in hardware.\n"); 419622a84b8dSQuaker Fang return (err); 419722a84b8dSQuaker Fang } 419822a84b8dSQuaker Fang 419922a84b8dSQuaker Fang /* 420022a84b8dSQuaker Fang * add broadcast node so that we can send broadcast frame 420122a84b8dSQuaker Fang */ 420222a84b8dSQuaker Fang (void) memset(&node, 0, sizeof (node)); 420322a84b8dSQuaker Fang (void) memset(node.sta.addr, 0xff, 6); 420422a84b8dSQuaker Fang node.mode = 0; 420522a84b8dSQuaker Fang node.sta.sta_id = IWP_BROADCAST_ID; 420622a84b8dSQuaker Fang node.station_flags = 0; 420722a84b8dSQuaker Fang 420822a84b8dSQuaker Fang err = iwp_cmd(sc, REPLY_ADD_STA, &node, sizeof (node), 0); 420922a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 421022a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_config(): " 421122a84b8dSQuaker Fang "failed to add broadcast node\n"); 421222a84b8dSQuaker Fang return (err); 421322a84b8dSQuaker Fang } 421422a84b8dSQuaker Fang 421522a84b8dSQuaker Fang return (err); 421622a84b8dSQuaker Fang } 421722a84b8dSQuaker Fang 421822a84b8dSQuaker Fang /* 421922a84b8dSQuaker Fang * quiesce(9E) entry point. 422022a84b8dSQuaker Fang * This function is called when the system is single-threaded at high 422122a84b8dSQuaker Fang * PIL with preemption disabled. Therefore, this function must not be 422222a84b8dSQuaker Fang * blocked. 422322a84b8dSQuaker Fang * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. 422422a84b8dSQuaker Fang * DDI_FAILURE indicates an error condition and should almost never happen. 422522a84b8dSQuaker Fang */ 422622a84b8dSQuaker Fang static int 422722a84b8dSQuaker Fang iwp_quiesce(dev_info_t *dip) 422822a84b8dSQuaker Fang { 422922a84b8dSQuaker Fang iwp_sc_t *sc; 423022a84b8dSQuaker Fang 423122a84b8dSQuaker Fang sc = ddi_get_soft_state(iwp_soft_state_p, ddi_get_instance(dip)); 423222a84b8dSQuaker Fang if (NULL == sc) { 423322a84b8dSQuaker Fang return (DDI_FAILURE); 423422a84b8dSQuaker Fang } 423522a84b8dSQuaker Fang 423622a84b8dSQuaker Fang #ifdef DEBUG 423722a84b8dSQuaker Fang /* by pass any messages, if it's quiesce */ 423822a84b8dSQuaker Fang iwp_dbg_flags = 0; 423922a84b8dSQuaker Fang #endif 424022a84b8dSQuaker Fang 424122a84b8dSQuaker Fang /* 424222a84b8dSQuaker Fang * No more blocking is allowed while we are in the 424322a84b8dSQuaker Fang * quiesce(9E) entry point. 424422a84b8dSQuaker Fang */ 424522a84b8dSQuaker Fang atomic_or_32(&sc->sc_flags, IWP_F_QUIESCED); 424622a84b8dSQuaker Fang 424722a84b8dSQuaker Fang /* 424822a84b8dSQuaker Fang * Disable and mask all interrupts. 424922a84b8dSQuaker Fang */ 425022a84b8dSQuaker Fang iwp_stop(sc); 425122a84b8dSQuaker Fang 425222a84b8dSQuaker Fang return (DDI_SUCCESS); 425322a84b8dSQuaker Fang } 425422a84b8dSQuaker Fang 425522a84b8dSQuaker Fang static void 425622a84b8dSQuaker Fang iwp_stop_master(iwp_sc_t *sc) 425722a84b8dSQuaker Fang { 425822a84b8dSQuaker Fang uint32_t tmp; 425922a84b8dSQuaker Fang int n; 426022a84b8dSQuaker Fang 426122a84b8dSQuaker Fang tmp = IWP_READ(sc, CSR_RESET); 426222a84b8dSQuaker Fang IWP_WRITE(sc, CSR_RESET, tmp | CSR_RESET_REG_FLAG_STOP_MASTER); 426322a84b8dSQuaker Fang 426422a84b8dSQuaker Fang tmp = IWP_READ(sc, CSR_GP_CNTRL); 426522a84b8dSQuaker Fang if ((tmp & CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE) == 426622a84b8dSQuaker Fang CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE) { 426722a84b8dSQuaker Fang return; 426822a84b8dSQuaker Fang } 426922a84b8dSQuaker Fang 427022a84b8dSQuaker Fang for (n = 0; n < 2000; n++) { 427122a84b8dSQuaker Fang if (IWP_READ(sc, CSR_RESET) & 427222a84b8dSQuaker Fang CSR_RESET_REG_FLAG_MASTER_DISABLED) { 427322a84b8dSQuaker Fang break; 427422a84b8dSQuaker Fang } 427522a84b8dSQuaker Fang DELAY(1000); 427622a84b8dSQuaker Fang } 427722a84b8dSQuaker Fang 427822a84b8dSQuaker Fang #ifdef DEBUG 427922a84b8dSQuaker Fang if (2000 == n) { 428022a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_HW, "iwp_stop_master(): " 428122a84b8dSQuaker Fang "timeout waiting for master stop\n")); 428222a84b8dSQuaker Fang } 428322a84b8dSQuaker Fang #endif 428422a84b8dSQuaker Fang } 428522a84b8dSQuaker Fang 428622a84b8dSQuaker Fang static int 428722a84b8dSQuaker Fang iwp_power_up(iwp_sc_t *sc) 428822a84b8dSQuaker Fang { 428922a84b8dSQuaker Fang uint32_t tmp; 429022a84b8dSQuaker Fang 429122a84b8dSQuaker Fang iwp_mac_access_enter(sc); 429222a84b8dSQuaker Fang tmp = iwp_reg_read(sc, ALM_APMG_PS_CTL); 429322a84b8dSQuaker Fang tmp &= ~APMG_PS_CTRL_REG_MSK_POWER_SRC; 429422a84b8dSQuaker Fang tmp |= APMG_PS_CTRL_REG_VAL_POWER_SRC_VMAIN; 429522a84b8dSQuaker Fang iwp_reg_write(sc, ALM_APMG_PS_CTL, tmp); 429622a84b8dSQuaker Fang iwp_mac_access_exit(sc); 429722a84b8dSQuaker Fang 429822a84b8dSQuaker Fang DELAY(5000); 429922a84b8dSQuaker Fang return (IWP_SUCCESS); 430022a84b8dSQuaker Fang } 430122a84b8dSQuaker Fang 430222a84b8dSQuaker Fang /* 430322a84b8dSQuaker Fang * hardware initialization 430422a84b8dSQuaker Fang */ 430522a84b8dSQuaker Fang static int 430622a84b8dSQuaker Fang iwp_preinit(iwp_sc_t *sc) 430722a84b8dSQuaker Fang { 430822a84b8dSQuaker Fang int n; 430922a84b8dSQuaker Fang uint8_t vlink; 431022a84b8dSQuaker Fang uint16_t radio_cfg; 431122a84b8dSQuaker Fang uint32_t tmp; 431222a84b8dSQuaker Fang 431322a84b8dSQuaker Fang /* 431422a84b8dSQuaker Fang * clear any pending interrupts 431522a84b8dSQuaker Fang */ 431622a84b8dSQuaker Fang IWP_WRITE(sc, CSR_INT, 0xffffffff); 431722a84b8dSQuaker Fang 431822a84b8dSQuaker Fang tmp = IWP_READ(sc, CSR_GIO_CHICKEN_BITS); 431922a84b8dSQuaker Fang IWP_WRITE(sc, CSR_GIO_CHICKEN_BITS, 432022a84b8dSQuaker Fang tmp | CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); 432122a84b8dSQuaker Fang 432222a84b8dSQuaker Fang tmp = IWP_READ(sc, CSR_GP_CNTRL); 432322a84b8dSQuaker Fang IWP_WRITE(sc, CSR_GP_CNTRL, tmp | CSR_GP_CNTRL_REG_FLAG_INIT_DONE); 432422a84b8dSQuaker Fang 432522a84b8dSQuaker Fang /* 432622a84b8dSQuaker Fang * wait for clock ready 432722a84b8dSQuaker Fang */ 432822a84b8dSQuaker Fang for (n = 0; n < 1000; n++) { 432922a84b8dSQuaker Fang if (IWP_READ(sc, CSR_GP_CNTRL) & 433022a84b8dSQuaker Fang CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY) { 433122a84b8dSQuaker Fang break; 433222a84b8dSQuaker Fang } 433322a84b8dSQuaker Fang DELAY(10); 433422a84b8dSQuaker Fang } 433522a84b8dSQuaker Fang 433622a84b8dSQuaker Fang if (1000 == n) { 433722a84b8dSQuaker Fang return (ETIMEDOUT); 433822a84b8dSQuaker Fang } 433922a84b8dSQuaker Fang 434022a84b8dSQuaker Fang iwp_mac_access_enter(sc); 434122a84b8dSQuaker Fang 434222a84b8dSQuaker Fang iwp_reg_write(sc, ALM_APMG_CLK_EN, APMG_CLK_REG_VAL_DMA_CLK_RQT); 434322a84b8dSQuaker Fang 434422a84b8dSQuaker Fang DELAY(20); 434522a84b8dSQuaker Fang tmp = iwp_reg_read(sc, ALM_APMG_PCIDEV_STT); 434622a84b8dSQuaker Fang iwp_reg_write(sc, ALM_APMG_PCIDEV_STT, tmp | 434722a84b8dSQuaker Fang APMG_DEV_STATE_REG_VAL_L1_ACTIVE_DISABLE); 434822a84b8dSQuaker Fang iwp_mac_access_exit(sc); 434922a84b8dSQuaker Fang 435022a84b8dSQuaker Fang radio_cfg = IWP_READ_EEP_SHORT(sc, EEP_SP_RADIO_CONFIGURATION); 435122a84b8dSQuaker Fang if (SP_RADIO_TYPE_MSK(radio_cfg) < SP_RADIO_TYPE_MAX) { 435222a84b8dSQuaker Fang tmp = IWP_READ(sc, CSR_HW_IF_CONFIG_REG); 435322a84b8dSQuaker Fang IWP_WRITE(sc, CSR_HW_IF_CONFIG_REG, 435422a84b8dSQuaker Fang tmp | SP_RADIO_TYPE_MSK(radio_cfg) | 435522a84b8dSQuaker Fang SP_RADIO_STEP_MSK(radio_cfg) | 435622a84b8dSQuaker Fang SP_RADIO_DASH_MSK(radio_cfg)); 435722a84b8dSQuaker Fang } else { 435822a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_preinit(): " 435922a84b8dSQuaker Fang "radio configuration information in eeprom is wrong\n"); 436022a84b8dSQuaker Fang return (IWP_FAIL); 436122a84b8dSQuaker Fang } 436222a84b8dSQuaker Fang 436322a84b8dSQuaker Fang 436422a84b8dSQuaker Fang IWP_WRITE(sc, CSR_INT_COALESCING, 512 / 32); 436522a84b8dSQuaker Fang 436622a84b8dSQuaker Fang (void) iwp_power_up(sc); 436722a84b8dSQuaker Fang 436822a84b8dSQuaker Fang if ((sc->sc_rev & 0x80) == 0x80 && (sc->sc_rev & 0x7f) < 8) { 436922a84b8dSQuaker Fang tmp = ddi_get32(sc->sc_cfg_handle, 437022a84b8dSQuaker Fang (uint32_t *)(sc->sc_cfg_base + 0xe8)); 437122a84b8dSQuaker Fang ddi_put32(sc->sc_cfg_handle, 437222a84b8dSQuaker Fang (uint32_t *)(sc->sc_cfg_base + 0xe8), 437322a84b8dSQuaker Fang tmp & ~(1 << 11)); 437422a84b8dSQuaker Fang } 437522a84b8dSQuaker Fang 437622a84b8dSQuaker Fang vlink = ddi_get8(sc->sc_cfg_handle, 437722a84b8dSQuaker Fang (uint8_t *)(sc->sc_cfg_base + 0xf0)); 437822a84b8dSQuaker Fang ddi_put8(sc->sc_cfg_handle, (uint8_t *)(sc->sc_cfg_base + 0xf0), 437922a84b8dSQuaker Fang vlink & ~2); 438022a84b8dSQuaker Fang 438122a84b8dSQuaker Fang tmp = IWP_READ(sc, CSR_HW_IF_CONFIG_REG); 438222a84b8dSQuaker Fang tmp |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | 438322a84b8dSQuaker Fang CSR_HW_IF_CONFIG_REG_BIT_MAC_SI; 438422a84b8dSQuaker Fang IWP_WRITE(sc, CSR_HW_IF_CONFIG_REG, tmp); 438522a84b8dSQuaker Fang 438622a84b8dSQuaker Fang /* 438722a84b8dSQuaker Fang * make sure power supply on each part of the hardware 438822a84b8dSQuaker Fang */ 438922a84b8dSQuaker Fang iwp_mac_access_enter(sc); 439022a84b8dSQuaker Fang tmp = iwp_reg_read(sc, ALM_APMG_PS_CTL); 439122a84b8dSQuaker Fang tmp |= APMG_PS_CTRL_REG_VAL_ALM_R_RESET_REQ; 439222a84b8dSQuaker Fang iwp_reg_write(sc, ALM_APMG_PS_CTL, tmp); 439322a84b8dSQuaker Fang DELAY(5); 439422a84b8dSQuaker Fang 439522a84b8dSQuaker Fang tmp = iwp_reg_read(sc, ALM_APMG_PS_CTL); 439622a84b8dSQuaker Fang tmp &= ~APMG_PS_CTRL_REG_VAL_ALM_R_RESET_REQ; 439722a84b8dSQuaker Fang iwp_reg_write(sc, ALM_APMG_PS_CTL, tmp); 439822a84b8dSQuaker Fang iwp_mac_access_exit(sc); 439922a84b8dSQuaker Fang 440022a84b8dSQuaker Fang if (PA_TYPE_MIX == sc->sc_chip_param.pa_type) { 440122a84b8dSQuaker Fang IWP_WRITE(sc, CSR_GP_DRIVER_REG, 440222a84b8dSQuaker Fang CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_MIX); 440322a84b8dSQuaker Fang } 440422a84b8dSQuaker Fang 440522a84b8dSQuaker Fang if (PA_TYPE_INTER == sc->sc_chip_param.pa_type) { 440622a84b8dSQuaker Fang 440722a84b8dSQuaker Fang IWP_WRITE(sc, CSR_GP_DRIVER_REG, 440822a84b8dSQuaker Fang CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA); 440922a84b8dSQuaker Fang } 441022a84b8dSQuaker Fang 441122a84b8dSQuaker Fang return (IWP_SUCCESS); 441222a84b8dSQuaker Fang } 441322a84b8dSQuaker Fang 441422a84b8dSQuaker Fang /* 441522a84b8dSQuaker Fang * set up semphore flag to own EEPROM 441622a84b8dSQuaker Fang */ 441722a84b8dSQuaker Fang static int 441822a84b8dSQuaker Fang iwp_eep_sem_down(iwp_sc_t *sc) 441922a84b8dSQuaker Fang { 442022a84b8dSQuaker Fang int count1, count2; 442122a84b8dSQuaker Fang uint32_t tmp; 442222a84b8dSQuaker Fang 442322a84b8dSQuaker Fang for (count1 = 0; count1 < 1000; count1++) { 442422a84b8dSQuaker Fang tmp = IWP_READ(sc, CSR_HW_IF_CONFIG_REG); 442522a84b8dSQuaker Fang IWP_WRITE(sc, CSR_HW_IF_CONFIG_REG, 442622a84b8dSQuaker Fang tmp | CSR_HW_IF_CONFIG_REG_EEP_SEM); 442722a84b8dSQuaker Fang 442822a84b8dSQuaker Fang for (count2 = 0; count2 < 2; count2++) { 442922a84b8dSQuaker Fang if (IWP_READ(sc, CSR_HW_IF_CONFIG_REG) & 443022a84b8dSQuaker Fang CSR_HW_IF_CONFIG_REG_EEP_SEM) { 443122a84b8dSQuaker Fang return (IWP_SUCCESS); 443222a84b8dSQuaker Fang } 443322a84b8dSQuaker Fang DELAY(10000); 443422a84b8dSQuaker Fang } 443522a84b8dSQuaker Fang } 443622a84b8dSQuaker Fang return (IWP_FAIL); 443722a84b8dSQuaker Fang } 443822a84b8dSQuaker Fang 443922a84b8dSQuaker Fang /* 444022a84b8dSQuaker Fang * reset semphore flag to release EEPROM 444122a84b8dSQuaker Fang */ 444222a84b8dSQuaker Fang static void 444322a84b8dSQuaker Fang iwp_eep_sem_up(iwp_sc_t *sc) 444422a84b8dSQuaker Fang { 444522a84b8dSQuaker Fang uint32_t tmp; 444622a84b8dSQuaker Fang 444722a84b8dSQuaker Fang tmp = IWP_READ(sc, CSR_HW_IF_CONFIG_REG); 444822a84b8dSQuaker Fang IWP_WRITE(sc, CSR_HW_IF_CONFIG_REG, 444922a84b8dSQuaker Fang tmp & (~CSR_HW_IF_CONFIG_REG_EEP_SEM)); 445022a84b8dSQuaker Fang } 445122a84b8dSQuaker Fang 445222a84b8dSQuaker Fang /* 445322a84b8dSQuaker Fang * This function read all infomation from eeprom 445422a84b8dSQuaker Fang */ 445522a84b8dSQuaker Fang static int 445622a84b8dSQuaker Fang iwp_eep_load(iwp_sc_t *sc) 445722a84b8dSQuaker Fang { 445822a84b8dSQuaker Fang int i, rr; 445922a84b8dSQuaker Fang uint32_t rv, tmp, eep_gp; 446022a84b8dSQuaker Fang uint16_t addr, eep_sz = sizeof (sc->sc_eep_map); 446122a84b8dSQuaker Fang uint16_t *eep_p = (uint16_t *)&sc->sc_eep_map; 446222a84b8dSQuaker Fang 446322a84b8dSQuaker Fang /* 446422a84b8dSQuaker Fang * read eeprom gp register in CSR 446522a84b8dSQuaker Fang */ 446622a84b8dSQuaker Fang eep_gp = IWP_READ(sc, CSR_EEPROM_GP); 446722a84b8dSQuaker Fang if ((eep_gp & CSR_EEPROM_GP_VALID_MSK) == 446822a84b8dSQuaker Fang CSR_EEPROM_GP_BAD_SIGNATURE) { 446922a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_EEPROM, "iwp_eep_load(): " 447022a84b8dSQuaker Fang "not find eeprom\n")); 447122a84b8dSQuaker Fang return (IWP_FAIL); 447222a84b8dSQuaker Fang } 447322a84b8dSQuaker Fang 447422a84b8dSQuaker Fang rr = iwp_eep_sem_down(sc); 447522a84b8dSQuaker Fang if (rr != 0) { 447622a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_EEPROM, "iwp_eep_load(): " 447722a84b8dSQuaker Fang "driver failed to own EEPROM\n")); 447822a84b8dSQuaker Fang return (IWP_FAIL); 447922a84b8dSQuaker Fang } 448022a84b8dSQuaker Fang 448122a84b8dSQuaker Fang for (addr = 0; addr < eep_sz; addr += 2) { 448222a84b8dSQuaker Fang IWP_WRITE(sc, CSR_EEPROM_REG, addr<<1); 448322a84b8dSQuaker Fang tmp = IWP_READ(sc, CSR_EEPROM_REG); 448422a84b8dSQuaker Fang IWP_WRITE(sc, CSR_EEPROM_REG, tmp & ~(0x2)); 448522a84b8dSQuaker Fang 448622a84b8dSQuaker Fang for (i = 0; i < 10; i++) { 448722a84b8dSQuaker Fang rv = IWP_READ(sc, CSR_EEPROM_REG); 448822a84b8dSQuaker Fang if (rv & 1) { 448922a84b8dSQuaker Fang break; 449022a84b8dSQuaker Fang } 449122a84b8dSQuaker Fang DELAY(10); 449222a84b8dSQuaker Fang } 449322a84b8dSQuaker Fang 449422a84b8dSQuaker Fang if (!(rv & 1)) { 449522a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_EEPROM, "iwp_eep_load(): " 449622a84b8dSQuaker Fang "time out when read eeprome\n")); 449722a84b8dSQuaker Fang iwp_eep_sem_up(sc); 449822a84b8dSQuaker Fang return (IWP_FAIL); 449922a84b8dSQuaker Fang } 450022a84b8dSQuaker Fang 450122a84b8dSQuaker Fang eep_p[addr/2] = LE_16(rv >> 16); 450222a84b8dSQuaker Fang } 450322a84b8dSQuaker Fang 450422a84b8dSQuaker Fang iwp_eep_sem_up(sc); 450522a84b8dSQuaker Fang return (IWP_SUCCESS); 450622a84b8dSQuaker Fang } 450722a84b8dSQuaker Fang 450822a84b8dSQuaker Fang /* 450922a84b8dSQuaker Fang * initialize mac address in ieee80211com_t struct 451022a84b8dSQuaker Fang */ 451122a84b8dSQuaker Fang static void 451222a84b8dSQuaker Fang iwp_get_mac_from_eep(iwp_sc_t *sc) 451322a84b8dSQuaker Fang { 451422a84b8dSQuaker Fang ieee80211com_t *ic = &sc->sc_ic; 451522a84b8dSQuaker Fang 451622a84b8dSQuaker Fang IEEE80211_ADDR_COPY(ic->ic_macaddr, &sc->sc_eep_map[EEP_MAC_ADDRESS]); 451722a84b8dSQuaker Fang 451822a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_EEPROM, "iwp_get_mac_from_eep(): " 451922a84b8dSQuaker Fang "mac:%2x:%2x:%2x:%2x:%2x:%2x\n", 452022a84b8dSQuaker Fang ic->ic_macaddr[0], ic->ic_macaddr[1], ic->ic_macaddr[2], 452122a84b8dSQuaker Fang ic->ic_macaddr[3], ic->ic_macaddr[4], ic->ic_macaddr[5])); 452222a84b8dSQuaker Fang } 452322a84b8dSQuaker Fang 452422a84b8dSQuaker Fang /* 452522a84b8dSQuaker Fang * main initialization function 452622a84b8dSQuaker Fang */ 452722a84b8dSQuaker Fang static int 452822a84b8dSQuaker Fang iwp_init(iwp_sc_t *sc) 452922a84b8dSQuaker Fang { 453022a84b8dSQuaker Fang int err = IWP_FAIL; 453122a84b8dSQuaker Fang clock_t clk; 453222a84b8dSQuaker Fang 453322a84b8dSQuaker Fang /* 453422a84b8dSQuaker Fang * release buffer for calibration 453522a84b8dSQuaker Fang */ 453622a84b8dSQuaker Fang iwp_release_calib_buffer(sc); 453722a84b8dSQuaker Fang 453822a84b8dSQuaker Fang mutex_enter(&sc->sc_glock); 453922a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_FW_INIT); 454022a84b8dSQuaker Fang 454122a84b8dSQuaker Fang err = iwp_init_common(sc); 454222a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 454322a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 454422a84b8dSQuaker Fang return (IWP_FAIL); 454522a84b8dSQuaker Fang } 454622a84b8dSQuaker Fang 454722a84b8dSQuaker Fang /* 454822a84b8dSQuaker Fang * backup ucode data part for future use. 454922a84b8dSQuaker Fang */ 455022a84b8dSQuaker Fang (void) memcpy(sc->sc_dma_fw_data_bak.mem_va, 455122a84b8dSQuaker Fang sc->sc_dma_fw_data.mem_va, 455222a84b8dSQuaker Fang sc->sc_dma_fw_data.alength); 455322a84b8dSQuaker Fang 455422a84b8dSQuaker Fang /* load firmware init segment into NIC */ 455522a84b8dSQuaker Fang err = iwp_load_init_firmware(sc); 455622a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 455722a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_init(): " 455822a84b8dSQuaker Fang "failed to setup init firmware\n"); 455922a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 456022a84b8dSQuaker Fang return (IWP_FAIL); 456122a84b8dSQuaker Fang } 456222a84b8dSQuaker Fang 456322a84b8dSQuaker Fang /* 456422a84b8dSQuaker Fang * now press "execute" start running 456522a84b8dSQuaker Fang */ 456622a84b8dSQuaker Fang IWP_WRITE(sc, CSR_RESET, 0); 456722a84b8dSQuaker Fang 456822a84b8dSQuaker Fang clk = ddi_get_lbolt() + drv_usectohz(1000000); 456922a84b8dSQuaker Fang while (!(sc->sc_flags & IWP_F_FW_INIT)) { 457022a84b8dSQuaker Fang if (cv_timedwait(&sc->sc_ucode_cv, 457122a84b8dSQuaker Fang &sc->sc_glock, clk) < 0) { 457222a84b8dSQuaker Fang break; 457322a84b8dSQuaker Fang } 457422a84b8dSQuaker Fang } 457522a84b8dSQuaker Fang 457622a84b8dSQuaker Fang if (!(sc->sc_flags & IWP_F_FW_INIT)) { 457722a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_init(): " 457822a84b8dSQuaker Fang "failed to process init alive.\n"); 457922a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 458022a84b8dSQuaker Fang return (IWP_FAIL); 458122a84b8dSQuaker Fang } 458222a84b8dSQuaker Fang 458322a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 458422a84b8dSQuaker Fang 458522a84b8dSQuaker Fang /* 458622a84b8dSQuaker Fang * stop chipset for initializing chipset again 458722a84b8dSQuaker Fang */ 458822a84b8dSQuaker Fang iwp_stop(sc); 458922a84b8dSQuaker Fang 459022a84b8dSQuaker Fang mutex_enter(&sc->sc_glock); 459122a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_FW_INIT); 459222a84b8dSQuaker Fang 459322a84b8dSQuaker Fang err = iwp_init_common(sc); 459422a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 459522a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 459622a84b8dSQuaker Fang return (IWP_FAIL); 459722a84b8dSQuaker Fang } 459822a84b8dSQuaker Fang 459922a84b8dSQuaker Fang /* 460022a84b8dSQuaker Fang * load firmware run segment into NIC 460122a84b8dSQuaker Fang */ 460222a84b8dSQuaker Fang err = iwp_load_run_firmware(sc); 460322a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 460422a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_init(): " 460522a84b8dSQuaker Fang "failed to setup run firmware\n"); 460622a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 460722a84b8dSQuaker Fang return (IWP_FAIL); 460822a84b8dSQuaker Fang } 460922a84b8dSQuaker Fang 461022a84b8dSQuaker Fang /* 461122a84b8dSQuaker Fang * now press "execute" start running 461222a84b8dSQuaker Fang */ 461322a84b8dSQuaker Fang IWP_WRITE(sc, CSR_RESET, 0); 461422a84b8dSQuaker Fang 461522a84b8dSQuaker Fang clk = ddi_get_lbolt() + drv_usectohz(1000000); 461622a84b8dSQuaker Fang while (!(sc->sc_flags & IWP_F_FW_INIT)) { 461722a84b8dSQuaker Fang if (cv_timedwait(&sc->sc_ucode_cv, 461822a84b8dSQuaker Fang &sc->sc_glock, clk) < 0) { 461922a84b8dSQuaker Fang break; 462022a84b8dSQuaker Fang } 462122a84b8dSQuaker Fang } 462222a84b8dSQuaker Fang 462322a84b8dSQuaker Fang if (!(sc->sc_flags & IWP_F_FW_INIT)) { 462422a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_init(): " 462522a84b8dSQuaker Fang "failed to process runtime alive.\n"); 462622a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 462722a84b8dSQuaker Fang return (IWP_FAIL); 462822a84b8dSQuaker Fang } 462922a84b8dSQuaker Fang 463022a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 463122a84b8dSQuaker Fang 463222a84b8dSQuaker Fang DELAY(1000); 463322a84b8dSQuaker Fang 463422a84b8dSQuaker Fang mutex_enter(&sc->sc_glock); 463522a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_FW_INIT); 463622a84b8dSQuaker Fang 463722a84b8dSQuaker Fang /* 463822a84b8dSQuaker Fang * at this point, the firmware is loaded OK, then config the hardware 463922a84b8dSQuaker Fang * with the ucode API, including rxon, txpower, etc. 464022a84b8dSQuaker Fang */ 464122a84b8dSQuaker Fang err = iwp_config(sc); 464222a84b8dSQuaker Fang if (err) { 464322a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_init(): " 464422a84b8dSQuaker Fang "failed to configure device\n"); 464522a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 464622a84b8dSQuaker Fang return (IWP_FAIL); 464722a84b8dSQuaker Fang } 464822a84b8dSQuaker Fang 464922a84b8dSQuaker Fang /* 465022a84b8dSQuaker Fang * at this point, hardware may receive beacons :) 465122a84b8dSQuaker Fang */ 465222a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 465322a84b8dSQuaker Fang return (IWP_SUCCESS); 465422a84b8dSQuaker Fang } 465522a84b8dSQuaker Fang 465622a84b8dSQuaker Fang /* 465722a84b8dSQuaker Fang * stop or disable NIC 465822a84b8dSQuaker Fang */ 465922a84b8dSQuaker Fang static void 466022a84b8dSQuaker Fang iwp_stop(iwp_sc_t *sc) 466122a84b8dSQuaker Fang { 466222a84b8dSQuaker Fang uint32_t tmp; 466322a84b8dSQuaker Fang int i; 466422a84b8dSQuaker Fang 466522a84b8dSQuaker Fang /* by pass if it's quiesced */ 466622a84b8dSQuaker Fang if (!(sc->sc_flags & IWP_F_QUIESCED)) { 466722a84b8dSQuaker Fang mutex_enter(&sc->sc_glock); 466822a84b8dSQuaker Fang } 466922a84b8dSQuaker Fang 467022a84b8dSQuaker Fang IWP_WRITE(sc, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); 467122a84b8dSQuaker Fang /* 467222a84b8dSQuaker Fang * disable interrupts 467322a84b8dSQuaker Fang */ 467422a84b8dSQuaker Fang IWP_WRITE(sc, CSR_INT_MASK, 0); 467522a84b8dSQuaker Fang IWP_WRITE(sc, CSR_INT, CSR_INI_SET_MASK); 467622a84b8dSQuaker Fang IWP_WRITE(sc, CSR_FH_INT_STATUS, 0xffffffff); 467722a84b8dSQuaker Fang 467822a84b8dSQuaker Fang /* 467922a84b8dSQuaker Fang * reset all Tx rings 468022a84b8dSQuaker Fang */ 468122a84b8dSQuaker Fang for (i = 0; i < IWP_NUM_QUEUES; i++) { 468222a84b8dSQuaker Fang iwp_reset_tx_ring(sc, &sc->sc_txq[i]); 468322a84b8dSQuaker Fang } 468422a84b8dSQuaker Fang 468522a84b8dSQuaker Fang /* 468622a84b8dSQuaker Fang * reset Rx ring 468722a84b8dSQuaker Fang */ 468822a84b8dSQuaker Fang iwp_reset_rx_ring(sc); 468922a84b8dSQuaker Fang 469022a84b8dSQuaker Fang iwp_mac_access_enter(sc); 469122a84b8dSQuaker Fang iwp_reg_write(sc, ALM_APMG_CLK_DIS, APMG_CLK_REG_VAL_DMA_CLK_RQT); 469222a84b8dSQuaker Fang iwp_mac_access_exit(sc); 469322a84b8dSQuaker Fang 469422a84b8dSQuaker Fang DELAY(5); 469522a84b8dSQuaker Fang 469622a84b8dSQuaker Fang iwp_stop_master(sc); 469722a84b8dSQuaker Fang 469822a84b8dSQuaker Fang mutex_enter(&sc->sc_mt_lock); 469922a84b8dSQuaker Fang sc->sc_tx_timer = 0; 470022a84b8dSQuaker Fang mutex_exit(&sc->sc_mt_lock); 470122a84b8dSQuaker Fang 470222a84b8dSQuaker Fang tmp = IWP_READ(sc, CSR_RESET); 470322a84b8dSQuaker Fang IWP_WRITE(sc, CSR_RESET, tmp | CSR_RESET_REG_FLAG_SW_RESET); 470422a84b8dSQuaker Fang 470522a84b8dSQuaker Fang /* by pass if it's quiesced */ 470622a84b8dSQuaker Fang if (!(sc->sc_flags & IWP_F_QUIESCED)) { 470722a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 470822a84b8dSQuaker Fang } 470922a84b8dSQuaker Fang } 471022a84b8dSQuaker Fang 471122a84b8dSQuaker Fang /* 471222a84b8dSQuaker Fang * Naive implementation of the Adaptive Multi Rate Retry algorithm: 471322a84b8dSQuaker Fang * "IEEE 802.11 Rate Adaptation: A Practical Approach" 471422a84b8dSQuaker Fang * Mathieu Lacage, Hossein Manshaei, Thierry Turletti 471522a84b8dSQuaker Fang * INRIA Sophia - Projet Planete 471622a84b8dSQuaker Fang * http://www-sop.inria.fr/rapports/sophia/RR-5208.html 471722a84b8dSQuaker Fang */ 471822a84b8dSQuaker Fang #define is_success(amrr) \ 471922a84b8dSQuaker Fang ((amrr)->retrycnt < (amrr)->txcnt / 10) 472022a84b8dSQuaker Fang #define is_failure(amrr) \ 472122a84b8dSQuaker Fang ((amrr)->retrycnt > (amrr)->txcnt / 3) 472222a84b8dSQuaker Fang #define is_enough(amrr) \ 472322a84b8dSQuaker Fang ((amrr)->txcnt > 200) 472422a84b8dSQuaker Fang #define not_very_few(amrr) \ 472522a84b8dSQuaker Fang ((amrr)->txcnt > 40) 472622a84b8dSQuaker Fang #define is_min_rate(in) \ 472722a84b8dSQuaker Fang (0 == (in)->in_txrate) 472822a84b8dSQuaker Fang #define is_max_rate(in) \ 472922a84b8dSQuaker Fang ((in)->in_rates.ir_nrates - 1 == (in)->in_txrate) 473022a84b8dSQuaker Fang #define increase_rate(in) \ 473122a84b8dSQuaker Fang ((in)->in_txrate++) 473222a84b8dSQuaker Fang #define decrease_rate(in) \ 473322a84b8dSQuaker Fang ((in)->in_txrate--) 473422a84b8dSQuaker Fang #define reset_cnt(amrr) \ 473522a84b8dSQuaker Fang { (amrr)->txcnt = (amrr)->retrycnt = 0; } 473622a84b8dSQuaker Fang 473722a84b8dSQuaker Fang #define IWP_AMRR_MIN_SUCCESS_THRESHOLD 1 473822a84b8dSQuaker Fang #define IWP_AMRR_MAX_SUCCESS_THRESHOLD 15 473922a84b8dSQuaker Fang 474022a84b8dSQuaker Fang static void 474122a84b8dSQuaker Fang iwp_amrr_init(iwp_amrr_t *amrr) 474222a84b8dSQuaker Fang { 474322a84b8dSQuaker Fang amrr->success = 0; 474422a84b8dSQuaker Fang amrr->recovery = 0; 474522a84b8dSQuaker Fang amrr->txcnt = amrr->retrycnt = 0; 474622a84b8dSQuaker Fang amrr->success_threshold = IWP_AMRR_MIN_SUCCESS_THRESHOLD; 474722a84b8dSQuaker Fang } 474822a84b8dSQuaker Fang 474922a84b8dSQuaker Fang static void 475022a84b8dSQuaker Fang iwp_amrr_timeout(iwp_sc_t *sc) 475122a84b8dSQuaker Fang { 475222a84b8dSQuaker Fang ieee80211com_t *ic = &sc->sc_ic; 475322a84b8dSQuaker Fang 475422a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_RATECTL, "iwp_amrr_timeout(): " 475522a84b8dSQuaker Fang "enter\n")); 475622a84b8dSQuaker Fang 475722a84b8dSQuaker Fang if (IEEE80211_M_STA == ic->ic_opmode) { 475822a84b8dSQuaker Fang iwp_amrr_ratectl(NULL, ic->ic_bss); 475922a84b8dSQuaker Fang } else { 476022a84b8dSQuaker Fang ieee80211_iterate_nodes(&ic->ic_sta, iwp_amrr_ratectl, NULL); 476122a84b8dSQuaker Fang } 476222a84b8dSQuaker Fang 476322a84b8dSQuaker Fang sc->sc_clk = ddi_get_lbolt(); 476422a84b8dSQuaker Fang } 476522a84b8dSQuaker Fang 476622a84b8dSQuaker Fang /* ARGSUSED */ 476722a84b8dSQuaker Fang static void 476822a84b8dSQuaker Fang iwp_amrr_ratectl(void *arg, ieee80211_node_t *in) 476922a84b8dSQuaker Fang { 477022a84b8dSQuaker Fang iwp_amrr_t *amrr = (iwp_amrr_t *)in; 477122a84b8dSQuaker Fang int need_change = 0; 477222a84b8dSQuaker Fang 477322a84b8dSQuaker Fang if (is_success(amrr) && is_enough(amrr)) { 477422a84b8dSQuaker Fang amrr->success++; 477522a84b8dSQuaker Fang if (amrr->success >= amrr->success_threshold && 477622a84b8dSQuaker Fang !is_max_rate(in)) { 477722a84b8dSQuaker Fang amrr->recovery = 1; 477822a84b8dSQuaker Fang amrr->success = 0; 477922a84b8dSQuaker Fang increase_rate(in); 478022a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_RATECTL, "iwp_amrr_ratectl(): " 478122a84b8dSQuaker Fang "AMRR increasing rate %d " 478222a84b8dSQuaker Fang "(txcnt=%d retrycnt=%d)\n", 478322a84b8dSQuaker Fang in->in_txrate, amrr->txcnt, 478422a84b8dSQuaker Fang amrr->retrycnt)); 478522a84b8dSQuaker Fang need_change = 1; 478622a84b8dSQuaker Fang } else { 478722a84b8dSQuaker Fang amrr->recovery = 0; 478822a84b8dSQuaker Fang } 478922a84b8dSQuaker Fang } else if (not_very_few(amrr) && is_failure(amrr)) { 479022a84b8dSQuaker Fang amrr->success = 0; 479122a84b8dSQuaker Fang if (!is_min_rate(in)) { 479222a84b8dSQuaker Fang if (amrr->recovery) { 479322a84b8dSQuaker Fang amrr->success_threshold++; 479422a84b8dSQuaker Fang if (amrr->success_threshold > 479522a84b8dSQuaker Fang IWP_AMRR_MAX_SUCCESS_THRESHOLD) { 479622a84b8dSQuaker Fang amrr->success_threshold = 479722a84b8dSQuaker Fang IWP_AMRR_MAX_SUCCESS_THRESHOLD; 479822a84b8dSQuaker Fang } 479922a84b8dSQuaker Fang } else { 480022a84b8dSQuaker Fang amrr->success_threshold = 480122a84b8dSQuaker Fang IWP_AMRR_MIN_SUCCESS_THRESHOLD; 480222a84b8dSQuaker Fang } 480322a84b8dSQuaker Fang decrease_rate(in); 480422a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_RATECTL, "iwp_amrr_ratectl(): " 480522a84b8dSQuaker Fang "AMRR decreasing rate %d " 480622a84b8dSQuaker Fang "(txcnt=%d retrycnt=%d)\n", 480722a84b8dSQuaker Fang in->in_txrate, amrr->txcnt, 480822a84b8dSQuaker Fang amrr->retrycnt)); 480922a84b8dSQuaker Fang need_change = 1; 481022a84b8dSQuaker Fang } 481122a84b8dSQuaker Fang amrr->recovery = 0; /* paper is incorrect */ 481222a84b8dSQuaker Fang } 481322a84b8dSQuaker Fang 481422a84b8dSQuaker Fang if (is_enough(amrr) || need_change) { 481522a84b8dSQuaker Fang reset_cnt(amrr); 481622a84b8dSQuaker Fang } 481722a84b8dSQuaker Fang } 481822a84b8dSQuaker Fang 481922a84b8dSQuaker Fang /* 482022a84b8dSQuaker Fang * translate indirect address in eeprom to direct address 482122a84b8dSQuaker Fang * in eeprom and return address of entry whos indirect address 482222a84b8dSQuaker Fang * is indi_addr 482322a84b8dSQuaker Fang */ 482422a84b8dSQuaker Fang static uint8_t * 482522a84b8dSQuaker Fang iwp_eep_addr_trans(iwp_sc_t *sc, uint32_t indi_addr) 482622a84b8dSQuaker Fang { 482722a84b8dSQuaker Fang uint32_t di_addr; 482822a84b8dSQuaker Fang uint16_t temp; 482922a84b8dSQuaker Fang 483022a84b8dSQuaker Fang if (!(indi_addr & INDIRECT_ADDRESS)) { 483122a84b8dSQuaker Fang di_addr = indi_addr; 483222a84b8dSQuaker Fang return (&sc->sc_eep_map[di_addr]); 483322a84b8dSQuaker Fang } 483422a84b8dSQuaker Fang 483522a84b8dSQuaker Fang switch (indi_addr & INDIRECT_TYPE_MSK) { 483622a84b8dSQuaker Fang case INDIRECT_GENERAL: 483722a84b8dSQuaker Fang temp = IWP_READ_EEP_SHORT(sc, EEP_LINK_GENERAL); 483822a84b8dSQuaker Fang break; 483922a84b8dSQuaker Fang case INDIRECT_HOST: 484022a84b8dSQuaker Fang temp = IWP_READ_EEP_SHORT(sc, EEP_LINK_HOST); 484122a84b8dSQuaker Fang break; 484222a84b8dSQuaker Fang case INDIRECT_REGULATORY: 484322a84b8dSQuaker Fang temp = IWP_READ_EEP_SHORT(sc, EEP_LINK_REGULATORY); 484422a84b8dSQuaker Fang break; 484522a84b8dSQuaker Fang case INDIRECT_CALIBRATION: 484622a84b8dSQuaker Fang temp = IWP_READ_EEP_SHORT(sc, EEP_LINK_CALIBRATION); 484722a84b8dSQuaker Fang break; 484822a84b8dSQuaker Fang case INDIRECT_PROCESS_ADJST: 484922a84b8dSQuaker Fang temp = IWP_READ_EEP_SHORT(sc, EEP_LINK_PROCESS_ADJST); 485022a84b8dSQuaker Fang break; 485122a84b8dSQuaker Fang case INDIRECT_OTHERS: 485222a84b8dSQuaker Fang temp = IWP_READ_EEP_SHORT(sc, EEP_LINK_OTHERS); 485322a84b8dSQuaker Fang break; 485422a84b8dSQuaker Fang default: 485522a84b8dSQuaker Fang temp = 0; 485622a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_eep_addr_trans(): " 485722a84b8dSQuaker Fang "incorrect indirect eeprom address.\n"); 485822a84b8dSQuaker Fang break; 485922a84b8dSQuaker Fang } 486022a84b8dSQuaker Fang 486122a84b8dSQuaker Fang di_addr = (indi_addr & ADDRESS_MSK) + (temp << 1); 486222a84b8dSQuaker Fang 486322a84b8dSQuaker Fang return (&sc->sc_eep_map[di_addr]); 486422a84b8dSQuaker Fang } 486522a84b8dSQuaker Fang 486622a84b8dSQuaker Fang /* 486722a84b8dSQuaker Fang * loade a section of ucode into NIC 486822a84b8dSQuaker Fang */ 486922a84b8dSQuaker Fang static int 487022a84b8dSQuaker Fang iwp_put_seg_fw(iwp_sc_t *sc, uint32_t addr_s, uint32_t addr_d, uint32_t len) 487122a84b8dSQuaker Fang { 487222a84b8dSQuaker Fang 487322a84b8dSQuaker Fang iwp_mac_access_enter(sc); 487422a84b8dSQuaker Fang 487522a84b8dSQuaker Fang IWP_WRITE(sc, IWP_FH_TCSR_CHNL_TX_CONFIG_REG(IWP_FH_SRVC_CHNL), 487622a84b8dSQuaker Fang IWP_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); 487722a84b8dSQuaker Fang 487822a84b8dSQuaker Fang IWP_WRITE(sc, IWP_FH_SRVC_CHNL_SRAM_ADDR_REG(IWP_FH_SRVC_CHNL), addr_d); 487922a84b8dSQuaker Fang 488022a84b8dSQuaker Fang IWP_WRITE(sc, IWP_FH_TFDIB_CTRL0_REG(IWP_FH_SRVC_CHNL), 488122a84b8dSQuaker Fang (addr_s & FH_MEM_TFDIB_DRAM_ADDR_LSB_MASK)); 488222a84b8dSQuaker Fang 488322a84b8dSQuaker Fang IWP_WRITE(sc, IWP_FH_TFDIB_CTRL1_REG(IWP_FH_SRVC_CHNL), len); 488422a84b8dSQuaker Fang 488522a84b8dSQuaker Fang IWP_WRITE(sc, IWP_FH_TCSR_CHNL_TX_BUF_STS_REG(IWP_FH_SRVC_CHNL), 488622a84b8dSQuaker Fang (1 << IWP_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM) | 488722a84b8dSQuaker Fang (1 << IWP_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX) | 488822a84b8dSQuaker Fang IWP_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID); 488922a84b8dSQuaker Fang 489022a84b8dSQuaker Fang IWP_WRITE(sc, IWP_FH_TCSR_CHNL_TX_CONFIG_REG(IWP_FH_SRVC_CHNL), 489122a84b8dSQuaker Fang IWP_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | 489222a84b8dSQuaker Fang IWP_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE_VAL | 489322a84b8dSQuaker Fang IWP_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); 489422a84b8dSQuaker Fang 489522a84b8dSQuaker Fang iwp_mac_access_exit(sc); 489622a84b8dSQuaker Fang 489722a84b8dSQuaker Fang return (IWP_SUCCESS); 489822a84b8dSQuaker Fang } 489922a84b8dSQuaker Fang 490022a84b8dSQuaker Fang /* 490122a84b8dSQuaker Fang * necessary setting during alive notification 490222a84b8dSQuaker Fang */ 490322a84b8dSQuaker Fang static int 490422a84b8dSQuaker Fang iwp_alive_common(iwp_sc_t *sc) 490522a84b8dSQuaker Fang { 490622a84b8dSQuaker Fang uint32_t base; 490722a84b8dSQuaker Fang uint32_t i; 490822a84b8dSQuaker Fang iwp_wimax_coex_cmd_t w_cmd; 490922a84b8dSQuaker Fang iwp_calibration_crystal_cmd_t c_cmd; 491022a84b8dSQuaker Fang uint32_t rv = IWP_FAIL; 491122a84b8dSQuaker Fang 491222a84b8dSQuaker Fang /* 491322a84b8dSQuaker Fang * initialize SCD related registers to make TX work. 491422a84b8dSQuaker Fang */ 491522a84b8dSQuaker Fang iwp_mac_access_enter(sc); 491622a84b8dSQuaker Fang 491722a84b8dSQuaker Fang /* 491822a84b8dSQuaker Fang * read sram address of data base. 491922a84b8dSQuaker Fang */ 492022a84b8dSQuaker Fang sc->sc_scd_base = iwp_reg_read(sc, IWP_SCD_SRAM_BASE_ADDR); 492122a84b8dSQuaker Fang 492222a84b8dSQuaker Fang for (base = sc->sc_scd_base + IWP_SCD_CONTEXT_DATA_OFFSET; 492322a84b8dSQuaker Fang base < sc->sc_scd_base + IWP_SCD_TX_STTS_BITMAP_OFFSET; 492422a84b8dSQuaker Fang base += 4) { 492522a84b8dSQuaker Fang iwp_mem_write(sc, base, 0); 492622a84b8dSQuaker Fang } 492722a84b8dSQuaker Fang 492822a84b8dSQuaker Fang for (; base < sc->sc_scd_base + IWP_SCD_TRANSLATE_TBL_OFFSET; 492922a84b8dSQuaker Fang base += 4) { 493022a84b8dSQuaker Fang iwp_mem_write(sc, base, 0); 493122a84b8dSQuaker Fang } 493222a84b8dSQuaker Fang 493322a84b8dSQuaker Fang for (i = 0; i < sizeof (uint16_t) * IWP_NUM_QUEUES; i += 4) { 493422a84b8dSQuaker Fang iwp_mem_write(sc, base + i, 0); 493522a84b8dSQuaker Fang } 493622a84b8dSQuaker Fang 493722a84b8dSQuaker Fang iwp_reg_write(sc, IWP_SCD_DRAM_BASE_ADDR, 493822a84b8dSQuaker Fang sc->sc_dma_sh.cookie.dmac_address >> 10); 493922a84b8dSQuaker Fang 494022a84b8dSQuaker Fang iwp_reg_write(sc, IWP_SCD_QUEUECHAIN_SEL, 494122a84b8dSQuaker Fang IWP_SCD_QUEUECHAIN_SEL_ALL(IWP_NUM_QUEUES)); 494222a84b8dSQuaker Fang 494322a84b8dSQuaker Fang iwp_reg_write(sc, IWP_SCD_AGGR_SEL, 0); 494422a84b8dSQuaker Fang 494522a84b8dSQuaker Fang for (i = 0; i < IWP_NUM_QUEUES; i++) { 494622a84b8dSQuaker Fang iwp_reg_write(sc, IWP_SCD_QUEUE_RDPTR(i), 0); 494722a84b8dSQuaker Fang IWP_WRITE(sc, HBUS_TARG_WRPTR, 0 | (i << 8)); 494822a84b8dSQuaker Fang iwp_mem_write(sc, sc->sc_scd_base + 494922a84b8dSQuaker Fang IWP_SCD_CONTEXT_QUEUE_OFFSET(i), 0); 495022a84b8dSQuaker Fang iwp_mem_write(sc, sc->sc_scd_base + 495122a84b8dSQuaker Fang IWP_SCD_CONTEXT_QUEUE_OFFSET(i) + 495222a84b8dSQuaker Fang sizeof (uint32_t), 495322a84b8dSQuaker Fang ((SCD_WIN_SIZE << IWP_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & 495422a84b8dSQuaker Fang IWP_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | 495522a84b8dSQuaker Fang ((SCD_FRAME_LIMIT << 495622a84b8dSQuaker Fang IWP_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & 495722a84b8dSQuaker Fang IWP_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); 495822a84b8dSQuaker Fang } 495922a84b8dSQuaker Fang 496022a84b8dSQuaker Fang iwp_reg_write(sc, IWP_SCD_INTERRUPT_MASK, (1 << IWP_NUM_QUEUES) - 1); 496122a84b8dSQuaker Fang 496222a84b8dSQuaker Fang iwp_reg_write(sc, (IWP_SCD_BASE + 0x10), 496322a84b8dSQuaker Fang SCD_TXFACT_REG_TXFIFO_MASK(0, 7)); 496422a84b8dSQuaker Fang 496522a84b8dSQuaker Fang IWP_WRITE(sc, HBUS_TARG_WRPTR, (IWP_CMD_QUEUE_NUM << 8)); 496622a84b8dSQuaker Fang iwp_reg_write(sc, IWP_SCD_QUEUE_RDPTR(IWP_CMD_QUEUE_NUM), 0); 496722a84b8dSQuaker Fang 496822a84b8dSQuaker Fang /* 496922a84b8dSQuaker Fang * queue 0-7 map to FIFO 0-7 and 497022a84b8dSQuaker Fang * all queues work under FIFO mode(none-scheduler_ack) 497122a84b8dSQuaker Fang */ 497222a84b8dSQuaker Fang for (i = 0; i < 4; i++) { 497322a84b8dSQuaker Fang iwp_reg_write(sc, IWP_SCD_QUEUE_STATUS_BITS(i), 497422a84b8dSQuaker Fang (1 << IWP_SCD_QUEUE_STTS_REG_POS_ACTIVE) | 497522a84b8dSQuaker Fang ((3-i) << IWP_SCD_QUEUE_STTS_REG_POS_TXF) | 497622a84b8dSQuaker Fang (1 << IWP_SCD_QUEUE_STTS_REG_POS_WSL) | 497722a84b8dSQuaker Fang IWP_SCD_QUEUE_STTS_REG_MSK); 497822a84b8dSQuaker Fang } 497922a84b8dSQuaker Fang 498022a84b8dSQuaker Fang iwp_reg_write(sc, IWP_SCD_QUEUE_STATUS_BITS(IWP_CMD_QUEUE_NUM), 498122a84b8dSQuaker Fang (1 << IWP_SCD_QUEUE_STTS_REG_POS_ACTIVE) | 498222a84b8dSQuaker Fang (IWP_CMD_FIFO_NUM << IWP_SCD_QUEUE_STTS_REG_POS_TXF) | 498322a84b8dSQuaker Fang (1 << IWP_SCD_QUEUE_STTS_REG_POS_WSL) | 498422a84b8dSQuaker Fang IWP_SCD_QUEUE_STTS_REG_MSK); 498522a84b8dSQuaker Fang 498622a84b8dSQuaker Fang for (i = 5; i < 7; i++) { 498722a84b8dSQuaker Fang iwp_reg_write(sc, IWP_SCD_QUEUE_STATUS_BITS(i), 498822a84b8dSQuaker Fang (1 << IWP_SCD_QUEUE_STTS_REG_POS_ACTIVE) | 498922a84b8dSQuaker Fang (i << IWP_SCD_QUEUE_STTS_REG_POS_TXF) | 499022a84b8dSQuaker Fang (1 << IWP_SCD_QUEUE_STTS_REG_POS_WSL) | 499122a84b8dSQuaker Fang IWP_SCD_QUEUE_STTS_REG_MSK); 499222a84b8dSQuaker Fang } 499322a84b8dSQuaker Fang 499422a84b8dSQuaker Fang iwp_mac_access_exit(sc); 499522a84b8dSQuaker Fang 499622a84b8dSQuaker Fang (void) memset(&w_cmd, 0, sizeof (w_cmd)); 499722a84b8dSQuaker Fang 499822a84b8dSQuaker Fang rv = iwp_cmd(sc, COEX_PRIORITY_TABLE_CMD, &w_cmd, sizeof (w_cmd), 1); 499922a84b8dSQuaker Fang if (rv != IWP_SUCCESS) { 500022a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_alive_common(): " 500122a84b8dSQuaker Fang "failed to send wimax coexist command.\n"); 500222a84b8dSQuaker Fang return (rv); 500322a84b8dSQuaker Fang } 500422a84b8dSQuaker Fang 500522a84b8dSQuaker Fang (void) memset(&c_cmd, 0, sizeof (c_cmd)); 500622a84b8dSQuaker Fang 500722a84b8dSQuaker Fang c_cmd.opCode = PHY_CALIBRATE_CRYSTAL_FRQ_CMD; 500822a84b8dSQuaker Fang c_cmd.data.cap_pin1 = LE_16(sc->sc_eep_calib->xtal_calib[0]); 500922a84b8dSQuaker Fang c_cmd.data.cap_pin2 = LE_16(sc->sc_eep_calib->xtal_calib[1]); 501022a84b8dSQuaker Fang 501122a84b8dSQuaker Fang rv = iwp_cmd(sc, REPLY_PHY_CALIBRATION_CMD, &c_cmd, sizeof (c_cmd), 1); 501222a84b8dSQuaker Fang if (rv != IWP_SUCCESS) { 501322a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_alive_common(): " 501422a84b8dSQuaker Fang "failed to send crystal frq calibration command.\n"); 501522a84b8dSQuaker Fang return (rv); 501622a84b8dSQuaker Fang } 501722a84b8dSQuaker Fang 501822a84b8dSQuaker Fang /* 501922a84b8dSQuaker Fang * make sure crystal frequency calibration ready 502022a84b8dSQuaker Fang * before next operations. 502122a84b8dSQuaker Fang */ 502222a84b8dSQuaker Fang DELAY(1000); 502322a84b8dSQuaker Fang 502422a84b8dSQuaker Fang return (IWP_SUCCESS); 502522a84b8dSQuaker Fang } 502622a84b8dSQuaker Fang 502722a84b8dSQuaker Fang /* 502822a84b8dSQuaker Fang * save results of calibration from ucode 502922a84b8dSQuaker Fang */ 503022a84b8dSQuaker Fang static void 503122a84b8dSQuaker Fang iwp_save_calib_result(iwp_sc_t *sc, iwp_rx_desc_t *desc) 503222a84b8dSQuaker Fang { 503322a84b8dSQuaker Fang struct iwp_calib_results *res_p = &sc->sc_calib_results; 503422a84b8dSQuaker Fang struct iwp_calib_hdr *calib_hdr = (struct iwp_calib_hdr *)(desc + 1); 503522a84b8dSQuaker Fang int len = LE_32(desc->len); 503622a84b8dSQuaker Fang 503722a84b8dSQuaker Fang /* 503822a84b8dSQuaker Fang * ensure the size of buffer is not too big 503922a84b8dSQuaker Fang */ 504022a84b8dSQuaker Fang len = (len & FH_RSCSR_FRAME_SIZE_MASK) - 4; 504122a84b8dSQuaker Fang 504222a84b8dSQuaker Fang switch (calib_hdr->op_code) { 504322a84b8dSQuaker Fang case PHY_CALIBRATE_LO_CMD: 504422a84b8dSQuaker Fang if (NULL == res_p->lo_res) { 504522a84b8dSQuaker Fang res_p->lo_res = kmem_alloc(len, KM_NOSLEEP); 504622a84b8dSQuaker Fang } 504722a84b8dSQuaker Fang 504822a84b8dSQuaker Fang if (NULL == res_p->lo_res) { 504922a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_save_calib_result(): " 505022a84b8dSQuaker Fang "failed to allocate memory.\n"); 505122a84b8dSQuaker Fang return; 505222a84b8dSQuaker Fang } 505322a84b8dSQuaker Fang 505422a84b8dSQuaker Fang res_p->lo_res_len = len; 505522a84b8dSQuaker Fang (void) memcpy(res_p->lo_res, calib_hdr, len); 505622a84b8dSQuaker Fang break; 505722a84b8dSQuaker Fang case PHY_CALIBRATE_TX_IQ_CMD: 505822a84b8dSQuaker Fang if (NULL == res_p->tx_iq_res) { 505922a84b8dSQuaker Fang res_p->tx_iq_res = kmem_alloc(len, KM_NOSLEEP); 506022a84b8dSQuaker Fang } 506122a84b8dSQuaker Fang 506222a84b8dSQuaker Fang if (NULL == res_p->tx_iq_res) { 506322a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_save_calib_result(): " 506422a84b8dSQuaker Fang "failed to allocate memory.\n"); 506522a84b8dSQuaker Fang return; 506622a84b8dSQuaker Fang } 506722a84b8dSQuaker Fang 506822a84b8dSQuaker Fang res_p->tx_iq_res_len = len; 506922a84b8dSQuaker Fang (void) memcpy(res_p->tx_iq_res, calib_hdr, len); 507022a84b8dSQuaker Fang break; 507122a84b8dSQuaker Fang case PHY_CALIBRATE_TX_IQ_PERD_CMD: 507222a84b8dSQuaker Fang if (NULL == res_p->tx_iq_perd_res) { 507322a84b8dSQuaker Fang res_p->tx_iq_perd_res = kmem_alloc(len, KM_NOSLEEP); 507422a84b8dSQuaker Fang } 507522a84b8dSQuaker Fang 507622a84b8dSQuaker Fang if (NULL == res_p->tx_iq_perd_res) { 507722a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_save_calib_result(): " 507822a84b8dSQuaker Fang "failed to allocate memory.\n"); 507922a84b8dSQuaker Fang } 508022a84b8dSQuaker Fang 508122a84b8dSQuaker Fang res_p->tx_iq_perd_res_len = len; 508222a84b8dSQuaker Fang (void) memcpy(res_p->tx_iq_perd_res, calib_hdr, len); 508322a84b8dSQuaker Fang break; 508422a84b8dSQuaker Fang case PHY_CALIBRATE_BASE_BAND_CMD: 508522a84b8dSQuaker Fang if (NULL == res_p->base_band_res) { 508622a84b8dSQuaker Fang res_p->base_band_res = kmem_alloc(len, KM_NOSLEEP); 508722a84b8dSQuaker Fang } 508822a84b8dSQuaker Fang 508922a84b8dSQuaker Fang if (NULL == res_p->base_band_res) { 509022a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_save_calib_result(): " 509122a84b8dSQuaker Fang "failed to allocate memory.\n"); 509222a84b8dSQuaker Fang } 509322a84b8dSQuaker Fang 509422a84b8dSQuaker Fang res_p->base_band_res_len = len; 509522a84b8dSQuaker Fang (void) memcpy(res_p->base_band_res, calib_hdr, len); 509622a84b8dSQuaker Fang break; 509722a84b8dSQuaker Fang default: 509822a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_save_calib_result(): " 509922a84b8dSQuaker Fang "incorrect calibration type(%d).\n", calib_hdr->op_code); 510022a84b8dSQuaker Fang break; 510122a84b8dSQuaker Fang } 510222a84b8dSQuaker Fang 510322a84b8dSQuaker Fang } 510422a84b8dSQuaker Fang 510522a84b8dSQuaker Fang static void 510622a84b8dSQuaker Fang iwp_release_calib_buffer(iwp_sc_t *sc) 510722a84b8dSQuaker Fang { 510822a84b8dSQuaker Fang if (sc->sc_calib_results.lo_res != NULL) { 510922a84b8dSQuaker Fang kmem_free(sc->sc_calib_results.lo_res, 511022a84b8dSQuaker Fang sc->sc_calib_results.lo_res_len); 511122a84b8dSQuaker Fang sc->sc_calib_results.lo_res = NULL; 511222a84b8dSQuaker Fang } 511322a84b8dSQuaker Fang 511422a84b8dSQuaker Fang if (sc->sc_calib_results.tx_iq_res != NULL) { 511522a84b8dSQuaker Fang kmem_free(sc->sc_calib_results.tx_iq_res, 511622a84b8dSQuaker Fang sc->sc_calib_results.tx_iq_res_len); 511722a84b8dSQuaker Fang sc->sc_calib_results.tx_iq_res = NULL; 511822a84b8dSQuaker Fang } 511922a84b8dSQuaker Fang 512022a84b8dSQuaker Fang if (sc->sc_calib_results.tx_iq_perd_res != NULL) { 512122a84b8dSQuaker Fang kmem_free(sc->sc_calib_results.tx_iq_perd_res, 512222a84b8dSQuaker Fang sc->sc_calib_results.tx_iq_perd_res_len); 512322a84b8dSQuaker Fang sc->sc_calib_results.tx_iq_perd_res = NULL; 512422a84b8dSQuaker Fang } 512522a84b8dSQuaker Fang 512622a84b8dSQuaker Fang if (sc->sc_calib_results.base_band_res != NULL) { 512722a84b8dSQuaker Fang kmem_free(sc->sc_calib_results.base_band_res, 512822a84b8dSQuaker Fang sc->sc_calib_results.base_band_res_len); 512922a84b8dSQuaker Fang sc->sc_calib_results.base_band_res = NULL; 513022a84b8dSQuaker Fang } 513122a84b8dSQuaker Fang 513222a84b8dSQuaker Fang } 513322a84b8dSQuaker Fang 513422a84b8dSQuaker Fang /* 513522a84b8dSQuaker Fang * common section of intialization 513622a84b8dSQuaker Fang */ 513722a84b8dSQuaker Fang static int 513822a84b8dSQuaker Fang iwp_init_common(iwp_sc_t *sc) 513922a84b8dSQuaker Fang { 514022a84b8dSQuaker Fang int32_t qid; 514122a84b8dSQuaker Fang uint32_t tmp; 514222a84b8dSQuaker Fang 514322a84b8dSQuaker Fang (void) iwp_preinit(sc); 514422a84b8dSQuaker Fang 514522a84b8dSQuaker Fang tmp = IWP_READ(sc, CSR_GP_CNTRL); 514622a84b8dSQuaker Fang if (!(tmp & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) { 514722a84b8dSQuaker Fang cmn_err(CE_NOTE, "iwp_init_common(): " 514822a84b8dSQuaker Fang "radio transmitter is off\n"); 514922a84b8dSQuaker Fang return (IWP_FAIL); 515022a84b8dSQuaker Fang } 515122a84b8dSQuaker Fang 515222a84b8dSQuaker Fang /* 515322a84b8dSQuaker Fang * init Rx ring 515422a84b8dSQuaker Fang */ 515522a84b8dSQuaker Fang iwp_mac_access_enter(sc); 515622a84b8dSQuaker Fang IWP_WRITE(sc, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); 515722a84b8dSQuaker Fang 515822a84b8dSQuaker Fang IWP_WRITE(sc, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); 515922a84b8dSQuaker Fang IWP_WRITE(sc, FH_RSCSR_CHNL0_RBDCB_BASE_REG, 516022a84b8dSQuaker Fang sc->sc_rxq.dma_desc.cookie.dmac_address >> 8); 516122a84b8dSQuaker Fang 516222a84b8dSQuaker Fang IWP_WRITE(sc, FH_RSCSR_CHNL0_STTS_WPTR_REG, 516322a84b8dSQuaker Fang ((uint32_t)(sc->sc_dma_sh.cookie.dmac_address + 516422a84b8dSQuaker Fang offsetof(struct iwp_shared, val0)) >> 4)); 516522a84b8dSQuaker Fang 516622a84b8dSQuaker Fang IWP_WRITE(sc, FH_MEM_RCSR_CHNL0_CONFIG_REG, 516722a84b8dSQuaker Fang FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | 516822a84b8dSQuaker Fang FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | 516922a84b8dSQuaker Fang IWP_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K | 517022a84b8dSQuaker Fang (RX_QUEUE_SIZE_LOG << 517122a84b8dSQuaker Fang FH_RCSR_RX_CONFIG_RBDCB_SIZE_BITSHIFT)); 517222a84b8dSQuaker Fang iwp_mac_access_exit(sc); 517322a84b8dSQuaker Fang IWP_WRITE(sc, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 517422a84b8dSQuaker Fang (RX_QUEUE_SIZE - 1) & ~0x7); 517522a84b8dSQuaker Fang 517622a84b8dSQuaker Fang /* 517722a84b8dSQuaker Fang * init Tx rings 517822a84b8dSQuaker Fang */ 517922a84b8dSQuaker Fang iwp_mac_access_enter(sc); 518022a84b8dSQuaker Fang iwp_reg_write(sc, IWP_SCD_TXFACT, 0); 518122a84b8dSQuaker Fang 518222a84b8dSQuaker Fang /* 518322a84b8dSQuaker Fang * keep warm page 518422a84b8dSQuaker Fang */ 518522a84b8dSQuaker Fang IWP_WRITE(sc, IWP_FH_KW_MEM_ADDR_REG, 518622a84b8dSQuaker Fang sc->sc_dma_kw.cookie.dmac_address >> 4); 518722a84b8dSQuaker Fang 518822a84b8dSQuaker Fang for (qid = 0; qid < IWP_NUM_QUEUES; qid++) { 518922a84b8dSQuaker Fang IWP_WRITE(sc, FH_MEM_CBBC_QUEUE(qid), 519022a84b8dSQuaker Fang sc->sc_txq[qid].dma_desc.cookie.dmac_address >> 8); 519122a84b8dSQuaker Fang IWP_WRITE(sc, IWP_FH_TCSR_CHNL_TX_CONFIG_REG(qid), 519222a84b8dSQuaker Fang IWP_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | 519322a84b8dSQuaker Fang IWP_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL); 519422a84b8dSQuaker Fang } 519522a84b8dSQuaker Fang 519622a84b8dSQuaker Fang iwp_mac_access_exit(sc); 519722a84b8dSQuaker Fang 519822a84b8dSQuaker Fang /* 519922a84b8dSQuaker Fang * clear "radio off" and "disable command" bits 520022a84b8dSQuaker Fang */ 520122a84b8dSQuaker Fang IWP_WRITE(sc, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); 520222a84b8dSQuaker Fang IWP_WRITE(sc, CSR_UCODE_DRV_GP1_CLR, 520322a84b8dSQuaker Fang CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); 520422a84b8dSQuaker Fang 520522a84b8dSQuaker Fang /* 520622a84b8dSQuaker Fang * clear any pending interrupts 520722a84b8dSQuaker Fang */ 520822a84b8dSQuaker Fang IWP_WRITE(sc, CSR_INT, 0xffffffff); 520922a84b8dSQuaker Fang 521022a84b8dSQuaker Fang /* 521122a84b8dSQuaker Fang * enable interrupts 521222a84b8dSQuaker Fang */ 521322a84b8dSQuaker Fang IWP_WRITE(sc, CSR_INT_MASK, CSR_INI_SET_MASK); 521422a84b8dSQuaker Fang 521522a84b8dSQuaker Fang IWP_WRITE(sc, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); 521622a84b8dSQuaker Fang IWP_WRITE(sc, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); 521722a84b8dSQuaker Fang 521822a84b8dSQuaker Fang return (IWP_SUCCESS); 521922a84b8dSQuaker Fang } 522022a84b8dSQuaker Fang 522122a84b8dSQuaker Fang static int 522222a84b8dSQuaker Fang iwp_fast_recover(iwp_sc_t *sc) 522322a84b8dSQuaker Fang { 522422a84b8dSQuaker Fang ieee80211com_t *ic = &sc->sc_ic; 522522a84b8dSQuaker Fang int err = IWP_FAIL; 522622a84b8dSQuaker Fang 522722a84b8dSQuaker Fang mutex_enter(&sc->sc_glock); 522822a84b8dSQuaker Fang 522922a84b8dSQuaker Fang /* restore runtime configuration */ 523022a84b8dSQuaker Fang bcopy(&sc->sc_config_save, &sc->sc_config, 523122a84b8dSQuaker Fang sizeof (sc->sc_config)); 523222a84b8dSQuaker Fang 523322a84b8dSQuaker Fang sc->sc_config.assoc_id = 0; 523422a84b8dSQuaker Fang sc->sc_config.filter_flags &= ~LE_32(RXON_FILTER_ASSOC_MSK); 523522a84b8dSQuaker Fang 523622a84b8dSQuaker Fang if ((err = iwp_hw_set_before_auth(sc)) != IWP_SUCCESS) { 523722a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_fast_recover(): " 523822a84b8dSQuaker Fang "could not setup authentication\n"); 523922a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 524022a84b8dSQuaker Fang return (err); 524122a84b8dSQuaker Fang } 524222a84b8dSQuaker Fang 524322a84b8dSQuaker Fang bcopy(&sc->sc_config_save, &sc->sc_config, 524422a84b8dSQuaker Fang sizeof (sc->sc_config)); 524522a84b8dSQuaker Fang 524622a84b8dSQuaker Fang /* update adapter's configuration */ 524722a84b8dSQuaker Fang err = iwp_run_state_config(sc); 524822a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 524922a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_fast_recover(): " 525022a84b8dSQuaker Fang "failed to setup association\n"); 525122a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 525222a84b8dSQuaker Fang return (err); 525322a84b8dSQuaker Fang } 525422a84b8dSQuaker Fang /* set LED on */ 525522a84b8dSQuaker Fang iwp_set_led(sc, 2, 0, 1); 525622a84b8dSQuaker Fang 525722a84b8dSQuaker Fang mutex_exit(&sc->sc_glock); 525822a84b8dSQuaker Fang 525922a84b8dSQuaker Fang atomic_and_32(&sc->sc_flags, ~IWP_F_HW_ERR_RECOVER); 526022a84b8dSQuaker Fang 526122a84b8dSQuaker Fang /* start queue */ 526222a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_FW, "iwp_fast_recover(): " 526322a84b8dSQuaker Fang "resume xmit\n")); 526422a84b8dSQuaker Fang mac_tx_update(ic->ic_mach); 526522a84b8dSQuaker Fang 526622a84b8dSQuaker Fang return (IWP_SUCCESS); 526722a84b8dSQuaker Fang } 526822a84b8dSQuaker Fang 526922a84b8dSQuaker Fang static int 527022a84b8dSQuaker Fang iwp_run_state_config(iwp_sc_t *sc) 527122a84b8dSQuaker Fang { 527222a84b8dSQuaker Fang struct ieee80211com *ic = &sc->sc_ic; 527322a84b8dSQuaker Fang ieee80211_node_t *in = ic->ic_bss; 527422a84b8dSQuaker Fang int err = IWP_FAIL; 527522a84b8dSQuaker Fang 527622a84b8dSQuaker Fang /* 527722a84b8dSQuaker Fang * update adapter's configuration 527822a84b8dSQuaker Fang */ 527922a84b8dSQuaker Fang sc->sc_config.assoc_id = in->in_associd & 0x3fff; 528022a84b8dSQuaker Fang 528122a84b8dSQuaker Fang /* 528222a84b8dSQuaker Fang * short preamble/slot time are 528322a84b8dSQuaker Fang * negotiated when associating 528422a84b8dSQuaker Fang */ 528522a84b8dSQuaker Fang sc->sc_config.flags &= 528622a84b8dSQuaker Fang ~LE_32(RXON_FLG_SHORT_PREAMBLE_MSK | 528722a84b8dSQuaker Fang RXON_FLG_SHORT_SLOT_MSK); 528822a84b8dSQuaker Fang 528922a84b8dSQuaker Fang if (ic->ic_flags & IEEE80211_F_SHSLOT) { 529022a84b8dSQuaker Fang sc->sc_config.flags |= 529122a84b8dSQuaker Fang LE_32(RXON_FLG_SHORT_SLOT_MSK); 529222a84b8dSQuaker Fang } 529322a84b8dSQuaker Fang 529422a84b8dSQuaker Fang if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) { 529522a84b8dSQuaker Fang sc->sc_config.flags |= 529622a84b8dSQuaker Fang LE_32(RXON_FLG_SHORT_PREAMBLE_MSK); 529722a84b8dSQuaker Fang } 529822a84b8dSQuaker Fang 529922a84b8dSQuaker Fang sc->sc_config.filter_flags |= 530022a84b8dSQuaker Fang LE_32(RXON_FILTER_ASSOC_MSK); 530122a84b8dSQuaker Fang 530222a84b8dSQuaker Fang if (ic->ic_opmode != IEEE80211_M_STA) { 530322a84b8dSQuaker Fang sc->sc_config.filter_flags |= 530422a84b8dSQuaker Fang LE_32(RXON_FILTER_BCON_AWARE_MSK); 530522a84b8dSQuaker Fang } 530622a84b8dSQuaker Fang 530722a84b8dSQuaker Fang IWP_DBG((IWP_DEBUG_80211, "iwp_run_state_config(): " 530822a84b8dSQuaker Fang "config chan %d flags %x" 530922a84b8dSQuaker Fang " filter_flags %x\n", 531022a84b8dSQuaker Fang sc->sc_config.chan, sc->sc_config.flags, 531122a84b8dSQuaker Fang sc->sc_config.filter_flags)); 531222a84b8dSQuaker Fang 531322a84b8dSQuaker Fang err = iwp_cmd(sc, REPLY_RXON, &sc->sc_config, 531422a84b8dSQuaker Fang sizeof (iwp_rxon_cmd_t), 1); 531522a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 531622a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_run_state_config(): " 531722a84b8dSQuaker Fang "could not update configuration\n"); 531822a84b8dSQuaker Fang return (err); 531922a84b8dSQuaker Fang } 532022a84b8dSQuaker Fang 532122a84b8dSQuaker Fang return (err); 532222a84b8dSQuaker Fang } 532322a84b8dSQuaker Fang 532422a84b8dSQuaker Fang /* 532522a84b8dSQuaker Fang * This function overwrites default configurations of 532622a84b8dSQuaker Fang * ieee80211com structure in Net80211 module. 532722a84b8dSQuaker Fang */ 532822a84b8dSQuaker Fang static void 532922a84b8dSQuaker Fang iwp_overwrite_ic_default(iwp_sc_t *sc) 533022a84b8dSQuaker Fang { 533122a84b8dSQuaker Fang ieee80211com_t *ic = &sc->sc_ic; 533222a84b8dSQuaker Fang 533322a84b8dSQuaker Fang sc->sc_newstate = ic->ic_newstate; 533422a84b8dSQuaker Fang ic->ic_newstate = iwp_newstate; 533522a84b8dSQuaker Fang ic->ic_node_alloc = iwp_node_alloc; 533622a84b8dSQuaker Fang ic->ic_node_free = iwp_node_free; 533722a84b8dSQuaker Fang } 533822a84b8dSQuaker Fang 533922a84b8dSQuaker Fang 534022a84b8dSQuaker Fang /* 534122a84b8dSQuaker Fang * This function adds AP station into hardware. 534222a84b8dSQuaker Fang */ 534322a84b8dSQuaker Fang static int 534422a84b8dSQuaker Fang iwp_add_ap_sta(iwp_sc_t *sc) 534522a84b8dSQuaker Fang { 534622a84b8dSQuaker Fang ieee80211com_t *ic = &sc->sc_ic; 534722a84b8dSQuaker Fang ieee80211_node_t *in = ic->ic_bss; 534822a84b8dSQuaker Fang iwp_add_sta_t node; 534922a84b8dSQuaker Fang int err = IWP_FAIL; 535022a84b8dSQuaker Fang 535122a84b8dSQuaker Fang /* 535222a84b8dSQuaker Fang * Add AP node into hardware. 535322a84b8dSQuaker Fang */ 535422a84b8dSQuaker Fang (void) memset(&node, 0, sizeof (node)); 535522a84b8dSQuaker Fang IEEE80211_ADDR_COPY(node.sta.addr, in->in_bssid); 535622a84b8dSQuaker Fang node.mode = STA_MODE_ADD_MSK; 535722a84b8dSQuaker Fang node.sta.sta_id = IWP_AP_ID; 535822a84b8dSQuaker Fang 535922a84b8dSQuaker Fang err = iwp_cmd(sc, REPLY_ADD_STA, &node, sizeof (node), 1); 536022a84b8dSQuaker Fang if (err != IWP_SUCCESS) { 536122a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_add_ap_sta(): " 536222a84b8dSQuaker Fang "failed to add AP node\n"); 536322a84b8dSQuaker Fang return (err); 536422a84b8dSQuaker Fang } 536522a84b8dSQuaker Fang 536622a84b8dSQuaker Fang return (err); 536722a84b8dSQuaker Fang } 536822a84b8dSQuaker Fang 536922a84b8dSQuaker Fang /* 537022a84b8dSQuaker Fang * Check EEPROM version and Calibration version. 537122a84b8dSQuaker Fang */ 537222a84b8dSQuaker Fang static int 537322a84b8dSQuaker Fang iwp_eep_ver_chk(iwp_sc_t *sc) 537422a84b8dSQuaker Fang { 537522a84b8dSQuaker Fang if ((IWP_READ_EEP_SHORT(sc, EEP_VERSION) < 0x011a) || 537622a84b8dSQuaker Fang (sc->sc_eep_calib->tx_pow_calib_hdr.calib_version < 4)) { 537722a84b8dSQuaker Fang cmn_err(CE_WARN, "iwp_eep_ver_chk(): " 537822a84b8dSQuaker Fang "unsupported eeprom detected\n"); 537922a84b8dSQuaker Fang return (IWP_FAIL); 538022a84b8dSQuaker Fang } 538122a84b8dSQuaker Fang 538222a84b8dSQuaker Fang return (IWP_SUCCESS); 538322a84b8dSQuaker Fang } 538422a84b8dSQuaker Fang 538522a84b8dSQuaker Fang /* 538622a84b8dSQuaker Fang * Determine parameters for all supported chips. 538722a84b8dSQuaker Fang */ 538822a84b8dSQuaker Fang static void 538922a84b8dSQuaker Fang iwp_set_chip_param(iwp_sc_t *sc) 539022a84b8dSQuaker Fang { 539122a84b8dSQuaker Fang if ((0x008d == sc->sc_dev_id) || 539222a84b8dSQuaker Fang (0x008e == sc->sc_dev_id)) { 539322a84b8dSQuaker Fang sc->sc_chip_param.phy_mode = PHY_MODE_G | 539422a84b8dSQuaker Fang PHY_MODE_A | PHY_MODE_N; 539522a84b8dSQuaker Fang 539622a84b8dSQuaker Fang sc->sc_chip_param.tx_ant = ANT_A | ANT_B; 539722a84b8dSQuaker Fang sc->sc_chip_param.rx_ant = ANT_A | ANT_B; 539822a84b8dSQuaker Fang 539922a84b8dSQuaker Fang sc->sc_chip_param.pa_type = PA_TYPE_MIX; 540022a84b8dSQuaker Fang } 540122a84b8dSQuaker Fang 540222a84b8dSQuaker Fang if ((0x422c == sc->sc_dev_id) || 540322a84b8dSQuaker Fang (0x4239 == sc->sc_dev_id)) { 540422a84b8dSQuaker Fang sc->sc_chip_param.phy_mode = PHY_MODE_G | 540522a84b8dSQuaker Fang PHY_MODE_A | PHY_MODE_N; 540622a84b8dSQuaker Fang 540722a84b8dSQuaker Fang sc->sc_chip_param.tx_ant = ANT_B | ANT_C; 540822a84b8dSQuaker Fang sc->sc_chip_param.rx_ant = ANT_B | ANT_C; 540922a84b8dSQuaker Fang 541022a84b8dSQuaker Fang sc->sc_chip_param.pa_type = PA_TYPE_INTER; 541122a84b8dSQuaker Fang } 541222a84b8dSQuaker Fang 541322a84b8dSQuaker Fang if ((0x422b == sc->sc_dev_id) || 541422a84b8dSQuaker Fang (0x4238 == sc->sc_dev_id)) { 541522a84b8dSQuaker Fang sc->sc_chip_param.phy_mode = PHY_MODE_G | 541622a84b8dSQuaker Fang PHY_MODE_A | PHY_MODE_N; 541722a84b8dSQuaker Fang 541822a84b8dSQuaker Fang sc->sc_chip_param.tx_ant = ANT_A | ANT_B | ANT_C; 541922a84b8dSQuaker Fang sc->sc_chip_param.rx_ant = ANT_A | ANT_B | ANT_C; 542022a84b8dSQuaker Fang 542122a84b8dSQuaker Fang sc->sc_chip_param.pa_type = PA_TYPE_SYSTEM; 542222a84b8dSQuaker Fang } 542322a84b8dSQuaker Fang } 5424