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
_init(void)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
_fini(void)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
_info(struct modinfo * mip)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
iwp_dbg(uint32_t flags,const char * fmt,...)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
iwp_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
iwp_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
iwp_destroy_locks(iwp_sc_t * sc)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
iwp_alloc_dma_mem(iwp_sc_t * sc,size_t memsize,ddi_dma_attr_t * dma_attr_p,ddi_device_acc_attr_t * acc_attr_p,uint_t dma_flags,iwp_dma_t * dma_p)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
iwp_free_dma_mem(iwp_dma_t * dma_p)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
iwp_alloc_fw_dma(iwp_sc_t * sc)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
iwp_free_fw_dma(iwp_sc_t * sc)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
iwp_alloc_shared(iwp_sc_t * sc)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
iwp_free_shared(iwp_sc_t * sc)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
iwp_alloc_kw(iwp_sc_t * sc)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
iwp_free_kw(iwp_sc_t * sc)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
iwp_alloc_rx_ring(iwp_sc_t * sc)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
iwp_reset_rx_ring(iwp_sc_t * sc)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
iwp_free_rx_ring(iwp_sc_t * sc)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
iwp_alloc_tx_ring(iwp_sc_t * sc,iwp_tx_ring_t * ring,int slots,int qid)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
iwp_reset_tx_ring(iwp_sc_t * sc,iwp_tx_ring_t * ring)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
iwp_free_tx_ring(iwp_tx_ring_t * ring)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
iwp_ring_init(iwp_sc_t * sc)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
iwp_ring_free(iwp_sc_t * sc)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 *
iwp_node_alloc(ieee80211com_t * ic)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
iwp_node_free(ieee80211_node_t * in)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
iwp_newstate(ieee80211com_t * ic,enum ieee80211_state nstate,int arg)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
iwp_mac_access_enter(iwp_sc_t * sc)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
iwp_mac_access_exit(iwp_sc_t * sc)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
iwp_mem_write(iwp_sc_t * sc,uint32_t addr,uint32_t data)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
iwp_reg_read(iwp_sc_t * sc,uint32_t addr)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
iwp_reg_write(iwp_sc_t * sc,uint32_t addr,uint32_t data)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
iwp_load_init_firmware(iwp_sc_t * sc)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
iwp_load_run_firmware(iwp_sc_t * sc)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
iwp_rx_phy_intr(iwp_sc_t * sc,iwp_rx_desc_t * desc)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
iwp_rx_mpdu_intr(iwp_sc_t * sc,iwp_rx_desc_t * desc)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
iwp_tx_intr(iwp_sc_t * sc,iwp_rx_desc_t * desc)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
iwp_cmd_intr(iwp_sc_t * sc,iwp_rx_desc_t * desc)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
iwp_ucode_alive(iwp_sc_t * sc,iwp_rx_desc_t * desc)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
iwp_rx_softintr(caddr_t arg,caddr_t unused)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
iwp_intr(caddr_t arg,caddr_t unused)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
iwp_rate_to_plcp(int rate)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 *
iwp_m_tx(void * arg,mblk_t * mp)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
iwp_send(ieee80211com_t * ic,mblk_t * mp,uint8_t type)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
iwp_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)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
iwp_m_getprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,void * wldp_buf)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
iwp_m_propinfo(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,mac_prop_info_handle_t prh)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
iwp_m_setprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,const void * wldp_buf)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
iwp_m_stat(void * arg,uint_t stat,uint64_t * val)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
iwp_m_start(void * arg)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
iwp_m_stop(void * arg)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
iwp_m_unicst(void * arg,const uint8_t * macaddr)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
iwp_m_multicst(void * arg,boolean_t add,const uint8_t * m)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
iwp_m_promisc(void * arg,boolean_t on)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
iwp_thread(iwp_sc_t * sc)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
iwp_cmd(iwp_sc_t * sc,int code,const void * buf,int size,int async)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
iwp_set_led(iwp_sc_t * sc,uint8_t id,uint8_t off,uint8_t on)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
iwp_hw_set_before_auth(iwp_sc_t * sc)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
iwp_scan(iwp_sc_t * sc)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
iwp_config(iwp_sc_t * sc)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
iwp_quiesce(dev_info_t * dip)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
iwp_stop_master(iwp_sc_t * sc)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
iwp_power_up(iwp_sc_t * sc)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
iwp_preinit(iwp_sc_t * sc)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
iwp_eep_sem_down(iwp_sc_t * sc)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
iwp_eep_sem_up(iwp_sc_t * sc)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
iwp_eep_load(iwp_sc_t * sc)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
iwp_get_mac_from_eep(iwp_sc_t * sc)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
iwp_init(iwp_sc_t * sc)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
iwp_stop(iwp_sc_t * sc)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
iwp_amrr_init(iwp_amrr_t * amrr)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
iwp_amrr_timeout(iwp_sc_t * sc)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
iwp_amrr_ratectl(void * arg,ieee80211_node_t * in)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 *
iwp_eep_addr_trans(iwp_sc_t * sc,uint32_t indi_addr)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
iwp_put_seg_fw(iwp_sc_t * sc,uint32_t addr_s,uint32_t addr_d,uint32_t len)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
iwp_alive_common(iwp_sc_t * sc)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
iwp_save_calib_result(iwp_sc_t * sc,iwp_rx_desc_t * desc)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
iwp_release_calib_buffer(iwp_sc_t * sc)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
iwp_init_common(iwp_sc_t * sc)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
iwp_fast_recover(iwp_sc_t * sc)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
iwp_run_state_config(iwp_sc_t * sc)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
iwp_overwrite_ic_default(iwp_sc_t * sc)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
iwp_add_ap_sta(iwp_sc_t * sc)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
iwp_eep_ver_chk(iwp_sc_t * sc)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
iwp_set_chip_param(iwp_sc_t * sc)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