110741SFei.Feng@Sun.COM /*
2*11878SVenu.Iyer@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
310741SFei.Feng@Sun.COM * Use is subject to license terms.
410741SFei.Feng@Sun.COM */
510741SFei.Feng@Sun.COM
610741SFei.Feng@Sun.COM /*
710741SFei.Feng@Sun.COM * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
810741SFei.Feng@Sun.COM * Copyright (c) 2007-2008 Marvell Semiconductor, Inc.
910741SFei.Feng@Sun.COM * All rights reserved.
1010741SFei.Feng@Sun.COM *
1110741SFei.Feng@Sun.COM * Redistribution and use in source and binary forms, with or without
1210741SFei.Feng@Sun.COM * modification, are permitted provided that the following conditions
1310741SFei.Feng@Sun.COM * are met:
1410741SFei.Feng@Sun.COM * 1. Redistributions of source code must retain the above copyright
1510741SFei.Feng@Sun.COM * notice, this list of conditions and the following disclaimer,
1610741SFei.Feng@Sun.COM * without modification.
1710741SFei.Feng@Sun.COM * 2. Redistributions in binary form must reproduce at minimum a disclaimer
1810741SFei.Feng@Sun.COM * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
1910741SFei.Feng@Sun.COM * redistribution must be conditioned upon including a substantially
2010741SFei.Feng@Sun.COM * similar Disclaimer requirement for further binary redistribution.
2110741SFei.Feng@Sun.COM *
2210741SFei.Feng@Sun.COM * NO WARRANTY
2310741SFei.Feng@Sun.COM * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2410741SFei.Feng@Sun.COM * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2510741SFei.Feng@Sun.COM * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
2610741SFei.Feng@Sun.COM * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
2710741SFei.Feng@Sun.COM * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
2810741SFei.Feng@Sun.COM * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2910741SFei.Feng@Sun.COM * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3010741SFei.Feng@Sun.COM * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
3110741SFei.Feng@Sun.COM * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3210741SFei.Feng@Sun.COM * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3310741SFei.Feng@Sun.COM * THE POSSIBILITY OF SUCH DAMAGES.
3410741SFei.Feng@Sun.COM */
3510741SFei.Feng@Sun.COM
3610741SFei.Feng@Sun.COM /*
3710741SFei.Feng@Sun.COM * Driver for the Marvell 88W8363 Wireless LAN controller.
3810741SFei.Feng@Sun.COM */
3910741SFei.Feng@Sun.COM #include <sys/stat.h>
4010741SFei.Feng@Sun.COM #include <sys/dlpi.h>
4110741SFei.Feng@Sun.COM #include <inet/common.h>
4210741SFei.Feng@Sun.COM #include <inet/mi.h>
4310741SFei.Feng@Sun.COM #include <sys/stream.h>
4410741SFei.Feng@Sun.COM #include <sys/errno.h>
4510741SFei.Feng@Sun.COM #include <sys/stropts.h>
4610741SFei.Feng@Sun.COM #include <sys/stat.h>
4710741SFei.Feng@Sun.COM #include <sys/sunddi.h>
4810741SFei.Feng@Sun.COM #include <sys/strsubr.h>
4910741SFei.Feng@Sun.COM #include <sys/strsun.h>
5010741SFei.Feng@Sun.COM #include <sys/pci.h>
5110741SFei.Feng@Sun.COM #include <sys/mac_provider.h>
5210741SFei.Feng@Sun.COM #include <sys/mac_wifi.h>
5310741SFei.Feng@Sun.COM #include <sys/net80211.h>
5410741SFei.Feng@Sun.COM #include <inet/wifi_ioctl.h>
5510741SFei.Feng@Sun.COM
5610741SFei.Feng@Sun.COM #include "mwl_var.h"
5710741SFei.Feng@Sun.COM
5810741SFei.Feng@Sun.COM static int mwl_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd);
5910741SFei.Feng@Sun.COM static int mwl_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd);
6010741SFei.Feng@Sun.COM static int mwl_quiesce(dev_info_t *devinfo);
6110741SFei.Feng@Sun.COM
6210741SFei.Feng@Sun.COM DDI_DEFINE_STREAM_OPS(mwl_dev_ops, nulldev, nulldev, mwl_attach, mwl_detach,
6310741SFei.Feng@Sun.COM nodev, NULL, D_MP, NULL, mwl_quiesce);
6410741SFei.Feng@Sun.COM
6510741SFei.Feng@Sun.COM static struct modldrv mwl_modldrv = {
6610741SFei.Feng@Sun.COM &mod_driverops, /* Type of module. This one is a driver */
6710741SFei.Feng@Sun.COM "Marvell 88W8363 WiFi driver v1.1", /* short description */
6810741SFei.Feng@Sun.COM &mwl_dev_ops /* driver specific ops */
6910741SFei.Feng@Sun.COM };
7010741SFei.Feng@Sun.COM
7110741SFei.Feng@Sun.COM static struct modlinkage modlinkage = {
7210741SFei.Feng@Sun.COM MODREV_1, (void *)&mwl_modldrv, NULL
7310741SFei.Feng@Sun.COM };
7410741SFei.Feng@Sun.COM
7510741SFei.Feng@Sun.COM static void *mwl_soft_state_p = NULL;
7610741SFei.Feng@Sun.COM
7710741SFei.Feng@Sun.COM static int mwl_m_stat(void *, uint_t, uint64_t *);
7810741SFei.Feng@Sun.COM static int mwl_m_start(void *);
7910741SFei.Feng@Sun.COM static void mwl_m_stop(void *);
8010741SFei.Feng@Sun.COM static int mwl_m_promisc(void *, boolean_t);
8110741SFei.Feng@Sun.COM static int mwl_m_multicst(void *, boolean_t, const uint8_t *);
8210741SFei.Feng@Sun.COM static int mwl_m_unicst(void *, const uint8_t *);
8310741SFei.Feng@Sun.COM static mblk_t *mwl_m_tx(void *, mblk_t *);
8410741SFei.Feng@Sun.COM static void mwl_m_ioctl(void *, queue_t *, mblk_t *);
8510741SFei.Feng@Sun.COM static int mwl_m_setprop(void *arg, const char *pr_name,
8610741SFei.Feng@Sun.COM mac_prop_id_t wldp_pr_num,
8710741SFei.Feng@Sun.COM uint_t wldp_length, const void *wldp_buf);
8810741SFei.Feng@Sun.COM static int mwl_m_getprop(void *arg, const char *pr_name,
89*11878SVenu.Iyer@Sun.COM mac_prop_id_t wldp_pr_num, uint_t wldp_length,
90*11878SVenu.Iyer@Sun.COM void *wldp_buf);
91*11878SVenu.Iyer@Sun.COM static void mwl_m_propinfo(void *, const char *, mac_prop_id_t,
92*11878SVenu.Iyer@Sun.COM mac_prop_info_handle_t);
9310741SFei.Feng@Sun.COM
9410741SFei.Feng@Sun.COM static mac_callbacks_t mwl_m_callbacks = {
95*11878SVenu.Iyer@Sun.COM MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
9610741SFei.Feng@Sun.COM mwl_m_stat,
9710741SFei.Feng@Sun.COM mwl_m_start,
9810741SFei.Feng@Sun.COM mwl_m_stop,
9910741SFei.Feng@Sun.COM mwl_m_promisc,
10010741SFei.Feng@Sun.COM mwl_m_multicst,
10110741SFei.Feng@Sun.COM mwl_m_unicst,
10210741SFei.Feng@Sun.COM mwl_m_tx,
103*11878SVenu.Iyer@Sun.COM NULL,
10410741SFei.Feng@Sun.COM mwl_m_ioctl,
10510741SFei.Feng@Sun.COM NULL,
10610741SFei.Feng@Sun.COM NULL,
10710741SFei.Feng@Sun.COM NULL,
10810741SFei.Feng@Sun.COM mwl_m_setprop,
109*11878SVenu.Iyer@Sun.COM mwl_m_getprop,
110*11878SVenu.Iyer@Sun.COM mwl_m_propinfo
11110741SFei.Feng@Sun.COM };
11210741SFei.Feng@Sun.COM
11310741SFei.Feng@Sun.COM #define MWL_DBG_ATTACH (1 << 0)
11410741SFei.Feng@Sun.COM #define MWL_DBG_DMA (1 << 1)
11510741SFei.Feng@Sun.COM #define MWL_DBG_FW (1 << 2)
11610741SFei.Feng@Sun.COM #define MWL_DBG_HW (1 << 3)
11710741SFei.Feng@Sun.COM #define MWL_DBG_INTR (1 << 4)
11810741SFei.Feng@Sun.COM #define MWL_DBG_RX (1 << 5)
11910741SFei.Feng@Sun.COM #define MWL_DBG_TX (1 << 6)
12010741SFei.Feng@Sun.COM #define MWL_DBG_CMD (1 << 7)
12110741SFei.Feng@Sun.COM #define MWL_DBG_CRYPTO (1 << 8)
12210741SFei.Feng@Sun.COM #define MWL_DBG_SR (1 << 9)
12310741SFei.Feng@Sun.COM #define MWL_DBG_MSG (1 << 10)
12410741SFei.Feng@Sun.COM
12510741SFei.Feng@Sun.COM uint32_t mwl_dbg_flags = 0x0;
12610741SFei.Feng@Sun.COM
12710741SFei.Feng@Sun.COM #ifdef DEBUG
12810741SFei.Feng@Sun.COM #define MWL_DBG \
12910741SFei.Feng@Sun.COM mwl_debug
13010741SFei.Feng@Sun.COM #else
13110741SFei.Feng@Sun.COM #define MWL_DBG
13210741SFei.Feng@Sun.COM #endif
13310741SFei.Feng@Sun.COM
13410741SFei.Feng@Sun.COM /*
13510741SFei.Feng@Sun.COM * PIO access attributes for registers
13610741SFei.Feng@Sun.COM */
13710741SFei.Feng@Sun.COM static ddi_device_acc_attr_t mwl_reg_accattr = {
13810741SFei.Feng@Sun.COM DDI_DEVICE_ATTR_V0,
13910741SFei.Feng@Sun.COM DDI_STRUCTURE_LE_ACC,
14010741SFei.Feng@Sun.COM DDI_STRICTORDER_ACC,
14110741SFei.Feng@Sun.COM DDI_DEFAULT_ACC
14210741SFei.Feng@Sun.COM };
14310741SFei.Feng@Sun.COM
14410741SFei.Feng@Sun.COM static ddi_device_acc_attr_t mwl_cmdbuf_accattr = {
14510741SFei.Feng@Sun.COM DDI_DEVICE_ATTR_V0,
14610741SFei.Feng@Sun.COM DDI_NEVERSWAP_ACC,
14710741SFei.Feng@Sun.COM DDI_STRICTORDER_ACC,
14810741SFei.Feng@Sun.COM DDI_DEFAULT_ACC
14910741SFei.Feng@Sun.COM };
15010741SFei.Feng@Sun.COM
15110741SFei.Feng@Sun.COM /*
15210741SFei.Feng@Sun.COM * DMA access attributes for descriptors and bufs: NOT to be byte swapped.
15310741SFei.Feng@Sun.COM */
15410741SFei.Feng@Sun.COM static ddi_device_acc_attr_t mwl_desc_accattr = {
15510741SFei.Feng@Sun.COM DDI_DEVICE_ATTR_V0,
15610741SFei.Feng@Sun.COM DDI_NEVERSWAP_ACC,
15710741SFei.Feng@Sun.COM DDI_STRICTORDER_ACC,
15810741SFei.Feng@Sun.COM DDI_DEFAULT_ACC
15910741SFei.Feng@Sun.COM };
16010741SFei.Feng@Sun.COM
16110741SFei.Feng@Sun.COM static ddi_device_acc_attr_t mwl_buf_accattr = {
16210741SFei.Feng@Sun.COM DDI_DEVICE_ATTR_V0,
16310741SFei.Feng@Sun.COM DDI_NEVERSWAP_ACC,
16410741SFei.Feng@Sun.COM DDI_STRICTORDER_ACC,
16510741SFei.Feng@Sun.COM DDI_DEFAULT_ACC
16610741SFei.Feng@Sun.COM };
16710741SFei.Feng@Sun.COM
16810741SFei.Feng@Sun.COM /*
16910741SFei.Feng@Sun.COM * Describes the chip's DMA engine
17010741SFei.Feng@Sun.COM */
17110741SFei.Feng@Sun.COM static ddi_dma_attr_t mwl_dma_attr = {
17210741SFei.Feng@Sun.COM DMA_ATTR_V0, /* dma_attr version */
17310741SFei.Feng@Sun.COM 0x0000000000000000ull, /* dma_attr_addr_lo */
17410741SFei.Feng@Sun.COM 0xFFFFFFFF, /* dma_attr_addr_hi */
17510741SFei.Feng@Sun.COM 0x00000000FFFFFFFFull, /* dma_attr_count_max */
17610741SFei.Feng@Sun.COM 0x0000000000000001ull, /* dma_attr_align */
17710741SFei.Feng@Sun.COM 0x00000FFF, /* dma_attr_burstsizes */
17810741SFei.Feng@Sun.COM 0x00000001, /* dma_attr_minxfer */
17910741SFei.Feng@Sun.COM 0x000000000000FFFFull, /* dma_attr_maxxfer */
18010741SFei.Feng@Sun.COM 0xFFFFFFFFFFFFFFFFull, /* dma_attr_seg */
18110741SFei.Feng@Sun.COM 1, /* dma_attr_sgllen */
18210741SFei.Feng@Sun.COM 0x00000001, /* dma_attr_granular */
18310741SFei.Feng@Sun.COM 0 /* dma_attr_flags */
18410741SFei.Feng@Sun.COM };
18510741SFei.Feng@Sun.COM
18610741SFei.Feng@Sun.COM /*
18710741SFei.Feng@Sun.COM * Supported rates for 802.11a/b/g modes (in 500Kbps unit).
18810741SFei.Feng@Sun.COM */
18910741SFei.Feng@Sun.COM static const struct ieee80211_rateset mwl_rateset_11b =
19010741SFei.Feng@Sun.COM { 4, { 2, 4, 11, 22 } };
19110741SFei.Feng@Sun.COM
19210741SFei.Feng@Sun.COM static const struct ieee80211_rateset mwl_rateset_11g =
19310741SFei.Feng@Sun.COM { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
19410741SFei.Feng@Sun.COM
19510741SFei.Feng@Sun.COM static int mwl_alloc_dma_mem(dev_info_t *, ddi_dma_attr_t *, size_t,
19610741SFei.Feng@Sun.COM ddi_device_acc_attr_t *, uint_t, uint_t,
19710741SFei.Feng@Sun.COM struct dma_area *);
19810741SFei.Feng@Sun.COM static void mwl_free_dma_mem(struct dma_area *);
19910741SFei.Feng@Sun.COM static int mwl_alloc_cmdbuf(struct mwl_softc *);
20010741SFei.Feng@Sun.COM static void mwl_free_cmdbuf(struct mwl_softc *);
20110741SFei.Feng@Sun.COM static int mwl_alloc_rx_ring(struct mwl_softc *, int);
20210741SFei.Feng@Sun.COM static void mwl_free_rx_ring(struct mwl_softc *);
20310741SFei.Feng@Sun.COM static int mwl_alloc_tx_ring(struct mwl_softc *, struct mwl_tx_ring *,
20410741SFei.Feng@Sun.COM int);
20510741SFei.Feng@Sun.COM static void mwl_free_tx_ring(struct mwl_softc *, struct mwl_tx_ring *);
20610741SFei.Feng@Sun.COM static int mwl_setupdma(struct mwl_softc *);
20710741SFei.Feng@Sun.COM static void mwl_txq_init(struct mwl_softc *, struct mwl_tx_ring *, int);
20810741SFei.Feng@Sun.COM static int mwl_tx_setup(struct mwl_softc *, int, int);
20910741SFei.Feng@Sun.COM static int mwl_setup_txq(struct mwl_softc *);
21010741SFei.Feng@Sun.COM static int mwl_fwload(struct mwl_softc *, void *);
21110741SFei.Feng@Sun.COM static int mwl_loadsym(ddi_modhandle_t, char *, char **, size_t *);
21210741SFei.Feng@Sun.COM static void mwlFwReset(struct mwl_softc *);
21310741SFei.Feng@Sun.COM static void mwlPokeSdramController(struct mwl_softc *, int);
21410741SFei.Feng@Sun.COM static void mwlTriggerPciCmd(struct mwl_softc *);
21510741SFei.Feng@Sun.COM static int mwlWaitFor(struct mwl_softc *, uint32_t);
21610741SFei.Feng@Sun.COM static int mwlSendBlock(struct mwl_softc *, int, const void *, size_t);
21710741SFei.Feng@Sun.COM static int mwlSendBlock2(struct mwl_softc *, const void *, size_t);
21810741SFei.Feng@Sun.COM static void mwlSendCmd(struct mwl_softc *);
21910741SFei.Feng@Sun.COM static int mwlExecuteCmd(struct mwl_softc *, unsigned short);
22010741SFei.Feng@Sun.COM static int mwlWaitForCmdComplete(struct mwl_softc *, uint16_t);
22110741SFei.Feng@Sun.COM static void dumpresult(struct mwl_softc *, int);
22210741SFei.Feng@Sun.COM static int mwlResetHalState(struct mwl_softc *);
22310741SFei.Feng@Sun.COM static int mwlGetPwrCalTable(struct mwl_softc *);
22410741SFei.Feng@Sun.COM static int mwlGetCalTable(struct mwl_softc *, uint8_t, uint8_t);
22510741SFei.Feng@Sun.COM static int mwlGetPwrCalTable(struct mwl_softc *);
22610741SFei.Feng@Sun.COM static void dumpcaldata(const char *, const uint8_t *, int);
22710741SFei.Feng@Sun.COM static void get2Ghz(MWL_HAL_CHANNELINFO *, const uint8_t *, int);
22810741SFei.Feng@Sun.COM static void get5Ghz(MWL_HAL_CHANNELINFO *, const uint8_t *, int);
22910741SFei.Feng@Sun.COM static void setmaxtxpow(struct mwl_hal_channel *, int, int);
23010741SFei.Feng@Sun.COM static uint16_t ieee2mhz(int);
23110741SFei.Feng@Sun.COM static const char *
23210741SFei.Feng@Sun.COM mwlcmdname(int);
23310741SFei.Feng@Sun.COM static int mwl_gethwspecs(struct mwl_softc *);
23410741SFei.Feng@Sun.COM static int mwl_getchannels(struct mwl_softc *);
23510741SFei.Feng@Sun.COM static void getchannels(struct mwl_softc *, int, int *,
23610741SFei.Feng@Sun.COM struct mwl_channel *);
23710741SFei.Feng@Sun.COM static void addchannels(struct mwl_channel *, int, int *,
23810741SFei.Feng@Sun.COM const MWL_HAL_CHANNELINFO *, int);
23910741SFei.Feng@Sun.COM static void addht40channels(struct mwl_channel *, int, int *,
24010741SFei.Feng@Sun.COM const MWL_HAL_CHANNELINFO *, int);
24110741SFei.Feng@Sun.COM static const struct mwl_channel *
24210741SFei.Feng@Sun.COM findchannel(const struct mwl_channel *, int,
24310741SFei.Feng@Sun.COM int, int);
24410741SFei.Feng@Sun.COM static void addchan(struct mwl_channel *, int, int, int, int);
24510741SFei.Feng@Sun.COM
24610741SFei.Feng@Sun.COM static int mwl_chan_set(struct mwl_softc *, struct mwl_channel *);
24710741SFei.Feng@Sun.COM static void mwl_mapchan(MWL_HAL_CHANNEL *, const struct mwl_channel *);
24810741SFei.Feng@Sun.COM static int mwl_setcurchanrates(struct mwl_softc *);
24910741SFei.Feng@Sun.COM const struct ieee80211_rateset *
25010741SFei.Feng@Sun.COM mwl_get_suprates(struct ieee80211com *,
25110741SFei.Feng@Sun.COM const struct mwl_channel *);
25210741SFei.Feng@Sun.COM static uint32_t cvtChannelFlags(const MWL_HAL_CHANNEL *);
25310741SFei.Feng@Sun.COM static const struct mwl_hal_channel *
25410741SFei.Feng@Sun.COM findhalchannel(const struct mwl_softc *,
25510741SFei.Feng@Sun.COM const MWL_HAL_CHANNEL *);
25610741SFei.Feng@Sun.COM enum ieee80211_phymode
25710741SFei.Feng@Sun.COM mwl_chan2mode(const struct mwl_channel *);
25810741SFei.Feng@Sun.COM static int mwl_map2regioncode(const struct mwl_regdomain *);
25910741SFei.Feng@Sun.COM static int mwl_startrecv(struct mwl_softc *);
26010741SFei.Feng@Sun.COM static int mwl_mode_init(struct mwl_softc *);
26110741SFei.Feng@Sun.COM static void mwl_hal_intrset(struct mwl_softc *, uint32_t);
26210741SFei.Feng@Sun.COM static void mwl_hal_getisr(struct mwl_softc *, uint32_t *);
26310741SFei.Feng@Sun.COM static int mwl_hal_sethwdma(struct mwl_softc *,
26410741SFei.Feng@Sun.COM const struct mwl_hal_txrxdma *);
26510741SFei.Feng@Sun.COM static int mwl_hal_getchannelinfo(struct mwl_softc *, int, int,
26610741SFei.Feng@Sun.COM const MWL_HAL_CHANNELINFO **);
26710741SFei.Feng@Sun.COM static int mwl_hal_setmac_locked(struct mwl_softc *, const uint8_t *);
26810741SFei.Feng@Sun.COM static int mwl_hal_keyreset(struct mwl_softc *, const MWL_HAL_KEYVAL *,
26910741SFei.Feng@Sun.COM const uint8_t mac[IEEE80211_ADDR_LEN]);
27010741SFei.Feng@Sun.COM static int mwl_hal_keyset(struct mwl_softc *, const MWL_HAL_KEYVAL *,
27110741SFei.Feng@Sun.COM const uint8_t mac[IEEE80211_ADDR_LEN]);
27210741SFei.Feng@Sun.COM static int mwl_hal_newstation(struct mwl_softc *, const uint8_t *,
27310741SFei.Feng@Sun.COM uint16_t, uint16_t, const MWL_HAL_PEERINFO *, int, int);
27410741SFei.Feng@Sun.COM static int mwl_hal_setantenna(struct mwl_softc *, MWL_HAL_ANTENNA, int);
27510741SFei.Feng@Sun.COM static int mwl_hal_setradio(struct mwl_softc *, int, MWL_HAL_PREAMBLE);
27610741SFei.Feng@Sun.COM static int mwl_hal_setwmm(struct mwl_softc *, int);
27710741SFei.Feng@Sun.COM static int mwl_hal_setchannel(struct mwl_softc *, const MWL_HAL_CHANNEL *);
27810741SFei.Feng@Sun.COM static int mwl_hal_settxpower(struct mwl_softc *, const MWL_HAL_CHANNEL *,
27910741SFei.Feng@Sun.COM uint8_t);
28010741SFei.Feng@Sun.COM static int mwl_hal_settxrate(struct mwl_softc *, MWL_HAL_TXRATE_HANDLING,
28110741SFei.Feng@Sun.COM const MWL_HAL_TXRATE *);
28210741SFei.Feng@Sun.COM static int mwl_hal_settxrate_auto(struct mwl_softc *,
28310741SFei.Feng@Sun.COM const MWL_HAL_TXRATE *);
28410741SFei.Feng@Sun.COM static int mwl_hal_setrateadaptmode(struct mwl_softc *, uint16_t);
28510741SFei.Feng@Sun.COM static int mwl_hal_setoptimizationlevel(struct mwl_softc *, int);
28610741SFei.Feng@Sun.COM static int mwl_hal_setregioncode(struct mwl_softc *, int);
28710741SFei.Feng@Sun.COM static int mwl_hal_setassocid(struct mwl_softc *, const uint8_t *,
28810741SFei.Feng@Sun.COM uint16_t);
28910741SFei.Feng@Sun.COM static int mwl_setrates(struct ieee80211com *);
29010741SFei.Feng@Sun.COM static int mwl_hal_setrtsthreshold(struct mwl_softc *, int);
29110741SFei.Feng@Sun.COM static int mwl_hal_setcsmode(struct mwl_softc *, MWL_HAL_CSMODE);
29210741SFei.Feng@Sun.COM static int mwl_hal_setpromisc(struct mwl_softc *, int);
29310741SFei.Feng@Sun.COM static int mwl_hal_start(struct mwl_softc *);
29410741SFei.Feng@Sun.COM static int mwl_hal_setinframode(struct mwl_softc *);
29510741SFei.Feng@Sun.COM static int mwl_hal_stop(struct mwl_softc *);
29610741SFei.Feng@Sun.COM static struct ieee80211_node *
29710741SFei.Feng@Sun.COM mwl_node_alloc(struct ieee80211com *);
29810741SFei.Feng@Sun.COM static void mwl_node_free(struct ieee80211_node *);
29910741SFei.Feng@Sun.COM static int mwl_key_alloc(struct ieee80211com *,
30010741SFei.Feng@Sun.COM const struct ieee80211_key *,
30110741SFei.Feng@Sun.COM ieee80211_keyix *, ieee80211_keyix *);
30210741SFei.Feng@Sun.COM static int mwl_key_delete(struct ieee80211com *,
30310741SFei.Feng@Sun.COM const struct ieee80211_key *);
30410741SFei.Feng@Sun.COM static int mwl_key_set(struct ieee80211com *, const struct ieee80211_key *,
30510741SFei.Feng@Sun.COM const uint8_t mac[IEEE80211_ADDR_LEN]);
30610741SFei.Feng@Sun.COM static void mwl_setanywepkey(struct ieee80211com *, const uint8_t *);
30710741SFei.Feng@Sun.COM static void mwl_setglobalkeys(struct ieee80211com *c);
30810741SFei.Feng@Sun.COM static int addgroupflags(MWL_HAL_KEYVAL *, const struct ieee80211_key *);
30910741SFei.Feng@Sun.COM static void mwl_hal_txstart(struct mwl_softc *, int);
31010741SFei.Feng@Sun.COM static int mwl_send(ieee80211com_t *, mblk_t *, uint8_t);
31110741SFei.Feng@Sun.COM static void mwl_next_scan(void *);
31210741SFei.Feng@Sun.COM static MWL_HAL_PEERINFO *
31310741SFei.Feng@Sun.COM mkpeerinfo(MWL_HAL_PEERINFO *, const struct ieee80211_node *);
31410741SFei.Feng@Sun.COM static uint32_t get_rate_bitmap(const struct ieee80211_rateset *);
31510741SFei.Feng@Sun.COM static int mwl_newstate(struct ieee80211com *, enum ieee80211_state, int);
31610741SFei.Feng@Sun.COM static int cvtrssi(uint8_t);
31710741SFei.Feng@Sun.COM static uint_t mwl_intr(caddr_t, caddr_t);
31810741SFei.Feng@Sun.COM static uint_t mwl_softintr(caddr_t, caddr_t);
31910741SFei.Feng@Sun.COM static void mwl_tx_intr(struct mwl_softc *);
32010741SFei.Feng@Sun.COM static void mwl_rx_intr(struct mwl_softc *);
32110741SFei.Feng@Sun.COM static int mwl_init(struct mwl_softc *);
32210741SFei.Feng@Sun.COM static void mwl_stop(struct mwl_softc *);
32310741SFei.Feng@Sun.COM static int mwl_resume(struct mwl_softc *);
32410741SFei.Feng@Sun.COM
32510741SFei.Feng@Sun.COM
32610741SFei.Feng@Sun.COM #ifdef DEBUG
32710741SFei.Feng@Sun.COM static void
mwl_debug(uint32_t dbg_flags,const int8_t * fmt,...)32810741SFei.Feng@Sun.COM mwl_debug(uint32_t dbg_flags, const int8_t *fmt, ...)
32910741SFei.Feng@Sun.COM {
33010741SFei.Feng@Sun.COM va_list args;
33110741SFei.Feng@Sun.COM
33210741SFei.Feng@Sun.COM if (dbg_flags & mwl_dbg_flags) {
33310741SFei.Feng@Sun.COM va_start(args, fmt);
33410741SFei.Feng@Sun.COM vcmn_err(CE_CONT, fmt, args);
33510741SFei.Feng@Sun.COM va_end(args);
33610741SFei.Feng@Sun.COM }
33710741SFei.Feng@Sun.COM }
33810741SFei.Feng@Sun.COM #endif
33910741SFei.Feng@Sun.COM
34010741SFei.Feng@Sun.COM /*
34110741SFei.Feng@Sun.COM * Allocate an DMA memory and a DMA handle for accessing it
34210741SFei.Feng@Sun.COM */
34310741SFei.Feng@Sun.COM static int
mwl_alloc_dma_mem(dev_info_t * devinfo,ddi_dma_attr_t * dma_attr,size_t memsize,ddi_device_acc_attr_t * attr_p,uint_t alloc_flags,uint_t bind_flags,struct dma_area * dma_p)34410741SFei.Feng@Sun.COM mwl_alloc_dma_mem(dev_info_t *devinfo, ddi_dma_attr_t *dma_attr,
34510741SFei.Feng@Sun.COM size_t memsize, ddi_device_acc_attr_t *attr_p, uint_t alloc_flags,
34610741SFei.Feng@Sun.COM uint_t bind_flags, struct dma_area *dma_p)
34710741SFei.Feng@Sun.COM {
34810741SFei.Feng@Sun.COM int err;
34910741SFei.Feng@Sun.COM
35010741SFei.Feng@Sun.COM /*
35110741SFei.Feng@Sun.COM * Allocate handle
35210741SFei.Feng@Sun.COM */
35310741SFei.Feng@Sun.COM err = ddi_dma_alloc_handle(devinfo, dma_attr,
35410741SFei.Feng@Sun.COM DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl);
35510741SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
35610741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_dma_mem(): "
35710741SFei.Feng@Sun.COM "failed to alloc handle\n");
35810741SFei.Feng@Sun.COM goto fail1;
35910741SFei.Feng@Sun.COM }
36010741SFei.Feng@Sun.COM
36110741SFei.Feng@Sun.COM /*
36210741SFei.Feng@Sun.COM * Allocate memory
36310741SFei.Feng@Sun.COM */
36410741SFei.Feng@Sun.COM err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize, attr_p,
36510741SFei.Feng@Sun.COM alloc_flags, DDI_DMA_SLEEP, NULL, &dma_p->mem_va,
36610741SFei.Feng@Sun.COM &dma_p->alength, &dma_p->acc_hdl);
36710741SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
36810741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_dma_mem(): "
36910741SFei.Feng@Sun.COM "failed to alloc mem\n");
37010741SFei.Feng@Sun.COM goto fail2;
37110741SFei.Feng@Sun.COM }
37210741SFei.Feng@Sun.COM
37310741SFei.Feng@Sun.COM /*
37410741SFei.Feng@Sun.COM * Bind the two together
37510741SFei.Feng@Sun.COM */
37610741SFei.Feng@Sun.COM err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL,
37710741SFei.Feng@Sun.COM dma_p->mem_va, dma_p->alength, bind_flags,
37810741SFei.Feng@Sun.COM DDI_DMA_SLEEP, NULL, &dma_p->cookie, &dma_p->ncookies);
37910741SFei.Feng@Sun.COM if (err != DDI_DMA_MAPPED) {
38010741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_dma_mem(): "
38110741SFei.Feng@Sun.COM "failed to bind handle\n");
38210741SFei.Feng@Sun.COM goto fail3;
38310741SFei.Feng@Sun.COM }
38410741SFei.Feng@Sun.COM
38510741SFei.Feng@Sun.COM if (dma_p->ncookies != 1) {
38610741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_dma_mem(): "
38710741SFei.Feng@Sun.COM "failed to alloc cookies\n");
38810741SFei.Feng@Sun.COM goto fail4;
38910741SFei.Feng@Sun.COM }
39010741SFei.Feng@Sun.COM
39110741SFei.Feng@Sun.COM dma_p->nslots = ~0U;
39210741SFei.Feng@Sun.COM dma_p->size = ~0U;
39310741SFei.Feng@Sun.COM dma_p->token = ~0U;
39410741SFei.Feng@Sun.COM dma_p->offset = 0;
39510741SFei.Feng@Sun.COM
39610741SFei.Feng@Sun.COM return (DDI_SUCCESS);
39710741SFei.Feng@Sun.COM
39810741SFei.Feng@Sun.COM fail4:
39910741SFei.Feng@Sun.COM (void) ddi_dma_unbind_handle(dma_p->dma_hdl);
40010741SFei.Feng@Sun.COM fail3:
40110741SFei.Feng@Sun.COM ddi_dma_mem_free(&dma_p->acc_hdl);
40210741SFei.Feng@Sun.COM fail2:
40310741SFei.Feng@Sun.COM ddi_dma_free_handle(&dma_p->dma_hdl);
40410741SFei.Feng@Sun.COM fail1:
40510741SFei.Feng@Sun.COM return (err);
40610741SFei.Feng@Sun.COM }
40710741SFei.Feng@Sun.COM
40810741SFei.Feng@Sun.COM static void
mwl_free_dma_mem(struct dma_area * dma_p)40910741SFei.Feng@Sun.COM mwl_free_dma_mem(struct dma_area *dma_p)
41010741SFei.Feng@Sun.COM {
41110741SFei.Feng@Sun.COM if (dma_p->dma_hdl != NULL) {
41210741SFei.Feng@Sun.COM (void) ddi_dma_unbind_handle(dma_p->dma_hdl);
41310741SFei.Feng@Sun.COM if (dma_p->acc_hdl != NULL) {
41410741SFei.Feng@Sun.COM ddi_dma_mem_free(&dma_p->acc_hdl);
41510741SFei.Feng@Sun.COM dma_p->acc_hdl = NULL;
41610741SFei.Feng@Sun.COM }
41710741SFei.Feng@Sun.COM ddi_dma_free_handle(&dma_p->dma_hdl);
41810741SFei.Feng@Sun.COM dma_p->ncookies = 0;
41910741SFei.Feng@Sun.COM dma_p->dma_hdl = NULL;
42010741SFei.Feng@Sun.COM }
42110741SFei.Feng@Sun.COM }
42210741SFei.Feng@Sun.COM
42310741SFei.Feng@Sun.COM static int
mwl_alloc_cmdbuf(struct mwl_softc * sc)42410741SFei.Feng@Sun.COM mwl_alloc_cmdbuf(struct mwl_softc *sc)
42510741SFei.Feng@Sun.COM {
42610741SFei.Feng@Sun.COM int err;
42710741SFei.Feng@Sun.COM size_t size;
42810741SFei.Feng@Sun.COM
42910741SFei.Feng@Sun.COM size = MWL_CMDBUF_SIZE;
43010741SFei.Feng@Sun.COM
43110741SFei.Feng@Sun.COM err = mwl_alloc_dma_mem(sc->sc_dev, &mwl_dma_attr, size,
43210741SFei.Feng@Sun.COM &mwl_cmdbuf_accattr, DDI_DMA_CONSISTENT,
43310741SFei.Feng@Sun.COM DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
43410741SFei.Feng@Sun.COM &sc->sc_cmd_dma);
43510741SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
43610741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_cmdbuf(): "
43710741SFei.Feng@Sun.COM "failed to alloc dma mem\n");
43810741SFei.Feng@Sun.COM return (DDI_FAILURE);
43910741SFei.Feng@Sun.COM }
44010741SFei.Feng@Sun.COM
44110741SFei.Feng@Sun.COM sc->sc_cmd_mem = (uint16_t *)sc->sc_cmd_dma.mem_va;
44210741SFei.Feng@Sun.COM sc->sc_cmd_dmaaddr = sc->sc_cmd_dma.cookie.dmac_address;
44310741SFei.Feng@Sun.COM
44410741SFei.Feng@Sun.COM return (DDI_SUCCESS);
44510741SFei.Feng@Sun.COM }
44610741SFei.Feng@Sun.COM
44710741SFei.Feng@Sun.COM static void
mwl_free_cmdbuf(struct mwl_softc * sc)44810741SFei.Feng@Sun.COM mwl_free_cmdbuf(struct mwl_softc *sc)
44910741SFei.Feng@Sun.COM {
45010741SFei.Feng@Sun.COM if (sc->sc_cmd_mem != NULL)
45110741SFei.Feng@Sun.COM mwl_free_dma_mem(&sc->sc_cmd_dma);
45210741SFei.Feng@Sun.COM }
45310741SFei.Feng@Sun.COM
45410741SFei.Feng@Sun.COM static int
mwl_alloc_rx_ring(struct mwl_softc * sc,int count)45510741SFei.Feng@Sun.COM mwl_alloc_rx_ring(struct mwl_softc *sc, int count)
45610741SFei.Feng@Sun.COM {
45710741SFei.Feng@Sun.COM struct mwl_rx_ring *ring;
45810741SFei.Feng@Sun.COM struct mwl_rxdesc *ds;
45910741SFei.Feng@Sun.COM struct mwl_rxbuf *bf;
46010741SFei.Feng@Sun.COM int i, err, datadlen;
46110741SFei.Feng@Sun.COM
46210741SFei.Feng@Sun.COM ring = &sc->sc_rxring;
46310741SFei.Feng@Sun.COM ring->count = count;
46410741SFei.Feng@Sun.COM ring->cur = ring->next = 0;
46510741SFei.Feng@Sun.COM err = mwl_alloc_dma_mem(sc->sc_dev, &mwl_dma_attr,
46610741SFei.Feng@Sun.COM count * sizeof (struct mwl_rxdesc),
46710741SFei.Feng@Sun.COM &mwl_desc_accattr,
46810741SFei.Feng@Sun.COM DDI_DMA_CONSISTENT, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
46910741SFei.Feng@Sun.COM &ring->rxdesc_dma);
47010741SFei.Feng@Sun.COM if (err) {
47110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_rxring(): "
47210741SFei.Feng@Sun.COM "alloc tx ring failed, size %d\n",
47310741SFei.Feng@Sun.COM (uint32_t)(count * sizeof (struct mwl_rxdesc)));
47410741SFei.Feng@Sun.COM return (DDI_FAILURE);
47510741SFei.Feng@Sun.COM }
47610741SFei.Feng@Sun.COM
47710741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_rx_ring(): "
47810741SFei.Feng@Sun.COM "dma len = %d\n", (uint32_t)(ring->rxdesc_dma.alength));
47910741SFei.Feng@Sun.COM ring->desc = (struct mwl_rxdesc *)ring->rxdesc_dma.mem_va;
48010741SFei.Feng@Sun.COM ring->physaddr = ring->rxdesc_dma.cookie.dmac_address;
48110741SFei.Feng@Sun.COM bzero(ring->desc, count * sizeof (struct mwl_rxdesc));
48210741SFei.Feng@Sun.COM
48310741SFei.Feng@Sun.COM datadlen = count * sizeof (struct mwl_rxbuf);
48410741SFei.Feng@Sun.COM ring->buf = (struct mwl_rxbuf *)kmem_zalloc(datadlen, KM_SLEEP);
48510741SFei.Feng@Sun.COM if (ring->buf == NULL) {
48610741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_rxring(): "
48710741SFei.Feng@Sun.COM "could not alloc rx ring data buffer\n");
48810741SFei.Feng@Sun.COM return (DDI_FAILURE);
48910741SFei.Feng@Sun.COM }
49010741SFei.Feng@Sun.COM bzero(ring->buf, count * sizeof (struct mwl_rxbuf));
49110741SFei.Feng@Sun.COM
49210741SFei.Feng@Sun.COM /*
49310741SFei.Feng@Sun.COM * Pre-allocate Rx buffers and populate Rx ring.
49410741SFei.Feng@Sun.COM */
49510741SFei.Feng@Sun.COM for (i = 0; i < count; i++) {
49610741SFei.Feng@Sun.COM ds = &ring->desc[i];
49710741SFei.Feng@Sun.COM bf = &ring->buf[i];
49810741SFei.Feng@Sun.COM /* alloc DMA memory */
49910741SFei.Feng@Sun.COM (void) mwl_alloc_dma_mem(sc->sc_dev, &mwl_dma_attr,
50010741SFei.Feng@Sun.COM sc->sc_dmabuf_size,
50110741SFei.Feng@Sun.COM &mwl_buf_accattr,
50210741SFei.Feng@Sun.COM DDI_DMA_STREAMING,
50310741SFei.Feng@Sun.COM DDI_DMA_READ | DDI_DMA_STREAMING,
50410741SFei.Feng@Sun.COM &bf->rxbuf_dma);
50510741SFei.Feng@Sun.COM bf->bf_mem = (uint8_t *)(bf->rxbuf_dma.mem_va);
50610741SFei.Feng@Sun.COM bf->bf_baddr = bf->rxbuf_dma.cookie.dmac_address;
50710741SFei.Feng@Sun.COM bf->bf_desc = ds;
50810741SFei.Feng@Sun.COM bf->bf_daddr = ring->physaddr + _PTRDIFF(ds, ring->desc);
50910741SFei.Feng@Sun.COM }
51010741SFei.Feng@Sun.COM
51110741SFei.Feng@Sun.COM (void) ddi_dma_sync(ring->rxdesc_dma.dma_hdl,
51210741SFei.Feng@Sun.COM 0,
51310741SFei.Feng@Sun.COM ring->rxdesc_dma.alength,
51410741SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV);
51510741SFei.Feng@Sun.COM
51610741SFei.Feng@Sun.COM return (0);
51710741SFei.Feng@Sun.COM }
51810741SFei.Feng@Sun.COM
51910741SFei.Feng@Sun.COM static void
mwl_free_rx_ring(struct mwl_softc * sc)52010741SFei.Feng@Sun.COM mwl_free_rx_ring(struct mwl_softc *sc)
52110741SFei.Feng@Sun.COM {
52210741SFei.Feng@Sun.COM struct mwl_rx_ring *ring;
52310741SFei.Feng@Sun.COM struct mwl_rxbuf *bf;
52410741SFei.Feng@Sun.COM int i;
52510741SFei.Feng@Sun.COM
52610741SFei.Feng@Sun.COM ring = &sc->sc_rxring;
52710741SFei.Feng@Sun.COM
52810741SFei.Feng@Sun.COM if (ring->desc != NULL) {
52910741SFei.Feng@Sun.COM mwl_free_dma_mem(&ring->rxdesc_dma);
53010741SFei.Feng@Sun.COM }
53110741SFei.Feng@Sun.COM
53210741SFei.Feng@Sun.COM if (ring->buf != NULL) {
53310741SFei.Feng@Sun.COM for (i = 0; i < ring->count; i++) {
53410741SFei.Feng@Sun.COM bf = &ring->buf[i];
53510741SFei.Feng@Sun.COM mwl_free_dma_mem(&bf->rxbuf_dma);
53610741SFei.Feng@Sun.COM }
53710741SFei.Feng@Sun.COM kmem_free(ring->buf,
53810741SFei.Feng@Sun.COM (ring->count * sizeof (struct mwl_rxbuf)));
53910741SFei.Feng@Sun.COM }
54010741SFei.Feng@Sun.COM }
54110741SFei.Feng@Sun.COM
54210741SFei.Feng@Sun.COM static int
mwl_alloc_tx_ring(struct mwl_softc * sc,struct mwl_tx_ring * ring,int count)54310741SFei.Feng@Sun.COM mwl_alloc_tx_ring(struct mwl_softc *sc, struct mwl_tx_ring *ring,
54410741SFei.Feng@Sun.COM int count)
54510741SFei.Feng@Sun.COM {
54610741SFei.Feng@Sun.COM struct mwl_txdesc *ds;
54710741SFei.Feng@Sun.COM struct mwl_txbuf *bf;
54810741SFei.Feng@Sun.COM int i, err, datadlen;
54910741SFei.Feng@Sun.COM
55010741SFei.Feng@Sun.COM ring->count = count;
55110741SFei.Feng@Sun.COM ring->queued = 0;
55210741SFei.Feng@Sun.COM ring->cur = ring->next = ring->stat = 0;
55310741SFei.Feng@Sun.COM err = mwl_alloc_dma_mem(sc->sc_dev, &mwl_dma_attr,
55410741SFei.Feng@Sun.COM count * sizeof (struct mwl_txdesc), &mwl_desc_accattr,
55510741SFei.Feng@Sun.COM DDI_DMA_CONSISTENT, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
55610741SFei.Feng@Sun.COM &ring->txdesc_dma);
55710741SFei.Feng@Sun.COM if (err) {
55810741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_tx_ring(): "
55910741SFei.Feng@Sun.COM "alloc tx ring failed, size %d\n",
56010741SFei.Feng@Sun.COM (uint32_t)(count * sizeof (struct mwl_txdesc)));
56110741SFei.Feng@Sun.COM return (DDI_FAILURE);
56210741SFei.Feng@Sun.COM }
56310741SFei.Feng@Sun.COM
56410741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_tx_ring(): "
56510741SFei.Feng@Sun.COM "dma len = %d\n", (uint32_t)(ring->txdesc_dma.alength));
56610741SFei.Feng@Sun.COM ring->desc = (struct mwl_txdesc *)ring->txdesc_dma.mem_va;
56710741SFei.Feng@Sun.COM ring->physaddr = ring->txdesc_dma.cookie.dmac_address;
56810741SFei.Feng@Sun.COM bzero(ring->desc, count * sizeof (struct mwl_txdesc));
56910741SFei.Feng@Sun.COM
57010741SFei.Feng@Sun.COM datadlen = count * sizeof (struct mwl_txbuf);
57110741SFei.Feng@Sun.COM ring->buf = kmem_zalloc(datadlen, KM_SLEEP);
57210741SFei.Feng@Sun.COM if (ring->buf == NULL) {
57310741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_tx_ring(): "
57410741SFei.Feng@Sun.COM "could not alloc tx ring data buffer\n");
57510741SFei.Feng@Sun.COM return (DDI_FAILURE);
57610741SFei.Feng@Sun.COM }
57710741SFei.Feng@Sun.COM bzero(ring->buf, count * sizeof (struct mwl_txbuf));
57810741SFei.Feng@Sun.COM
57910741SFei.Feng@Sun.COM for (i = 0; i < count; i++) {
58010741SFei.Feng@Sun.COM ds = &ring->desc[i];
58110741SFei.Feng@Sun.COM bf = &ring->buf[i];
58210741SFei.Feng@Sun.COM /* alloc DMA memory */
58310741SFei.Feng@Sun.COM (void) mwl_alloc_dma_mem(sc->sc_dev, &mwl_dma_attr,
58410741SFei.Feng@Sun.COM sc->sc_dmabuf_size,
58510741SFei.Feng@Sun.COM &mwl_buf_accattr,
58610741SFei.Feng@Sun.COM DDI_DMA_STREAMING,
58710741SFei.Feng@Sun.COM DDI_DMA_WRITE | DDI_DMA_STREAMING,
58810741SFei.Feng@Sun.COM &bf->txbuf_dma);
58910741SFei.Feng@Sun.COM bf->bf_baddr = bf->txbuf_dma.cookie.dmac_address;
59010741SFei.Feng@Sun.COM bf->bf_mem = (uint8_t *)(bf->txbuf_dma.mem_va);
59110741SFei.Feng@Sun.COM bf->bf_daddr = ring->physaddr + _PTRDIFF(ds, ring->desc);
59210741SFei.Feng@Sun.COM bf->bf_desc = ds;
59310741SFei.Feng@Sun.COM }
59410741SFei.Feng@Sun.COM
59510741SFei.Feng@Sun.COM (void) ddi_dma_sync(ring->txdesc_dma.dma_hdl,
59610741SFei.Feng@Sun.COM 0,
59710741SFei.Feng@Sun.COM ring->txdesc_dma.alength,
59810741SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV);
59910741SFei.Feng@Sun.COM
60010741SFei.Feng@Sun.COM return (0);
60110741SFei.Feng@Sun.COM }
60210741SFei.Feng@Sun.COM
60310741SFei.Feng@Sun.COM /* ARGSUSED */
60410741SFei.Feng@Sun.COM static void
mwl_free_tx_ring(struct mwl_softc * sc,struct mwl_tx_ring * ring)60510741SFei.Feng@Sun.COM mwl_free_tx_ring(struct mwl_softc *sc, struct mwl_tx_ring *ring)
60610741SFei.Feng@Sun.COM {
60710741SFei.Feng@Sun.COM struct mwl_txbuf *bf;
60810741SFei.Feng@Sun.COM int i;
60910741SFei.Feng@Sun.COM
61010741SFei.Feng@Sun.COM if (ring->desc != NULL) {
61110741SFei.Feng@Sun.COM mwl_free_dma_mem(&ring->txdesc_dma);
61210741SFei.Feng@Sun.COM }
61310741SFei.Feng@Sun.COM
61410741SFei.Feng@Sun.COM if (ring->buf != NULL) {
61510741SFei.Feng@Sun.COM for (i = 0; i < ring->count; i++) {
61610741SFei.Feng@Sun.COM bf = &ring->buf[i];
61710741SFei.Feng@Sun.COM mwl_free_dma_mem(&bf->txbuf_dma);
61810741SFei.Feng@Sun.COM }
61910741SFei.Feng@Sun.COM kmem_free(ring->buf,
62010741SFei.Feng@Sun.COM (ring->count * sizeof (struct mwl_txbuf)));
62110741SFei.Feng@Sun.COM }
62210741SFei.Feng@Sun.COM }
62310741SFei.Feng@Sun.COM
62410741SFei.Feng@Sun.COM /*
62510741SFei.Feng@Sun.COM * Inform the f/w about location of the tx/rx dma data structures
62610741SFei.Feng@Sun.COM * and related state. This cmd must be done immediately after a
62710741SFei.Feng@Sun.COM * mwl_hal_gethwspecs call or the f/w will lockup.
62810741SFei.Feng@Sun.COM */
62910741SFei.Feng@Sun.COM static int
mwl_hal_sethwdma(struct mwl_softc * sc,const struct mwl_hal_txrxdma * dma)63010741SFei.Feng@Sun.COM mwl_hal_sethwdma(struct mwl_softc *sc, const struct mwl_hal_txrxdma *dma)
63110741SFei.Feng@Sun.COM {
63210741SFei.Feng@Sun.COM HostCmd_DS_SET_HW_SPEC *pCmd;
63310741SFei.Feng@Sun.COM int retval;
63410741SFei.Feng@Sun.COM
63510741SFei.Feng@Sun.COM _CMD_SETUP(pCmd, HostCmd_DS_SET_HW_SPEC, HostCmd_CMD_SET_HW_SPEC);
63610741SFei.Feng@Sun.COM pCmd->WcbBase[0] = LE_32(dma->wcbBase[0]);
63710741SFei.Feng@Sun.COM pCmd->WcbBase[1] = LE_32(dma->wcbBase[1]);
63810741SFei.Feng@Sun.COM pCmd->WcbBase[2] = LE_32(dma->wcbBase[2]);
63910741SFei.Feng@Sun.COM pCmd->WcbBase[3] = LE_32(dma->wcbBase[3]);
64010741SFei.Feng@Sun.COM pCmd->TxWcbNumPerQueue = LE_32(dma->maxNumTxWcb);
64110741SFei.Feng@Sun.COM pCmd->NumTxQueues = LE_32(dma->maxNumWCB);
64210741SFei.Feng@Sun.COM pCmd->TotalRxWcb = LE_32(1); /* XXX */
64310741SFei.Feng@Sun.COM pCmd->RxPdWrPtr = LE_32(dma->rxDescRead);
64410741SFei.Feng@Sun.COM /*
64510741SFei.Feng@Sun.COM * pCmd->Flags = LE_32(SET_HW_SPEC_HOSTFORM_BEACON
64610741SFei.Feng@Sun.COM * #ifdef MWL_HOST_PS_SUPPORT
64710741SFei.Feng@Sun.COM * | SET_HW_SPEC_HOST_POWERSAVE
64810741SFei.Feng@Sun.COM * #endif
64910741SFei.Feng@Sun.COM * | SET_HW_SPEC_HOSTFORM_PROBERESP);
65010741SFei.Feng@Sun.COM */
65110741SFei.Feng@Sun.COM pCmd->Flags = 0;
65210741SFei.Feng@Sun.COM /* disable multi-bss operation for A1-A4 parts */
65310741SFei.Feng@Sun.COM if (sc->sc_revs.mh_macRev < 5)
65410741SFei.Feng@Sun.COM pCmd->Flags |= LE_32(SET_HW_SPEC_DISABLEMBSS);
65510741SFei.Feng@Sun.COM
65610741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_HW_SPEC);
65710741SFei.Feng@Sun.COM if (retval == 0) {
65810741SFei.Feng@Sun.COM if (pCmd->Flags & LE_32(SET_HW_SPEC_DISABLEMBSS))
65910741SFei.Feng@Sun.COM sc->sc_hw_flags &= ~MHF_MBSS;
66010741SFei.Feng@Sun.COM else
66110741SFei.Feng@Sun.COM sc->sc_hw_flags |= MHF_MBSS;
66210741SFei.Feng@Sun.COM }
66310741SFei.Feng@Sun.COM
66410741SFei.Feng@Sun.COM return (retval);
66510741SFei.Feng@Sun.COM }
66610741SFei.Feng@Sun.COM
66710741SFei.Feng@Sun.COM /*
66810741SFei.Feng@Sun.COM * Inform firmware of our tx/rx dma setup. The BAR 0
66910741SFei.Feng@Sun.COM * writes below are for compatibility with older firmware.
67010741SFei.Feng@Sun.COM * For current firmware we send this information with a
67110741SFei.Feng@Sun.COM * cmd block via mwl_hal_sethwdma.
67210741SFei.Feng@Sun.COM */
67310741SFei.Feng@Sun.COM static int
mwl_setupdma(struct mwl_softc * sc)67410741SFei.Feng@Sun.COM mwl_setupdma(struct mwl_softc *sc)
67510741SFei.Feng@Sun.COM {
67610741SFei.Feng@Sun.COM int i, err;
67710741SFei.Feng@Sun.COM
67810741SFei.Feng@Sun.COM sc->sc_hwdma.rxDescRead = sc->sc_rxring.physaddr;
67910741SFei.Feng@Sun.COM mwl_mem_write4(sc, sc->sc_hwspecs.rxDescRead, sc->sc_hwdma.rxDescRead);
68010741SFei.Feng@Sun.COM mwl_mem_write4(sc, sc->sc_hwspecs.rxDescWrite, sc->sc_hwdma.rxDescRead);
68110741SFei.Feng@Sun.COM
68210741SFei.Feng@Sun.COM for (i = 0; i < MWL_NUM_TX_QUEUES - MWL_NUM_ACK_QUEUES; i++) {
68310741SFei.Feng@Sun.COM struct mwl_tx_ring *txring = &sc->sc_txring[i];
68410741SFei.Feng@Sun.COM sc->sc_hwdma.wcbBase[i] = txring->physaddr;
68510741SFei.Feng@Sun.COM mwl_mem_write4(sc, sc->sc_hwspecs.wcbBase[i],
68610741SFei.Feng@Sun.COM sc->sc_hwdma.wcbBase[i]);
68710741SFei.Feng@Sun.COM }
68810741SFei.Feng@Sun.COM sc->sc_hwdma.maxNumTxWcb = MWL_TX_RING_COUNT;
68910741SFei.Feng@Sun.COM sc->sc_hwdma.maxNumWCB = MWL_NUM_TX_QUEUES - MWL_NUM_ACK_QUEUES;
69010741SFei.Feng@Sun.COM
69110741SFei.Feng@Sun.COM err = mwl_hal_sethwdma(sc, &sc->sc_hwdma);
69210741SFei.Feng@Sun.COM if (err != 0) {
69310741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_DMA, "mwl: mwl_setupdma(): "
69410741SFei.Feng@Sun.COM "unable to setup tx/rx dma; hal status %u\n", err);
69510741SFei.Feng@Sun.COM /* XXX */
69610741SFei.Feng@Sun.COM }
69710741SFei.Feng@Sun.COM
69810741SFei.Feng@Sun.COM return (err);
69910741SFei.Feng@Sun.COM }
70010741SFei.Feng@Sun.COM
70110741SFei.Feng@Sun.COM /* ARGSUSED */
70210741SFei.Feng@Sun.COM static void
mwl_txq_init(struct mwl_softc * sc,struct mwl_tx_ring * txring,int qnum)70310741SFei.Feng@Sun.COM mwl_txq_init(struct mwl_softc *sc, struct mwl_tx_ring *txring, int qnum)
70410741SFei.Feng@Sun.COM {
70510741SFei.Feng@Sun.COM struct mwl_txbuf *bf;
70610741SFei.Feng@Sun.COM struct mwl_txdesc *ds;
70710741SFei.Feng@Sun.COM int i;
70810741SFei.Feng@Sun.COM
70910741SFei.Feng@Sun.COM txring->qnum = qnum;
71010741SFei.Feng@Sun.COM txring->txpri = 0; /* XXX */
71110741SFei.Feng@Sun.COM
71210741SFei.Feng@Sun.COM bf = txring->buf;
71310741SFei.Feng@Sun.COM ds = txring->desc;
71410741SFei.Feng@Sun.COM for (i = 0; i < MWL_TX_RING_COUNT - 1; i++) {
71510741SFei.Feng@Sun.COM bf++;
71610741SFei.Feng@Sun.COM ds->pPhysNext = bf->bf_daddr;
71710741SFei.Feng@Sun.COM ds++;
71810741SFei.Feng@Sun.COM }
71910741SFei.Feng@Sun.COM bf = txring->buf;
72010741SFei.Feng@Sun.COM ds->pPhysNext = LE_32(bf->bf_daddr);
72110741SFei.Feng@Sun.COM }
72210741SFei.Feng@Sun.COM
72310741SFei.Feng@Sun.COM /*
72410741SFei.Feng@Sun.COM * Setup a hardware data transmit queue for the specified
72510741SFei.Feng@Sun.COM * access control. We record the mapping from ac's
72610741SFei.Feng@Sun.COM * to h/w queues for use by mwl_tx_start.
72710741SFei.Feng@Sun.COM */
72810741SFei.Feng@Sun.COM static int
mwl_tx_setup(struct mwl_softc * sc,int ac,int mvtype)72910741SFei.Feng@Sun.COM mwl_tx_setup(struct mwl_softc *sc, int ac, int mvtype)
73010741SFei.Feng@Sun.COM {
73110741SFei.Feng@Sun.COM #define N(a) (sizeof (a)/sizeof (a[0]))
73210741SFei.Feng@Sun.COM struct mwl_tx_ring *txring;
73310741SFei.Feng@Sun.COM
73410741SFei.Feng@Sun.COM if (ac >= N(sc->sc_ac2q)) {
73510741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_DMA, "mwl: mwl_tx_setup(): "
73610741SFei.Feng@Sun.COM "AC %u out of range, max %u!\n",
73710741SFei.Feng@Sun.COM ac, (uint_t)N(sc->sc_ac2q));
73810741SFei.Feng@Sun.COM return (0);
73910741SFei.Feng@Sun.COM }
74010741SFei.Feng@Sun.COM if (mvtype >= MWL_NUM_TX_QUEUES) {
74110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_DMA, "mwl: mwl_tx_setup(): "
74210741SFei.Feng@Sun.COM "mvtype %u out of range, max %u!\n",
74310741SFei.Feng@Sun.COM mvtype, MWL_NUM_TX_QUEUES);
74410741SFei.Feng@Sun.COM return (0);
74510741SFei.Feng@Sun.COM }
74610741SFei.Feng@Sun.COM txring = &sc->sc_txring[mvtype];
74710741SFei.Feng@Sun.COM mwl_txq_init(sc, txring, mvtype);
74810741SFei.Feng@Sun.COM sc->sc_ac2q[ac] = txring;
74910741SFei.Feng@Sun.COM return (1);
75010741SFei.Feng@Sun.COM #undef N
75110741SFei.Feng@Sun.COM }
75210741SFei.Feng@Sun.COM
75310741SFei.Feng@Sun.COM static int
mwl_setup_txq(struct mwl_softc * sc)75410741SFei.Feng@Sun.COM mwl_setup_txq(struct mwl_softc *sc)
75510741SFei.Feng@Sun.COM {
75610741SFei.Feng@Sun.COM int err = 0;
75710741SFei.Feng@Sun.COM
75810741SFei.Feng@Sun.COM /* NB: insure BK queue is the lowest priority h/w queue */
75910741SFei.Feng@Sun.COM if (!mwl_tx_setup(sc, WME_AC_BK, MWL_WME_AC_BK)) {
76010741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_DMA, "mwl: mwl_setup_txq(): "
76110741SFei.Feng@Sun.COM "unable to setup xmit queue for %s traffic!\n",
76210741SFei.Feng@Sun.COM mwl_wme_acnames[WME_AC_BK]);
76310741SFei.Feng@Sun.COM err = EIO;
76410741SFei.Feng@Sun.COM return (err);
76510741SFei.Feng@Sun.COM }
76610741SFei.Feng@Sun.COM if (!mwl_tx_setup(sc, WME_AC_BE, MWL_WME_AC_BE) ||
76710741SFei.Feng@Sun.COM !mwl_tx_setup(sc, WME_AC_VI, MWL_WME_AC_VI) ||
76810741SFei.Feng@Sun.COM !mwl_tx_setup(sc, WME_AC_VO, MWL_WME_AC_VO)) {
76910741SFei.Feng@Sun.COM /*
77010741SFei.Feng@Sun.COM * Not enough hardware tx queues to properly do WME;
77110741SFei.Feng@Sun.COM * just punt and assign them all to the same h/w queue.
77210741SFei.Feng@Sun.COM * We could do a better job of this if, for example,
77310741SFei.Feng@Sun.COM * we allocate queues when we switch from station to
77410741SFei.Feng@Sun.COM * AP mode.
77510741SFei.Feng@Sun.COM */
77610741SFei.Feng@Sun.COM sc->sc_ac2q[WME_AC_BE] = sc->sc_ac2q[WME_AC_BK];
77710741SFei.Feng@Sun.COM sc->sc_ac2q[WME_AC_VI] = sc->sc_ac2q[WME_AC_BK];
77810741SFei.Feng@Sun.COM sc->sc_ac2q[WME_AC_VO] = sc->sc_ac2q[WME_AC_BK];
77910741SFei.Feng@Sun.COM }
78010741SFei.Feng@Sun.COM
78110741SFei.Feng@Sun.COM return (err);
78210741SFei.Feng@Sun.COM }
78310741SFei.Feng@Sun.COM
78410741SFei.Feng@Sun.COM /*
78510741SFei.Feng@Sun.COM * find mwl firmware module's "_start" "_end" symbols
78610741SFei.Feng@Sun.COM * and get its size.
78710741SFei.Feng@Sun.COM */
78810741SFei.Feng@Sun.COM static int
mwl_loadsym(ddi_modhandle_t modp,char * sym,char ** start,size_t * len)78910741SFei.Feng@Sun.COM mwl_loadsym(ddi_modhandle_t modp, char *sym, char **start, size_t *len)
79010741SFei.Feng@Sun.COM {
79110741SFei.Feng@Sun.COM char start_sym[64];
79210741SFei.Feng@Sun.COM char end_sym[64];
79310741SFei.Feng@Sun.COM char *p, *end;
79410741SFei.Feng@Sun.COM int rv;
79510741SFei.Feng@Sun.COM size_t n;
79610741SFei.Feng@Sun.COM
79710741SFei.Feng@Sun.COM (void) snprintf(start_sym, sizeof (start_sym), "%s_start", sym);
79810741SFei.Feng@Sun.COM (void) snprintf(end_sym, sizeof (end_sym), "%s_end", sym);
79910741SFei.Feng@Sun.COM
80010741SFei.Feng@Sun.COM p = (char *)ddi_modsym(modp, start_sym, &rv);
80110741SFei.Feng@Sun.COM if (p == NULL || rv != 0) {
80210741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_FW, "mwl: mwl_loadsym(): "
80310741SFei.Feng@Sun.COM "mod %s: symbol %s not found\n", sym, start_sym);
80410741SFei.Feng@Sun.COM return (-1);
80510741SFei.Feng@Sun.COM }
80610741SFei.Feng@Sun.COM
80710741SFei.Feng@Sun.COM end = (char *)ddi_modsym(modp, end_sym, &rv);
80810741SFei.Feng@Sun.COM if (end == NULL || rv != 0) {
80910741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_FW, "mwl: mwl_loadsym(): "
81010741SFei.Feng@Sun.COM "mod %s: symbol %s not found\n", sym, end_sym);
81110741SFei.Feng@Sun.COM return (-1);
81210741SFei.Feng@Sun.COM }
81310741SFei.Feng@Sun.COM
81410741SFei.Feng@Sun.COM n = _PTRDIFF(end, p);
81510741SFei.Feng@Sun.COM *start = p;
81610741SFei.Feng@Sun.COM *len = n;
81710741SFei.Feng@Sun.COM
81810741SFei.Feng@Sun.COM return (0);
81910741SFei.Feng@Sun.COM }
82010741SFei.Feng@Sun.COM
82110741SFei.Feng@Sun.COM static void
mwlFwReset(struct mwl_softc * sc)82210741SFei.Feng@Sun.COM mwlFwReset(struct mwl_softc *sc)
82310741SFei.Feng@Sun.COM {
82410741SFei.Feng@Sun.COM if (mwl_ctl_read4(sc, MACREG_REG_INT_CODE) == 0xffffffff) {
82510741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_FW, "mwl: mwlFWReset(): "
82610741SFei.Feng@Sun.COM "device not present!\n");
82710741SFei.Feng@Sun.COM return;
82810741SFei.Feng@Sun.COM }
82910741SFei.Feng@Sun.COM
83010741SFei.Feng@Sun.COM mwl_ctl_write4(sc, MACREG_REG_H2A_INTERRUPT_EVENTS, ISR_RESET);
83110741SFei.Feng@Sun.COM sc->sc_hw_flags &= ~MHF_FWHANG;
83210741SFei.Feng@Sun.COM }
83310741SFei.Feng@Sun.COM
83410741SFei.Feng@Sun.COM static void
mwlPokeSdramController(struct mwl_softc * sc,int SDRAMSIZE_Addr)83510741SFei.Feng@Sun.COM mwlPokeSdramController(struct mwl_softc *sc, int SDRAMSIZE_Addr)
83610741SFei.Feng@Sun.COM {
83710741SFei.Feng@Sun.COM /* Set up sdram controller for superflyv2 */
83810741SFei.Feng@Sun.COM mwl_ctl_write4(sc, 0x00006014, 0x33);
83910741SFei.Feng@Sun.COM mwl_ctl_write4(sc, 0x00006018, 0xa3a2632);
84010741SFei.Feng@Sun.COM mwl_ctl_write4(sc, 0x00006010, SDRAMSIZE_Addr);
84110741SFei.Feng@Sun.COM }
84210741SFei.Feng@Sun.COM
84310741SFei.Feng@Sun.COM static void
mwlTriggerPciCmd(struct mwl_softc * sc)84410741SFei.Feng@Sun.COM mwlTriggerPciCmd(struct mwl_softc *sc)
84510741SFei.Feng@Sun.COM {
84610741SFei.Feng@Sun.COM (void) ddi_dma_sync(sc->sc_cmd_dma.dma_hdl,
84710741SFei.Feng@Sun.COM 0,
84810741SFei.Feng@Sun.COM sc->sc_cmd_dma.alength,
84910741SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV);
85010741SFei.Feng@Sun.COM
85110741SFei.Feng@Sun.COM mwl_ctl_write4(sc, MACREG_REG_GEN_PTR, sc->sc_cmd_dmaaddr);
85210741SFei.Feng@Sun.COM (void) mwl_ctl_read4(sc, MACREG_REG_INT_CODE);
85310741SFei.Feng@Sun.COM
85410741SFei.Feng@Sun.COM mwl_ctl_write4(sc, MACREG_REG_INT_CODE, 0x00);
85510741SFei.Feng@Sun.COM (void) mwl_ctl_read4(sc, MACREG_REG_INT_CODE);
85610741SFei.Feng@Sun.COM
85710741SFei.Feng@Sun.COM mwl_ctl_write4(sc, MACREG_REG_H2A_INTERRUPT_EVENTS,
85810741SFei.Feng@Sun.COM MACREG_H2ARIC_BIT_DOOR_BELL);
85910741SFei.Feng@Sun.COM (void) mwl_ctl_read4(sc, MACREG_REG_INT_CODE);
86010741SFei.Feng@Sun.COM }
86110741SFei.Feng@Sun.COM
86210741SFei.Feng@Sun.COM static int
mwlWaitFor(struct mwl_softc * sc,uint32_t val)86310741SFei.Feng@Sun.COM mwlWaitFor(struct mwl_softc *sc, uint32_t val)
86410741SFei.Feng@Sun.COM {
86510741SFei.Feng@Sun.COM int i;
86610741SFei.Feng@Sun.COM
86710741SFei.Feng@Sun.COM for (i = 0; i < FW_MAX_NUM_CHECKS; i++) {
86810741SFei.Feng@Sun.COM DELAY(FW_CHECK_USECS);
86910741SFei.Feng@Sun.COM if (mwl_ctl_read4(sc, MACREG_REG_INT_CODE) == val)
87010741SFei.Feng@Sun.COM return (1);
87110741SFei.Feng@Sun.COM }
87210741SFei.Feng@Sun.COM return (0);
87310741SFei.Feng@Sun.COM }
87410741SFei.Feng@Sun.COM
87510741SFei.Feng@Sun.COM /*
87610741SFei.Feng@Sun.COM * Firmware block xmit when talking to the boot-rom.
87710741SFei.Feng@Sun.COM */
87810741SFei.Feng@Sun.COM static int
mwlSendBlock(struct mwl_softc * sc,int bsize,const void * data,size_t dsize)87910741SFei.Feng@Sun.COM mwlSendBlock(struct mwl_softc *sc, int bsize, const void *data, size_t dsize)
88010741SFei.Feng@Sun.COM {
88110741SFei.Feng@Sun.COM sc->sc_cmd_mem[0] = LE_16(HostCmd_CMD_CODE_DNLD);
88210741SFei.Feng@Sun.COM sc->sc_cmd_mem[1] = LE_16(bsize);
88310741SFei.Feng@Sun.COM (void) memcpy(&sc->sc_cmd_mem[4], data, dsize);
88410741SFei.Feng@Sun.COM mwlTriggerPciCmd(sc);
88510741SFei.Feng@Sun.COM /* XXX 2000 vs 200 */
88610741SFei.Feng@Sun.COM if (mwlWaitFor(sc, MACREG_INT_CODE_CMD_FINISHED)) {
88710741SFei.Feng@Sun.COM mwl_ctl_write4(sc, MACREG_REG_INT_CODE, 0);
88810741SFei.Feng@Sun.COM return (1);
88910741SFei.Feng@Sun.COM }
89010741SFei.Feng@Sun.COM
89110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_FW, "mwl: mwlSendBlock(): "
89210741SFei.Feng@Sun.COM "timeout waiting for CMD_FINISHED, INT_CODE 0x%x\n",
89310741SFei.Feng@Sun.COM mwl_ctl_read4(sc, MACREG_REG_INT_CODE));
89410741SFei.Feng@Sun.COM return (0);
89510741SFei.Feng@Sun.COM }
89610741SFei.Feng@Sun.COM
89710741SFei.Feng@Sun.COM /*
89810741SFei.Feng@Sun.COM * Firmware block xmit when talking to the 1st-stage loader.
89910741SFei.Feng@Sun.COM */
90010741SFei.Feng@Sun.COM static int
mwlSendBlock2(struct mwl_softc * sc,const void * data,size_t dsize)90110741SFei.Feng@Sun.COM mwlSendBlock2(struct mwl_softc *sc, const void *data, size_t dsize)
90210741SFei.Feng@Sun.COM {
90310741SFei.Feng@Sun.COM (void) memcpy(&sc->sc_cmd_mem[0], data, dsize);
90410741SFei.Feng@Sun.COM mwlTriggerPciCmd(sc);
90510741SFei.Feng@Sun.COM if (mwlWaitFor(sc, MACREG_INT_CODE_CMD_FINISHED)) {
90610741SFei.Feng@Sun.COM mwl_ctl_write4(sc, MACREG_REG_INT_CODE, 0);
90710741SFei.Feng@Sun.COM return (1);
90810741SFei.Feng@Sun.COM }
90910741SFei.Feng@Sun.COM
91010741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_FW, "mwl: mwlSendBlock2(): "
91110741SFei.Feng@Sun.COM "timeout waiting for CMD_FINISHED, INT_CODE 0x%x\n",
91210741SFei.Feng@Sun.COM mwl_ctl_read4(sc, MACREG_REG_INT_CODE));
91310741SFei.Feng@Sun.COM return (0);
91410741SFei.Feng@Sun.COM }
91510741SFei.Feng@Sun.COM
91610741SFei.Feng@Sun.COM /* ARGSUSED */
91710741SFei.Feng@Sun.COM static int
mwl_fwload(struct mwl_softc * sc,void * fwargs)91810741SFei.Feng@Sun.COM mwl_fwload(struct mwl_softc *sc, void *fwargs)
91910741SFei.Feng@Sun.COM {
92010741SFei.Feng@Sun.COM char *fwname = "mwlfw";
92110741SFei.Feng@Sun.COM char *fwbootname = "mwlboot";
92210741SFei.Feng@Sun.COM char *fwbinname = "mw88W8363fw";
92310741SFei.Feng@Sun.COM char *fwboot_index, *fw_index;
92410741SFei.Feng@Sun.COM uint8_t *fw, *fwboot;
92510741SFei.Feng@Sun.COM ddi_modhandle_t modfw;
92610741SFei.Feng@Sun.COM /* XXX get from firmware header */
92710741SFei.Feng@Sun.COM uint32_t FwReadySignature = HostCmd_SOFTAP_FWRDY_SIGNATURE;
92810741SFei.Feng@Sun.COM uint32_t OpMode = HostCmd_SOFTAP_MODE;
92910741SFei.Feng@Sun.COM const uint8_t *fp, *ep;
93010741SFei.Feng@Sun.COM size_t fw_size, fwboot_size;
93110741SFei.Feng@Sun.COM uint32_t blocksize, nbytes;
93210741SFei.Feng@Sun.COM int i, rv, err, ntries;
93310741SFei.Feng@Sun.COM
93410741SFei.Feng@Sun.COM rv = err = 0;
93510741SFei.Feng@Sun.COM fw = fwboot = NULL;
93610741SFei.Feng@Sun.COM fw_index = fwboot_index = NULL;
93710741SFei.Feng@Sun.COM
93810741SFei.Feng@Sun.COM modfw = ddi_modopen(fwname, KRTLD_MODE_FIRST, &rv);
93910741SFei.Feng@Sun.COM if (modfw == NULL) {
94010741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_FW, "mwl: mwl_fwload(): "
94110741SFei.Feng@Sun.COM "module %s not found\n", fwname);
94210741SFei.Feng@Sun.COM err = -1;
94310741SFei.Feng@Sun.COM goto bad2;
94410741SFei.Feng@Sun.COM }
94510741SFei.Feng@Sun.COM
94610741SFei.Feng@Sun.COM err = mwl_loadsym(modfw, fwbootname, &fwboot_index, &fwboot_size);
94710741SFei.Feng@Sun.COM if (err != 0) {
94810741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_FW, "mwl: mwl_fwload(): "
94910741SFei.Feng@Sun.COM "could not get boot firmware\n");
95010741SFei.Feng@Sun.COM err = -1;
95110741SFei.Feng@Sun.COM goto bad2;
95210741SFei.Feng@Sun.COM }
95310741SFei.Feng@Sun.COM
95410741SFei.Feng@Sun.COM err = mwl_loadsym(modfw, fwbinname, &fw_index, &fw_size);
95510741SFei.Feng@Sun.COM if (err != 0) {
95610741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_FW, "mwl: mwl_fwload(): "
95710741SFei.Feng@Sun.COM "could not get firmware\n");
95810741SFei.Feng@Sun.COM err = -1;
95910741SFei.Feng@Sun.COM goto bad2;
96010741SFei.Feng@Sun.COM }
96110741SFei.Feng@Sun.COM
96210741SFei.Feng@Sun.COM fwboot = (uint8_t *)kmem_alloc(fwboot_size, KM_SLEEP);
96310741SFei.Feng@Sun.COM if (fwboot == NULL) {
96410741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_FW, "mwl: mwl_loadfirmware(): "
96510741SFei.Feng@Sun.COM "failed to alloc boot firmware memory\n");
96610741SFei.Feng@Sun.COM err = -1;
96710741SFei.Feng@Sun.COM goto bad2;
96810741SFei.Feng@Sun.COM }
96910741SFei.Feng@Sun.COM (void) memcpy(fwboot, fwboot_index, fwboot_size);
97010741SFei.Feng@Sun.COM
97110741SFei.Feng@Sun.COM fw = (uint8_t *)kmem_alloc(fw_size, KM_SLEEP);
97210741SFei.Feng@Sun.COM if (fw == NULL) {
97310741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_FW, "mwl: mwl_loadfirmware(): "
97410741SFei.Feng@Sun.COM "failed to alloc firmware memory\n");
97510741SFei.Feng@Sun.COM err = -1;
97610741SFei.Feng@Sun.COM goto bad2;
97710741SFei.Feng@Sun.COM }
97810741SFei.Feng@Sun.COM (void) memcpy(fw, fw_index, fw_size);
97910741SFei.Feng@Sun.COM
98010741SFei.Feng@Sun.COM if (modfw != NULL)
98110741SFei.Feng@Sun.COM (void) ddi_modclose(modfw);
98210741SFei.Feng@Sun.COM
98310741SFei.Feng@Sun.COM if (fw_size < 4) {
98410741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_FW, "mwl: mwl_fwload(): "
98510741SFei.Feng@Sun.COM "could not load firmware image %s\n",
98610741SFei.Feng@Sun.COM fwname);
98710741SFei.Feng@Sun.COM err = ENXIO;
98810741SFei.Feng@Sun.COM goto bad2;
98910741SFei.Feng@Sun.COM }
99010741SFei.Feng@Sun.COM
99110741SFei.Feng@Sun.COM if (fw[0] == 0x01 && fw[1] == 0x00 &&
99210741SFei.Feng@Sun.COM fw[2] == 0x00 && fw[3] == 0x00) {
99310741SFei.Feng@Sun.COM /*
99410741SFei.Feng@Sun.COM * 2-stage load, get the boot firmware.
99510741SFei.Feng@Sun.COM */
99610741SFei.Feng@Sun.COM if (fwboot == NULL) {
99710741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_FW, "mwl: mwl_fwload(): "
99810741SFei.Feng@Sun.COM "could not load firmware image %s\n",
99910741SFei.Feng@Sun.COM fwbootname);
100010741SFei.Feng@Sun.COM err = ENXIO;
100110741SFei.Feng@Sun.COM goto bad2;
100210741SFei.Feng@Sun.COM }
100310741SFei.Feng@Sun.COM } else
100410741SFei.Feng@Sun.COM fwboot = NULL;
100510741SFei.Feng@Sun.COM
100610741SFei.Feng@Sun.COM mwlFwReset(sc);
100710741SFei.Feng@Sun.COM
100810741SFei.Feng@Sun.COM mwl_ctl_write4(sc, MACREG_REG_A2H_INTERRUPT_CLEAR_SEL,
100910741SFei.Feng@Sun.COM MACREG_A2HRIC_BIT_MASK);
101010741SFei.Feng@Sun.COM mwl_ctl_write4(sc, MACREG_REG_A2H_INTERRUPT_CAUSE, 0x00);
101110741SFei.Feng@Sun.COM mwl_ctl_write4(sc, MACREG_REG_A2H_INTERRUPT_MASK, 0x00);
101210741SFei.Feng@Sun.COM mwl_ctl_write4(sc, MACREG_REG_A2H_INTERRUPT_STATUS_MASK,
101310741SFei.Feng@Sun.COM MACREG_A2HRIC_BIT_MASK);
101410741SFei.Feng@Sun.COM if (sc->sc_SDRAMSIZE_Addr != 0) {
101510741SFei.Feng@Sun.COM /* Set up sdram controller for superflyv2 */
101610741SFei.Feng@Sun.COM mwlPokeSdramController(sc, sc->sc_SDRAMSIZE_Addr);
101710741SFei.Feng@Sun.COM }
101810741SFei.Feng@Sun.COM
101910741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_FW, "mwl: mwl_fwload(): "
102010741SFei.Feng@Sun.COM "load %s firmware image (%u bytes)\n",
102110741SFei.Feng@Sun.COM fwname, (unsigned int)fw_size);
102210741SFei.Feng@Sun.COM
102310741SFei.Feng@Sun.COM if (fwboot != NULL) {
102410741SFei.Feng@Sun.COM /*
102510741SFei.Feng@Sun.COM * Do 2-stage load. The 1st stage loader is setup
102610741SFei.Feng@Sun.COM * with the bootrom loader then we load the real
102710741SFei.Feng@Sun.COM * image using a different handshake. With this
102810741SFei.Feng@Sun.COM * mechanism the firmware is segmented into chunks
102910741SFei.Feng@Sun.COM * that have a CRC. If a chunk is incorrect we'll
103010741SFei.Feng@Sun.COM * be told to retransmit.
103110741SFei.Feng@Sun.COM */
103210741SFei.Feng@Sun.COM /* XXX assumes hlpimage fits in a block */
103310741SFei.Feng@Sun.COM /* NB: zero size block indicates download is finished */
103410741SFei.Feng@Sun.COM if (!mwlSendBlock(sc, fwboot_size, fwboot, fwboot_size) ||
103510741SFei.Feng@Sun.COM !mwlSendBlock(sc, 0, NULL, 0)) {
103610741SFei.Feng@Sun.COM err = ETIMEDOUT;
103710741SFei.Feng@Sun.COM goto bad;
103810741SFei.Feng@Sun.COM }
103910741SFei.Feng@Sun.COM DELAY(200 * FW_CHECK_USECS);
104010741SFei.Feng@Sun.COM if (sc->sc_SDRAMSIZE_Addr != 0) {
104110741SFei.Feng@Sun.COM /* Set up sdram controller for superflyv2 */
104210741SFei.Feng@Sun.COM mwlPokeSdramController(sc, sc->sc_SDRAMSIZE_Addr);
104310741SFei.Feng@Sun.COM }
104410741SFei.Feng@Sun.COM nbytes = ntries = 0; /* NB: silence compiler */
104510741SFei.Feng@Sun.COM for (fp = fw, ep = fp + fw_size; fp < ep; ) {
104610741SFei.Feng@Sun.COM mwl_ctl_write4(sc, MACREG_REG_INT_CODE, 0);
104710741SFei.Feng@Sun.COM blocksize = mwl_ctl_read4(sc, MACREG_REG_SCRATCH);
104810741SFei.Feng@Sun.COM if (blocksize == 0) /* download complete */
104910741SFei.Feng@Sun.COM break;
105010741SFei.Feng@Sun.COM if (blocksize > 0x00000c00) {
105110741SFei.Feng@Sun.COM err = EINVAL;
105210741SFei.Feng@Sun.COM goto bad;
105310741SFei.Feng@Sun.COM }
105410741SFei.Feng@Sun.COM if ((blocksize & 0x1) == 0) {
105510741SFei.Feng@Sun.COM /* block successfully downloaded, advance */
105610741SFei.Feng@Sun.COM fp += nbytes;
105710741SFei.Feng@Sun.COM ntries = 0;
105810741SFei.Feng@Sun.COM } else {
105910741SFei.Feng@Sun.COM if (++ntries > 2) {
106010741SFei.Feng@Sun.COM /*
106110741SFei.Feng@Sun.COM * Guard against f/w telling us to
106210741SFei.Feng@Sun.COM * retry infinitely.
106310741SFei.Feng@Sun.COM */
106410741SFei.Feng@Sun.COM err = ELOOP;
106510741SFei.Feng@Sun.COM goto bad;
106610741SFei.Feng@Sun.COM }
106710741SFei.Feng@Sun.COM /* clear NAK bit/flag */
106810741SFei.Feng@Sun.COM blocksize &= ~0x1;
106910741SFei.Feng@Sun.COM }
107010741SFei.Feng@Sun.COM if (blocksize > _PTRDIFF(ep, fp)) {
107110741SFei.Feng@Sun.COM /* XXX this should not happen, what to do? */
107210741SFei.Feng@Sun.COM blocksize = _PTRDIFF(ep, fp);
107310741SFei.Feng@Sun.COM }
107410741SFei.Feng@Sun.COM nbytes = blocksize;
107510741SFei.Feng@Sun.COM if (!mwlSendBlock2(sc, fp, nbytes)) {
107610741SFei.Feng@Sun.COM err = ETIMEDOUT;
107710741SFei.Feng@Sun.COM goto bad;
107810741SFei.Feng@Sun.COM }
107910741SFei.Feng@Sun.COM }
108010741SFei.Feng@Sun.COM } else {
108110741SFei.Feng@Sun.COM for (fp = fw, ep = fp + fw_size; fp < ep; ) {
108210741SFei.Feng@Sun.COM nbytes = _PTRDIFF(ep, fp);
108310741SFei.Feng@Sun.COM if (nbytes > FW_DOWNLOAD_BLOCK_SIZE)
108410741SFei.Feng@Sun.COM nbytes = FW_DOWNLOAD_BLOCK_SIZE;
108510741SFei.Feng@Sun.COM if (!mwlSendBlock(sc, FW_DOWNLOAD_BLOCK_SIZE, fp,
108610741SFei.Feng@Sun.COM nbytes)) {
108710741SFei.Feng@Sun.COM err = EIO;
108810741SFei.Feng@Sun.COM goto bad;
108910741SFei.Feng@Sun.COM }
109010741SFei.Feng@Sun.COM fp += nbytes;
109110741SFei.Feng@Sun.COM }
109210741SFei.Feng@Sun.COM }
109310741SFei.Feng@Sun.COM
109410741SFei.Feng@Sun.COM /*
109510741SFei.Feng@Sun.COM * Wait for firmware to startup; we monitor the
109610741SFei.Feng@Sun.COM * INT_CODE register waiting for a signature to
109710741SFei.Feng@Sun.COM * written back indicating it's ready to go.
109810741SFei.Feng@Sun.COM */
109910741SFei.Feng@Sun.COM sc->sc_cmd_mem[1] = 0;
110010741SFei.Feng@Sun.COM /*
110110741SFei.Feng@Sun.COM * XXX WAR for mfg fw download
110210741SFei.Feng@Sun.COM */
110310741SFei.Feng@Sun.COM if (OpMode != HostCmd_STA_MODE)
110410741SFei.Feng@Sun.COM mwlTriggerPciCmd(sc);
110510741SFei.Feng@Sun.COM for (i = 0; i < FW_MAX_NUM_CHECKS; i++) {
110610741SFei.Feng@Sun.COM mwl_ctl_write4(sc, MACREG_REG_GEN_PTR, OpMode);
110710741SFei.Feng@Sun.COM DELAY(FW_CHECK_USECS);
110810741SFei.Feng@Sun.COM if (mwl_ctl_read4(sc, MACREG_REG_INT_CODE) ==
110910741SFei.Feng@Sun.COM FwReadySignature) {
111010741SFei.Feng@Sun.COM mwl_ctl_write4(sc, MACREG_REG_INT_CODE, 0x00);
111110741SFei.Feng@Sun.COM return (mwlResetHalState(sc));
111210741SFei.Feng@Sun.COM }
111310741SFei.Feng@Sun.COM }
111410741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_FW, "mwl: mwl_fwload(): "
111510741SFei.Feng@Sun.COM "firmware download timeout\n");
111610741SFei.Feng@Sun.COM return (ETIMEDOUT);
111710741SFei.Feng@Sun.COM bad:
111810741SFei.Feng@Sun.COM mwlFwReset(sc);
111910741SFei.Feng@Sun.COM bad2:
112010741SFei.Feng@Sun.COM if (fw != NULL)
112110741SFei.Feng@Sun.COM kmem_free(fw, fw_size);
112210741SFei.Feng@Sun.COM if (fwboot != NULL)
112310741SFei.Feng@Sun.COM kmem_free(fwboot, fwboot_size);
112410741SFei.Feng@Sun.COM fwboot = fw = NULL;
112510741SFei.Feng@Sun.COM fwboot_index = fw_index = NULL;
112610741SFei.Feng@Sun.COM if (modfw != NULL)
112710741SFei.Feng@Sun.COM (void) ddi_modclose(modfw);
112810741SFei.Feng@Sun.COM return (err);
112910741SFei.Feng@Sun.COM }
113010741SFei.Feng@Sun.COM
113110741SFei.Feng@Sun.COM /*
113210741SFei.Feng@Sun.COM * Low level firmware cmd block handshake support.
113310741SFei.Feng@Sun.COM */
113410741SFei.Feng@Sun.COM static void
mwlSendCmd(struct mwl_softc * sc)113510741SFei.Feng@Sun.COM mwlSendCmd(struct mwl_softc *sc)
113610741SFei.Feng@Sun.COM {
113710741SFei.Feng@Sun.COM (void) ddi_dma_sync(sc->sc_cmd_dma.dma_hdl,
113810741SFei.Feng@Sun.COM 0,
113910741SFei.Feng@Sun.COM sc->sc_cmd_dma.alength,
114010741SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV);
114110741SFei.Feng@Sun.COM
114210741SFei.Feng@Sun.COM mwl_ctl_write4(sc, MACREG_REG_GEN_PTR, sc->sc_cmd_dmaaddr);
114310741SFei.Feng@Sun.COM (void) mwl_ctl_read4(sc, MACREG_REG_INT_CODE);
114410741SFei.Feng@Sun.COM
114510741SFei.Feng@Sun.COM mwl_ctl_write4(sc, MACREG_REG_H2A_INTERRUPT_EVENTS,
114610741SFei.Feng@Sun.COM MACREG_H2ARIC_BIT_DOOR_BELL);
114710741SFei.Feng@Sun.COM }
114810741SFei.Feng@Sun.COM
114910741SFei.Feng@Sun.COM static int
mwlExecuteCmd(struct mwl_softc * sc,unsigned short cmd)115010741SFei.Feng@Sun.COM mwlExecuteCmd(struct mwl_softc *sc, unsigned short cmd)
115110741SFei.Feng@Sun.COM {
115210741SFei.Feng@Sun.COM if (mwl_ctl_read4(sc, MACREG_REG_INT_CODE) == 0xffffffff) {
115310741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_CMD, "mwl: mwlExecuteCmd(): "
115410741SFei.Feng@Sun.COM "device not present!\n");
115510741SFei.Feng@Sun.COM return (EIO);
115610741SFei.Feng@Sun.COM }
115710741SFei.Feng@Sun.COM mwlSendCmd(sc);
115810741SFei.Feng@Sun.COM if (!mwlWaitForCmdComplete(sc, 0x8000 | cmd)) {
115910741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_CMD, "mwl: mwlExecuteCmd(): "
116010741SFei.Feng@Sun.COM "timeout waiting for f/w cmd %s\n", mwlcmdname(cmd));
116110741SFei.Feng@Sun.COM return (ETIMEDOUT);
116210741SFei.Feng@Sun.COM }
116310741SFei.Feng@Sun.COM (void) ddi_dma_sync(sc->sc_cmd_dma.dma_hdl,
116410741SFei.Feng@Sun.COM 0,
116510741SFei.Feng@Sun.COM sc->sc_cmd_dma.alength,
116610741SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV);
116710741SFei.Feng@Sun.COM
116810741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_CMD, "mwl: mwlExecuteCmd(): "
116910741SFei.Feng@Sun.COM "send cmd %s\n", mwlcmdname(cmd));
117010741SFei.Feng@Sun.COM
117110741SFei.Feng@Sun.COM if (mwl_dbg_flags & MWL_DBG_CMD)
117210741SFei.Feng@Sun.COM dumpresult(sc, 1);
117310741SFei.Feng@Sun.COM
117410741SFei.Feng@Sun.COM return (0);
117510741SFei.Feng@Sun.COM }
117610741SFei.Feng@Sun.COM
117710741SFei.Feng@Sun.COM static int
mwlWaitForCmdComplete(struct mwl_softc * sc,uint16_t cmdCode)117810741SFei.Feng@Sun.COM mwlWaitForCmdComplete(struct mwl_softc *sc, uint16_t cmdCode)
117910741SFei.Feng@Sun.COM {
118010741SFei.Feng@Sun.COM #define MAX_WAIT_FW_COMPLETE_ITERATIONS 10000
118110741SFei.Feng@Sun.COM int i;
118210741SFei.Feng@Sun.COM
118310741SFei.Feng@Sun.COM for (i = 0; i < MAX_WAIT_FW_COMPLETE_ITERATIONS; i++) {
118410741SFei.Feng@Sun.COM if (sc->sc_cmd_mem[0] == LE_16(cmdCode))
118510741SFei.Feng@Sun.COM return (1);
118610741SFei.Feng@Sun.COM DELAY(1 * 1000);
118710741SFei.Feng@Sun.COM }
118810741SFei.Feng@Sun.COM return (0);
118910741SFei.Feng@Sun.COM #undef MAX_WAIT_FW_COMPLETE_ITERATIONS
119010741SFei.Feng@Sun.COM }
119110741SFei.Feng@Sun.COM
119210741SFei.Feng@Sun.COM static const char *
mwlcmdname(int cmd)119310741SFei.Feng@Sun.COM mwlcmdname(int cmd)
119410741SFei.Feng@Sun.COM {
119510741SFei.Feng@Sun.COM static char buf[12];
119610741SFei.Feng@Sun.COM #define CMD(x) case HostCmd_CMD_##x: return #x
119710741SFei.Feng@Sun.COM switch (cmd) {
119810741SFei.Feng@Sun.COM CMD(CODE_DNLD);
119910741SFei.Feng@Sun.COM CMD(GET_HW_SPEC);
120010741SFei.Feng@Sun.COM CMD(SET_HW_SPEC);
120110741SFei.Feng@Sun.COM CMD(MAC_MULTICAST_ADR);
120210741SFei.Feng@Sun.COM CMD(802_11_GET_STAT);
120310741SFei.Feng@Sun.COM CMD(MAC_REG_ACCESS);
120410741SFei.Feng@Sun.COM CMD(BBP_REG_ACCESS);
120510741SFei.Feng@Sun.COM CMD(RF_REG_ACCESS);
120610741SFei.Feng@Sun.COM CMD(802_11_RADIO_CONTROL);
120710741SFei.Feng@Sun.COM CMD(802_11_RF_TX_POWER);
120810741SFei.Feng@Sun.COM CMD(802_11_RF_ANTENNA);
120910741SFei.Feng@Sun.COM CMD(SET_BEACON);
121010741SFei.Feng@Sun.COM CMD(SET_RF_CHANNEL);
121110741SFei.Feng@Sun.COM CMD(SET_AID);
121210741SFei.Feng@Sun.COM CMD(SET_INFRA_MODE);
121310741SFei.Feng@Sun.COM CMD(SET_G_PROTECT_FLAG);
121410741SFei.Feng@Sun.COM CMD(802_11_RTS_THSD);
121510741SFei.Feng@Sun.COM CMD(802_11_SET_SLOT);
121610741SFei.Feng@Sun.COM CMD(SET_EDCA_PARAMS);
121710741SFei.Feng@Sun.COM CMD(802_11H_DETECT_RADAR);
121810741SFei.Feng@Sun.COM CMD(SET_WMM_MODE);
121910741SFei.Feng@Sun.COM CMD(HT_GUARD_INTERVAL);
122010741SFei.Feng@Sun.COM CMD(SET_FIXED_RATE);
122110741SFei.Feng@Sun.COM CMD(SET_LINKADAPT_CS_MODE);
122210741SFei.Feng@Sun.COM CMD(SET_MAC_ADDR);
122310741SFei.Feng@Sun.COM CMD(SET_RATE_ADAPT_MODE);
122410741SFei.Feng@Sun.COM CMD(BSS_START);
122510741SFei.Feng@Sun.COM CMD(SET_NEW_STN);
122610741SFei.Feng@Sun.COM CMD(SET_KEEP_ALIVE);
122710741SFei.Feng@Sun.COM CMD(SET_APMODE);
122810741SFei.Feng@Sun.COM CMD(SET_SWITCH_CHANNEL);
122910741SFei.Feng@Sun.COM CMD(UPDATE_ENCRYPTION);
123010741SFei.Feng@Sun.COM CMD(BASTREAM);
123110741SFei.Feng@Sun.COM CMD(SET_RIFS);
123210741SFei.Feng@Sun.COM CMD(SET_N_PROTECT_FLAG);
123310741SFei.Feng@Sun.COM CMD(SET_N_PROTECT_OPMODE);
123410741SFei.Feng@Sun.COM CMD(SET_OPTIMIZATION_LEVEL);
123510741SFei.Feng@Sun.COM CMD(GET_CALTABLE);
123610741SFei.Feng@Sun.COM CMD(SET_MIMOPSHT);
123710741SFei.Feng@Sun.COM CMD(GET_BEACON);
123810741SFei.Feng@Sun.COM CMD(SET_REGION_CODE);
123910741SFei.Feng@Sun.COM CMD(SET_POWERSAVESTATION);
124010741SFei.Feng@Sun.COM CMD(SET_TIM);
124110741SFei.Feng@Sun.COM CMD(GET_TIM);
124210741SFei.Feng@Sun.COM CMD(GET_SEQNO);
124310741SFei.Feng@Sun.COM CMD(DWDS_ENABLE);
124410741SFei.Feng@Sun.COM CMD(AMPDU_RETRY_RATEDROP_MODE);
124510741SFei.Feng@Sun.COM CMD(CFEND_ENABLE);
124610741SFei.Feng@Sun.COM }
124710741SFei.Feng@Sun.COM (void) snprintf(buf, sizeof (buf), "0x%x", cmd);
124810741SFei.Feng@Sun.COM return (buf);
124910741SFei.Feng@Sun.COM #undef CMD
125010741SFei.Feng@Sun.COM }
125110741SFei.Feng@Sun.COM
125210741SFei.Feng@Sun.COM static void
dumpresult(struct mwl_softc * sc,int showresult)125310741SFei.Feng@Sun.COM dumpresult(struct mwl_softc *sc, int showresult)
125410741SFei.Feng@Sun.COM {
125510741SFei.Feng@Sun.COM const FWCmdHdr *h = (const FWCmdHdr *)sc->sc_cmd_mem;
125610741SFei.Feng@Sun.COM int len;
125710741SFei.Feng@Sun.COM
125810741SFei.Feng@Sun.COM len = LE_16(h->Length);
125910741SFei.Feng@Sun.COM #ifdef MWL_MBSS_SUPPORT
126010741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_CMD, "mwl: mwl_dumpresult(): "
126110741SFei.Feng@Sun.COM "Cmd %s Length %d SeqNum %d MacId %d",
126210741SFei.Feng@Sun.COM mwlcmdname(LE_16(h->Cmd) & ~0x8000), len, h->SeqNum, h->MacId);
126310741SFei.Feng@Sun.COM #else
126410741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_CMD, "mwl: mwl_dumpresult(): "
126510741SFei.Feng@Sun.COM "Cmd %s Length %d SeqNum %d",
126610741SFei.Feng@Sun.COM mwlcmdname(LE_16(h->Cmd) & ~0x8000), len, LE_16(h->SeqNum));
126710741SFei.Feng@Sun.COM #endif
126810741SFei.Feng@Sun.COM if (showresult) {
126910741SFei.Feng@Sun.COM const char *results[] =
127010741SFei.Feng@Sun.COM { "OK", "ERROR", "NOT_SUPPORT", "PENDING", "BUSY",
127110741SFei.Feng@Sun.COM "PARTIAL_DATA" };
127210741SFei.Feng@Sun.COM int result = LE_16(h->Result);
127310741SFei.Feng@Sun.COM
127410741SFei.Feng@Sun.COM if (result <= HostCmd_RESULT_PARTIAL_DATA)
127510741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_CMD, "mwl: dumpresult(): "
127610741SFei.Feng@Sun.COM "Result %s", results[result]);
127710741SFei.Feng@Sun.COM else
127810741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_CMD, "mwl: dumpresult(): "
127910741SFei.Feng@Sun.COM "Result %d", result);
128010741SFei.Feng@Sun.COM }
128110741SFei.Feng@Sun.COM }
128210741SFei.Feng@Sun.COM
128310741SFei.Feng@Sun.COM static int
mwlGetCalTable(struct mwl_softc * sc,uint8_t annex,uint8_t index)128410741SFei.Feng@Sun.COM mwlGetCalTable(struct mwl_softc *sc, uint8_t annex, uint8_t index)
128510741SFei.Feng@Sun.COM {
128610741SFei.Feng@Sun.COM HostCmd_FW_GET_CALTABLE *pCmd;
128710741SFei.Feng@Sun.COM int retval;
128810741SFei.Feng@Sun.COM
128910741SFei.Feng@Sun.COM _CMD_SETUP(pCmd, HostCmd_FW_GET_CALTABLE, HostCmd_CMD_GET_CALTABLE);
129010741SFei.Feng@Sun.COM pCmd->annex = annex;
129110741SFei.Feng@Sun.COM pCmd->index = index;
129210747SFei.Feng@Sun.COM (void) memset(pCmd->calTbl, 0, sizeof (pCmd->calTbl));
129310741SFei.Feng@Sun.COM
129410741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_GET_CALTABLE);
129510741SFei.Feng@Sun.COM if (retval == 0 &&
129610741SFei.Feng@Sun.COM pCmd->calTbl[0] != annex && annex != 0 && annex != 255)
129710741SFei.Feng@Sun.COM retval = EIO;
129810741SFei.Feng@Sun.COM return (retval);
129910741SFei.Feng@Sun.COM }
130010741SFei.Feng@Sun.COM
130110741SFei.Feng@Sun.COM /*
130210741SFei.Feng@Sun.COM * Construct channel info for 2.4GHz channels from cal data.
130310741SFei.Feng@Sun.COM */
130410741SFei.Feng@Sun.COM static void
get2Ghz(MWL_HAL_CHANNELINFO * ci,const uint8_t table[],int len)130510741SFei.Feng@Sun.COM get2Ghz(MWL_HAL_CHANNELINFO *ci, const uint8_t table[], int len)
130610741SFei.Feng@Sun.COM {
130710741SFei.Feng@Sun.COM int i, j;
130810741SFei.Feng@Sun.COM
130910741SFei.Feng@Sun.COM j = 0;
131010741SFei.Feng@Sun.COM for (i = 0; i < len; i += 4) {
131110741SFei.Feng@Sun.COM struct mwl_hal_channel *hc = &ci->channels[j];
131210741SFei.Feng@Sun.COM hc->ieee = 1+j;
131310741SFei.Feng@Sun.COM hc->freq = ieee2mhz(1+j);
131410741SFei.Feng@Sun.COM (void) memcpy(hc->targetPowers, &table[i], 4);
131510741SFei.Feng@Sun.COM setmaxtxpow(hc, 0, 4);
131610741SFei.Feng@Sun.COM j++;
131710741SFei.Feng@Sun.COM }
131810741SFei.Feng@Sun.COM ci->nchannels = j;
131910741SFei.Feng@Sun.COM ci->freqLow = ieee2mhz(1);
132010741SFei.Feng@Sun.COM ci->freqHigh = ieee2mhz(j);
132110741SFei.Feng@Sun.COM }
132210741SFei.Feng@Sun.COM
132310741SFei.Feng@Sun.COM /*
132410741SFei.Feng@Sun.COM * Construct channel info for 5GHz channels from cal data.
132510741SFei.Feng@Sun.COM */
132610741SFei.Feng@Sun.COM static void
get5Ghz(MWL_HAL_CHANNELINFO * ci,const uint8_t table[],int len)132710741SFei.Feng@Sun.COM get5Ghz(MWL_HAL_CHANNELINFO *ci, const uint8_t table[], int len)
132810741SFei.Feng@Sun.COM {
132910741SFei.Feng@Sun.COM int i, j, f, l, h;
133010741SFei.Feng@Sun.COM
133110741SFei.Feng@Sun.COM l = 32000;
133210741SFei.Feng@Sun.COM h = 0;
133310741SFei.Feng@Sun.COM j = 0;
133410741SFei.Feng@Sun.COM for (i = 0; i < len; i += 4) {
133510741SFei.Feng@Sun.COM struct mwl_hal_channel *hc;
133610741SFei.Feng@Sun.COM
133710741SFei.Feng@Sun.COM if (table[i] == 0)
133810741SFei.Feng@Sun.COM continue;
133910741SFei.Feng@Sun.COM f = 5000 + 5*table[i];
134010741SFei.Feng@Sun.COM if (f < l)
134110741SFei.Feng@Sun.COM l = f;
134210741SFei.Feng@Sun.COM if (f > h)
134310741SFei.Feng@Sun.COM h = f;
134410741SFei.Feng@Sun.COM hc = &ci->channels[j];
134510741SFei.Feng@Sun.COM hc->freq = (uint16_t)f;
134610741SFei.Feng@Sun.COM hc->ieee = table[i];
134710741SFei.Feng@Sun.COM (void) memcpy(hc->targetPowers, &table[i], 4);
134810741SFei.Feng@Sun.COM setmaxtxpow(hc, 1, 4); /* NB: col 1 is the freq, skip */
134910741SFei.Feng@Sun.COM j++;
135010741SFei.Feng@Sun.COM }
135110741SFei.Feng@Sun.COM ci->nchannels = j;
135210741SFei.Feng@Sun.COM ci->freqLow = (uint16_t)((l == 32000) ? 0 : l);
135310741SFei.Feng@Sun.COM ci->freqHigh = (uint16_t)h;
135410741SFei.Feng@Sun.COM }
135510741SFei.Feng@Sun.COM
135610741SFei.Feng@Sun.COM /*
135710741SFei.Feng@Sun.COM * Calculate the max tx power from the channel's cal data.
135810741SFei.Feng@Sun.COM */
135910741SFei.Feng@Sun.COM static void
setmaxtxpow(struct mwl_hal_channel * hc,int i,int maxix)136010741SFei.Feng@Sun.COM setmaxtxpow(struct mwl_hal_channel *hc, int i, int maxix)
136110741SFei.Feng@Sun.COM {
136210741SFei.Feng@Sun.COM hc->maxTxPow = hc->targetPowers[i];
136310741SFei.Feng@Sun.COM for (i++; i < maxix; i++)
136410741SFei.Feng@Sun.COM if (hc->targetPowers[i] > hc->maxTxPow)
136510741SFei.Feng@Sun.COM hc->maxTxPow = hc->targetPowers[i];
136610741SFei.Feng@Sun.COM }
136710741SFei.Feng@Sun.COM
136810741SFei.Feng@Sun.COM static uint16_t
ieee2mhz(int chan)136910741SFei.Feng@Sun.COM ieee2mhz(int chan)
137010741SFei.Feng@Sun.COM {
137110741SFei.Feng@Sun.COM if (chan == 14)
137210741SFei.Feng@Sun.COM return (2484);
137310741SFei.Feng@Sun.COM if (chan < 14)
137410741SFei.Feng@Sun.COM return (2407 + chan * 5);
137510741SFei.Feng@Sun.COM return (2512 + (chan - 15) * 20);
137610741SFei.Feng@Sun.COM }
137710741SFei.Feng@Sun.COM
137810741SFei.Feng@Sun.COM static void
dumpcaldata(const char * name,const uint8_t * table,int n)137910741SFei.Feng@Sun.COM dumpcaldata(const char *name, const uint8_t *table, int n)
138010741SFei.Feng@Sun.COM {
138110741SFei.Feng@Sun.COM int i;
138210741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_HW, "\n%s:\n", name);
138310741SFei.Feng@Sun.COM for (i = 0; i < n; i += 4)
138410741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_HW, "[%2d] %3d %3d %3d %3d\n",
138510741SFei.Feng@Sun.COM i/4, table[i+0], table[i+1], table[i+2], table[i+3]);
138610741SFei.Feng@Sun.COM }
138710741SFei.Feng@Sun.COM
138810741SFei.Feng@Sun.COM static int
mwlGetPwrCalTable(struct mwl_softc * sc)138910741SFei.Feng@Sun.COM mwlGetPwrCalTable(struct mwl_softc *sc)
139010741SFei.Feng@Sun.COM {
139110741SFei.Feng@Sun.COM const uint8_t *data;
139210741SFei.Feng@Sun.COM MWL_HAL_CHANNELINFO *ci;
139310741SFei.Feng@Sun.COM int len;
139410741SFei.Feng@Sun.COM
139510741SFei.Feng@Sun.COM /* NB: we hold the lock so it's ok to use cmdbuf */
139610741SFei.Feng@Sun.COM data = ((const HostCmd_FW_GET_CALTABLE *) sc->sc_cmd_mem)->calTbl;
139710741SFei.Feng@Sun.COM if (mwlGetCalTable(sc, 33, 0) == 0) {
139810741SFei.Feng@Sun.COM len = (data[2] | (data[3] << 8)) - 12;
139910741SFei.Feng@Sun.COM if (len > PWTAGETRATETABLE20M)
140010741SFei.Feng@Sun.COM len = PWTAGETRATETABLE20M;
140110741SFei.Feng@Sun.COM dumpcaldata("2.4G 20M", &data[12], len);
140210741SFei.Feng@Sun.COM get2Ghz(&sc->sc_20M, &data[12], len);
140310741SFei.Feng@Sun.COM }
140410741SFei.Feng@Sun.COM if (mwlGetCalTable(sc, 34, 0) == 0) {
140510741SFei.Feng@Sun.COM len = (data[2] | (data[3] << 8)) - 12;
140610741SFei.Feng@Sun.COM if (len > PWTAGETRATETABLE40M)
140710741SFei.Feng@Sun.COM len = PWTAGETRATETABLE40M;
140810741SFei.Feng@Sun.COM dumpcaldata("2.4G 40M", &data[12], len);
140910741SFei.Feng@Sun.COM ci = &sc->sc_40M;
141010741SFei.Feng@Sun.COM get2Ghz(ci, &data[12], len);
141110741SFei.Feng@Sun.COM }
141210741SFei.Feng@Sun.COM if (mwlGetCalTable(sc, 35, 0) == 0) {
141310741SFei.Feng@Sun.COM len = (data[2] | (data[3] << 8)) - 20;
141410741SFei.Feng@Sun.COM if (len > PWTAGETRATETABLE20M_5G)
141510741SFei.Feng@Sun.COM len = PWTAGETRATETABLE20M_5G;
141610741SFei.Feng@Sun.COM dumpcaldata("5G 20M", &data[20], len);
141710741SFei.Feng@Sun.COM get5Ghz(&sc->sc_20M_5G, &data[20], len);
141810741SFei.Feng@Sun.COM }
141910741SFei.Feng@Sun.COM if (mwlGetCalTable(sc, 36, 0) == 0) {
142010741SFei.Feng@Sun.COM len = (data[2] | (data[3] << 8)) - 20;
142110741SFei.Feng@Sun.COM if (len > PWTAGETRATETABLE40M_5G)
142210741SFei.Feng@Sun.COM len = PWTAGETRATETABLE40M_5G;
142310741SFei.Feng@Sun.COM dumpcaldata("5G 40M", &data[20], len);
142410741SFei.Feng@Sun.COM ci = &sc->sc_40M_5G;
142510741SFei.Feng@Sun.COM get5Ghz(ci, &data[20], len);
142610741SFei.Feng@Sun.COM }
142710741SFei.Feng@Sun.COM sc->sc_hw_flags |= MHF_CALDATA;
142810741SFei.Feng@Sun.COM return (0);
142910741SFei.Feng@Sun.COM }
143010741SFei.Feng@Sun.COM
143110741SFei.Feng@Sun.COM /*
143210741SFei.Feng@Sun.COM * Reset internal state after a firmware download.
143310741SFei.Feng@Sun.COM */
143410741SFei.Feng@Sun.COM static int
mwlResetHalState(struct mwl_softc * sc)143510741SFei.Feng@Sun.COM mwlResetHalState(struct mwl_softc *sc)
143610741SFei.Feng@Sun.COM {
143710741SFei.Feng@Sun.COM int err = 0;
143810741SFei.Feng@Sun.COM
143910741SFei.Feng@Sun.COM /*
144010741SFei.Feng@Sun.COM * Fetch cal data for later use.
144110741SFei.Feng@Sun.COM * XXX may want to fetch other stuff too.
144210741SFei.Feng@Sun.COM */
144310741SFei.Feng@Sun.COM /* XXX check return */
144410741SFei.Feng@Sun.COM if ((sc->sc_hw_flags & MHF_CALDATA) == 0)
144510741SFei.Feng@Sun.COM err = mwlGetPwrCalTable(sc);
144610741SFei.Feng@Sun.COM return (err);
144710741SFei.Feng@Sun.COM }
144810741SFei.Feng@Sun.COM
144910741SFei.Feng@Sun.COM #define IEEE80211_CHAN_HTG (IEEE80211_CHAN_HT|IEEE80211_CHAN_G)
145010741SFei.Feng@Sun.COM #define IEEE80211_CHAN_HTA (IEEE80211_CHAN_HT|IEEE80211_CHAN_A)
145110741SFei.Feng@Sun.COM
145210741SFei.Feng@Sun.COM static void
addchan(struct mwl_channel * c,int freq,int flags,int ieee,int txpow)145310741SFei.Feng@Sun.COM addchan(struct mwl_channel *c, int freq, int flags, int ieee, int txpow)
145410741SFei.Feng@Sun.COM {
145510741SFei.Feng@Sun.COM c->ic_freq = (uint16_t)freq;
145610741SFei.Feng@Sun.COM c->ic_flags = flags;
145710741SFei.Feng@Sun.COM c->ic_ieee = (uint8_t)ieee;
145810741SFei.Feng@Sun.COM c->ic_minpower = 0;
145910741SFei.Feng@Sun.COM c->ic_maxpower = 2*txpow;
146010741SFei.Feng@Sun.COM c->ic_maxregpower = (uint8_t)txpow;
146110741SFei.Feng@Sun.COM }
146210741SFei.Feng@Sun.COM
146310741SFei.Feng@Sun.COM static const struct mwl_channel *
findchannel(const struct mwl_channel chans[],int nchans,int freq,int flags)146410741SFei.Feng@Sun.COM findchannel(const struct mwl_channel chans[], int nchans,
146510741SFei.Feng@Sun.COM int freq, int flags)
146610741SFei.Feng@Sun.COM {
146710741SFei.Feng@Sun.COM const struct mwl_channel *c;
146810741SFei.Feng@Sun.COM int i;
146910741SFei.Feng@Sun.COM
147010741SFei.Feng@Sun.COM for (i = 0; i < nchans; i++) {
147110741SFei.Feng@Sun.COM c = &chans[i];
147210741SFei.Feng@Sun.COM if (c->ic_freq == freq && c->ic_flags == flags)
147310741SFei.Feng@Sun.COM return (c);
147410741SFei.Feng@Sun.COM }
147510741SFei.Feng@Sun.COM return (NULL);
147610741SFei.Feng@Sun.COM }
147710741SFei.Feng@Sun.COM
147810741SFei.Feng@Sun.COM static void
addht40channels(struct mwl_channel chans[],int maxchans,int * nchans,const MWL_HAL_CHANNELINFO * ci,int flags)147910741SFei.Feng@Sun.COM addht40channels(struct mwl_channel chans[], int maxchans, int *nchans,
148010741SFei.Feng@Sun.COM const MWL_HAL_CHANNELINFO *ci, int flags)
148110741SFei.Feng@Sun.COM {
148210741SFei.Feng@Sun.COM struct mwl_channel *c;
148310741SFei.Feng@Sun.COM const struct mwl_channel *extc;
148410741SFei.Feng@Sun.COM const struct mwl_hal_channel *hc;
148510741SFei.Feng@Sun.COM int i;
148610741SFei.Feng@Sun.COM
148710741SFei.Feng@Sun.COM c = &chans[*nchans];
148810741SFei.Feng@Sun.COM
148910741SFei.Feng@Sun.COM flags &= ~IEEE80211_CHAN_HT;
149010741SFei.Feng@Sun.COM for (i = 0; i < ci->nchannels; i++) {
149110741SFei.Feng@Sun.COM /*
149210741SFei.Feng@Sun.COM * Each entry defines an HT40 channel pair; find the
149310741SFei.Feng@Sun.COM * extension channel above and the insert the pair.
149410741SFei.Feng@Sun.COM */
149510741SFei.Feng@Sun.COM hc = &ci->channels[i];
149610741SFei.Feng@Sun.COM extc = findchannel(chans, *nchans, hc->freq+20,
149710741SFei.Feng@Sun.COM flags | IEEE80211_CHAN_HT20);
149810741SFei.Feng@Sun.COM if (extc != NULL) {
149910741SFei.Feng@Sun.COM if (*nchans >= maxchans)
150010741SFei.Feng@Sun.COM break;
150110741SFei.Feng@Sun.COM addchan(c, hc->freq, flags | IEEE80211_CHAN_HT40U,
150210741SFei.Feng@Sun.COM hc->ieee, hc->maxTxPow);
150310741SFei.Feng@Sun.COM c->ic_extieee = extc->ic_ieee;
150410741SFei.Feng@Sun.COM c++, (*nchans)++;
150510741SFei.Feng@Sun.COM if (*nchans >= maxchans)
150610741SFei.Feng@Sun.COM break;
150710741SFei.Feng@Sun.COM addchan(c, extc->ic_freq, flags | IEEE80211_CHAN_HT40D,
150810741SFei.Feng@Sun.COM extc->ic_ieee, hc->maxTxPow);
150910741SFei.Feng@Sun.COM c->ic_extieee = hc->ieee;
151010741SFei.Feng@Sun.COM c++, (*nchans)++;
151110741SFei.Feng@Sun.COM }
151210741SFei.Feng@Sun.COM }
151310741SFei.Feng@Sun.COM }
151410741SFei.Feng@Sun.COM
151510741SFei.Feng@Sun.COM static void
addchannels(struct mwl_channel chans[],int maxchans,int * nchans,const MWL_HAL_CHANNELINFO * ci,int flags)151610741SFei.Feng@Sun.COM addchannels(struct mwl_channel chans[], int maxchans, int *nchans,
151710741SFei.Feng@Sun.COM const MWL_HAL_CHANNELINFO *ci, int flags)
151810741SFei.Feng@Sun.COM {
151910741SFei.Feng@Sun.COM struct mwl_channel *c;
152010741SFei.Feng@Sun.COM int i;
152110741SFei.Feng@Sun.COM
152210741SFei.Feng@Sun.COM c = &chans[*nchans];
152310741SFei.Feng@Sun.COM
152410741SFei.Feng@Sun.COM for (i = 0; i < ci->nchannels; i++) {
152510741SFei.Feng@Sun.COM const struct mwl_hal_channel *hc;
152610741SFei.Feng@Sun.COM
152710741SFei.Feng@Sun.COM hc = &ci->channels[i];
152810741SFei.Feng@Sun.COM if (*nchans >= maxchans)
152910741SFei.Feng@Sun.COM break;
153010741SFei.Feng@Sun.COM addchan(c, hc->freq, flags, hc->ieee, hc->maxTxPow);
153110741SFei.Feng@Sun.COM c++, (*nchans)++;
153210741SFei.Feng@Sun.COM
153310741SFei.Feng@Sun.COM if (flags == IEEE80211_CHAN_G || flags == IEEE80211_CHAN_HTG) {
153410741SFei.Feng@Sun.COM /* g channel have a separate b-only entry */
153510741SFei.Feng@Sun.COM if (*nchans >= maxchans)
153610741SFei.Feng@Sun.COM break;
153710741SFei.Feng@Sun.COM c[0] = c[-1];
153810741SFei.Feng@Sun.COM c[-1].ic_flags = IEEE80211_CHAN_B;
153910741SFei.Feng@Sun.COM c++, (*nchans)++;
154010741SFei.Feng@Sun.COM }
154110741SFei.Feng@Sun.COM if (flags == IEEE80211_CHAN_HTG) {
154210741SFei.Feng@Sun.COM /* HT g channel have a separate g-only entry */
154310741SFei.Feng@Sun.COM if (*nchans >= maxchans)
154410741SFei.Feng@Sun.COM break;
154510741SFei.Feng@Sun.COM c[-1].ic_flags = IEEE80211_CHAN_G;
154610741SFei.Feng@Sun.COM c[0] = c[-1];
154710741SFei.Feng@Sun.COM c[0].ic_flags &= ~IEEE80211_CHAN_HT;
154810741SFei.Feng@Sun.COM c[0].ic_flags |= IEEE80211_CHAN_HT20; /* HT20 */
154910741SFei.Feng@Sun.COM c++, (*nchans)++;
155010741SFei.Feng@Sun.COM }
155110741SFei.Feng@Sun.COM if (flags == IEEE80211_CHAN_HTA) {
155210741SFei.Feng@Sun.COM /* HT a channel have a separate a-only entry */
155310741SFei.Feng@Sun.COM if (*nchans >= maxchans)
155410741SFei.Feng@Sun.COM break;
155510741SFei.Feng@Sun.COM c[-1].ic_flags = IEEE80211_CHAN_A;
155610741SFei.Feng@Sun.COM c[0] = c[-1];
155710741SFei.Feng@Sun.COM c[0].ic_flags &= ~IEEE80211_CHAN_HT;
155810741SFei.Feng@Sun.COM c[0].ic_flags |= IEEE80211_CHAN_HT20; /* HT20 */
155910741SFei.Feng@Sun.COM c++, (*nchans)++;
156010741SFei.Feng@Sun.COM }
156110741SFei.Feng@Sun.COM }
156210741SFei.Feng@Sun.COM }
156310741SFei.Feng@Sun.COM
156410741SFei.Feng@Sun.COM static int
mwl_hal_getchannelinfo(struct mwl_softc * sc,int band,int chw,const MWL_HAL_CHANNELINFO ** ci)156510741SFei.Feng@Sun.COM mwl_hal_getchannelinfo(struct mwl_softc *sc, int band, int chw,
156610741SFei.Feng@Sun.COM const MWL_HAL_CHANNELINFO **ci)
156710741SFei.Feng@Sun.COM {
156810741SFei.Feng@Sun.COM switch (band) {
156910741SFei.Feng@Sun.COM case MWL_FREQ_BAND_2DOT4GHZ:
157010741SFei.Feng@Sun.COM *ci = (chw == MWL_CH_20_MHz_WIDTH) ? &sc->sc_20M : &sc->sc_40M;
157110741SFei.Feng@Sun.COM break;
157210741SFei.Feng@Sun.COM case MWL_FREQ_BAND_5GHZ:
157310741SFei.Feng@Sun.COM *ci = (chw == MWL_CH_20_MHz_WIDTH) ?
157410741SFei.Feng@Sun.COM &sc->sc_20M_5G : &sc->sc_40M_5G;
157510741SFei.Feng@Sun.COM break;
157610741SFei.Feng@Sun.COM default:
157710741SFei.Feng@Sun.COM return (EINVAL);
157810741SFei.Feng@Sun.COM }
157910741SFei.Feng@Sun.COM return (((*ci)->freqLow == (*ci)->freqHigh) ? EINVAL : 0);
158010741SFei.Feng@Sun.COM }
158110741SFei.Feng@Sun.COM
158210741SFei.Feng@Sun.COM static void
getchannels(struct mwl_softc * sc,int maxchans,int * nchans,struct mwl_channel chans[])158310741SFei.Feng@Sun.COM getchannels(struct mwl_softc *sc, int maxchans, int *nchans,
158410741SFei.Feng@Sun.COM struct mwl_channel chans[])
158510741SFei.Feng@Sun.COM {
158610741SFei.Feng@Sun.COM const MWL_HAL_CHANNELINFO *ci;
158710741SFei.Feng@Sun.COM
158810741SFei.Feng@Sun.COM /*
158910741SFei.Feng@Sun.COM * Use the channel info from the hal to craft the
159010741SFei.Feng@Sun.COM * channel list. Note that we pass back an unsorted
159110741SFei.Feng@Sun.COM * list; the caller is required to sort it for us
159210741SFei.Feng@Sun.COM * (if desired).
159310741SFei.Feng@Sun.COM */
159410741SFei.Feng@Sun.COM *nchans = 0;
159510741SFei.Feng@Sun.COM if (mwl_hal_getchannelinfo(sc,
159610741SFei.Feng@Sun.COM MWL_FREQ_BAND_2DOT4GHZ, MWL_CH_20_MHz_WIDTH, &ci) == 0)
159710741SFei.Feng@Sun.COM addchannels(chans, maxchans, nchans, ci, IEEE80211_CHAN_HTG);
159810741SFei.Feng@Sun.COM if (mwl_hal_getchannelinfo(sc,
159910741SFei.Feng@Sun.COM MWL_FREQ_BAND_5GHZ, MWL_CH_20_MHz_WIDTH, &ci) == 0)
160010741SFei.Feng@Sun.COM addchannels(chans, maxchans, nchans, ci, IEEE80211_CHAN_HTA);
160110741SFei.Feng@Sun.COM if (mwl_hal_getchannelinfo(sc,
160210741SFei.Feng@Sun.COM MWL_FREQ_BAND_2DOT4GHZ, MWL_CH_40_MHz_WIDTH, &ci) == 0)
160310741SFei.Feng@Sun.COM addht40channels(chans, maxchans, nchans, ci,
160410741SFei.Feng@Sun.COM IEEE80211_CHAN_HTG);
160510741SFei.Feng@Sun.COM if (mwl_hal_getchannelinfo(sc,
160610741SFei.Feng@Sun.COM MWL_FREQ_BAND_5GHZ, MWL_CH_40_MHz_WIDTH, &ci) == 0)
160710741SFei.Feng@Sun.COM addht40channels(chans, maxchans, nchans, ci,
160810741SFei.Feng@Sun.COM IEEE80211_CHAN_HTA);
160910741SFei.Feng@Sun.COM }
161010741SFei.Feng@Sun.COM
161110741SFei.Feng@Sun.COM static int
mwl_getchannels(struct mwl_softc * sc)161210741SFei.Feng@Sun.COM mwl_getchannels(struct mwl_softc *sc)
161310741SFei.Feng@Sun.COM {
161410741SFei.Feng@Sun.COM /*
161510741SFei.Feng@Sun.COM * Use the channel info from the hal to craft the
161610741SFei.Feng@Sun.COM * channel list for net80211. Note that we pass up
161710741SFei.Feng@Sun.COM * an unsorted list; net80211 will sort it for us.
161810741SFei.Feng@Sun.COM */
161910747SFei.Feng@Sun.COM (void) memset(sc->sc_channels, 0, sizeof (sc->sc_channels));
162010741SFei.Feng@Sun.COM sc->sc_nchans = 0;
162110741SFei.Feng@Sun.COM getchannels(sc, IEEE80211_CHAN_MAX, &sc->sc_nchans, sc->sc_channels);
162210741SFei.Feng@Sun.COM
162310741SFei.Feng@Sun.COM sc->sc_regdomain.regdomain = SKU_DEBUG;
162410741SFei.Feng@Sun.COM sc->sc_regdomain.country = CTRY_DEFAULT;
162510741SFei.Feng@Sun.COM sc->sc_regdomain.location = 'I';
162610741SFei.Feng@Sun.COM sc->sc_regdomain.isocc[0] = ' '; /* XXX? */
162710741SFei.Feng@Sun.COM sc->sc_regdomain.isocc[1] = ' ';
162810741SFei.Feng@Sun.COM return (sc->sc_nchans == 0 ? EIO : 0);
162910741SFei.Feng@Sun.COM }
163010741SFei.Feng@Sun.COM
163110741SFei.Feng@Sun.COM #undef IEEE80211_CHAN_HTA
163210741SFei.Feng@Sun.COM #undef IEEE80211_CHAN_HTG
163310741SFei.Feng@Sun.COM
163410741SFei.Feng@Sun.COM /*
163510741SFei.Feng@Sun.COM * Return "hw specs". Note this must be the first
163610741SFei.Feng@Sun.COM * cmd MUST be done after a firmware download or the
163710741SFei.Feng@Sun.COM * f/w will lockup.
163810741SFei.Feng@Sun.COM * XXX move into the hal so driver doesn't need to be responsible
163910741SFei.Feng@Sun.COM */
164010741SFei.Feng@Sun.COM static int
mwl_gethwspecs(struct mwl_softc * sc)164110741SFei.Feng@Sun.COM mwl_gethwspecs(struct mwl_softc *sc)
164210741SFei.Feng@Sun.COM {
164310741SFei.Feng@Sun.COM struct mwl_hal_hwspec *hw;
164410741SFei.Feng@Sun.COM HostCmd_DS_GET_HW_SPEC *pCmd;
164510741SFei.Feng@Sun.COM int retval;
164610741SFei.Feng@Sun.COM
164710741SFei.Feng@Sun.COM hw = &sc->sc_hwspecs;
164810741SFei.Feng@Sun.COM _CMD_SETUP(pCmd, HostCmd_DS_GET_HW_SPEC, HostCmd_CMD_GET_HW_SPEC);
164910747SFei.Feng@Sun.COM (void) memset(&pCmd->PermanentAddr[0], 0xff, IEEE80211_ADDR_LEN);
165010741SFei.Feng@Sun.COM pCmd->ulFwAwakeCookie = LE_32((unsigned int)sc->sc_cmd_dmaaddr + 2048);
165110741SFei.Feng@Sun.COM
165210741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_GET_HW_SPEC);
165310741SFei.Feng@Sun.COM if (retval == 0) {
165410741SFei.Feng@Sun.COM IEEE80211_ADDR_COPY(hw->macAddr, pCmd->PermanentAddr);
165510741SFei.Feng@Sun.COM hw->wcbBase[0] = LE_32(pCmd->WcbBase0) & 0x0000ffff;
165610741SFei.Feng@Sun.COM hw->wcbBase[1] = LE_32(pCmd->WcbBase1[0]) & 0x0000ffff;
165710741SFei.Feng@Sun.COM hw->wcbBase[2] = LE_32(pCmd->WcbBase1[1]) & 0x0000ffff;
165810741SFei.Feng@Sun.COM hw->wcbBase[3] = LE_32(pCmd->WcbBase1[2]) & 0x0000ffff;
165910741SFei.Feng@Sun.COM hw->rxDescRead = LE_32(pCmd->RxPdRdPtr)& 0x0000ffff;
166010741SFei.Feng@Sun.COM hw->rxDescWrite = LE_32(pCmd->RxPdWrPtr)& 0x0000ffff;
166110741SFei.Feng@Sun.COM hw->regionCode = LE_16(pCmd->RegionCode) & 0x00ff;
166210741SFei.Feng@Sun.COM hw->fwReleaseNumber = LE_32(pCmd->FWReleaseNumber);
166310741SFei.Feng@Sun.COM hw->maxNumWCB = LE_16(pCmd->NumOfWCB);
166410741SFei.Feng@Sun.COM hw->maxNumMCAddr = LE_16(pCmd->NumOfMCastAddr);
166510741SFei.Feng@Sun.COM hw->numAntennas = LE_16(pCmd->NumberOfAntenna);
166610741SFei.Feng@Sun.COM hw->hwVersion = pCmd->Version;
166710741SFei.Feng@Sun.COM hw->hostInterface = pCmd->HostIf;
166810741SFei.Feng@Sun.COM
166910741SFei.Feng@Sun.COM sc->sc_revs.mh_macRev = hw->hwVersion; /* XXX */
167010741SFei.Feng@Sun.COM sc->sc_revs.mh_phyRev = hw->hostInterface; /* XXX */
167110741SFei.Feng@Sun.COM }
167210741SFei.Feng@Sun.COM
167310741SFei.Feng@Sun.COM return (retval);
167410741SFei.Feng@Sun.COM }
167510741SFei.Feng@Sun.COM
167610741SFei.Feng@Sun.COM static int
mwl_hal_setmac_locked(struct mwl_softc * sc,const uint8_t addr[IEEE80211_ADDR_LEN])167710741SFei.Feng@Sun.COM mwl_hal_setmac_locked(struct mwl_softc *sc,
167810741SFei.Feng@Sun.COM const uint8_t addr[IEEE80211_ADDR_LEN])
167910741SFei.Feng@Sun.COM {
168010741SFei.Feng@Sun.COM HostCmd_DS_SET_MAC *pCmd;
168110741SFei.Feng@Sun.COM
168210741SFei.Feng@Sun.COM _VCMD_SETUP(pCmd, HostCmd_DS_SET_MAC, HostCmd_CMD_SET_MAC_ADDR);
168310741SFei.Feng@Sun.COM IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], addr);
168410741SFei.Feng@Sun.COM #ifdef MWL_MBSS_SUPPORT
168510741SFei.Feng@Sun.COM /* NB: already byte swapped */
168610741SFei.Feng@Sun.COM pCmd->MacType = WL_MAC_TYPE_PRIMARY_CLIENT;
168710741SFei.Feng@Sun.COM #endif
168810741SFei.Feng@Sun.COM return (mwlExecuteCmd(sc, HostCmd_CMD_SET_MAC_ADDR));
168910741SFei.Feng@Sun.COM }
169010741SFei.Feng@Sun.COM
169110741SFei.Feng@Sun.COM static void
cvtPeerInfo(PeerInfo_t * to,const MWL_HAL_PEERINFO * from)169210741SFei.Feng@Sun.COM cvtPeerInfo(PeerInfo_t *to, const MWL_HAL_PEERINFO *from)
169310741SFei.Feng@Sun.COM {
169410741SFei.Feng@Sun.COM to->LegacyRateBitMap = LE_32(from->LegacyRateBitMap);
169510741SFei.Feng@Sun.COM to->HTRateBitMap = LE_32(from->HTRateBitMap);
169610741SFei.Feng@Sun.COM to->CapInfo = LE_16(from->CapInfo);
169710741SFei.Feng@Sun.COM to->HTCapabilitiesInfo = LE_16(from->HTCapabilitiesInfo);
169810741SFei.Feng@Sun.COM to->MacHTParamInfo = from->MacHTParamInfo;
169910741SFei.Feng@Sun.COM to->AddHtInfo.ControlChan = from->AddHtInfo.ControlChan;
170010741SFei.Feng@Sun.COM to->AddHtInfo.AddChan = from->AddHtInfo.AddChan;
170110741SFei.Feng@Sun.COM to->AddHtInfo.OpMode = LE_16(from->AddHtInfo.OpMode);
170210741SFei.Feng@Sun.COM to->AddHtInfo.stbc = LE_16(from->AddHtInfo.stbc);
170310741SFei.Feng@Sun.COM }
170410741SFei.Feng@Sun.COM
170510741SFei.Feng@Sun.COM /* XXX station id must be in [0..63] */
170610741SFei.Feng@Sun.COM static int
mwl_hal_newstation(struct mwl_softc * sc,const uint8_t addr[IEEE80211_ADDR_LEN],uint16_t aid,uint16_t sid,const MWL_HAL_PEERINFO * peer,int isQosSta,int wmeInfo)170710741SFei.Feng@Sun.COM mwl_hal_newstation(struct mwl_softc *sc,
170810741SFei.Feng@Sun.COM const uint8_t addr[IEEE80211_ADDR_LEN], uint16_t aid, uint16_t sid,
170910741SFei.Feng@Sun.COM const MWL_HAL_PEERINFO *peer, int isQosSta, int wmeInfo)
171010741SFei.Feng@Sun.COM {
171110741SFei.Feng@Sun.COM HostCmd_FW_SET_NEW_STN *pCmd;
171210741SFei.Feng@Sun.COM int retval;
171310741SFei.Feng@Sun.COM
171410741SFei.Feng@Sun.COM _VCMD_SETUP(pCmd, HostCmd_FW_SET_NEW_STN, HostCmd_CMD_SET_NEW_STN);
171510741SFei.Feng@Sun.COM pCmd->AID = LE_16(aid);
171610741SFei.Feng@Sun.COM pCmd->StnId = LE_16(sid);
171710741SFei.Feng@Sun.COM pCmd->Action = LE_16(0); /* SET */
171810741SFei.Feng@Sun.COM if (peer != NULL) {
171910741SFei.Feng@Sun.COM /* NB: must fix up byte order */
172010741SFei.Feng@Sun.COM cvtPeerInfo(&pCmd->PeerInfo, peer);
172110741SFei.Feng@Sun.COM }
172210741SFei.Feng@Sun.COM IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], addr);
172310741SFei.Feng@Sun.COM pCmd->Qosinfo = (uint8_t)wmeInfo;
172410741SFei.Feng@Sun.COM pCmd->isQosSta = (isQosSta != 0);
172510741SFei.Feng@Sun.COM
172610741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_HW, "mwl: mwl_hal_newstation(): "
172710741SFei.Feng@Sun.COM "LegacyRateBitMap %x, CapInfo %x\n",
172810741SFei.Feng@Sun.COM pCmd->PeerInfo.LegacyRateBitMap, pCmd->PeerInfo.CapInfo);
172910741SFei.Feng@Sun.COM
173010741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_NEW_STN);
173110741SFei.Feng@Sun.COM return (retval);
173210741SFei.Feng@Sun.COM }
173310741SFei.Feng@Sun.COM
173410741SFei.Feng@Sun.COM /*
173510741SFei.Feng@Sun.COM * Configure antenna use.
173610741SFei.Feng@Sun.COM * Takes effect immediately.
173710741SFei.Feng@Sun.COM * XXX tx antenna setting ignored
173810741SFei.Feng@Sun.COM * XXX rx antenna setting should always be 3 (for now)
173910741SFei.Feng@Sun.COM */
174010741SFei.Feng@Sun.COM static int
mwl_hal_setantenna(struct mwl_softc * sc,MWL_HAL_ANTENNA dirSet,int ant)174110741SFei.Feng@Sun.COM mwl_hal_setantenna(struct mwl_softc *sc, MWL_HAL_ANTENNA dirSet, int ant)
174210741SFei.Feng@Sun.COM {
174310741SFei.Feng@Sun.COM HostCmd_DS_802_11_RF_ANTENNA *pCmd;
174410741SFei.Feng@Sun.COM int retval;
174510741SFei.Feng@Sun.COM
174610741SFei.Feng@Sun.COM if (!(dirSet == WL_ANTENNATYPE_RX || dirSet == WL_ANTENNATYPE_TX))
174710741SFei.Feng@Sun.COM return (EINVAL);
174810741SFei.Feng@Sun.COM
174910741SFei.Feng@Sun.COM _CMD_SETUP(pCmd, HostCmd_DS_802_11_RF_ANTENNA,
175010741SFei.Feng@Sun.COM HostCmd_CMD_802_11_RF_ANTENNA);
175110741SFei.Feng@Sun.COM pCmd->Action = LE_16(dirSet);
175210741SFei.Feng@Sun.COM if (ant == 0) /* default to all/both antennae */
175310741SFei.Feng@Sun.COM ant = 3;
175410741SFei.Feng@Sun.COM pCmd->AntennaMode = LE_16(ant);
175510741SFei.Feng@Sun.COM
175610741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_802_11_RF_ANTENNA);
175710741SFei.Feng@Sun.COM return (retval);
175810741SFei.Feng@Sun.COM }
175910741SFei.Feng@Sun.COM
176010741SFei.Feng@Sun.COM /*
176110741SFei.Feng@Sun.COM * Configure radio.
176210741SFei.Feng@Sun.COM * Takes effect immediately.
176310741SFei.Feng@Sun.COM * XXX preamble installed after set fixed rate cmd
176410741SFei.Feng@Sun.COM */
176510741SFei.Feng@Sun.COM static int
mwl_hal_setradio(struct mwl_softc * sc,int onoff,MWL_HAL_PREAMBLE preamble)176610741SFei.Feng@Sun.COM mwl_hal_setradio(struct mwl_softc *sc, int onoff, MWL_HAL_PREAMBLE preamble)
176710741SFei.Feng@Sun.COM {
176810741SFei.Feng@Sun.COM HostCmd_DS_802_11_RADIO_CONTROL *pCmd;
176910741SFei.Feng@Sun.COM int retval;
177010741SFei.Feng@Sun.COM
177110741SFei.Feng@Sun.COM _CMD_SETUP(pCmd, HostCmd_DS_802_11_RADIO_CONTROL,
177210741SFei.Feng@Sun.COM HostCmd_CMD_802_11_RADIO_CONTROL);
177310741SFei.Feng@Sun.COM pCmd->Action = LE_16(HostCmd_ACT_GEN_SET);
177410741SFei.Feng@Sun.COM if (onoff == 0)
177510741SFei.Feng@Sun.COM pCmd->Control = 0;
177610741SFei.Feng@Sun.COM else
177710741SFei.Feng@Sun.COM pCmd->Control = LE_16(preamble);
177810741SFei.Feng@Sun.COM pCmd->RadioOn = LE_16(onoff);
177910741SFei.Feng@Sun.COM
178010741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_802_11_RADIO_CONTROL);
178110741SFei.Feng@Sun.COM return (retval);
178210741SFei.Feng@Sun.COM }
178310741SFei.Feng@Sun.COM
178410741SFei.Feng@Sun.COM static int
mwl_hal_setwmm(struct mwl_softc * sc,int onoff)178510741SFei.Feng@Sun.COM mwl_hal_setwmm(struct mwl_softc *sc, int onoff)
178610741SFei.Feng@Sun.COM {
178710741SFei.Feng@Sun.COM HostCmd_FW_SetWMMMode *pCmd;
178810741SFei.Feng@Sun.COM int retval;
178910741SFei.Feng@Sun.COM
179010741SFei.Feng@Sun.COM _CMD_SETUP(pCmd, HostCmd_FW_SetWMMMode,
179110741SFei.Feng@Sun.COM HostCmd_CMD_SET_WMM_MODE);
179210741SFei.Feng@Sun.COM pCmd->Action = LE_16(onoff);
179310741SFei.Feng@Sun.COM
179410741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_WMM_MODE);
179510741SFei.Feng@Sun.COM return (retval);
179610741SFei.Feng@Sun.COM }
179710741SFei.Feng@Sun.COM
179810741SFei.Feng@Sun.COM /*
179910741SFei.Feng@Sun.COM * Convert public channel flags definition to a
180010741SFei.Feng@Sun.COM * value suitable for feeding to the firmware.
180110741SFei.Feng@Sun.COM * Note this includes byte swapping.
180210741SFei.Feng@Sun.COM */
180310741SFei.Feng@Sun.COM static uint32_t
cvtChannelFlags(const MWL_HAL_CHANNEL * chan)180410741SFei.Feng@Sun.COM cvtChannelFlags(const MWL_HAL_CHANNEL *chan)
180510741SFei.Feng@Sun.COM {
180610741SFei.Feng@Sun.COM uint32_t w;
180710741SFei.Feng@Sun.COM
180810741SFei.Feng@Sun.COM /*
180910741SFei.Feng@Sun.COM * NB: f/w only understands FREQ_BAND_5GHZ, supplying the more
181010741SFei.Feng@Sun.COM * precise band info causes it to lockup (sometimes).
181110741SFei.Feng@Sun.COM */
181210741SFei.Feng@Sun.COM w = (chan->channelFlags.FreqBand == MWL_FREQ_BAND_2DOT4GHZ) ?
181310741SFei.Feng@Sun.COM FREQ_BAND_2DOT4GHZ : FREQ_BAND_5GHZ;
181410741SFei.Feng@Sun.COM switch (chan->channelFlags.ChnlWidth) {
181510741SFei.Feng@Sun.COM case MWL_CH_10_MHz_WIDTH:
181610741SFei.Feng@Sun.COM w |= CH_10_MHz_WIDTH;
181710741SFei.Feng@Sun.COM break;
181810741SFei.Feng@Sun.COM case MWL_CH_20_MHz_WIDTH:
181910741SFei.Feng@Sun.COM w |= CH_20_MHz_WIDTH;
182010741SFei.Feng@Sun.COM break;
182110741SFei.Feng@Sun.COM case MWL_CH_40_MHz_WIDTH:
182210741SFei.Feng@Sun.COM default:
182310741SFei.Feng@Sun.COM w |= CH_40_MHz_WIDTH;
182410741SFei.Feng@Sun.COM break;
182510741SFei.Feng@Sun.COM }
182610741SFei.Feng@Sun.COM switch (chan->channelFlags.ExtChnlOffset) {
182710741SFei.Feng@Sun.COM case MWL_EXT_CH_NONE:
182810741SFei.Feng@Sun.COM w |= EXT_CH_NONE;
182910741SFei.Feng@Sun.COM break;
183010741SFei.Feng@Sun.COM case MWL_EXT_CH_ABOVE_CTRL_CH:
183110741SFei.Feng@Sun.COM w |= EXT_CH_ABOVE_CTRL_CH;
183210741SFei.Feng@Sun.COM break;
183310741SFei.Feng@Sun.COM case MWL_EXT_CH_BELOW_CTRL_CH:
183410741SFei.Feng@Sun.COM w |= EXT_CH_BELOW_CTRL_CH;
183510741SFei.Feng@Sun.COM break;
183610741SFei.Feng@Sun.COM }
183710741SFei.Feng@Sun.COM return (LE_32(w));
183810741SFei.Feng@Sun.COM }
183910741SFei.Feng@Sun.COM
184010741SFei.Feng@Sun.COM static int
mwl_hal_setchannel(struct mwl_softc * sc,const MWL_HAL_CHANNEL * chan)184110741SFei.Feng@Sun.COM mwl_hal_setchannel(struct mwl_softc *sc, const MWL_HAL_CHANNEL *chan)
184210741SFei.Feng@Sun.COM {
184310741SFei.Feng@Sun.COM HostCmd_FW_SET_RF_CHANNEL *pCmd;
184410741SFei.Feng@Sun.COM int retval;
184510741SFei.Feng@Sun.COM
184610741SFei.Feng@Sun.COM _CMD_SETUP(pCmd, HostCmd_FW_SET_RF_CHANNEL, HostCmd_CMD_SET_RF_CHANNEL);
184710741SFei.Feng@Sun.COM pCmd->Action = LE_16(HostCmd_ACT_GEN_SET);
184810741SFei.Feng@Sun.COM pCmd->CurrentChannel = chan->channel;
184910741SFei.Feng@Sun.COM pCmd->ChannelFlags = cvtChannelFlags(chan); /* NB: byte-swapped */
185010741SFei.Feng@Sun.COM
185110741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_RF_CHANNEL);
185210741SFei.Feng@Sun.COM return (retval);
185310741SFei.Feng@Sun.COM }
185410741SFei.Feng@Sun.COM
185510741SFei.Feng@Sun.COM static int
mwl_hal_settxpower(struct mwl_softc * sc,const MWL_HAL_CHANNEL * c,uint8_t maxtxpow)185610741SFei.Feng@Sun.COM mwl_hal_settxpower(struct mwl_softc *sc,
185710741SFei.Feng@Sun.COM const MWL_HAL_CHANNEL *c, uint8_t maxtxpow)
185810741SFei.Feng@Sun.COM {
185910741SFei.Feng@Sun.COM HostCmd_DS_802_11_RF_TX_POWER *pCmd;
186010741SFei.Feng@Sun.COM const struct mwl_hal_channel *hc;
186110741SFei.Feng@Sun.COM int i = 0, retval;
186210741SFei.Feng@Sun.COM
186310741SFei.Feng@Sun.COM hc = findhalchannel(sc, c);
186410741SFei.Feng@Sun.COM if (hc == NULL) {
186510741SFei.Feng@Sun.COM /* XXX temp while testing */
186610741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_HW, "mwl: mwl_hal_settxpower(): "
186710741SFei.Feng@Sun.COM "no cal data for channel %u band %u width %u ext %u\n",
186810741SFei.Feng@Sun.COM c->channel, c->channelFlags.FreqBand,
186910741SFei.Feng@Sun.COM c->channelFlags.ChnlWidth, c->channelFlags.ExtChnlOffset);
187010741SFei.Feng@Sun.COM return (EINVAL);
187110741SFei.Feng@Sun.COM }
187210741SFei.Feng@Sun.COM
187310741SFei.Feng@Sun.COM _CMD_SETUP(pCmd, HostCmd_DS_802_11_RF_TX_POWER,
187410741SFei.Feng@Sun.COM HostCmd_CMD_802_11_RF_TX_POWER);
187510741SFei.Feng@Sun.COM pCmd->Action = LE_16(HostCmd_ACT_GEN_SET_LIST);
187610741SFei.Feng@Sun.COM /* NB: 5Ghz cal data have the channel # in [0]; don't truncate */
187710741SFei.Feng@Sun.COM if (c->channelFlags.FreqBand == MWL_FREQ_BAND_5GHZ)
187810741SFei.Feng@Sun.COM pCmd->PowerLevelList[i++] = LE_16(hc->targetPowers[0]);
187910741SFei.Feng@Sun.COM for (; i < 4; i++) {
188010741SFei.Feng@Sun.COM uint16_t pow = hc->targetPowers[i];
188110741SFei.Feng@Sun.COM if (pow > maxtxpow)
188210741SFei.Feng@Sun.COM pow = maxtxpow;
188310741SFei.Feng@Sun.COM pCmd->PowerLevelList[i] = LE_16(pow);
188410741SFei.Feng@Sun.COM }
188510741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_802_11_RF_TX_POWER);
188610741SFei.Feng@Sun.COM return (retval);
188710741SFei.Feng@Sun.COM }
188810741SFei.Feng@Sun.COM
188910741SFei.Feng@Sun.COM #define RATEVAL(r) ((r) &~ RATE_MCS)
189010741SFei.Feng@Sun.COM #define RATETYPE(r) (((r) & RATE_MCS) ? HT_RATE_TYPE : LEGACY_RATE_TYPE)
189110741SFei.Feng@Sun.COM
189210741SFei.Feng@Sun.COM static int
mwl_hal_settxrate(struct mwl_softc * sc,MWL_HAL_TXRATE_HANDLING handling,const MWL_HAL_TXRATE * rate)189310741SFei.Feng@Sun.COM mwl_hal_settxrate(struct mwl_softc *sc, MWL_HAL_TXRATE_HANDLING handling,
189410741SFei.Feng@Sun.COM const MWL_HAL_TXRATE *rate)
189510741SFei.Feng@Sun.COM {
189610741SFei.Feng@Sun.COM HostCmd_FW_USE_FIXED_RATE *pCmd;
189710741SFei.Feng@Sun.COM FIXED_RATE_ENTRY *fp;
189810741SFei.Feng@Sun.COM int retval, i, n;
189910741SFei.Feng@Sun.COM
190010741SFei.Feng@Sun.COM _VCMD_SETUP(pCmd, HostCmd_FW_USE_FIXED_RATE,
190110741SFei.Feng@Sun.COM HostCmd_CMD_SET_FIXED_RATE);
190210741SFei.Feng@Sun.COM
190310741SFei.Feng@Sun.COM pCmd->MulticastRate = RATEVAL(rate->McastRate);
190410741SFei.Feng@Sun.COM pCmd->MultiRateTxType = RATETYPE(rate->McastRate);
190510741SFei.Feng@Sun.COM /* NB: no rate type field */
190610741SFei.Feng@Sun.COM pCmd->ManagementRate = RATEVAL(rate->MgtRate);
190710747SFei.Feng@Sun.COM (void) memset(pCmd->FixedRateTable, 0, sizeof (pCmd->FixedRateTable));
190810741SFei.Feng@Sun.COM if (handling == RATE_FIXED) {
190910741SFei.Feng@Sun.COM pCmd->Action = LE_32(HostCmd_ACT_GEN_SET);
191010741SFei.Feng@Sun.COM pCmd->AllowRateDrop = LE_32(FIXED_RATE_WITHOUT_AUTORATE_DROP);
191110741SFei.Feng@Sun.COM fp = pCmd->FixedRateTable;
191210741SFei.Feng@Sun.COM fp->FixedRate =
191310741SFei.Feng@Sun.COM LE_32(RATEVAL(rate->RateSeries[0].Rate));
191410741SFei.Feng@Sun.COM fp->FixRateTypeFlags.FixRateType =
191510741SFei.Feng@Sun.COM LE_32(RATETYPE(rate->RateSeries[0].Rate));
191610741SFei.Feng@Sun.COM pCmd->EntryCount = LE_32(1);
191710741SFei.Feng@Sun.COM } else if (handling == RATE_FIXED_DROP) {
191810741SFei.Feng@Sun.COM pCmd->Action = LE_32(HostCmd_ACT_GEN_SET);
191910741SFei.Feng@Sun.COM pCmd->AllowRateDrop = LE_32(FIXED_RATE_WITH_AUTO_RATE_DROP);
192010741SFei.Feng@Sun.COM n = 0;
192110741SFei.Feng@Sun.COM fp = pCmd->FixedRateTable;
192210741SFei.Feng@Sun.COM for (i = 0; i < 4; i++) {
192310741SFei.Feng@Sun.COM if (rate->RateSeries[0].TryCount == 0)
192410741SFei.Feng@Sun.COM break;
192510741SFei.Feng@Sun.COM fp->FixRateTypeFlags.FixRateType =
192610741SFei.Feng@Sun.COM LE_32(RATETYPE(rate->RateSeries[i].Rate));
192710741SFei.Feng@Sun.COM fp->FixedRate =
192810741SFei.Feng@Sun.COM LE_32(RATEVAL(rate->RateSeries[i].Rate));
192910741SFei.Feng@Sun.COM fp->FixRateTypeFlags.RetryCountValid =
193010741SFei.Feng@Sun.COM LE_32(RETRY_COUNT_VALID);
193110741SFei.Feng@Sun.COM fp->RetryCount =
193210741SFei.Feng@Sun.COM LE_32(rate->RateSeries[i].TryCount-1);
193310741SFei.Feng@Sun.COM n++;
193410741SFei.Feng@Sun.COM }
193510741SFei.Feng@Sun.COM pCmd->EntryCount = LE_32(n);
193610741SFei.Feng@Sun.COM } else
193710741SFei.Feng@Sun.COM pCmd->Action = LE_32(HostCmd_ACT_NOT_USE_FIXED_RATE);
193810741SFei.Feng@Sun.COM
193910741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_FIXED_RATE);
194010741SFei.Feng@Sun.COM return (retval);
194110741SFei.Feng@Sun.COM }
194210741SFei.Feng@Sun.COM
194310741SFei.Feng@Sun.COM static int
mwl_hal_settxrate_auto(struct mwl_softc * sc,const MWL_HAL_TXRATE * rate)194410741SFei.Feng@Sun.COM mwl_hal_settxrate_auto(struct mwl_softc *sc, const MWL_HAL_TXRATE *rate)
194510741SFei.Feng@Sun.COM {
194610741SFei.Feng@Sun.COM HostCmd_FW_USE_FIXED_RATE *pCmd;
194710741SFei.Feng@Sun.COM int retval;
194810741SFei.Feng@Sun.COM
194910741SFei.Feng@Sun.COM _CMD_SETUP(pCmd, HostCmd_FW_USE_FIXED_RATE,
195010741SFei.Feng@Sun.COM HostCmd_CMD_SET_FIXED_RATE);
195110741SFei.Feng@Sun.COM
195210741SFei.Feng@Sun.COM pCmd->MulticastRate = RATEVAL(rate->McastRate);
195310741SFei.Feng@Sun.COM pCmd->MultiRateTxType = RATETYPE(rate->McastRate);
195410741SFei.Feng@Sun.COM /* NB: no rate type field */
195510741SFei.Feng@Sun.COM pCmd->ManagementRate = RATEVAL(rate->MgtRate);
195610747SFei.Feng@Sun.COM (void) memset(pCmd->FixedRateTable, 0, sizeof (pCmd->FixedRateTable));
195710741SFei.Feng@Sun.COM pCmd->Action = LE_32(HostCmd_ACT_NOT_USE_FIXED_RATE);
195810741SFei.Feng@Sun.COM
195910741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_FIXED_RATE);
196010741SFei.Feng@Sun.COM return (retval);
196110741SFei.Feng@Sun.COM }
196210741SFei.Feng@Sun.COM
196310741SFei.Feng@Sun.COM #undef RATEVAL
196410741SFei.Feng@Sun.COM #undef RATETYPE
196510741SFei.Feng@Sun.COM
196610741SFei.Feng@Sun.COM /* XXX 0 = indoor, 1 = outdoor */
196710741SFei.Feng@Sun.COM static int
mwl_hal_setrateadaptmode(struct mwl_softc * sc,uint16_t mode)196810741SFei.Feng@Sun.COM mwl_hal_setrateadaptmode(struct mwl_softc *sc, uint16_t mode)
196910741SFei.Feng@Sun.COM {
197010741SFei.Feng@Sun.COM HostCmd_DS_SET_RATE_ADAPT_MODE *pCmd;
197110741SFei.Feng@Sun.COM int retval;
197210741SFei.Feng@Sun.COM
197310741SFei.Feng@Sun.COM _CMD_SETUP(pCmd, HostCmd_DS_SET_RATE_ADAPT_MODE,
197410741SFei.Feng@Sun.COM HostCmd_CMD_SET_RATE_ADAPT_MODE);
197510741SFei.Feng@Sun.COM pCmd->Action = LE_16(HostCmd_ACT_GEN_SET);
197610741SFei.Feng@Sun.COM pCmd->RateAdaptMode = LE_16(mode);
197710741SFei.Feng@Sun.COM
197810741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_RATE_ADAPT_MODE);
197910741SFei.Feng@Sun.COM return (retval);
198010741SFei.Feng@Sun.COM }
198110741SFei.Feng@Sun.COM
198210741SFei.Feng@Sun.COM static int
mwl_hal_setoptimizationlevel(struct mwl_softc * sc,int level)198310741SFei.Feng@Sun.COM mwl_hal_setoptimizationlevel(struct mwl_softc *sc, int level)
198410741SFei.Feng@Sun.COM {
198510741SFei.Feng@Sun.COM HostCmd_FW_SET_OPTIMIZATION_LEVEL *pCmd;
198610741SFei.Feng@Sun.COM int retval;
198710741SFei.Feng@Sun.COM
198810741SFei.Feng@Sun.COM _CMD_SETUP(pCmd, HostCmd_FW_SET_OPTIMIZATION_LEVEL,
198910741SFei.Feng@Sun.COM HostCmd_CMD_SET_OPTIMIZATION_LEVEL);
199010741SFei.Feng@Sun.COM pCmd->OptLevel = (uint8_t)level;
199110741SFei.Feng@Sun.COM
199210741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_OPTIMIZATION_LEVEL);
199310741SFei.Feng@Sun.COM return (retval);
199410741SFei.Feng@Sun.COM }
199510741SFei.Feng@Sun.COM
199610741SFei.Feng@Sun.COM /*
199710741SFei.Feng@Sun.COM * Set the region code that selects the radar bin'ing agorithm.
199810741SFei.Feng@Sun.COM */
199910741SFei.Feng@Sun.COM static int
mwl_hal_setregioncode(struct mwl_softc * sc,int regionCode)200010741SFei.Feng@Sun.COM mwl_hal_setregioncode(struct mwl_softc *sc, int regionCode)
200110741SFei.Feng@Sun.COM {
200210741SFei.Feng@Sun.COM HostCmd_SET_REGIONCODE_INFO *pCmd;
200310741SFei.Feng@Sun.COM int retval;
200410741SFei.Feng@Sun.COM
200510741SFei.Feng@Sun.COM _CMD_SETUP(pCmd, HostCmd_SET_REGIONCODE_INFO,
200610741SFei.Feng@Sun.COM HostCmd_CMD_SET_REGION_CODE);
200710741SFei.Feng@Sun.COM /* XXX map pseudo-codes to fw codes */
200810741SFei.Feng@Sun.COM switch (regionCode) {
200910741SFei.Feng@Sun.COM case DOMAIN_CODE_ETSI_131:
201010741SFei.Feng@Sun.COM pCmd->regionCode = LE_16(DOMAIN_CODE_ETSI);
201110741SFei.Feng@Sun.COM break;
201210741SFei.Feng@Sun.COM default:
201310741SFei.Feng@Sun.COM pCmd->regionCode = LE_16(regionCode);
201410741SFei.Feng@Sun.COM break;
201510741SFei.Feng@Sun.COM }
201610741SFei.Feng@Sun.COM
201710741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_REGION_CODE);
201810741SFei.Feng@Sun.COM return (retval);
201910741SFei.Feng@Sun.COM }
202010741SFei.Feng@Sun.COM
202110741SFei.Feng@Sun.COM static int
mwl_hal_setassocid(struct mwl_softc * sc,const uint8_t bssId[IEEE80211_ADDR_LEN],uint16_t assocId)202210741SFei.Feng@Sun.COM mwl_hal_setassocid(struct mwl_softc *sc,
202310741SFei.Feng@Sun.COM const uint8_t bssId[IEEE80211_ADDR_LEN], uint16_t assocId)
202410741SFei.Feng@Sun.COM {
202510741SFei.Feng@Sun.COM HostCmd_FW_SET_AID *pCmd = (HostCmd_FW_SET_AID *) &sc->sc_cmd_mem[0];
202610741SFei.Feng@Sun.COM int retval;
202710741SFei.Feng@Sun.COM
202810741SFei.Feng@Sun.COM _VCMD_SETUP(pCmd, HostCmd_FW_SET_AID, HostCmd_CMD_SET_AID);
202910741SFei.Feng@Sun.COM pCmd->AssocID = LE_16(assocId);
203010741SFei.Feng@Sun.COM IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], bssId);
203110741SFei.Feng@Sun.COM
203210741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_AID);
203310741SFei.Feng@Sun.COM return (retval);
203410741SFei.Feng@Sun.COM }
203510741SFei.Feng@Sun.COM
203610741SFei.Feng@Sun.COM /*
203710741SFei.Feng@Sun.COM * Inform firmware of tx rate parameters. Called whenever
203810741SFei.Feng@Sun.COM * user-settable params change and after a channel change.
203910741SFei.Feng@Sun.COM */
204010741SFei.Feng@Sun.COM static int
mwl_setrates(struct ieee80211com * ic)204110741SFei.Feng@Sun.COM mwl_setrates(struct ieee80211com *ic)
204210741SFei.Feng@Sun.COM {
204310741SFei.Feng@Sun.COM struct mwl_softc *sc = (struct mwl_softc *)ic;
204410741SFei.Feng@Sun.COM MWL_HAL_TXRATE rates;
204510741SFei.Feng@Sun.COM
204610741SFei.Feng@Sun.COM const struct ieee80211_rateset *rs;
204710741SFei.Feng@Sun.COM rs = &ic->ic_bss->in_rates;
204810741SFei.Feng@Sun.COM
204910741SFei.Feng@Sun.COM /*
205010741SFei.Feng@Sun.COM * Update the h/w rate map.
205110741SFei.Feng@Sun.COM * NB: 0x80 for MCS is passed through unchanged
205210741SFei.Feng@Sun.COM */
205310747SFei.Feng@Sun.COM (void) memset(&rates, 0, sizeof (rates));
205410741SFei.Feng@Sun.COM /* rate used to send management frames */
205510741SFei.Feng@Sun.COM rates.MgtRate = rs->ir_rates[0] & IEEE80211_RATE_VAL;
205610741SFei.Feng@Sun.COM /* rate used to send multicast frames */
205710741SFei.Feng@Sun.COM rates.McastRate = rates.MgtRate;
205810741SFei.Feng@Sun.COM
205910741SFei.Feng@Sun.COM return (mwl_hal_settxrate(sc, RATE_AUTO, &rates));
206010741SFei.Feng@Sun.COM }
206110741SFei.Feng@Sun.COM
206210741SFei.Feng@Sun.COM /*
206310741SFei.Feng@Sun.COM * Set packet size threshold for implicit use of RTS.
206410741SFei.Feng@Sun.COM * Takes effect immediately.
206510741SFei.Feng@Sun.COM * XXX packet length > threshold =>'s RTS
206610741SFei.Feng@Sun.COM */
206710741SFei.Feng@Sun.COM static int
mwl_hal_setrtsthreshold(struct mwl_softc * sc,int threshold)206810741SFei.Feng@Sun.COM mwl_hal_setrtsthreshold(struct mwl_softc *sc, int threshold)
206910741SFei.Feng@Sun.COM {
207010741SFei.Feng@Sun.COM HostCmd_DS_802_11_RTS_THSD *pCmd;
207110741SFei.Feng@Sun.COM int retval;
207210741SFei.Feng@Sun.COM
207310741SFei.Feng@Sun.COM _VCMD_SETUP(pCmd, HostCmd_DS_802_11_RTS_THSD,
207410741SFei.Feng@Sun.COM HostCmd_CMD_802_11_RTS_THSD);
207510741SFei.Feng@Sun.COM pCmd->Action = LE_16(HostCmd_ACT_GEN_SET);
207610741SFei.Feng@Sun.COM pCmd->Threshold = LE_16(threshold);
207710741SFei.Feng@Sun.COM
207810741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_802_11_RTS_THSD);
207910741SFei.Feng@Sun.COM return (retval);
208010741SFei.Feng@Sun.COM }
208110741SFei.Feng@Sun.COM
208210741SFei.Feng@Sun.COM static int
mwl_hal_setcsmode(struct mwl_softc * sc,MWL_HAL_CSMODE csmode)208310741SFei.Feng@Sun.COM mwl_hal_setcsmode(struct mwl_softc *sc, MWL_HAL_CSMODE csmode)
208410741SFei.Feng@Sun.COM {
208510741SFei.Feng@Sun.COM HostCmd_DS_SET_LINKADAPT_CS_MODE *pCmd;
208610741SFei.Feng@Sun.COM int retval;
208710741SFei.Feng@Sun.COM
208810741SFei.Feng@Sun.COM _CMD_SETUP(pCmd, HostCmd_DS_SET_LINKADAPT_CS_MODE,
208910741SFei.Feng@Sun.COM HostCmd_CMD_SET_LINKADAPT_CS_MODE);
209010741SFei.Feng@Sun.COM pCmd->Action = LE_16(HostCmd_ACT_GEN_SET);
209110741SFei.Feng@Sun.COM pCmd->CSMode = LE_16(csmode);
209210741SFei.Feng@Sun.COM
209310741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_LINKADAPT_CS_MODE);
209410741SFei.Feng@Sun.COM return (retval);
209510741SFei.Feng@Sun.COM }
209610741SFei.Feng@Sun.COM
209710741SFei.Feng@Sun.COM static int
mwl_hal_setpromisc(struct mwl_softc * sc,int ena)209810741SFei.Feng@Sun.COM mwl_hal_setpromisc(struct mwl_softc *sc, int ena)
209910741SFei.Feng@Sun.COM {
210010741SFei.Feng@Sun.COM uint32_t v;
210110741SFei.Feng@Sun.COM
210210741SFei.Feng@Sun.COM v = mwl_ctl_read4(sc, MACREG_REG_PROMISCUOUS);
210310741SFei.Feng@Sun.COM mwl_ctl_write4(sc, MACREG_REG_PROMISCUOUS, ena ? v | 1 : v & ~1);
210410741SFei.Feng@Sun.COM
210510741SFei.Feng@Sun.COM return (0);
210610741SFei.Feng@Sun.COM }
210710741SFei.Feng@Sun.COM
210810741SFei.Feng@Sun.COM static int
mwl_hal_start(struct mwl_softc * sc)210910741SFei.Feng@Sun.COM mwl_hal_start(struct mwl_softc *sc)
211010741SFei.Feng@Sun.COM {
211110741SFei.Feng@Sun.COM HostCmd_DS_BSS_START *pCmd;
211210741SFei.Feng@Sun.COM int retval;
211310741SFei.Feng@Sun.COM
211410741SFei.Feng@Sun.COM _VCMD_SETUP(pCmd, HostCmd_DS_BSS_START, HostCmd_CMD_BSS_START);
211510741SFei.Feng@Sun.COM pCmd->Enable = LE_32(HostCmd_ACT_GEN_ON);
211610741SFei.Feng@Sun.COM
211710741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_BSS_START);
211810741SFei.Feng@Sun.COM return (retval);
211910741SFei.Feng@Sun.COM }
212010741SFei.Feng@Sun.COM
212110741SFei.Feng@Sun.COM /*
212210741SFei.Feng@Sun.COM * Enable sta-mode operation (disables beacon frame xmit).
212310741SFei.Feng@Sun.COM */
212410741SFei.Feng@Sun.COM static int
mwl_hal_setinframode(struct mwl_softc * sc)212510741SFei.Feng@Sun.COM mwl_hal_setinframode(struct mwl_softc *sc)
212610741SFei.Feng@Sun.COM {
212710741SFei.Feng@Sun.COM HostCmd_FW_SET_INFRA_MODE *pCmd;
212810741SFei.Feng@Sun.COM int retval;
212910741SFei.Feng@Sun.COM
213010741SFei.Feng@Sun.COM _VCMD_SETUP(pCmd, HostCmd_FW_SET_INFRA_MODE,
213110741SFei.Feng@Sun.COM HostCmd_CMD_SET_INFRA_MODE);
213210741SFei.Feng@Sun.COM
213310741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_INFRA_MODE);
213410741SFei.Feng@Sun.COM return (retval);
213510741SFei.Feng@Sun.COM }
213610741SFei.Feng@Sun.COM
213710741SFei.Feng@Sun.COM static int
mwl_hal_stop(struct mwl_softc * sc)213810741SFei.Feng@Sun.COM mwl_hal_stop(struct mwl_softc *sc)
213910741SFei.Feng@Sun.COM {
214010741SFei.Feng@Sun.COM HostCmd_DS_BSS_START *pCmd;
214110741SFei.Feng@Sun.COM int retval;
214210741SFei.Feng@Sun.COM
214310741SFei.Feng@Sun.COM _VCMD_SETUP(pCmd, HostCmd_DS_BSS_START,
214410741SFei.Feng@Sun.COM HostCmd_CMD_BSS_START);
214510741SFei.Feng@Sun.COM pCmd->Enable = LE_32(HostCmd_ACT_GEN_OFF);
214610741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_BSS_START);
214710741SFei.Feng@Sun.COM
214810741SFei.Feng@Sun.COM return (retval);
214910741SFei.Feng@Sun.COM }
215010741SFei.Feng@Sun.COM
215110741SFei.Feng@Sun.COM static int
mwl_hal_keyset(struct mwl_softc * sc,const MWL_HAL_KEYVAL * kv,const uint8_t mac[IEEE80211_ADDR_LEN])215210741SFei.Feng@Sun.COM mwl_hal_keyset(struct mwl_softc *sc, const MWL_HAL_KEYVAL *kv,
215310741SFei.Feng@Sun.COM const uint8_t mac[IEEE80211_ADDR_LEN])
215410741SFei.Feng@Sun.COM {
215510741SFei.Feng@Sun.COM HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY *pCmd;
215610741SFei.Feng@Sun.COM int retval;
215710741SFei.Feng@Sun.COM
215810741SFei.Feng@Sun.COM _VCMD_SETUP(pCmd, HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY,
215910741SFei.Feng@Sun.COM HostCmd_CMD_UPDATE_ENCRYPTION);
216010741SFei.Feng@Sun.COM if (kv->keyFlags & (KEY_FLAG_TXGROUPKEY|KEY_FLAG_RXGROUPKEY))
216110741SFei.Feng@Sun.COM pCmd->ActionType = LE_32(EncrActionTypeSetGroupKey);
216210741SFei.Feng@Sun.COM else
216310741SFei.Feng@Sun.COM pCmd->ActionType = LE_32(EncrActionTypeSetKey);
216410741SFei.Feng@Sun.COM pCmd->KeyParam.Length = LE_16(sizeof (pCmd->KeyParam));
216510741SFei.Feng@Sun.COM pCmd->KeyParam.KeyTypeId = LE_16(kv->keyTypeId);
216610741SFei.Feng@Sun.COM pCmd->KeyParam.KeyInfo = LE_32(kv->keyFlags);
216710741SFei.Feng@Sun.COM pCmd->KeyParam.KeyIndex = LE_32(kv->keyIndex);
216810741SFei.Feng@Sun.COM /* NB: includes TKIP MIC keys */
216910741SFei.Feng@Sun.COM (void) memcpy(&pCmd->KeyParam.Key, &kv->key, kv->keyLen);
217010741SFei.Feng@Sun.COM switch (kv->keyTypeId) {
217110741SFei.Feng@Sun.COM case KEY_TYPE_ID_WEP:
217210741SFei.Feng@Sun.COM pCmd->KeyParam.KeyLen = LE_16(kv->keyLen);
217310741SFei.Feng@Sun.COM break;
217410741SFei.Feng@Sun.COM case KEY_TYPE_ID_TKIP:
217510741SFei.Feng@Sun.COM pCmd->KeyParam.KeyLen = LE_16(sizeof (TKIP_TYPE_KEY));
217610741SFei.Feng@Sun.COM pCmd->KeyParam.Key.TkipKey.TkipRsc.low =
217710741SFei.Feng@Sun.COM LE_16(kv->key.tkip.rsc.low);
217810741SFei.Feng@Sun.COM pCmd->KeyParam.Key.TkipKey.TkipRsc.high =
217910741SFei.Feng@Sun.COM LE_32(kv->key.tkip.rsc.high);
218010741SFei.Feng@Sun.COM pCmd->KeyParam.Key.TkipKey.TkipTsc.low =
218110741SFei.Feng@Sun.COM LE_16(kv->key.tkip.tsc.low);
218210741SFei.Feng@Sun.COM pCmd->KeyParam.Key.TkipKey.TkipTsc.high =
218310741SFei.Feng@Sun.COM LE_32(kv->key.tkip.tsc.high);
218410741SFei.Feng@Sun.COM break;
218510741SFei.Feng@Sun.COM case KEY_TYPE_ID_AES:
218610741SFei.Feng@Sun.COM pCmd->KeyParam.KeyLen = LE_16(sizeof (AES_TYPE_KEY));
218710741SFei.Feng@Sun.COM break;
218810741SFei.Feng@Sun.COM }
218910741SFei.Feng@Sun.COM #ifdef MWL_MBSS_SUPPORT
219010741SFei.Feng@Sun.COM IEEE80211_ADDR_COPY(pCmd->KeyParam.Macaddr, mac);
219110741SFei.Feng@Sun.COM #else
219210741SFei.Feng@Sun.COM IEEE80211_ADDR_COPY(pCmd->Macaddr, mac);
219310741SFei.Feng@Sun.COM #endif
219410741SFei.Feng@Sun.COM
219510741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_UPDATE_ENCRYPTION);
219610741SFei.Feng@Sun.COM return (retval);
219710741SFei.Feng@Sun.COM }
219810741SFei.Feng@Sun.COM
219910741SFei.Feng@Sun.COM static int
mwl_hal_keyreset(struct mwl_softc * sc,const MWL_HAL_KEYVAL * kv,const uint8_t mac[IEEE80211_ADDR_LEN])220010741SFei.Feng@Sun.COM mwl_hal_keyreset(struct mwl_softc *sc, const MWL_HAL_KEYVAL *kv,
220110741SFei.Feng@Sun.COM const uint8_t mac[IEEE80211_ADDR_LEN])
220210741SFei.Feng@Sun.COM {
220310741SFei.Feng@Sun.COM HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY *pCmd;
220410741SFei.Feng@Sun.COM int retval;
220510741SFei.Feng@Sun.COM
220610741SFei.Feng@Sun.COM _VCMD_SETUP(pCmd, HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY,
220710741SFei.Feng@Sun.COM HostCmd_CMD_UPDATE_ENCRYPTION);
220810741SFei.Feng@Sun.COM pCmd->ActionType = LE_16(EncrActionTypeRemoveKey);
220910741SFei.Feng@Sun.COM pCmd->KeyParam.Length = LE_16(sizeof (pCmd->KeyParam));
221010741SFei.Feng@Sun.COM pCmd->KeyParam.KeyTypeId = LE_16(kv->keyTypeId);
221110741SFei.Feng@Sun.COM pCmd->KeyParam.KeyInfo = LE_32(kv->keyFlags);
221210741SFei.Feng@Sun.COM pCmd->KeyParam.KeyIndex = LE_32(kv->keyIndex);
221310741SFei.Feng@Sun.COM #ifdef MWL_MBSS_SUPPORT
221410741SFei.Feng@Sun.COM IEEE80211_ADDR_COPY(pCmd->KeyParam.Macaddr, mac);
221510741SFei.Feng@Sun.COM #else
221610741SFei.Feng@Sun.COM IEEE80211_ADDR_COPY(pCmd->Macaddr, mac);
221710741SFei.Feng@Sun.COM #endif
221810741SFei.Feng@Sun.COM retval = mwlExecuteCmd(sc, HostCmd_CMD_UPDATE_ENCRYPTION);
221910741SFei.Feng@Sun.COM return (retval);
222010741SFei.Feng@Sun.COM }
222110741SFei.Feng@Sun.COM
222210741SFei.Feng@Sun.COM /* ARGSUSED */
222310741SFei.Feng@Sun.COM static struct ieee80211_node *
mwl_node_alloc(struct ieee80211com * ic)222410741SFei.Feng@Sun.COM mwl_node_alloc(struct ieee80211com *ic)
222510741SFei.Feng@Sun.COM {
222610741SFei.Feng@Sun.COM struct mwl_node *mn;
222710741SFei.Feng@Sun.COM
222810741SFei.Feng@Sun.COM mn = kmem_zalloc(sizeof (struct mwl_node), KM_SLEEP);
222910741SFei.Feng@Sun.COM if (mn == NULL) {
223010741SFei.Feng@Sun.COM /* XXX stat+msg */
223110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_MSG, "mwl: mwl_node_alloc(): "
223210741SFei.Feng@Sun.COM "alloc node failed\n");
223310741SFei.Feng@Sun.COM return (NULL);
223410741SFei.Feng@Sun.COM }
223510741SFei.Feng@Sun.COM return (&mn->mn_node);
223610741SFei.Feng@Sun.COM }
223710741SFei.Feng@Sun.COM
223810741SFei.Feng@Sun.COM static void
mwl_node_free(struct ieee80211_node * ni)223910741SFei.Feng@Sun.COM mwl_node_free(struct ieee80211_node *ni)
224010741SFei.Feng@Sun.COM {
224110741SFei.Feng@Sun.COM struct ieee80211com *ic = ni->in_ic;
224210741SFei.Feng@Sun.COM struct mwl_node *mn = MWL_NODE(ni);
224310741SFei.Feng@Sun.COM
224410741SFei.Feng@Sun.COM if (mn->mn_staid != 0) {
224510741SFei.Feng@Sun.COM // mwl_hal_delstation(mn->mn_hvap, vap->iv_myaddr);
224610741SFei.Feng@Sun.COM // delstaid(sc, mn->mn_staid);
224710741SFei.Feng@Sun.COM mn->mn_staid = 0;
224810741SFei.Feng@Sun.COM }
224910741SFei.Feng@Sun.COM ic->ic_node_cleanup(ni);
225010741SFei.Feng@Sun.COM kmem_free(ni, sizeof (struct mwl_node));
225110741SFei.Feng@Sun.COM }
225210741SFei.Feng@Sun.COM
225310741SFei.Feng@Sun.COM /*
225410741SFei.Feng@Sun.COM * Allocate a key cache slot for a unicast key. The
225510741SFei.Feng@Sun.COM * firmware handles key allocation and every station is
225610741SFei.Feng@Sun.COM * guaranteed key space so we are always successful.
225710741SFei.Feng@Sun.COM */
225810741SFei.Feng@Sun.COM static int
mwl_key_alloc(struct ieee80211com * ic,const struct ieee80211_key * k,ieee80211_keyix * keyix,ieee80211_keyix * rxkeyix)225910741SFei.Feng@Sun.COM mwl_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
226010741SFei.Feng@Sun.COM ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
226110741SFei.Feng@Sun.COM {
226210741SFei.Feng@Sun.COM if (k->wk_keyix != IEEE80211_KEYIX_NONE ||
226310741SFei.Feng@Sun.COM (k->wk_flags & IEEE80211_KEY_GROUP)) {
226410741SFei.Feng@Sun.COM if (!(&ic->ic_nw_keys[0] <= k &&
226510741SFei.Feng@Sun.COM k < &ic->ic_nw_keys[IEEE80211_WEP_NKID])) {
226610741SFei.Feng@Sun.COM /* should not happen */
226710741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_CRYPTO, "mwl: mwl_key_alloc(): "
226810741SFei.Feng@Sun.COM "bogus group key\n");
226910741SFei.Feng@Sun.COM return (0);
227010741SFei.Feng@Sun.COM }
227110741SFei.Feng@Sun.COM /* give the caller what they requested */
227210741SFei.Feng@Sun.COM *keyix = *rxkeyix = k - ic->ic_nw_keys;
227310741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_CRYPTO, "mwl: mwl_key_alloc(): "
227410741SFei.Feng@Sun.COM "alloc GROUP key keyix %x, rxkeyix %x\n",
227510741SFei.Feng@Sun.COM *keyix, *rxkeyix);
227610741SFei.Feng@Sun.COM } else {
227710741SFei.Feng@Sun.COM /*
227810741SFei.Feng@Sun.COM * Firmware handles key allocation.
227910741SFei.Feng@Sun.COM */
228010741SFei.Feng@Sun.COM *keyix = *rxkeyix = 0;
228110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_CRYPTO, "mwl: mwl_key_alloc(): "
228210741SFei.Feng@Sun.COM "reset key index in key allocation\n");
228310741SFei.Feng@Sun.COM }
228410741SFei.Feng@Sun.COM
228510741SFei.Feng@Sun.COM return (1);
228610741SFei.Feng@Sun.COM }
228710741SFei.Feng@Sun.COM
228810741SFei.Feng@Sun.COM /*
228910741SFei.Feng@Sun.COM * Delete a key entry allocated by mwl_key_alloc.
229010741SFei.Feng@Sun.COM */
229110741SFei.Feng@Sun.COM static int
mwl_key_delete(struct ieee80211com * ic,const struct ieee80211_key * k)229210741SFei.Feng@Sun.COM mwl_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k)
229310741SFei.Feng@Sun.COM {
229410741SFei.Feng@Sun.COM struct mwl_softc *sc = (struct mwl_softc *)ic;
229510741SFei.Feng@Sun.COM MWL_HAL_KEYVAL hk;
229610741SFei.Feng@Sun.COM const uint8_t bcastaddr[IEEE80211_ADDR_LEN] =
229710741SFei.Feng@Sun.COM { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
229810741SFei.Feng@Sun.COM
229910747SFei.Feng@Sun.COM (void) memset(&hk, 0, sizeof (hk));
230010741SFei.Feng@Sun.COM hk.keyIndex = k->wk_keyix;
230110741SFei.Feng@Sun.COM switch (k->wk_cipher->ic_cipher) {
230210741SFei.Feng@Sun.COM case IEEE80211_CIPHER_WEP:
230310741SFei.Feng@Sun.COM hk.keyTypeId = KEY_TYPE_ID_WEP;
230410741SFei.Feng@Sun.COM break;
230510741SFei.Feng@Sun.COM case IEEE80211_CIPHER_TKIP:
230610741SFei.Feng@Sun.COM hk.keyTypeId = KEY_TYPE_ID_TKIP;
230710741SFei.Feng@Sun.COM break;
230810741SFei.Feng@Sun.COM case IEEE80211_CIPHER_AES_CCM:
230910741SFei.Feng@Sun.COM hk.keyTypeId = KEY_TYPE_ID_AES;
231010741SFei.Feng@Sun.COM break;
231110741SFei.Feng@Sun.COM default:
231210741SFei.Feng@Sun.COM /* XXX should not happen */
231310741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_CRYPTO, "mwl: mwl_key_delete(): "
231410741SFei.Feng@Sun.COM "unknown cipher %d\n", k->wk_cipher->ic_cipher);
231510741SFei.Feng@Sun.COM return (0);
231610741SFei.Feng@Sun.COM }
231710741SFei.Feng@Sun.COM return (mwl_hal_keyreset(sc, &hk, bcastaddr) == 0);
231810741SFei.Feng@Sun.COM }
231910741SFei.Feng@Sun.COM
232010741SFei.Feng@Sun.COM /*
232110741SFei.Feng@Sun.COM * Set the key cache contents for the specified key. Key cache
232210741SFei.Feng@Sun.COM * slot(s) must already have been allocated by mwl_key_alloc.
232310741SFei.Feng@Sun.COM */
232410741SFei.Feng@Sun.COM /* ARGSUSED */
232510741SFei.Feng@Sun.COM static int
mwl_key_set(struct ieee80211com * ic,const struct ieee80211_key * k,const uint8_t mac[IEEE80211_ADDR_LEN])232610741SFei.Feng@Sun.COM mwl_key_set(struct ieee80211com *ic, const struct ieee80211_key *k,
232710741SFei.Feng@Sun.COM const uint8_t mac[IEEE80211_ADDR_LEN])
232810741SFei.Feng@Sun.COM {
232910741SFei.Feng@Sun.COM #define GRPXMIT (IEEE80211_KEY_XMIT | IEEE80211_KEY_GROUP)
233010741SFei.Feng@Sun.COM /* NB: static wep keys are marked GROUP+tx/rx; GTK will be tx or rx */
233110741SFei.Feng@Sun.COM #define IEEE80211_IS_STATICKEY(k) \
233210741SFei.Feng@Sun.COM (((k)->wk_flags & (GRPXMIT|IEEE80211_KEY_RECV)) == \
233310741SFei.Feng@Sun.COM (GRPXMIT|IEEE80211_KEY_RECV))
233410741SFei.Feng@Sun.COM struct mwl_softc *sc = (struct mwl_softc *)ic;
233510741SFei.Feng@Sun.COM const struct ieee80211_cipher *cip = k->wk_cipher;
233610741SFei.Feng@Sun.COM const uint8_t *macaddr;
233710741SFei.Feng@Sun.COM MWL_HAL_KEYVAL hk;
233810741SFei.Feng@Sun.COM
233910747SFei.Feng@Sun.COM (void) memset(&hk, 0, sizeof (hk));
234010741SFei.Feng@Sun.COM hk.keyIndex = k->wk_keyix;
234110741SFei.Feng@Sun.COM switch (cip->ic_cipher) {
234210741SFei.Feng@Sun.COM case IEEE80211_CIPHER_WEP:
234310741SFei.Feng@Sun.COM hk.keyTypeId = KEY_TYPE_ID_WEP;
234410741SFei.Feng@Sun.COM hk.keyLen = k->wk_keylen;
234510741SFei.Feng@Sun.COM if (k->wk_keyix == ic->ic_def_txkey)
234610741SFei.Feng@Sun.COM hk.keyFlags = KEY_FLAG_WEP_TXKEY;
234710741SFei.Feng@Sun.COM if (!IEEE80211_IS_STATICKEY(k)) {
234810741SFei.Feng@Sun.COM /* NB: WEP is never used for the PTK */
234910741SFei.Feng@Sun.COM (void) addgroupflags(&hk, k);
235010741SFei.Feng@Sun.COM }
235110741SFei.Feng@Sun.COM break;
235210741SFei.Feng@Sun.COM case IEEE80211_CIPHER_TKIP:
235310741SFei.Feng@Sun.COM hk.keyTypeId = KEY_TYPE_ID_TKIP;
235410741SFei.Feng@Sun.COM hk.key.tkip.tsc.high = (uint32_t)(k->wk_keytsc >> 16);
235510741SFei.Feng@Sun.COM hk.key.tkip.tsc.low = (uint16_t)k->wk_keytsc;
235610741SFei.Feng@Sun.COM hk.keyFlags = KEY_FLAG_TSC_VALID | KEY_FLAG_MICKEY_VALID;
235710741SFei.Feng@Sun.COM hk.keyLen = k->wk_keylen + IEEE80211_MICBUF_SIZE;
235810741SFei.Feng@Sun.COM if (!addgroupflags(&hk, k))
235910741SFei.Feng@Sun.COM hk.keyFlags |= KEY_FLAG_PAIRWISE;
236010741SFei.Feng@Sun.COM break;
236110741SFei.Feng@Sun.COM case IEEE80211_CIPHER_AES_CCM:
236210741SFei.Feng@Sun.COM hk.keyTypeId = KEY_TYPE_ID_AES;
236310741SFei.Feng@Sun.COM hk.keyLen = k->wk_keylen;
236410741SFei.Feng@Sun.COM if (!addgroupflags(&hk, k))
236510741SFei.Feng@Sun.COM hk.keyFlags |= KEY_FLAG_PAIRWISE;
236610741SFei.Feng@Sun.COM break;
236710741SFei.Feng@Sun.COM default:
236810741SFei.Feng@Sun.COM /* XXX should not happen */
236910741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_CRYPTO, "mwl: mwl_key_set(): "
237010741SFei.Feng@Sun.COM "unknown cipher %d\n",
237110741SFei.Feng@Sun.COM k->wk_cipher->ic_cipher);
237210741SFei.Feng@Sun.COM return (0);
237310741SFei.Feng@Sun.COM }
237410741SFei.Feng@Sun.COM /*
237510741SFei.Feng@Sun.COM * NB: tkip mic keys get copied here too; the layout
237610741SFei.Feng@Sun.COM * just happens to match that in ieee80211_key.
237710741SFei.Feng@Sun.COM */
237810741SFei.Feng@Sun.COM (void) memcpy(hk.key.aes, k->wk_key, hk.keyLen);
237910741SFei.Feng@Sun.COM
238010741SFei.Feng@Sun.COM /*
238110741SFei.Feng@Sun.COM * Locate address of sta db entry for writing key;
238210741SFei.Feng@Sun.COM * the convention unfortunately is somewhat different
238310741SFei.Feng@Sun.COM * than how net80211, hostapd, and wpa_supplicant think.
238410741SFei.Feng@Sun.COM */
238510741SFei.Feng@Sun.COM
238610741SFei.Feng@Sun.COM /*
238710741SFei.Feng@Sun.COM * NB: keys plumbed before the sta reaches AUTH state
238810741SFei.Feng@Sun.COM * will be discarded or written to the wrong sta db
238910741SFei.Feng@Sun.COM * entry because iv_bss is meaningless. This is ok
239010741SFei.Feng@Sun.COM * (right now) because we handle deferred plumbing of
239110741SFei.Feng@Sun.COM * WEP keys when the sta reaches AUTH state.
239210741SFei.Feng@Sun.COM */
239310741SFei.Feng@Sun.COM macaddr = ic->ic_bss->in_bssid;
239410741SFei.Feng@Sun.COM if (k->wk_flags & IEEE80211_KEY_XMIT) {
239510741SFei.Feng@Sun.COM /* XXX plumb to local sta db too for static key wep */
239610741SFei.Feng@Sun.COM (void) mwl_hal_keyset(sc, &hk, ic->ic_macaddr);
239710741SFei.Feng@Sun.COM }
239810741SFei.Feng@Sun.COM return (mwl_hal_keyset(sc, &hk, macaddr) == 0);
239910741SFei.Feng@Sun.COM #undef IEEE80211_IS_STATICKEY
240010741SFei.Feng@Sun.COM #undef GRPXMIT
240110741SFei.Feng@Sun.COM }
240210741SFei.Feng@Sun.COM
240310741SFei.Feng@Sun.COM /*
240410741SFei.Feng@Sun.COM * Plumb any static WEP key for the station. This is
240510741SFei.Feng@Sun.COM * necessary as we must propagate the key from the
240610741SFei.Feng@Sun.COM * global key table of the vap to each sta db entry.
240710741SFei.Feng@Sun.COM */
240810741SFei.Feng@Sun.COM static void
mwl_setanywepkey(struct ieee80211com * ic,const uint8_t mac[IEEE80211_ADDR_LEN])240910741SFei.Feng@Sun.COM mwl_setanywepkey(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN])
241010741SFei.Feng@Sun.COM {
241110741SFei.Feng@Sun.COM if ((ic->ic_flags & (IEEE80211_F_PRIVACY|IEEE80211_F_WPA)) ==
241210741SFei.Feng@Sun.COM IEEE80211_F_PRIVACY &&
241310741SFei.Feng@Sun.COM ic->ic_def_txkey != IEEE80211_KEYIX_NONE &&
241410741SFei.Feng@Sun.COM ic->ic_nw_keys[ic->ic_def_txkey].wk_keyix != IEEE80211_KEYIX_NONE)
241510741SFei.Feng@Sun.COM (void) mwl_key_set(ic, &ic->ic_nw_keys[ic->ic_def_txkey], mac);
241610741SFei.Feng@Sun.COM }
241710741SFei.Feng@Sun.COM
241810741SFei.Feng@Sun.COM static void
mwl_setglobalkeys(struct ieee80211com * ic)241910741SFei.Feng@Sun.COM mwl_setglobalkeys(struct ieee80211com *ic)
242010741SFei.Feng@Sun.COM {
242110741SFei.Feng@Sun.COM struct ieee80211_key *wk;
242210741SFei.Feng@Sun.COM
242310741SFei.Feng@Sun.COM wk = &ic->ic_nw_keys[0];
242410741SFei.Feng@Sun.COM for (; wk < &ic->ic_nw_keys[IEEE80211_WEP_NKID]; wk++)
242510741SFei.Feng@Sun.COM if (wk->wk_keyix != IEEE80211_KEYIX_NONE)
242610741SFei.Feng@Sun.COM (void) mwl_key_set(ic, wk, ic->ic_macaddr);
242710741SFei.Feng@Sun.COM }
242810741SFei.Feng@Sun.COM
242910741SFei.Feng@Sun.COM static int
addgroupflags(MWL_HAL_KEYVAL * hk,const struct ieee80211_key * k)243010741SFei.Feng@Sun.COM addgroupflags(MWL_HAL_KEYVAL *hk, const struct ieee80211_key *k)
243110741SFei.Feng@Sun.COM {
243210741SFei.Feng@Sun.COM if (k->wk_flags & IEEE80211_KEY_GROUP) {
243310741SFei.Feng@Sun.COM if (k->wk_flags & IEEE80211_KEY_XMIT)
243410741SFei.Feng@Sun.COM hk->keyFlags |= KEY_FLAG_TXGROUPKEY;
243510741SFei.Feng@Sun.COM if (k->wk_flags & IEEE80211_KEY_RECV)
243610741SFei.Feng@Sun.COM hk->keyFlags |= KEY_FLAG_RXGROUPKEY;
243710741SFei.Feng@Sun.COM return (1);
243810741SFei.Feng@Sun.COM } else
243910741SFei.Feng@Sun.COM return (0);
244010741SFei.Feng@Sun.COM }
244110741SFei.Feng@Sun.COM
244210741SFei.Feng@Sun.COM /*
244310741SFei.Feng@Sun.COM * Set/change channels.
244410741SFei.Feng@Sun.COM */
244510741SFei.Feng@Sun.COM static int
mwl_chan_set(struct mwl_softc * sc,struct mwl_channel * chan)244610741SFei.Feng@Sun.COM mwl_chan_set(struct mwl_softc *sc, struct mwl_channel *chan)
244710741SFei.Feng@Sun.COM {
244810741SFei.Feng@Sun.COM MWL_HAL_CHANNEL hchan;
244910741SFei.Feng@Sun.COM int maxtxpow;
245010741SFei.Feng@Sun.COM
245110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_HW, "mwl: mwl_chan_set(): "
245210741SFei.Feng@Sun.COM "chan %u MHz/flags 0x%x\n",
245310741SFei.Feng@Sun.COM chan->ic_freq, chan->ic_flags);
245410741SFei.Feng@Sun.COM
245510741SFei.Feng@Sun.COM /*
245610741SFei.Feng@Sun.COM * Convert to a HAL channel description with
245710741SFei.Feng@Sun.COM * the flags constrained to reflect the current
245810741SFei.Feng@Sun.COM * operating mode.
245910741SFei.Feng@Sun.COM */
246010741SFei.Feng@Sun.COM mwl_mapchan(&hchan, chan);
246110741SFei.Feng@Sun.COM mwl_hal_intrset(sc, 0); /* disable interrupts */
246210741SFei.Feng@Sun.COM
246310741SFei.Feng@Sun.COM (void) mwl_hal_setchannel(sc, &hchan);
246410741SFei.Feng@Sun.COM /*
246510741SFei.Feng@Sun.COM * Tx power is cap'd by the regulatory setting and
246610741SFei.Feng@Sun.COM * possibly a user-set limit. We pass the min of
246710741SFei.Feng@Sun.COM * these to the hal to apply them to the cal data
246810741SFei.Feng@Sun.COM * for this channel.
246910741SFei.Feng@Sun.COM * XXX min bound?
247010741SFei.Feng@Sun.COM */
247110741SFei.Feng@Sun.COM maxtxpow = 2 * chan->ic_maxregpower;
247210741SFei.Feng@Sun.COM if (maxtxpow > 100)
247310741SFei.Feng@Sun.COM maxtxpow = 100;
247410741SFei.Feng@Sun.COM (void) mwl_hal_settxpower(sc, &hchan, maxtxpow / 2);
247510741SFei.Feng@Sun.COM /* NB: potentially change mcast/mgt rates */
247610741SFei.Feng@Sun.COM (void) mwl_setcurchanrates(sc);
247710741SFei.Feng@Sun.COM
247810741SFei.Feng@Sun.COM sc->sc_curchan = hchan;
247910741SFei.Feng@Sun.COM mwl_hal_intrset(sc, sc->sc_imask);
248010741SFei.Feng@Sun.COM
248110741SFei.Feng@Sun.COM return (0);
248210741SFei.Feng@Sun.COM }
248310741SFei.Feng@Sun.COM
248410741SFei.Feng@Sun.COM /*
248510741SFei.Feng@Sun.COM * Convert net80211 channel to a HAL channel.
248610741SFei.Feng@Sun.COM */
248710741SFei.Feng@Sun.COM static void
mwl_mapchan(MWL_HAL_CHANNEL * hc,const struct mwl_channel * chan)248810741SFei.Feng@Sun.COM mwl_mapchan(MWL_HAL_CHANNEL *hc, const struct mwl_channel *chan)
248910741SFei.Feng@Sun.COM {
249010741SFei.Feng@Sun.COM hc->channel = chan->ic_ieee;
249110741SFei.Feng@Sun.COM
249210741SFei.Feng@Sun.COM *(uint32_t *)&hc->channelFlags = 0;
249310741SFei.Feng@Sun.COM if (((chan)->ic_flags & IEEE80211_CHAN_2GHZ) != 0)
249410741SFei.Feng@Sun.COM hc->channelFlags.FreqBand = MWL_FREQ_BAND_2DOT4GHZ;
249510741SFei.Feng@Sun.COM else if (((chan)->ic_flags & IEEE80211_CHAN_5GHZ) != 0)
249610741SFei.Feng@Sun.COM hc->channelFlags.FreqBand = MWL_FREQ_BAND_5GHZ;
249710741SFei.Feng@Sun.COM if (((chan)->ic_flags & IEEE80211_CHAN_HT40) != 0) {
249810741SFei.Feng@Sun.COM hc->channelFlags.ChnlWidth = MWL_CH_40_MHz_WIDTH;
249910741SFei.Feng@Sun.COM if (((chan)->ic_flags & IEEE80211_CHAN_HT40U) != 0)
250010741SFei.Feng@Sun.COM hc->channelFlags.ExtChnlOffset =
250110741SFei.Feng@Sun.COM MWL_EXT_CH_ABOVE_CTRL_CH;
250210741SFei.Feng@Sun.COM else
250310741SFei.Feng@Sun.COM hc->channelFlags.ExtChnlOffset =
250410741SFei.Feng@Sun.COM MWL_EXT_CH_BELOW_CTRL_CH;
250510741SFei.Feng@Sun.COM } else
250610741SFei.Feng@Sun.COM hc->channelFlags.ChnlWidth = MWL_CH_20_MHz_WIDTH;
250710741SFei.Feng@Sun.COM /* XXX 10MHz channels */
250810741SFei.Feng@Sun.COM }
250910741SFei.Feng@Sun.COM
251010741SFei.Feng@Sun.COM /*
251110741SFei.Feng@Sun.COM * Return the phy mode for with the specified channel.
251210741SFei.Feng@Sun.COM */
251310741SFei.Feng@Sun.COM enum ieee80211_phymode
mwl_chan2mode(const struct mwl_channel * chan)251410741SFei.Feng@Sun.COM mwl_chan2mode(const struct mwl_channel *chan)
251510741SFei.Feng@Sun.COM {
251610741SFei.Feng@Sun.COM
251710741SFei.Feng@Sun.COM if (IEEE80211_IS_CHAN_HTA(chan))
251810741SFei.Feng@Sun.COM return (IEEE80211_MODE_11NA);
251910741SFei.Feng@Sun.COM else if (IEEE80211_IS_CHAN_HTG(chan))
252010741SFei.Feng@Sun.COM return (IEEE80211_MODE_11NG);
252110741SFei.Feng@Sun.COM else if (IEEE80211_IS_CHAN_108G(chan))
252210741SFei.Feng@Sun.COM return (IEEE80211_MODE_TURBO_G);
252310741SFei.Feng@Sun.COM else if (IEEE80211_IS_CHAN_ST(chan))
252410741SFei.Feng@Sun.COM return (IEEE80211_MODE_STURBO_A);
252510741SFei.Feng@Sun.COM else if (IEEE80211_IS_CHAN_TURBO(chan))
252610741SFei.Feng@Sun.COM return (IEEE80211_MODE_TURBO_A);
252710741SFei.Feng@Sun.COM else if (IEEE80211_IS_CHAN_HALF(chan))
252810741SFei.Feng@Sun.COM return (IEEE80211_MODE_HALF);
252910741SFei.Feng@Sun.COM else if (IEEE80211_IS_CHAN_QUARTER(chan))
253010741SFei.Feng@Sun.COM return (IEEE80211_MODE_QUARTER);
253110741SFei.Feng@Sun.COM else if (IEEE80211_IS_CHAN_A(chan))
253210741SFei.Feng@Sun.COM return (IEEE80211_MODE_11A);
253310741SFei.Feng@Sun.COM else if (IEEE80211_IS_CHAN_ANYG(chan))
253410741SFei.Feng@Sun.COM return (IEEE80211_MODE_11G);
253510741SFei.Feng@Sun.COM else if (IEEE80211_IS_CHAN_B(chan))
253610741SFei.Feng@Sun.COM return (IEEE80211_MODE_11B);
253710741SFei.Feng@Sun.COM else if (IEEE80211_IS_CHAN_FHSS(chan))
253810741SFei.Feng@Sun.COM return (IEEE80211_MODE_FH);
253910741SFei.Feng@Sun.COM
254010741SFei.Feng@Sun.COM /* NB: should not get here */
254110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_HW, "mwl: mwl_chan2mode(): "
254210741SFei.Feng@Sun.COM "cannot map channel to mode; freq %u flags 0x%x\n",
254310741SFei.Feng@Sun.COM chan->ic_freq, chan->ic_flags);
254410741SFei.Feng@Sun.COM return (IEEE80211_MODE_11B);
254510741SFei.Feng@Sun.COM }
254610741SFei.Feng@Sun.COM
254710741SFei.Feng@Sun.COM /* XXX inline or eliminate? */
254810741SFei.Feng@Sun.COM const struct ieee80211_rateset *
mwl_get_suprates(struct ieee80211com * ic,const struct mwl_channel * c)254910741SFei.Feng@Sun.COM mwl_get_suprates(struct ieee80211com *ic, const struct mwl_channel *c)
255010741SFei.Feng@Sun.COM {
255110741SFei.Feng@Sun.COM /* XXX does this work for 11ng basic rates? */
255210741SFei.Feng@Sun.COM return (&ic->ic_sup_rates[mwl_chan2mode(c)]);
255310741SFei.Feng@Sun.COM }
255410741SFei.Feng@Sun.COM
255510741SFei.Feng@Sun.COM /*
255610741SFei.Feng@Sun.COM * Inform firmware of tx rate parameters.
255710741SFei.Feng@Sun.COM * Called after a channel change.
255810741SFei.Feng@Sun.COM */
255910741SFei.Feng@Sun.COM static int
mwl_setcurchanrates(struct mwl_softc * sc)256010741SFei.Feng@Sun.COM mwl_setcurchanrates(struct mwl_softc *sc)
256110741SFei.Feng@Sun.COM {
256210741SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
256310741SFei.Feng@Sun.COM const struct ieee80211_rateset *rs;
256410741SFei.Feng@Sun.COM MWL_HAL_TXRATE rates;
256510741SFei.Feng@Sun.COM
256610747SFei.Feng@Sun.COM (void) memset(&rates, 0, sizeof (rates));
256710741SFei.Feng@Sun.COM rs = mwl_get_suprates(ic, sc->sc_cur_chan);
256810741SFei.Feng@Sun.COM /* rate used to send management frames */
256910741SFei.Feng@Sun.COM rates.MgtRate = rs->ir_rates[0] & IEEE80211_RATE_VAL;
257010741SFei.Feng@Sun.COM /* rate used to send multicast frames */
257110741SFei.Feng@Sun.COM rates.McastRate = rates.MgtRate;
257210741SFei.Feng@Sun.COM
257310741SFei.Feng@Sun.COM return (mwl_hal_settxrate_auto(sc, &rates));
257410741SFei.Feng@Sun.COM }
257510741SFei.Feng@Sun.COM
257610741SFei.Feng@Sun.COM static const struct mwl_hal_channel *
findhalchannel(const struct mwl_softc * sc,const MWL_HAL_CHANNEL * c)257710741SFei.Feng@Sun.COM findhalchannel(const struct mwl_softc *sc, const MWL_HAL_CHANNEL *c)
257810741SFei.Feng@Sun.COM {
257910741SFei.Feng@Sun.COM const struct mwl_hal_channel *hc;
258010741SFei.Feng@Sun.COM const MWL_HAL_CHANNELINFO *ci;
258110741SFei.Feng@Sun.COM int chan = c->channel, i;
258210741SFei.Feng@Sun.COM
258310741SFei.Feng@Sun.COM if (c->channelFlags.FreqBand == MWL_FREQ_BAND_2DOT4GHZ) {
258410741SFei.Feng@Sun.COM i = chan - 1;
258510741SFei.Feng@Sun.COM if (c->channelFlags.ChnlWidth == MWL_CH_40_MHz_WIDTH) {
258610741SFei.Feng@Sun.COM ci = &sc->sc_40M;
258710741SFei.Feng@Sun.COM if (c->channelFlags.ExtChnlOffset ==
258810741SFei.Feng@Sun.COM MWL_EXT_CH_BELOW_CTRL_CH)
258910741SFei.Feng@Sun.COM i -= 4;
259010741SFei.Feng@Sun.COM } else
259110741SFei.Feng@Sun.COM ci = &sc->sc_20M;
259210741SFei.Feng@Sun.COM /* 2.4G channel table is directly indexed */
259310741SFei.Feng@Sun.COM hc = ((unsigned)i < ci->nchannels) ? &ci->channels[i] : NULL;
259410741SFei.Feng@Sun.COM } else if (c->channelFlags.FreqBand == MWL_FREQ_BAND_5GHZ) {
259510741SFei.Feng@Sun.COM if (c->channelFlags.ChnlWidth == MWL_CH_40_MHz_WIDTH) {
259610741SFei.Feng@Sun.COM ci = &sc->sc_40M_5G;
259710741SFei.Feng@Sun.COM if (c->channelFlags.ExtChnlOffset ==
259810741SFei.Feng@Sun.COM MWL_EXT_CH_BELOW_CTRL_CH)
259910741SFei.Feng@Sun.COM chan -= 4;
260010741SFei.Feng@Sun.COM } else
260110741SFei.Feng@Sun.COM ci = &sc->sc_20M_5G;
260210741SFei.Feng@Sun.COM /* 5GHz channel table is sparse and must be searched */
260310741SFei.Feng@Sun.COM for (i = 0; i < ci->nchannels; i++)
260410741SFei.Feng@Sun.COM if (ci->channels[i].ieee == chan)
260510741SFei.Feng@Sun.COM break;
260610741SFei.Feng@Sun.COM hc = (i < ci->nchannels) ? &ci->channels[i] : NULL;
260710741SFei.Feng@Sun.COM } else
260810741SFei.Feng@Sun.COM hc = NULL;
260910741SFei.Feng@Sun.COM return (hc);
261010741SFei.Feng@Sun.COM }
261110741SFei.Feng@Sun.COM
261210741SFei.Feng@Sun.COM /*
261310741SFei.Feng@Sun.COM * Map SKU+country code to region code for radar bin'ing.
261410741SFei.Feng@Sun.COM */
261510741SFei.Feng@Sun.COM static int
mwl_map2regioncode(const struct mwl_regdomain * rd)261610741SFei.Feng@Sun.COM mwl_map2regioncode(const struct mwl_regdomain *rd)
261710741SFei.Feng@Sun.COM {
261810741SFei.Feng@Sun.COM switch (rd->regdomain) {
261910741SFei.Feng@Sun.COM case SKU_FCC:
262010741SFei.Feng@Sun.COM case SKU_FCC3:
262110741SFei.Feng@Sun.COM return (DOMAIN_CODE_FCC);
262210741SFei.Feng@Sun.COM case SKU_CA:
262310741SFei.Feng@Sun.COM return (DOMAIN_CODE_IC);
262410741SFei.Feng@Sun.COM case SKU_ETSI:
262510741SFei.Feng@Sun.COM case SKU_ETSI2:
262610741SFei.Feng@Sun.COM case SKU_ETSI3:
262710741SFei.Feng@Sun.COM if (rd->country == CTRY_SPAIN)
262810741SFei.Feng@Sun.COM return (DOMAIN_CODE_SPAIN);
262910741SFei.Feng@Sun.COM if (rd->country == CTRY_FRANCE || rd->country == CTRY_FRANCE2)
263010741SFei.Feng@Sun.COM return (DOMAIN_CODE_FRANCE);
263110741SFei.Feng@Sun.COM /* XXX force 1.3.1 radar type */
263210741SFei.Feng@Sun.COM return (DOMAIN_CODE_ETSI_131);
263310741SFei.Feng@Sun.COM case SKU_JAPAN:
263410741SFei.Feng@Sun.COM return (DOMAIN_CODE_MKK);
263510741SFei.Feng@Sun.COM case SKU_ROW:
263610741SFei.Feng@Sun.COM return (DOMAIN_CODE_DGT); /* Taiwan */
263710741SFei.Feng@Sun.COM case SKU_APAC:
263810741SFei.Feng@Sun.COM case SKU_APAC2:
263910741SFei.Feng@Sun.COM case SKU_APAC3:
264010741SFei.Feng@Sun.COM return (DOMAIN_CODE_AUS); /* Australia */
264110741SFei.Feng@Sun.COM }
264210741SFei.Feng@Sun.COM /* XXX KOREA? */
264310741SFei.Feng@Sun.COM return (DOMAIN_CODE_FCC); /* XXX? */
264410741SFei.Feng@Sun.COM }
264510741SFei.Feng@Sun.COM
264610741SFei.Feng@Sun.COM /*
264710741SFei.Feng@Sun.COM * Setup the rx data structures. This should only be
264810741SFei.Feng@Sun.COM * done once or we may get out of sync with the firmware.
264910741SFei.Feng@Sun.COM */
265010741SFei.Feng@Sun.COM static int
mwl_startrecv(struct mwl_softc * sc)265110741SFei.Feng@Sun.COM mwl_startrecv(struct mwl_softc *sc)
265210741SFei.Feng@Sun.COM {
265310741SFei.Feng@Sun.COM struct mwl_rx_ring *ring;
265410741SFei.Feng@Sun.COM struct mwl_rxdesc *ds;
265510741SFei.Feng@Sun.COM struct mwl_rxbuf *bf, *prev;
265610741SFei.Feng@Sun.COM
265710741SFei.Feng@Sun.COM int i;
265810741SFei.Feng@Sun.COM
265910741SFei.Feng@Sun.COM ring = &sc->sc_rxring;
266010741SFei.Feng@Sun.COM bf = ring->buf;
266110741SFei.Feng@Sun.COM
266210741SFei.Feng@Sun.COM prev = NULL;
266310741SFei.Feng@Sun.COM for (i = 0; i < MWL_RX_RING_COUNT; i++, bf++) {
266410741SFei.Feng@Sun.COM ds = bf->bf_desc;
266510741SFei.Feng@Sun.COM /*
266610741SFei.Feng@Sun.COM * NB: DMA buffer contents is known to be unmodified
266710741SFei.Feng@Sun.COM * so there's no need to flush the data cache.
266810741SFei.Feng@Sun.COM */
266910741SFei.Feng@Sun.COM
267010741SFei.Feng@Sun.COM /*
267110741SFei.Feng@Sun.COM * Setup descriptor.
267210741SFei.Feng@Sun.COM */
267310741SFei.Feng@Sun.COM ds->QosCtrl = 0;
267410741SFei.Feng@Sun.COM ds->RSSI = 0;
267510741SFei.Feng@Sun.COM ds->Status = EAGLE_RXD_STATUS_IDLE;
267610741SFei.Feng@Sun.COM ds->Channel = 0;
267710741SFei.Feng@Sun.COM ds->PktLen = LE_16(MWL_AGGR_SIZE);
267810741SFei.Feng@Sun.COM ds->SQ2 = 0;
267910741SFei.Feng@Sun.COM ds->pPhysBuffData = LE_32(bf->bf_baddr);
268010741SFei.Feng@Sun.COM /* NB: don't touch pPhysNext, set once */
268110741SFei.Feng@Sun.COM ds->RxControl = EAGLE_RXD_CTRL_DRIVER_OWN;
268210741SFei.Feng@Sun.COM
268310741SFei.Feng@Sun.COM (void) ddi_dma_sync(ring->rxdesc_dma.dma_hdl,
268410741SFei.Feng@Sun.COM i * sizeof (struct mwl_rxdesc),
268510741SFei.Feng@Sun.COM sizeof (struct mwl_rxdesc),
268610741SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV);
268710741SFei.Feng@Sun.COM
268810741SFei.Feng@Sun.COM if (prev != NULL) {
268910741SFei.Feng@Sun.COM ds = prev->bf_desc;
269010741SFei.Feng@Sun.COM ds->pPhysNext = LE_32(bf->bf_daddr);
269110741SFei.Feng@Sun.COM }
269210741SFei.Feng@Sun.COM prev = bf;
269310741SFei.Feng@Sun.COM }
269410741SFei.Feng@Sun.COM
269510741SFei.Feng@Sun.COM if (prev != NULL) {
269610741SFei.Feng@Sun.COM ds = prev->bf_desc;
269710741SFei.Feng@Sun.COM ds->pPhysNext = ring->physaddr;
269810741SFei.Feng@Sun.COM }
269910741SFei.Feng@Sun.COM
270010741SFei.Feng@Sun.COM /* set filters, etc. */
270110741SFei.Feng@Sun.COM (void) mwl_mode_init(sc);
270210741SFei.Feng@Sun.COM
270310741SFei.Feng@Sun.COM return (0);
270410741SFei.Feng@Sun.COM }
270510741SFei.Feng@Sun.COM
270610741SFei.Feng@Sun.COM static int
mwl_mode_init(struct mwl_softc * sc)270710741SFei.Feng@Sun.COM mwl_mode_init(struct mwl_softc *sc)
270810741SFei.Feng@Sun.COM {
270910741SFei.Feng@Sun.COM /*
271010741SFei.Feng@Sun.COM * NB: Ignore promisc in hostap mode; it's set by the
271110741SFei.Feng@Sun.COM * bridge. This is wrong but we have no way to
271210741SFei.Feng@Sun.COM * identify internal requests (from the bridge)
271310741SFei.Feng@Sun.COM * versus external requests such as for tcpdump.
271410741SFei.Feng@Sun.COM */
271510741SFei.Feng@Sun.COM /* mwl_setmcastfilter - not support now */
271610741SFei.Feng@Sun.COM (void) mwl_hal_setpromisc(sc, 0);
271710741SFei.Feng@Sun.COM
271810741SFei.Feng@Sun.COM return (0);
271910741SFei.Feng@Sun.COM }
272010741SFei.Feng@Sun.COM
272110741SFei.Feng@Sun.COM /*
272210741SFei.Feng@Sun.COM * Kick the firmware to tell it there are new tx descriptors
272310741SFei.Feng@Sun.COM * for processing. The driver says what h/w q has work in
272410741SFei.Feng@Sun.COM * case the f/w ever gets smarter.
272510741SFei.Feng@Sun.COM */
272610741SFei.Feng@Sun.COM /* ARGSUSED */
272710741SFei.Feng@Sun.COM static void
mwl_hal_txstart(struct mwl_softc * sc,int qnum)272810741SFei.Feng@Sun.COM mwl_hal_txstart(struct mwl_softc *sc, int qnum)
272910741SFei.Feng@Sun.COM {
273010741SFei.Feng@Sun.COM
273110741SFei.Feng@Sun.COM mwl_ctl_write4(sc, MACREG_REG_H2A_INTERRUPT_EVENTS,
273210741SFei.Feng@Sun.COM MACREG_H2ARIC_BIT_PPA_READY);
273310741SFei.Feng@Sun.COM (void) mwl_ctl_read4(sc, MACREG_REG_INT_CODE);
273410741SFei.Feng@Sun.COM }
273510741SFei.Feng@Sun.COM
273610741SFei.Feng@Sun.COM static int
mwl_send(ieee80211com_t * ic,mblk_t * mp,uint8_t type)273710741SFei.Feng@Sun.COM mwl_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
273810741SFei.Feng@Sun.COM {
273910741SFei.Feng@Sun.COM struct mwl_softc *sc = (struct mwl_softc *)ic;
274010741SFei.Feng@Sun.COM struct mwl_tx_ring *ring;
274110741SFei.Feng@Sun.COM struct mwl_txdesc *ds;
274210741SFei.Feng@Sun.COM struct mwl_txbuf *bf;
274310741SFei.Feng@Sun.COM struct ieee80211_frame *wh, *wh1;
274410741SFei.Feng@Sun.COM struct ieee80211_node *ni = NULL;
274510741SFei.Feng@Sun.COM
274610741SFei.Feng@Sun.COM int err, off;
274710741SFei.Feng@Sun.COM int mblen, pktlen, hdrlen;
274810741SFei.Feng@Sun.COM mblk_t *m, *m0;
274910741SFei.Feng@Sun.COM uint8_t *addr_4, *txbuf;
275010741SFei.Feng@Sun.COM uint16_t *pfwlen;
275110741SFei.Feng@Sun.COM
275210741SFei.Feng@Sun.COM MWL_TXLOCK(sc);
275310741SFei.Feng@Sun.COM
275410741SFei.Feng@Sun.COM err = DDI_SUCCESS;
275510741SFei.Feng@Sun.COM if (!MWL_IS_RUNNING(sc) || MWL_IS_SUSPEND(sc)) {
275610741SFei.Feng@Sun.COM err = ENXIO;
275710741SFei.Feng@Sun.COM goto fail1;
275810741SFei.Feng@Sun.COM }
275910741SFei.Feng@Sun.COM
276010741SFei.Feng@Sun.COM ring = &sc->sc_txring[1];
276110741SFei.Feng@Sun.COM if (ring->queued > 15) {
276210741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_TX, "mwl: mwl_send(): "
276310741SFei.Feng@Sun.COM "no txbuf, %d\n", ring->queued);
276410741SFei.Feng@Sun.COM sc->sc_need_sched = 1;
276510741SFei.Feng@Sun.COM sc->sc_tx_nobuf++;
276610741SFei.Feng@Sun.COM err = ENOMEM;
276710741SFei.Feng@Sun.COM goto fail1;
276810741SFei.Feng@Sun.COM }
276910741SFei.Feng@Sun.COM
277010741SFei.Feng@Sun.COM m = allocb(msgdsize(mp) + 32, BPRI_MED);
277110741SFei.Feng@Sun.COM if (m == NULL) {
277210741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_TX, "mwl: mwl_send():"
277310741SFei.Feng@Sun.COM "can't alloc mblk.\n");
277410741SFei.Feng@Sun.COM err = DDI_FAILURE;
277510741SFei.Feng@Sun.COM goto fail1;
277610741SFei.Feng@Sun.COM }
277710741SFei.Feng@Sun.COM
277810741SFei.Feng@Sun.COM for (off = 0, m0 = mp; m0 != NULL; m0 = m0->b_cont) {
277910741SFei.Feng@Sun.COM mblen = MBLKL(m0);
278010741SFei.Feng@Sun.COM (void) bcopy(m0->b_rptr, m->b_rptr + off, mblen);
278110741SFei.Feng@Sun.COM off += mblen;
278210741SFei.Feng@Sun.COM }
278310741SFei.Feng@Sun.COM m->b_wptr += off;
278410741SFei.Feng@Sun.COM
278510741SFei.Feng@Sun.COM wh = (struct ieee80211_frame *)m->b_rptr;
278610741SFei.Feng@Sun.COM ni = ieee80211_find_txnode(ic, wh->i_addr1);
278710741SFei.Feng@Sun.COM if (ni == NULL) {
278810741SFei.Feng@Sun.COM err = DDI_FAILURE;
278910741SFei.Feng@Sun.COM sc->sc_tx_err++;
279010741SFei.Feng@Sun.COM goto fail2;
279110741SFei.Feng@Sun.COM }
279210741SFei.Feng@Sun.COM
279310741SFei.Feng@Sun.COM hdrlen = sizeof (*wh);
279410741SFei.Feng@Sun.COM pktlen = msgdsize(m);
279510741SFei.Feng@Sun.COM
279610741SFei.Feng@Sun.COM (void) ieee80211_encap(ic, m, ni);
279710741SFei.Feng@Sun.COM
279810741SFei.Feng@Sun.COM if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
279910741SFei.Feng@Sun.COM const struct ieee80211_cipher *cip;
280010741SFei.Feng@Sun.COM struct ieee80211_key *k;
280110741SFei.Feng@Sun.COM k = ieee80211_crypto_encap(ic, m);
280210741SFei.Feng@Sun.COM if (k == NULL) {
280310741SFei.Feng@Sun.COM sc->sc_tx_err++;
280410741SFei.Feng@Sun.COM err = DDI_FAILURE;
280510741SFei.Feng@Sun.COM goto fail3;
280610741SFei.Feng@Sun.COM }
280710741SFei.Feng@Sun.COM
280810741SFei.Feng@Sun.COM /*
280910741SFei.Feng@Sun.COM * Adjust the packet length for the crypto additions
281010741SFei.Feng@Sun.COM * done during encap and any other bits that the f/w
281110741SFei.Feng@Sun.COM * will add later on.
281210741SFei.Feng@Sun.COM */
281310741SFei.Feng@Sun.COM cip = k->wk_cipher;
281410741SFei.Feng@Sun.COM pktlen += cip->ic_header + cip->ic_miclen + cip->ic_trailer;
281510741SFei.Feng@Sun.COM /* packet header may have moved, reset our local pointer */
281610741SFei.Feng@Sun.COM wh = (struct ieee80211_frame *)m->b_rptr;
281710741SFei.Feng@Sun.COM }
281810741SFei.Feng@Sun.COM
281910741SFei.Feng@Sun.COM ds = &ring->desc[ring->cur];
282010741SFei.Feng@Sun.COM bf = &ring->buf[ring->cur];
282110741SFei.Feng@Sun.COM
282210741SFei.Feng@Sun.COM bf->bf_node = ieee80211_ref_node(ni);
282310741SFei.Feng@Sun.COM txbuf = (uint8_t *)bf->bf_mem;
282410741SFei.Feng@Sun.COM
282510741SFei.Feng@Sun.COM /*
282610741SFei.Feng@Sun.COM * inject FW specific fields into the 802.11 frame
282710741SFei.Feng@Sun.COM *
282810741SFei.Feng@Sun.COM * 2 bytes FW len (inject)
282910741SFei.Feng@Sun.COM * 24 bytes 802.11 frame header
283010741SFei.Feng@Sun.COM * 6 bytes addr4 (inject)
283110741SFei.Feng@Sun.COM * n bytes 802.11 frame body
283210741SFei.Feng@Sun.COM */
283310741SFei.Feng@Sun.COM pfwlen = (uint16_t *)txbuf;
283410741SFei.Feng@Sun.COM *pfwlen = pktlen - hdrlen;
283510741SFei.Feng@Sun.COM wh1 = (struct ieee80211_frame *)(txbuf + 2);
283610741SFei.Feng@Sun.COM bcopy(wh, wh1, sizeof (struct ieee80211_frame));
283710741SFei.Feng@Sun.COM addr_4 = txbuf + (sizeof (struct ieee80211_frame) + sizeof (uint16_t));
283810741SFei.Feng@Sun.COM (void) memset(addr_4, 0, 6);
283910741SFei.Feng@Sun.COM bcopy(m->b_rptr + sizeof (struct ieee80211_frame), txbuf + 32, *pfwlen);
284010741SFei.Feng@Sun.COM pktlen += 8;
284110741SFei.Feng@Sun.COM
284210741SFei.Feng@Sun.COM (void) ddi_dma_sync(bf->txbuf_dma.dma_hdl,
284310741SFei.Feng@Sun.COM 0,
284410741SFei.Feng@Sun.COM pktlen,
284510741SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV);
284610741SFei.Feng@Sun.COM
284710741SFei.Feng@Sun.COM ds->QosCtrl = 0;
284810741SFei.Feng@Sun.COM ds->PktLen = (uint16_t)pktlen;
284910741SFei.Feng@Sun.COM ds->PktPtr = bf->bf_baddr;
285010741SFei.Feng@Sun.COM ds->Status = LE_32(EAGLE_TXD_STATUS_FW_OWNED);
285110741SFei.Feng@Sun.COM ds->Format = 0;
285210741SFei.Feng@Sun.COM ds->pad = 0;
285310741SFei.Feng@Sun.COM ds->ack_wcb_addr = 0;
285410741SFei.Feng@Sun.COM ds->TxPriority = 1;
285510741SFei.Feng@Sun.COM
285610741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_TX, "mwl: mwl_send(): "
285710741SFei.Feng@Sun.COM "tx desc Status %x, DataRate %x, TxPriority %x, QosCtrl %x, "
285810741SFei.Feng@Sun.COM "PktLen %x, SapPktInfo %x, Format %x, Pad %x, ack_wcb_addr %x\n",
285910741SFei.Feng@Sun.COM ds->Status, ds->DataRate, ds->TxPriority, ds->QosCtrl, ds->PktLen,
286010741SFei.Feng@Sun.COM ds->SapPktInfo, ds->Format, ds->pad, ds->ack_wcb_addr);
286110741SFei.Feng@Sun.COM
286210741SFei.Feng@Sun.COM (void) ddi_dma_sync(ring->txdesc_dma.dma_hdl,
286310741SFei.Feng@Sun.COM ring->cur * sizeof (struct mwl_txdesc),
286410741SFei.Feng@Sun.COM sizeof (struct mwl_txdesc),
286510741SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV);
286610741SFei.Feng@Sun.COM
286710741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_TX, "mwl: mwl_send(): "
286810741SFei.Feng@Sun.COM "pktlen = %u, slot = %u, queued = %x\n",
286910741SFei.Feng@Sun.COM mblen, ring->cur, ring->queued);
287010741SFei.Feng@Sun.COM
287110741SFei.Feng@Sun.COM ring->queued++;
287210741SFei.Feng@Sun.COM ring->cur = (ring->cur + 1) % MWL_TX_RING_COUNT;
287310741SFei.Feng@Sun.COM
287410741SFei.Feng@Sun.COM /*
287510741SFei.Feng@Sun.COM * NB: We don't need to lock against tx done because
287610741SFei.Feng@Sun.COM * this just prods the firmware to check the transmit
287710741SFei.Feng@Sun.COM * descriptors. The firmware will also start fetching
287810741SFei.Feng@Sun.COM * descriptors by itself if it notices new ones are
287910741SFei.Feng@Sun.COM * present when it goes to deliver a tx done interrupt
288010741SFei.Feng@Sun.COM * to the host. So if we race with tx done processing
288110741SFei.Feng@Sun.COM * it's ok. Delivering the kick here rather than in
288210741SFei.Feng@Sun.COM * mwl_tx_start is an optimization to avoid poking the
288310741SFei.Feng@Sun.COM * firmware for each packet.
288410741SFei.Feng@Sun.COM *
288510741SFei.Feng@Sun.COM * NB: the queue id isn't used so 0 is ok.
288610741SFei.Feng@Sun.COM */
288710741SFei.Feng@Sun.COM mwl_hal_txstart(sc, 0);
288810741SFei.Feng@Sun.COM
288910741SFei.Feng@Sun.COM ic->ic_stats.is_tx_frags++;
289010741SFei.Feng@Sun.COM ic->ic_stats.is_tx_bytes += pktlen;
289110741SFei.Feng@Sun.COM
289210741SFei.Feng@Sun.COM fail3:
289310741SFei.Feng@Sun.COM ieee80211_free_node(ni);
289410741SFei.Feng@Sun.COM fail2:
289510741SFei.Feng@Sun.COM freemsg(m);
289610741SFei.Feng@Sun.COM fail1:
289710741SFei.Feng@Sun.COM if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA ||
289810741SFei.Feng@Sun.COM err == DDI_SUCCESS)
289910741SFei.Feng@Sun.COM freemsg(mp);
290010741SFei.Feng@Sun.COM MWL_TXUNLOCK(sc);
290110741SFei.Feng@Sun.COM return (err);
290210741SFei.Feng@Sun.COM }
290310741SFei.Feng@Sun.COM
290410741SFei.Feng@Sun.COM /*
290510741SFei.Feng@Sun.COM * This function is called periodically (every 200ms) during scanning to
290610741SFei.Feng@Sun.COM * switch from one channel to another.
290710741SFei.Feng@Sun.COM */
290810741SFei.Feng@Sun.COM static void
mwl_next_scan(void * arg)290910741SFei.Feng@Sun.COM mwl_next_scan(void *arg)
291010741SFei.Feng@Sun.COM {
291110741SFei.Feng@Sun.COM struct mwl_softc *sc = (struct mwl_softc *)arg;
291210741SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
291310741SFei.Feng@Sun.COM
291410741SFei.Feng@Sun.COM if (ic->ic_state == IEEE80211_S_SCAN)
291510741SFei.Feng@Sun.COM (void) ieee80211_next_scan(ic);
291610741SFei.Feng@Sun.COM
291710741SFei.Feng@Sun.COM sc->sc_scan_id = 0;
291810741SFei.Feng@Sun.COM }
291910741SFei.Feng@Sun.COM
292010741SFei.Feng@Sun.COM /*
292110741SFei.Feng@Sun.COM * Convert a legacy rate set to a firmware bitmask.
292210741SFei.Feng@Sun.COM */
292310741SFei.Feng@Sun.COM static uint32_t
get_rate_bitmap(const struct ieee80211_rateset * rs)292410741SFei.Feng@Sun.COM get_rate_bitmap(const struct ieee80211_rateset *rs)
292510741SFei.Feng@Sun.COM {
292610741SFei.Feng@Sun.COM uint32_t rates;
292710741SFei.Feng@Sun.COM int i;
292810741SFei.Feng@Sun.COM
292910741SFei.Feng@Sun.COM rates = 0;
293010741SFei.Feng@Sun.COM for (i = 0; i < rs->ir_nrates; i++)
293110741SFei.Feng@Sun.COM switch (rs->ir_rates[i] & IEEE80211_RATE_VAL) {
293210741SFei.Feng@Sun.COM case 2: rates |= 0x001; break;
293310741SFei.Feng@Sun.COM case 4: rates |= 0x002; break;
293410741SFei.Feng@Sun.COM case 11: rates |= 0x004; break;
293510741SFei.Feng@Sun.COM case 22: rates |= 0x008; break;
293610741SFei.Feng@Sun.COM case 44: rates |= 0x010; break;
293710741SFei.Feng@Sun.COM case 12: rates |= 0x020; break;
293810741SFei.Feng@Sun.COM case 18: rates |= 0x040; break;
293910741SFei.Feng@Sun.COM case 24: rates |= 0x080; break;
294010741SFei.Feng@Sun.COM case 36: rates |= 0x100; break;
294110741SFei.Feng@Sun.COM case 48: rates |= 0x200; break;
294210741SFei.Feng@Sun.COM case 72: rates |= 0x400; break;
294310741SFei.Feng@Sun.COM case 96: rates |= 0x800; break;
294410741SFei.Feng@Sun.COM case 108: rates |= 0x1000; break;
294510741SFei.Feng@Sun.COM }
294610741SFei.Feng@Sun.COM return (rates);
294710741SFei.Feng@Sun.COM }
294810741SFei.Feng@Sun.COM
294910741SFei.Feng@Sun.COM /*
295010741SFei.Feng@Sun.COM * Craft station database entry for station.
295110741SFei.Feng@Sun.COM * NB: use host byte order here, the hal handles byte swapping.
295210741SFei.Feng@Sun.COM */
295310741SFei.Feng@Sun.COM static MWL_HAL_PEERINFO *
mkpeerinfo(MWL_HAL_PEERINFO * pi,const struct ieee80211_node * ni)295410741SFei.Feng@Sun.COM mkpeerinfo(MWL_HAL_PEERINFO *pi, const struct ieee80211_node *ni)
295510741SFei.Feng@Sun.COM {
295610747SFei.Feng@Sun.COM (void) memset(pi, 0, sizeof (*pi));
295710741SFei.Feng@Sun.COM pi->LegacyRateBitMap = get_rate_bitmap(&ni->in_rates);
295810741SFei.Feng@Sun.COM pi->CapInfo = ni->in_capinfo;
295910741SFei.Feng@Sun.COM return (pi);
296010741SFei.Feng@Sun.COM }
296110741SFei.Feng@Sun.COM
296210741SFei.Feng@Sun.COM static int
mwl_newstate(struct ieee80211com * ic,enum ieee80211_state nstate,int arg)296310741SFei.Feng@Sun.COM mwl_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
296410741SFei.Feng@Sun.COM {
296510741SFei.Feng@Sun.COM struct mwl_softc *sc = (struct mwl_softc *)ic;
296610741SFei.Feng@Sun.COM enum ieee80211_state ostate;
296710741SFei.Feng@Sun.COM struct ieee80211_channel *ic_chan;
296810741SFei.Feng@Sun.COM struct ieee80211_node *ni = NULL;
296910741SFei.Feng@Sun.COM MWL_HAL_PEERINFO pi;
297010741SFei.Feng@Sun.COM uint32_t chan;
297110741SFei.Feng@Sun.COM
297210741SFei.Feng@Sun.COM if (sc->sc_scan_id != 0) {
297310741SFei.Feng@Sun.COM (void) untimeout(sc->sc_scan_id);
297410741SFei.Feng@Sun.COM sc->sc_scan_id = 0;
297510741SFei.Feng@Sun.COM }
297610741SFei.Feng@Sun.COM
297710741SFei.Feng@Sun.COM MWL_GLOCK(sc);
297810741SFei.Feng@Sun.COM
297910741SFei.Feng@Sun.COM ostate = ic->ic_state;
298010741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_MSG, "mwl: mwl_newstate(): "
298110741SFei.Feng@Sun.COM "ostate %x -> nstate %x\n",
298210741SFei.Feng@Sun.COM ostate, nstate);
298310741SFei.Feng@Sun.COM
298410741SFei.Feng@Sun.COM switch (nstate) {
298510741SFei.Feng@Sun.COM case IEEE80211_S_INIT:
298610741SFei.Feng@Sun.COM break;
298710741SFei.Feng@Sun.COM case IEEE80211_S_SCAN:
298810741SFei.Feng@Sun.COM if (ostate != IEEE80211_S_INIT) {
298910741SFei.Feng@Sun.COM ic_chan = ic->ic_curchan;
299010741SFei.Feng@Sun.COM chan = ieee80211_chan2ieee(ic, ic_chan);
299110741SFei.Feng@Sun.COM if (chan != 0 && chan != IEEE80211_CHAN_ANY) {
299210741SFei.Feng@Sun.COM sc->sc_cur_chan =
299310741SFei.Feng@Sun.COM &sc->sc_channels[3 * chan - 2];
299410741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_MSG, "mwl: mwl_newstate(): "
299510741SFei.Feng@Sun.COM "chan num is %u, sc chan is %u\n",
299610741SFei.Feng@Sun.COM chan, sc->sc_cur_chan->ic_ieee);
299710741SFei.Feng@Sun.COM (void) mwl_chan_set(sc, sc->sc_cur_chan);
299810741SFei.Feng@Sun.COM }
299910741SFei.Feng@Sun.COM }
300010741SFei.Feng@Sun.COM sc->sc_scan_id = timeout(mwl_next_scan, (void *)sc,
300110741SFei.Feng@Sun.COM drv_usectohz(250000));
300210741SFei.Feng@Sun.COM break;
300310741SFei.Feng@Sun.COM case IEEE80211_S_AUTH:
300410741SFei.Feng@Sun.COM ic_chan = ic->ic_curchan;
300510741SFei.Feng@Sun.COM chan = ieee80211_chan2ieee(ic, ic_chan);
300610741SFei.Feng@Sun.COM sc->sc_cur_chan = &sc->sc_channels[3 * chan - 2];
300710741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_MSG, "mwl: mwl_newstate(): "
300810741SFei.Feng@Sun.COM "chan num is %u, sc chan is %u\n",
300910741SFei.Feng@Sun.COM chan, sc->sc_cur_chan->ic_ieee);
301010741SFei.Feng@Sun.COM (void) mwl_chan_set(sc, sc->sc_cur_chan);
301110741SFei.Feng@Sun.COM ni = ic->ic_bss;
301210741SFei.Feng@Sun.COM (void) mwl_hal_newstation(sc, ic->ic_macaddr, 0, 0, NULL, 0, 0);
301310741SFei.Feng@Sun.COM mwl_setanywepkey(ic, ni->in_macaddr);
301410741SFei.Feng@Sun.COM break;
301510741SFei.Feng@Sun.COM case IEEE80211_S_ASSOC:
301610741SFei.Feng@Sun.COM break;
301710741SFei.Feng@Sun.COM case IEEE80211_S_RUN:
301810741SFei.Feng@Sun.COM ni = ic->ic_bss;
301910741SFei.Feng@Sun.COM (void) mwl_hal_newstation(sc,
302010741SFei.Feng@Sun.COM ic->ic_macaddr, 0, 0, mkpeerinfo(&pi, ni), 0, 0);
302110741SFei.Feng@Sun.COM mwl_setglobalkeys(ic);
302210741SFei.Feng@Sun.COM (void) mwl_hal_setassocid(sc,
302310741SFei.Feng@Sun.COM ic->ic_bss->in_bssid, ic->ic_bss->in_associd);
302410741SFei.Feng@Sun.COM (void) mwl_setrates(ic);
302510741SFei.Feng@Sun.COM (void) mwl_hal_setrtsthreshold(sc, ic->ic_rtsthreshold);
302610741SFei.Feng@Sun.COM (void) mwl_hal_setcsmode(sc, CSMODE_AUTO_ENA);
302710741SFei.Feng@Sun.COM break;
302810741SFei.Feng@Sun.COM default:
302910741SFei.Feng@Sun.COM break;
303010741SFei.Feng@Sun.COM }
303110741SFei.Feng@Sun.COM
303210741SFei.Feng@Sun.COM MWL_GUNLOCK(sc);
303310741SFei.Feng@Sun.COM
303410741SFei.Feng@Sun.COM return (sc->sc_newstate(ic, nstate, arg));
303510741SFei.Feng@Sun.COM }
303610741SFei.Feng@Sun.COM
303710741SFei.Feng@Sun.COM /*
303810741SFei.Feng@Sun.COM * Set the interrupt mask.
303910741SFei.Feng@Sun.COM */
304010741SFei.Feng@Sun.COM static void
mwl_hal_intrset(struct mwl_softc * sc,uint32_t mask)304110741SFei.Feng@Sun.COM mwl_hal_intrset(struct mwl_softc *sc, uint32_t mask)
304210741SFei.Feng@Sun.COM {
304310741SFei.Feng@Sun.COM mwl_ctl_write4(sc, MACREG_REG_A2H_INTERRUPT_MASK, 0);
304410741SFei.Feng@Sun.COM (void) mwl_ctl_read4(sc, MACREG_REG_INT_CODE);
304510741SFei.Feng@Sun.COM
304610741SFei.Feng@Sun.COM sc->sc_hal_imask = mask;
304710741SFei.Feng@Sun.COM mwl_ctl_write4(sc, MACREG_REG_A2H_INTERRUPT_MASK, mask);
304810741SFei.Feng@Sun.COM (void) mwl_ctl_read4(sc, MACREG_REG_INT_CODE);
304910741SFei.Feng@Sun.COM }
305010741SFei.Feng@Sun.COM
305110741SFei.Feng@Sun.COM /*
305210741SFei.Feng@Sun.COM * Return the current ISR setting and clear the cause.
305310741SFei.Feng@Sun.COM */
305410741SFei.Feng@Sun.COM static void
mwl_hal_getisr(struct mwl_softc * sc,uint32_t * status)305510741SFei.Feng@Sun.COM mwl_hal_getisr(struct mwl_softc *sc, uint32_t *status)
305610741SFei.Feng@Sun.COM {
305710741SFei.Feng@Sun.COM uint32_t cause;
305810741SFei.Feng@Sun.COM
305910741SFei.Feng@Sun.COM cause = mwl_ctl_read4(sc, MACREG_REG_A2H_INTERRUPT_CAUSE);
306010741SFei.Feng@Sun.COM if (cause == 0xffffffff) { /* card removed */
306110741SFei.Feng@Sun.COM cause = 0;
306210741SFei.Feng@Sun.COM } else if (cause != 0) {
306310741SFei.Feng@Sun.COM /* clear cause bits */
306410741SFei.Feng@Sun.COM mwl_ctl_write4(sc, MACREG_REG_A2H_INTERRUPT_CAUSE,
306510741SFei.Feng@Sun.COM cause & ~sc->sc_hal_imask);
306610741SFei.Feng@Sun.COM (void) mwl_ctl_read4(sc, MACREG_REG_INT_CODE);
306710741SFei.Feng@Sun.COM cause &= sc->sc_hal_imask;
306810741SFei.Feng@Sun.COM }
306910741SFei.Feng@Sun.COM *status = cause;
307010741SFei.Feng@Sun.COM }
307110741SFei.Feng@Sun.COM
307210741SFei.Feng@Sun.COM static void
mwl_tx_intr(struct mwl_softc * sc)307310741SFei.Feng@Sun.COM mwl_tx_intr(struct mwl_softc *sc)
307410741SFei.Feng@Sun.COM {
307510741SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
307610741SFei.Feng@Sun.COM struct mwl_tx_ring *ring;
307710741SFei.Feng@Sun.COM struct mwl_txdesc *ds;
307810741SFei.Feng@Sun.COM
307910741SFei.Feng@Sun.COM uint32_t status;
308010741SFei.Feng@Sun.COM
308110741SFei.Feng@Sun.COM MWL_TXLOCK(sc);
308210741SFei.Feng@Sun.COM
308310741SFei.Feng@Sun.COM ring = &sc->sc_txring[1];
308410741SFei.Feng@Sun.COM
308510741SFei.Feng@Sun.COM if (!(ring->queued)) {
308610741SFei.Feng@Sun.COM MWL_TXUNLOCK(sc);
308710741SFei.Feng@Sun.COM return;
308810741SFei.Feng@Sun.COM }
308910741SFei.Feng@Sun.COM
309010741SFei.Feng@Sun.COM (void) ddi_dma_sync(ring->txdesc_dma.dma_hdl,
309110741SFei.Feng@Sun.COM 0,
309210741SFei.Feng@Sun.COM ring->txdesc_dma.alength,
309310741SFei.Feng@Sun.COM DDI_DMA_SYNC_FORCPU);
309410741SFei.Feng@Sun.COM
309510741SFei.Feng@Sun.COM for (;;) {
309610741SFei.Feng@Sun.COM ds = &ring->desc[ring->next];
309710741SFei.Feng@Sun.COM
309810741SFei.Feng@Sun.COM status = LE_32(ds->Status);
309910741SFei.Feng@Sun.COM
310010741SFei.Feng@Sun.COM if (status & LE_32(EAGLE_TXD_STATUS_FW_OWNED)) {
310110741SFei.Feng@Sun.COM break;
310210741SFei.Feng@Sun.COM }
310310741SFei.Feng@Sun.COM
310410741SFei.Feng@Sun.COM if (status == LE_32(EAGLE_TXD_STATUS_IDLE)) {
310510741SFei.Feng@Sun.COM break;
310610741SFei.Feng@Sun.COM }
310710741SFei.Feng@Sun.COM
310810741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_TX, "mwl: mwl_tx_intr(): "
310910741SFei.Feng@Sun.COM "recv tx desc status %x, datarate %x, txpriority %x, "
311010741SFei.Feng@Sun.COM "QosCtrl %x, pktLen %x, SapPktInfo %x, Format %x, "
311110741SFei.Feng@Sun.COM "pad %x, ack_wcb_addr %x\n",
311210741SFei.Feng@Sun.COM ds->Status, ds->DataRate, ds->TxPriority,
311310741SFei.Feng@Sun.COM ds->QosCtrl, ds->PktLen, ds->SapPktInfo,
311410741SFei.Feng@Sun.COM ds->Format, ds->pad, ds->ack_wcb_addr);
311510741SFei.Feng@Sun.COM
311610741SFei.Feng@Sun.COM /* descriptor is no longer valid */
311710741SFei.Feng@Sun.COM ds->Status = LE_32(EAGLE_TXD_STATUS_IDLE);
311810741SFei.Feng@Sun.COM
311910741SFei.Feng@Sun.COM (void) ddi_dma_sync(ring->txdesc_dma.dma_hdl,
312010741SFei.Feng@Sun.COM ring->next * sizeof (struct mwl_txdesc),
312110741SFei.Feng@Sun.COM sizeof (struct mwl_txdesc),
312210741SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV);
312310741SFei.Feng@Sun.COM
312410741SFei.Feng@Sun.COM ring->queued--;
312510741SFei.Feng@Sun.COM ring->next = (ring->next + 1) % MWL_TX_RING_COUNT;
312610741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_TX, "mwl: mwl_tx_intr(): "
312710741SFei.Feng@Sun.COM " tx done idx=%u, queued= %d\n",
312810741SFei.Feng@Sun.COM ring->next, ring->queued);
312910741SFei.Feng@Sun.COM
313010741SFei.Feng@Sun.COM if (sc->sc_need_sched &&
313110741SFei.Feng@Sun.COM (ring->queued < MWL_TX_RING_COUNT)) {
313210741SFei.Feng@Sun.COM sc->sc_need_sched = 0;
313310741SFei.Feng@Sun.COM mac_tx_update(ic->ic_mach);
313410741SFei.Feng@Sun.COM }
313510741SFei.Feng@Sun.COM
313610741SFei.Feng@Sun.COM }
313710741SFei.Feng@Sun.COM
313810741SFei.Feng@Sun.COM MWL_TXUNLOCK(sc);
313910741SFei.Feng@Sun.COM }
314010741SFei.Feng@Sun.COM
314110741SFei.Feng@Sun.COM /*
314210741SFei.Feng@Sun.COM * Convert hardware signal strength to rssi. The value
314310741SFei.Feng@Sun.COM * provided by the device has the noise floor added in;
314410741SFei.Feng@Sun.COM * we need to compensate for this but we don't have that
314510741SFei.Feng@Sun.COM * so we use a fixed value.
314610741SFei.Feng@Sun.COM *
314710741SFei.Feng@Sun.COM * The offset of 8 is good for both 2.4 and 5GHz. The LNA
314810741SFei.Feng@Sun.COM * offset is already set as part of the initial gain. This
314910741SFei.Feng@Sun.COM * will give at least +/- 3dB for 2.4GHz and +/- 5dB for 5GHz.
315010741SFei.Feng@Sun.COM */
315110741SFei.Feng@Sun.COM static int
cvtrssi(uint8_t ssi)315210741SFei.Feng@Sun.COM cvtrssi(uint8_t ssi)
315310741SFei.Feng@Sun.COM {
315410741SFei.Feng@Sun.COM int rssi = (int)ssi + 8;
315510741SFei.Feng@Sun.COM /* XXX hack guess until we have a real noise floor */
315610741SFei.Feng@Sun.COM rssi = 2 * (87 - rssi); /* NB: .5 dBm units */
315710741SFei.Feng@Sun.COM return (rssi < 0 ? 0 : rssi > 127 ? 127 : rssi);
315810741SFei.Feng@Sun.COM }
315910741SFei.Feng@Sun.COM
316010741SFei.Feng@Sun.COM static void
mwl_rx_intr(struct mwl_softc * sc)316110741SFei.Feng@Sun.COM mwl_rx_intr(struct mwl_softc *sc)
316210741SFei.Feng@Sun.COM {
316310741SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
316410741SFei.Feng@Sun.COM struct mwl_rx_ring *ring;
316510741SFei.Feng@Sun.COM struct ieee80211_node *ni;
316610741SFei.Feng@Sun.COM struct ieee80211_frame *wh;
316710741SFei.Feng@Sun.COM
316810741SFei.Feng@Sun.COM struct mwl_rxbuf *bf;
316910741SFei.Feng@Sun.COM struct mwl_rxdesc *ds;
317010741SFei.Feng@Sun.COM mblk_t *mp0;
317110741SFei.Feng@Sun.COM
317210741SFei.Feng@Sun.COM int ntodo, len, rssi;
317310741SFei.Feng@Sun.COM uint8_t *data, status;
317410741SFei.Feng@Sun.COM
317510741SFei.Feng@Sun.COM MWL_RXLOCK(sc);
317610741SFei.Feng@Sun.COM
317710741SFei.Feng@Sun.COM ring = &sc->sc_rxring;
317810741SFei.Feng@Sun.COM for (ntodo = MWL_RX_RING_COUNT; ntodo > 0; ntodo--) {
317910741SFei.Feng@Sun.COM bf = &ring->buf[ring->cur];
318010741SFei.Feng@Sun.COM ds = bf->bf_desc;
318110741SFei.Feng@Sun.COM data = bf->bf_mem;
318210741SFei.Feng@Sun.COM
318310741SFei.Feng@Sun.COM (void) ddi_dma_sync(ring->rxdesc_dma.dma_hdl,
318410741SFei.Feng@Sun.COM ring->cur * sizeof (struct mwl_rxdesc),
318510741SFei.Feng@Sun.COM sizeof (struct mwl_rxdesc),
318610741SFei.Feng@Sun.COM DDI_DMA_SYNC_FORCPU);
318710741SFei.Feng@Sun.COM
318810741SFei.Feng@Sun.COM if (ds->RxControl != EAGLE_RXD_CTRL_DMA_OWN)
318910741SFei.Feng@Sun.COM break;
319010741SFei.Feng@Sun.COM
319110741SFei.Feng@Sun.COM status = ds->Status;
319210741SFei.Feng@Sun.COM if (status & EAGLE_RXD_STATUS_DECRYPT_ERR_MASK) {
319310741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_CRYPTO, "mwl: mwl_rx_intr(): "
319410741SFei.Feng@Sun.COM "rx decrypt error\n");
319510741SFei.Feng@Sun.COM sc->sc_rx_err++;
319610741SFei.Feng@Sun.COM }
319710741SFei.Feng@Sun.COM
319810741SFei.Feng@Sun.COM /*
319910741SFei.Feng@Sun.COM * Sync the data buffer.
320010741SFei.Feng@Sun.COM */
320110741SFei.Feng@Sun.COM len = LE_16(ds->PktLen);
320210741SFei.Feng@Sun.COM
320310741SFei.Feng@Sun.COM (void) ddi_dma_sync(bf->rxbuf_dma.dma_hdl,
320410741SFei.Feng@Sun.COM 0,
320510741SFei.Feng@Sun.COM bf->rxbuf_dma.alength,
320610741SFei.Feng@Sun.COM DDI_DMA_SYNC_FORCPU);
320710741SFei.Feng@Sun.COM
320810741SFei.Feng@Sun.COM if (len < 32 || len > sc->sc_dmabuf_size) {
320910741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_RX, "mwl: mwl_rx_intr(): "
321010741SFei.Feng@Sun.COM "packet len error %d\n", len);
321110741SFei.Feng@Sun.COM sc->sc_rx_err++;
321210741SFei.Feng@Sun.COM goto rxnext;
321310741SFei.Feng@Sun.COM }
321410741SFei.Feng@Sun.COM
321510741SFei.Feng@Sun.COM mp0 = allocb(sc->sc_dmabuf_size, BPRI_MED);
321610741SFei.Feng@Sun.COM if (mp0 == NULL) {
321710741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_RX, "mwl: mwl_rx_intr(): "
321810741SFei.Feng@Sun.COM "alloc mblk error\n");
321910741SFei.Feng@Sun.COM sc->sc_rx_nobuf++;
322010741SFei.Feng@Sun.COM goto rxnext;
322110741SFei.Feng@Sun.COM }
322210741SFei.Feng@Sun.COM bcopy(data+ 2, mp0->b_wptr, 24);
322310741SFei.Feng@Sun.COM mp0->b_wptr += 24;
322410741SFei.Feng@Sun.COM bcopy(data + 32, mp0->b_wptr, len - 32);
322510741SFei.Feng@Sun.COM mp0->b_wptr += (len - 32);
322610741SFei.Feng@Sun.COM
322710741SFei.Feng@Sun.COM wh = (struct ieee80211_frame *)mp0->b_rptr;
322810741SFei.Feng@Sun.COM if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
322910741SFei.Feng@Sun.COM IEEE80211_FC0_TYPE_CTL) {
323010741SFei.Feng@Sun.COM freemsg(mp0);
323110741SFei.Feng@Sun.COM goto rxnext;
323210741SFei.Feng@Sun.COM }
323310741SFei.Feng@Sun.COM
323410741SFei.Feng@Sun.COM /*
323510741SFei.Feng@Sun.COM * The f/w strips WEP header but doesn't clear
323610741SFei.Feng@Sun.COM * the WEP bit; mark the packet with M_WEP so
323710741SFei.Feng@Sun.COM * net80211 will treat the data as decrypted.
323810741SFei.Feng@Sun.COM * While here also clear the PWR_MGT bit since
323910741SFei.Feng@Sun.COM * power save is handled by the firmware and
324010741SFei.Feng@Sun.COM * passing this up will potentially cause the
324110741SFei.Feng@Sun.COM * upper layer to put a station in power save
324210741SFei.Feng@Sun.COM * (except when configured with MWL_HOST_PS_SUPPORT).
324310741SFei.Feng@Sun.COM */
324410741SFei.Feng@Sun.COM #ifdef MWL_HOST_PS_SUPPORT
324510741SFei.Feng@Sun.COM wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
324610741SFei.Feng@Sun.COM #else
324710741SFei.Feng@Sun.COM wh->i_fc[1] &= ~(IEEE80211_FC1_WEP | IEEE80211_FC1_PWR_MGT);
324810741SFei.Feng@Sun.COM #endif
324910741SFei.Feng@Sun.COM
325010741SFei.Feng@Sun.COM /* calculate rssi early so we can re-use for each aggregate */
325110741SFei.Feng@Sun.COM rssi = cvtrssi(ds->RSSI);
325210741SFei.Feng@Sun.COM
325310741SFei.Feng@Sun.COM ni = ieee80211_find_rxnode(ic, wh);
325410741SFei.Feng@Sun.COM
325510741SFei.Feng@Sun.COM /* send the frame to the 802.11 layer */
325610741SFei.Feng@Sun.COM (void) ieee80211_input(ic, mp0, ni, rssi, 0);
325710741SFei.Feng@Sun.COM ieee80211_free_node(ni);
325810741SFei.Feng@Sun.COM rxnext:
325910741SFei.Feng@Sun.COM /*
326010741SFei.Feng@Sun.COM * Setup descriptor.
326110741SFei.Feng@Sun.COM */
326210741SFei.Feng@Sun.COM ds->QosCtrl = 0;
326310741SFei.Feng@Sun.COM ds->RSSI = 0;
326410741SFei.Feng@Sun.COM ds->Status = EAGLE_RXD_STATUS_IDLE;
326510741SFei.Feng@Sun.COM ds->Channel = 0;
326610741SFei.Feng@Sun.COM ds->PktLen = LE_16(MWL_AGGR_SIZE);
326710741SFei.Feng@Sun.COM ds->SQ2 = 0;
326810741SFei.Feng@Sun.COM ds->pPhysBuffData = bf->bf_baddr;
326910741SFei.Feng@Sun.COM /* NB: don't touch pPhysNext, set once */
327010741SFei.Feng@Sun.COM ds->RxControl = EAGLE_RXD_CTRL_DRIVER_OWN;
327110741SFei.Feng@Sun.COM
327210741SFei.Feng@Sun.COM (void) ddi_dma_sync(ring->rxdesc_dma.dma_hdl,
327310741SFei.Feng@Sun.COM ring->cur * sizeof (struct mwl_rxdesc),
327410741SFei.Feng@Sun.COM sizeof (struct mwl_rxdesc),
327510741SFei.Feng@Sun.COM DDI_DMA_SYNC_FORDEV);
327610741SFei.Feng@Sun.COM
327710741SFei.Feng@Sun.COM /* NB: ignore ENOMEM so we process more descriptors */
327810741SFei.Feng@Sun.COM ring->cur = (ring->cur + 1) % MWL_RX_RING_COUNT;
327910741SFei.Feng@Sun.COM }
328010741SFei.Feng@Sun.COM
328110741SFei.Feng@Sun.COM MWL_RXUNLOCK(sc);
328210741SFei.Feng@Sun.COM }
328310741SFei.Feng@Sun.COM
328410741SFei.Feng@Sun.COM /*ARGSUSED*/
328510741SFei.Feng@Sun.COM static uint_t
mwl_softintr(caddr_t data,caddr_t unused)328610741SFei.Feng@Sun.COM mwl_softintr(caddr_t data, caddr_t unused)
328710741SFei.Feng@Sun.COM {
328810741SFei.Feng@Sun.COM struct mwl_softc *sc = (struct mwl_softc *)data;
328910741SFei.Feng@Sun.COM
329010741SFei.Feng@Sun.COM /*
329110741SFei.Feng@Sun.COM * Check if the soft interrupt is triggered by another
329210741SFei.Feng@Sun.COM * driver at the same level.
329310741SFei.Feng@Sun.COM */
329410741SFei.Feng@Sun.COM MWL_GLOCK(sc);
329510741SFei.Feng@Sun.COM if (sc->sc_rx_pend) {
329610741SFei.Feng@Sun.COM sc->sc_rx_pend = 0;
329710741SFei.Feng@Sun.COM MWL_GUNLOCK(sc);
329810741SFei.Feng@Sun.COM mwl_rx_intr(sc);
329910741SFei.Feng@Sun.COM return (DDI_INTR_CLAIMED);
330010741SFei.Feng@Sun.COM }
330110741SFei.Feng@Sun.COM MWL_GUNLOCK(sc);
330210741SFei.Feng@Sun.COM
330310741SFei.Feng@Sun.COM return (DDI_INTR_UNCLAIMED);
330410741SFei.Feng@Sun.COM }
330510741SFei.Feng@Sun.COM
330610741SFei.Feng@Sun.COM /*ARGSUSED*/
330710741SFei.Feng@Sun.COM static uint_t
mwl_intr(caddr_t arg,caddr_t unused)330810741SFei.Feng@Sun.COM mwl_intr(caddr_t arg, caddr_t unused)
330910741SFei.Feng@Sun.COM {
331010741SFei.Feng@Sun.COM struct mwl_softc *sc = (struct mwl_softc *)arg;
331110741SFei.Feng@Sun.COM uint32_t status;
331210741SFei.Feng@Sun.COM
331310741SFei.Feng@Sun.COM MWL_GLOCK(sc);
331410741SFei.Feng@Sun.COM
331510741SFei.Feng@Sun.COM if (!MWL_IS_RUNNING(sc) || MWL_IS_SUSPEND(sc)) {
331610741SFei.Feng@Sun.COM MWL_GUNLOCK(sc);
331710741SFei.Feng@Sun.COM return (DDI_INTR_UNCLAIMED);
331810741SFei.Feng@Sun.COM }
331910741SFei.Feng@Sun.COM
332010741SFei.Feng@Sun.COM /*
332110741SFei.Feng@Sun.COM * Figure out the reason(s) for the interrupt.
332210741SFei.Feng@Sun.COM */
332310741SFei.Feng@Sun.COM mwl_hal_getisr(sc, &status); /* NB: clears ISR too */
332410741SFei.Feng@Sun.COM if (status == 0) {
332510741SFei.Feng@Sun.COM MWL_GUNLOCK(sc);
332610741SFei.Feng@Sun.COM return (DDI_INTR_UNCLAIMED);
332710741SFei.Feng@Sun.COM }
332810741SFei.Feng@Sun.COM
332910741SFei.Feng@Sun.COM if (status & MACREG_A2HRIC_BIT_RX_RDY) {
333010741SFei.Feng@Sun.COM sc->sc_rx_pend = 1;
333110741SFei.Feng@Sun.COM (void) ddi_intr_trigger_softint(sc->sc_softintr_hdl, NULL);
333210741SFei.Feng@Sun.COM }
333310741SFei.Feng@Sun.COM if (status & MACREG_A2HRIC_BIT_TX_DONE) {
333410741SFei.Feng@Sun.COM mwl_tx_intr(sc);
333510741SFei.Feng@Sun.COM }
333610741SFei.Feng@Sun.COM if (status & MACREG_A2HRIC_BIT_BA_WATCHDOG) {
333710741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_INTR, "mwl: mwl_intr(): "
333810741SFei.Feng@Sun.COM "ba watchdog\n");
333910741SFei.Feng@Sun.COM }
334010741SFei.Feng@Sun.COM if (status & MACREG_A2HRIC_BIT_OPC_DONE) {
334110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_INTR, "mwl: mwl_intr(): "
334210741SFei.Feng@Sun.COM "opc done\n");
334310741SFei.Feng@Sun.COM }
334410741SFei.Feng@Sun.COM if (status & MACREG_A2HRIC_BIT_MAC_EVENT) {
334510741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_INTR, "mwl: mwl_intr(): "
334610741SFei.Feng@Sun.COM "mac event\n");
334710741SFei.Feng@Sun.COM }
334810741SFei.Feng@Sun.COM if (status & MACREG_A2HRIC_BIT_ICV_ERROR) {
334910741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_INTR, "mwl: mwl_intr(): "
335010741SFei.Feng@Sun.COM "ICV error\n");
335110741SFei.Feng@Sun.COM }
335210741SFei.Feng@Sun.COM if (status & MACREG_A2HRIC_BIT_QUEUE_EMPTY) {
335310741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_INTR, "mwl: mwl_intr(): "
335410741SFei.Feng@Sun.COM "queue empty\n");
335510741SFei.Feng@Sun.COM }
335610741SFei.Feng@Sun.COM if (status & MACREG_A2HRIC_BIT_QUEUE_FULL) {
335710741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_INTR, "mwl: mwl_intr(): "
335810741SFei.Feng@Sun.COM "queue full\n");
335910741SFei.Feng@Sun.COM }
336010741SFei.Feng@Sun.COM if (status & MACREG_A2HRIC_BIT_RADAR_DETECT) {
336110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_INTR, "mwl: mwl_intr(): "
336210741SFei.Feng@Sun.COM "radar detect\n");
336310741SFei.Feng@Sun.COM }
336410741SFei.Feng@Sun.COM if (status & MACREG_A2HRIC_BIT_CHAN_SWITCH) {
336510741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_INTR, "mwl: mwl_intr(): "
336610741SFei.Feng@Sun.COM "chan switch\n");
336710741SFei.Feng@Sun.COM }
336810741SFei.Feng@Sun.COM
336910741SFei.Feng@Sun.COM MWL_GUNLOCK(sc);
337010741SFei.Feng@Sun.COM
337110741SFei.Feng@Sun.COM return (DDI_INTR_CLAIMED);
337210741SFei.Feng@Sun.COM }
337310741SFei.Feng@Sun.COM
337410741SFei.Feng@Sun.COM static int
mwl_init(struct mwl_softc * sc)337510741SFei.Feng@Sun.COM mwl_init(struct mwl_softc *sc)
337610741SFei.Feng@Sun.COM {
337710741SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
337810741SFei.Feng@Sun.COM int err = 0;
337910741SFei.Feng@Sun.COM
338010741SFei.Feng@Sun.COM mwl_hal_intrset(sc, 0);
338110741SFei.Feng@Sun.COM
338210741SFei.Feng@Sun.COM sc->sc_txantenna = 0; /* h/w default */
338310741SFei.Feng@Sun.COM sc->sc_rxantenna = 0; /* h/w default */
338410741SFei.Feng@Sun.COM
338510741SFei.Feng@Sun.COM err = mwl_hal_setantenna(sc, WL_ANTENNATYPE_RX, sc->sc_rxantenna);
338610741SFei.Feng@Sun.COM if (err != 0) {
338710741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_HW, "mwl: mwl_init(): "
338810741SFei.Feng@Sun.COM "could not set rx antenna\n");
338910741SFei.Feng@Sun.COM goto fail;
339010741SFei.Feng@Sun.COM }
339110741SFei.Feng@Sun.COM
339210741SFei.Feng@Sun.COM err = mwl_hal_setantenna(sc, WL_ANTENNATYPE_TX, sc->sc_txantenna);
339310741SFei.Feng@Sun.COM if (err != 0) {
339410741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_HW, "mwl: init(): "
339510741SFei.Feng@Sun.COM "could not set tx antenna\n");
339610741SFei.Feng@Sun.COM goto fail;
339710741SFei.Feng@Sun.COM }
339810741SFei.Feng@Sun.COM
339910741SFei.Feng@Sun.COM err = mwl_hal_setradio(sc, 1, WL_AUTO_PREAMBLE);
340010741SFei.Feng@Sun.COM if (err != 0) {
340110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_HW, "mwl: init(): "
340210741SFei.Feng@Sun.COM "could not set radio\n");
340310741SFei.Feng@Sun.COM goto fail;
340410741SFei.Feng@Sun.COM }
340510741SFei.Feng@Sun.COM
340610741SFei.Feng@Sun.COM err = mwl_hal_setwmm(sc, (ic->ic_flags & IEEE80211_F_WME) != 0);
340710741SFei.Feng@Sun.COM if (err != 0) {
340810741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_HW, "mwl: init(): "
340910741SFei.Feng@Sun.COM "could not set wme\n");
341010741SFei.Feng@Sun.COM goto fail;
341110741SFei.Feng@Sun.COM }
341210741SFei.Feng@Sun.COM
341310741SFei.Feng@Sun.COM /* select default channel */
341410741SFei.Feng@Sun.COM ic->ic_ibss_chan = &ic->ic_sup_channels[0];
341510741SFei.Feng@Sun.COM ic->ic_curchan = ic->ic_ibss_chan;
341610741SFei.Feng@Sun.COM sc->sc_cur_chan = &sc->sc_channels[1];
341710741SFei.Feng@Sun.COM
341810741SFei.Feng@Sun.COM err = mwl_chan_set(sc, sc->sc_cur_chan);
341910741SFei.Feng@Sun.COM if (err != 0) {
342010741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_HW, "mwl: init(): "
342110741SFei.Feng@Sun.COM "could not set wme\n");
342210741SFei.Feng@Sun.COM goto fail;
342310741SFei.Feng@Sun.COM }
342410741SFei.Feng@Sun.COM
342510741SFei.Feng@Sun.COM err = mwl_hal_setrateadaptmode(sc, 0);
342610741SFei.Feng@Sun.COM if (err != 0) {
342710741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_HW, "mwl: init(): "
342810741SFei.Feng@Sun.COM "could not set rate adapt mode\n");
342910741SFei.Feng@Sun.COM goto fail;
343010741SFei.Feng@Sun.COM }
343110741SFei.Feng@Sun.COM
343210741SFei.Feng@Sun.COM err = mwl_hal_setoptimizationlevel(sc,
343310741SFei.Feng@Sun.COM (ic->ic_flags & IEEE80211_F_BURST) != 0);
343410741SFei.Feng@Sun.COM if (err != 0) {
343510741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_HW, "mwl: init(): "
343610741SFei.Feng@Sun.COM "could not set optimization level\n");
343710741SFei.Feng@Sun.COM goto fail;
343810741SFei.Feng@Sun.COM }
343910741SFei.Feng@Sun.COM
344010741SFei.Feng@Sun.COM err = mwl_hal_setregioncode(sc, mwl_map2regioncode(&sc->sc_regdomain));
344110741SFei.Feng@Sun.COM if (err != 0) {
344210741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_HW, "mwl: init(): "
344310741SFei.Feng@Sun.COM "could not set regioncode\n");
344410741SFei.Feng@Sun.COM goto fail;
344510741SFei.Feng@Sun.COM }
344610741SFei.Feng@Sun.COM
344710741SFei.Feng@Sun.COM err = mwl_startrecv(sc);
344810741SFei.Feng@Sun.COM if (err != 0) {
344910741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_HW, "mwl: init(): "
345010741SFei.Feng@Sun.COM "could not set start recv logic\n");
345110741SFei.Feng@Sun.COM goto fail;
345210741SFei.Feng@Sun.COM }
345310741SFei.Feng@Sun.COM
345410741SFei.Feng@Sun.COM /*
345510741SFei.Feng@Sun.COM * Enable interrupts.
345610741SFei.Feng@Sun.COM */
345710741SFei.Feng@Sun.COM sc->sc_imask = MACREG_A2HRIC_BIT_RX_RDY
345810741SFei.Feng@Sun.COM | MACREG_A2HRIC_BIT_TX_DONE
345910741SFei.Feng@Sun.COM | MACREG_A2HRIC_BIT_OPC_DONE
346010741SFei.Feng@Sun.COM | MACREG_A2HRIC_BIT_ICV_ERROR
346110741SFei.Feng@Sun.COM | MACREG_A2HRIC_BIT_RADAR_DETECT
346210741SFei.Feng@Sun.COM | MACREG_A2HRIC_BIT_CHAN_SWITCH
346310741SFei.Feng@Sun.COM | MACREG_A2HRIC_BIT_BA_WATCHDOG
346410741SFei.Feng@Sun.COM | MACREQ_A2HRIC_BIT_TX_ACK;
346510741SFei.Feng@Sun.COM
346610741SFei.Feng@Sun.COM mwl_hal_intrset(sc, sc->sc_imask);
346710741SFei.Feng@Sun.COM
346810741SFei.Feng@Sun.COM err = mwl_hal_start(sc);
346910741SFei.Feng@Sun.COM if (err != 0) {
347010741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_HW, "mwl: init(): "
347110741SFei.Feng@Sun.COM "could not get hal start\n");
347210741SFei.Feng@Sun.COM goto fail;
347310741SFei.Feng@Sun.COM }
347410741SFei.Feng@Sun.COM
347510741SFei.Feng@Sun.COM err = mwl_hal_setinframode(sc);
347610741SFei.Feng@Sun.COM if (err != 0) {
347710741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_HW, "mwl: init(): "
347810741SFei.Feng@Sun.COM "could not set infra mode\n");
347910741SFei.Feng@Sun.COM goto fail;
348010741SFei.Feng@Sun.COM }
348110741SFei.Feng@Sun.COM
348210741SFei.Feng@Sun.COM fail:
348310741SFei.Feng@Sun.COM return (err);
348410741SFei.Feng@Sun.COM }
348510741SFei.Feng@Sun.COM
348610741SFei.Feng@Sun.COM static int
mwl_resume(struct mwl_softc * sc)348710741SFei.Feng@Sun.COM mwl_resume(struct mwl_softc *sc)
348810741SFei.Feng@Sun.COM {
348910741SFei.Feng@Sun.COM int qid, err = 0;
349010741SFei.Feng@Sun.COM
349110741SFei.Feng@Sun.COM err = mwl_fwload(sc, NULL);
349210741SFei.Feng@Sun.COM if (err != 0) {
349310741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_SR, "mwl: mwl_resume(): "
349410741SFei.Feng@Sun.COM "failed to load fw\n");
349510741SFei.Feng@Sun.COM goto fail;
349610741SFei.Feng@Sun.COM }
349710741SFei.Feng@Sun.COM
349810741SFei.Feng@Sun.COM err = mwl_gethwspecs(sc);
349910741SFei.Feng@Sun.COM if (err != 0) {
350010741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_SR, "mwl: mwl_resume(): "
350110741SFei.Feng@Sun.COM "failed to get hw spec\n");
350210741SFei.Feng@Sun.COM goto fail;
350310741SFei.Feng@Sun.COM }
350410741SFei.Feng@Sun.COM
350510741SFei.Feng@Sun.COM err = mwl_alloc_rx_ring(sc, MWL_RX_RING_COUNT);
350610741SFei.Feng@Sun.COM if (err != 0) {
350710741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_SR, "mwl: mwl_resume(): "
350810741SFei.Feng@Sun.COM "could not alloc cmd dma buffer\n");
350910741SFei.Feng@Sun.COM goto fail;
351010741SFei.Feng@Sun.COM }
351110741SFei.Feng@Sun.COM
351210741SFei.Feng@Sun.COM for (qid = 0; qid < MWL_NUM_TX_QUEUES; qid++) {
351310741SFei.Feng@Sun.COM err = mwl_alloc_tx_ring(sc,
351410741SFei.Feng@Sun.COM &sc->sc_txring[qid], MWL_TX_RING_COUNT);
351510741SFei.Feng@Sun.COM if (err != 0) {
351610741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_SR, "mwl: mwl_resume(): "
351710741SFei.Feng@Sun.COM "could not alloc tx ring %d\n", qid);
351810741SFei.Feng@Sun.COM goto fail;
351910741SFei.Feng@Sun.COM }
352010741SFei.Feng@Sun.COM }
352110741SFei.Feng@Sun.COM
352210741SFei.Feng@Sun.COM err = mwl_setupdma(sc);
352310741SFei.Feng@Sun.COM if (err != 0) {
352410741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_SR, "mwl: mwl_resume(): "
352510741SFei.Feng@Sun.COM "could not setup dma\n");
352610741SFei.Feng@Sun.COM goto fail;
352710741SFei.Feng@Sun.COM }
352810741SFei.Feng@Sun.COM
352910741SFei.Feng@Sun.COM err = mwl_setup_txq(sc);
353010741SFei.Feng@Sun.COM if (err != 0) {
353110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_SR, "mwl: mwl_resume(): "
353210741SFei.Feng@Sun.COM "could not setup txq\n");
353310741SFei.Feng@Sun.COM goto fail;
353410741SFei.Feng@Sun.COM }
353510741SFei.Feng@Sun.COM
353610741SFei.Feng@Sun.COM fail:
353710741SFei.Feng@Sun.COM return (err);
353810741SFei.Feng@Sun.COM }
353910741SFei.Feng@Sun.COM
354010741SFei.Feng@Sun.COM static void
mwl_stop(struct mwl_softc * sc)354110741SFei.Feng@Sun.COM mwl_stop(struct mwl_softc *sc)
354210741SFei.Feng@Sun.COM {
354310741SFei.Feng@Sun.COM int err;
354410741SFei.Feng@Sun.COM
354510741SFei.Feng@Sun.COM /* by pass if it's quiesced */
354610741SFei.Feng@Sun.COM if (!MWL_IS_QUIESCE(sc))
354710741SFei.Feng@Sun.COM MWL_GLOCK(sc);
354810741SFei.Feng@Sun.COM
354910741SFei.Feng@Sun.COM err = mwl_hal_stop(sc);
355010741SFei.Feng@Sun.COM if (err != 0) {
355110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_HW, "mwl: mwl_stop(): "
355210741SFei.Feng@Sun.COM "could not stop hw\n");
355310741SFei.Feng@Sun.COM }
355410741SFei.Feng@Sun.COM
355510741SFei.Feng@Sun.COM /* by pass if it's quiesced */
355610741SFei.Feng@Sun.COM if (!MWL_IS_QUIESCE(sc))
355710741SFei.Feng@Sun.COM MWL_GUNLOCK(sc);
355810741SFei.Feng@Sun.COM }
355910741SFei.Feng@Sun.COM
356010741SFei.Feng@Sun.COM static int
mwl_m_stat(void * arg,uint_t stat,uint64_t * val)356110741SFei.Feng@Sun.COM mwl_m_stat(void *arg, uint_t stat, uint64_t *val)
356210741SFei.Feng@Sun.COM {
356310741SFei.Feng@Sun.COM struct mwl_softc *sc = (struct mwl_softc *)arg;
356410741SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
356510741SFei.Feng@Sun.COM struct ieee80211_node *ni = NULL;
356610741SFei.Feng@Sun.COM struct ieee80211_rateset *rs = NULL;
356710741SFei.Feng@Sun.COM
356810741SFei.Feng@Sun.COM MWL_GLOCK(sc);
356910741SFei.Feng@Sun.COM switch (stat) {
357010741SFei.Feng@Sun.COM case MAC_STAT_IFSPEED:
357110741SFei.Feng@Sun.COM ni = ic->ic_bss;
357210741SFei.Feng@Sun.COM rs = &ni->in_rates;
357310741SFei.Feng@Sun.COM *val = ((ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) ?
357410741SFei.Feng@Sun.COM (rs->ir_rates[ni->in_txrate] & IEEE80211_RATE_VAL)
357510741SFei.Feng@Sun.COM : ic->ic_fixed_rate) / 2 * 1000000;
357610741SFei.Feng@Sun.COM break;
357710741SFei.Feng@Sun.COM case MAC_STAT_NOXMTBUF:
357810741SFei.Feng@Sun.COM *val = sc->sc_tx_nobuf;
357910741SFei.Feng@Sun.COM break;
358010741SFei.Feng@Sun.COM case MAC_STAT_NORCVBUF:
358110741SFei.Feng@Sun.COM *val = sc->sc_rx_nobuf;
358210741SFei.Feng@Sun.COM break;
358310741SFei.Feng@Sun.COM case MAC_STAT_IERRORS:
358410741SFei.Feng@Sun.COM *val = sc->sc_rx_err;
358510741SFei.Feng@Sun.COM break;
358610741SFei.Feng@Sun.COM case MAC_STAT_RBYTES:
358710741SFei.Feng@Sun.COM *val = ic->ic_stats.is_rx_bytes;
358810741SFei.Feng@Sun.COM break;
358910741SFei.Feng@Sun.COM case MAC_STAT_IPACKETS:
359010741SFei.Feng@Sun.COM *val = ic->ic_stats.is_rx_frags;
359110741SFei.Feng@Sun.COM break;
359210741SFei.Feng@Sun.COM case MAC_STAT_OBYTES:
359310741SFei.Feng@Sun.COM *val = ic->ic_stats.is_tx_bytes;
359410741SFei.Feng@Sun.COM break;
359510741SFei.Feng@Sun.COM case MAC_STAT_OPACKETS:
359610741SFei.Feng@Sun.COM *val = ic->ic_stats.is_tx_frags;
359710741SFei.Feng@Sun.COM break;
359810741SFei.Feng@Sun.COM case MAC_STAT_OERRORS:
359910741SFei.Feng@Sun.COM case WIFI_STAT_TX_FAILED:
360010741SFei.Feng@Sun.COM *val = sc->sc_tx_err;
360110741SFei.Feng@Sun.COM break;
360210741SFei.Feng@Sun.COM case WIFI_STAT_TX_RETRANS:
360310741SFei.Feng@Sun.COM *val = sc->sc_tx_retries;
360410741SFei.Feng@Sun.COM break;
360510741SFei.Feng@Sun.COM case WIFI_STAT_FCS_ERRORS:
360610741SFei.Feng@Sun.COM case WIFI_STAT_WEP_ERRORS:
360710741SFei.Feng@Sun.COM case WIFI_STAT_TX_FRAGS:
360810741SFei.Feng@Sun.COM case WIFI_STAT_MCAST_TX:
360910741SFei.Feng@Sun.COM case WIFI_STAT_RTS_SUCCESS:
361010741SFei.Feng@Sun.COM case WIFI_STAT_RTS_FAILURE:
361110741SFei.Feng@Sun.COM case WIFI_STAT_ACK_FAILURE:
361210741SFei.Feng@Sun.COM case WIFI_STAT_RX_FRAGS:
361310741SFei.Feng@Sun.COM case WIFI_STAT_MCAST_RX:
361410741SFei.Feng@Sun.COM case WIFI_STAT_RX_DUPS:
361510741SFei.Feng@Sun.COM MWL_GUNLOCK(sc);
361610741SFei.Feng@Sun.COM return (ieee80211_stat(ic, stat, val));
361710741SFei.Feng@Sun.COM default:
361810741SFei.Feng@Sun.COM MWL_GUNLOCK(sc);
361910741SFei.Feng@Sun.COM return (ENOTSUP);
362010741SFei.Feng@Sun.COM }
362110741SFei.Feng@Sun.COM
362210741SFei.Feng@Sun.COM MWL_GUNLOCK(sc);
362310741SFei.Feng@Sun.COM return (0);
362410741SFei.Feng@Sun.COM }
362510741SFei.Feng@Sun.COM
362610741SFei.Feng@Sun.COM static int
mwl_m_start(void * arg)362710741SFei.Feng@Sun.COM mwl_m_start(void *arg)
362810741SFei.Feng@Sun.COM {
362910741SFei.Feng@Sun.COM struct mwl_softc *sc = (struct mwl_softc *)arg;
363010741SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
363110741SFei.Feng@Sun.COM int err;
363210741SFei.Feng@Sun.COM
363310741SFei.Feng@Sun.COM err = mwl_init(sc);
363410741SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
363510741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_HW, "mwl: mwl_m_start():"
363610741SFei.Feng@Sun.COM "Hardware initialization failed\n");
363710741SFei.Feng@Sun.COM goto fail1;
363810741SFei.Feng@Sun.COM }
363910741SFei.Feng@Sun.COM
364010741SFei.Feng@Sun.COM ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
364110741SFei.Feng@Sun.COM
364210741SFei.Feng@Sun.COM MWL_GLOCK(sc);
364310741SFei.Feng@Sun.COM sc->sc_flags |= MWL_F_RUNNING;
364410741SFei.Feng@Sun.COM MWL_GUNLOCK(sc);
364510741SFei.Feng@Sun.COM
364610741SFei.Feng@Sun.COM return (0);
364710741SFei.Feng@Sun.COM fail1:
364810741SFei.Feng@Sun.COM mwl_stop(sc);
364910741SFei.Feng@Sun.COM return (err);
365010741SFei.Feng@Sun.COM }
365110741SFei.Feng@Sun.COM
365210741SFei.Feng@Sun.COM static void
mwl_m_stop(void * arg)365310741SFei.Feng@Sun.COM mwl_m_stop(void *arg)
365410741SFei.Feng@Sun.COM {
365510741SFei.Feng@Sun.COM struct mwl_softc *sc = (struct mwl_softc *)arg;
365610741SFei.Feng@Sun.COM
365710741SFei.Feng@Sun.COM mwl_stop(sc);
365810741SFei.Feng@Sun.COM
365910741SFei.Feng@Sun.COM ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1);
366010741SFei.Feng@Sun.COM
366110741SFei.Feng@Sun.COM MWL_GLOCK(sc);
366210741SFei.Feng@Sun.COM sc->sc_flags &= ~MWL_F_RUNNING;
366310741SFei.Feng@Sun.COM MWL_GUNLOCK(sc);
366410741SFei.Feng@Sun.COM }
366510741SFei.Feng@Sun.COM
366610741SFei.Feng@Sun.COM /*ARGSUSED*/
366710741SFei.Feng@Sun.COM static int
mwl_m_promisc(void * arg,boolean_t on)366810741SFei.Feng@Sun.COM mwl_m_promisc(void *arg, boolean_t on)
366910741SFei.Feng@Sun.COM {
367010741SFei.Feng@Sun.COM struct mwl_softc *sc = (struct mwl_softc *)arg;
367110741SFei.Feng@Sun.COM int err;
367210741SFei.Feng@Sun.COM
367310741SFei.Feng@Sun.COM err = mwl_hal_setpromisc(sc, on);
367410741SFei.Feng@Sun.COM
367510741SFei.Feng@Sun.COM return (err);
367610741SFei.Feng@Sun.COM }
367710741SFei.Feng@Sun.COM
367810741SFei.Feng@Sun.COM /*ARGSUSED*/
367910741SFei.Feng@Sun.COM static int
mwl_m_multicst(void * arg,boolean_t add,const uint8_t * mca)368010741SFei.Feng@Sun.COM mwl_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
368110741SFei.Feng@Sun.COM {
368210741SFei.Feng@Sun.COM return (ENOTSUP);
368310741SFei.Feng@Sun.COM }
368410741SFei.Feng@Sun.COM
368510741SFei.Feng@Sun.COM /*ARGSUSED*/
368610741SFei.Feng@Sun.COM static int
mwl_m_unicst(void * arg,const uint8_t * macaddr)368710741SFei.Feng@Sun.COM mwl_m_unicst(void *arg, const uint8_t *macaddr)
368810741SFei.Feng@Sun.COM {
368910741SFei.Feng@Sun.COM return (ENOTSUP);
369010741SFei.Feng@Sun.COM }
369110741SFei.Feng@Sun.COM
369210741SFei.Feng@Sun.COM static mblk_t *
mwl_m_tx(void * arg,mblk_t * mp)369310741SFei.Feng@Sun.COM mwl_m_tx(void *arg, mblk_t *mp)
369410741SFei.Feng@Sun.COM {
369510741SFei.Feng@Sun.COM struct mwl_softc *sc = (struct mwl_softc *)arg;
369610741SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
369710741SFei.Feng@Sun.COM mblk_t *next;
369810741SFei.Feng@Sun.COM
369910741SFei.Feng@Sun.COM if (MWL_IS_SUSPEND(sc)) {
370010741SFei.Feng@Sun.COM freemsgchain(mp);
370110741SFei.Feng@Sun.COM return (NULL);
370210741SFei.Feng@Sun.COM }
370310741SFei.Feng@Sun.COM
370410741SFei.Feng@Sun.COM /*
370510741SFei.Feng@Sun.COM * No data frames go out unless we're associated; this
370610741SFei.Feng@Sun.COM * should not happen as the 802.11 layer does not enable
370710741SFei.Feng@Sun.COM * the xmit queue until we enter the RUN state.
370810741SFei.Feng@Sun.COM */
370910741SFei.Feng@Sun.COM if (ic->ic_state != IEEE80211_S_RUN) {
371010741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_TX, "mwl: mwl_m_tx(): "
371110741SFei.Feng@Sun.COM "discard, state %u\n", ic->ic_state);
371210741SFei.Feng@Sun.COM freemsgchain(mp);
371310741SFei.Feng@Sun.COM return (NULL);
371410741SFei.Feng@Sun.COM }
371510741SFei.Feng@Sun.COM
371610741SFei.Feng@Sun.COM while (mp != NULL) {
371710741SFei.Feng@Sun.COM next = mp->b_next;
371810741SFei.Feng@Sun.COM mp->b_next = NULL;
371910741SFei.Feng@Sun.COM if (mwl_send(ic, mp, IEEE80211_FC0_TYPE_DATA) !=
372010741SFei.Feng@Sun.COM DDI_SUCCESS) {
372110741SFei.Feng@Sun.COM mp->b_next = next;
372210741SFei.Feng@Sun.COM break;
372310741SFei.Feng@Sun.COM }
372410741SFei.Feng@Sun.COM mp = next;
372510741SFei.Feng@Sun.COM }
372610741SFei.Feng@Sun.COM return (mp);
372710741SFei.Feng@Sun.COM }
372810741SFei.Feng@Sun.COM
372910741SFei.Feng@Sun.COM static void
mwl_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)373010741SFei.Feng@Sun.COM mwl_m_ioctl(void* arg, queue_t *wq, mblk_t *mp)
373110741SFei.Feng@Sun.COM {
373210741SFei.Feng@Sun.COM struct mwl_softc *sc = (struct mwl_softc *)arg;
373310741SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic;
373410741SFei.Feng@Sun.COM int err;
373510741SFei.Feng@Sun.COM
373610741SFei.Feng@Sun.COM err = ieee80211_ioctl(ic, wq, mp);
373710741SFei.Feng@Sun.COM if (err == ENETRESET) {
373810741SFei.Feng@Sun.COM if (ic->ic_des_esslen) {
373910741SFei.Feng@Sun.COM if (MWL_IS_RUNNING(sc)) {
374010741SFei.Feng@Sun.COM (void) mwl_init(sc);
374110741SFei.Feng@Sun.COM (void) ieee80211_new_state(ic,
374210741SFei.Feng@Sun.COM IEEE80211_S_SCAN, -1);
374310741SFei.Feng@Sun.COM }
374410741SFei.Feng@Sun.COM }
374510741SFei.Feng@Sun.COM }
374610741SFei.Feng@Sun.COM }
374710741SFei.Feng@Sun.COM
374810741SFei.Feng@Sun.COM /*
374910741SFei.Feng@Sun.COM * Call back function for get/set proporty
375010741SFei.Feng@Sun.COM */
375110741SFei.Feng@Sun.COM static int
mwl_m_getprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,void * wldp_buf)375210741SFei.Feng@Sun.COM mwl_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
3753*11878SVenu.Iyer@Sun.COM uint_t wldp_length, void *wldp_buf)
375410741SFei.Feng@Sun.COM {
375510741SFei.Feng@Sun.COM struct mwl_softc *sc = (struct mwl_softc *)arg;
375610741SFei.Feng@Sun.COM int err = 0;
375710741SFei.Feng@Sun.COM
375810741SFei.Feng@Sun.COM err = ieee80211_getprop(&sc->sc_ic, pr_name, wldp_pr_num,
3759*11878SVenu.Iyer@Sun.COM wldp_length, wldp_buf);
376010741SFei.Feng@Sun.COM
376110741SFei.Feng@Sun.COM return (err);
376210741SFei.Feng@Sun.COM }
376310741SFei.Feng@Sun.COM
3764*11878SVenu.Iyer@Sun.COM static void
mwl_m_propinfo(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,mac_prop_info_handle_t prh)3765*11878SVenu.Iyer@Sun.COM mwl_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
3766*11878SVenu.Iyer@Sun.COM mac_prop_info_handle_t prh)
3767*11878SVenu.Iyer@Sun.COM {
3768*11878SVenu.Iyer@Sun.COM struct mwl_softc *sc = (struct mwl_softc *)arg;
3769*11878SVenu.Iyer@Sun.COM
3770*11878SVenu.Iyer@Sun.COM ieee80211_propinfo(&sc->sc_ic, pr_name, wldp_pr_num, prh);
3771*11878SVenu.Iyer@Sun.COM }
3772*11878SVenu.Iyer@Sun.COM
377310741SFei.Feng@Sun.COM static int
mwl_m_setprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,const void * wldp_buf)377410741SFei.Feng@Sun.COM mwl_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
377510741SFei.Feng@Sun.COM uint_t wldp_length, const void *wldp_buf)
377610741SFei.Feng@Sun.COM {
377710741SFei.Feng@Sun.COM struct mwl_softc *sc = (struct mwl_softc *)arg;
377810741SFei.Feng@Sun.COM ieee80211com_t *ic = &sc->sc_ic;
377910741SFei.Feng@Sun.COM int err;
378010741SFei.Feng@Sun.COM
378110741SFei.Feng@Sun.COM err = ieee80211_setprop(ic, pr_name, wldp_pr_num, wldp_length,
378210741SFei.Feng@Sun.COM wldp_buf);
378310741SFei.Feng@Sun.COM if (err == ENETRESET) {
378410741SFei.Feng@Sun.COM if (ic->ic_des_esslen) {
378510741SFei.Feng@Sun.COM if (MWL_IS_RUNNING(sc)) {
378610741SFei.Feng@Sun.COM (void) mwl_init(sc);
378710741SFei.Feng@Sun.COM (void) ieee80211_new_state(ic,
378810741SFei.Feng@Sun.COM IEEE80211_S_SCAN, -1);
378910741SFei.Feng@Sun.COM }
379010741SFei.Feng@Sun.COM }
379110741SFei.Feng@Sun.COM err = 0;
379210741SFei.Feng@Sun.COM }
379310741SFei.Feng@Sun.COM return (err);
379410741SFei.Feng@Sun.COM }
379510741SFei.Feng@Sun.COM
379610741SFei.Feng@Sun.COM static int
mwl_attach(dev_info_t * devinfo,ddi_attach_cmd_t cmd)379710741SFei.Feng@Sun.COM mwl_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
379810741SFei.Feng@Sun.COM {
379910741SFei.Feng@Sun.COM struct mwl_softc *sc;
380010741SFei.Feng@Sun.COM struct ieee80211com *ic;
380110741SFei.Feng@Sun.COM int i, err, qid, instance;
380210741SFei.Feng@Sun.COM int intr_type, intr_count, intr_actual;
380310741SFei.Feng@Sun.COM char strbuf[32];
380410741SFei.Feng@Sun.COM uint8_t csz;
380510741SFei.Feng@Sun.COM uint16_t vendor_id, device_id, command;
380610741SFei.Feng@Sun.COM
380710741SFei.Feng@Sun.COM wifi_data_t wd = { 0 };
380810741SFei.Feng@Sun.COM mac_register_t *macp;
380910741SFei.Feng@Sun.COM
381010741SFei.Feng@Sun.COM switch (cmd) {
381110741SFei.Feng@Sun.COM case DDI_ATTACH:
381210741SFei.Feng@Sun.COM break;
381310741SFei.Feng@Sun.COM case DDI_RESUME:
381410741SFei.Feng@Sun.COM sc = ddi_get_soft_state(mwl_soft_state_p,
381510741SFei.Feng@Sun.COM ddi_get_instance(devinfo));
381610741SFei.Feng@Sun.COM ASSERT(sc != NULL);
381710741SFei.Feng@Sun.COM MWL_GLOCK(sc);
381810741SFei.Feng@Sun.COM sc->sc_flags &= ~MWL_F_SUSPEND;
381910741SFei.Feng@Sun.COM MWL_GUNLOCK(sc);
382010741SFei.Feng@Sun.COM if (mwl_resume(sc) != 0) {
382110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_SR, "mwl: mwl_attach(): "
382210741SFei.Feng@Sun.COM "failed to resume\n");
382310741SFei.Feng@Sun.COM return (DDI_FAILURE);
382410741SFei.Feng@Sun.COM }
382510741SFei.Feng@Sun.COM if (MWL_IS_RUNNING(sc)) {
382610741SFei.Feng@Sun.COM (void) mwl_init(sc);
382710741SFei.Feng@Sun.COM ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1);
382810741SFei.Feng@Sun.COM }
382910741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_SR, "mwl: mwl_attach(): "
383010741SFei.Feng@Sun.COM "resume now\n");
383110741SFei.Feng@Sun.COM return (DDI_SUCCESS);
383210741SFei.Feng@Sun.COM default:
383310741SFei.Feng@Sun.COM return (DDI_FAILURE);
383410741SFei.Feng@Sun.COM }
383510741SFei.Feng@Sun.COM
383610741SFei.Feng@Sun.COM instance = ddi_get_instance(devinfo);
383710741SFei.Feng@Sun.COM if (ddi_soft_state_zalloc(mwl_soft_state_p,
383810741SFei.Feng@Sun.COM ddi_get_instance(devinfo)) != DDI_SUCCESS) {
383910741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
384010741SFei.Feng@Sun.COM "Unable to alloc soft state\n");
384110741SFei.Feng@Sun.COM return (DDI_FAILURE);
384210741SFei.Feng@Sun.COM }
384310741SFei.Feng@Sun.COM
384410741SFei.Feng@Sun.COM sc = ddi_get_soft_state(mwl_soft_state_p, ddi_get_instance(devinfo));
384510741SFei.Feng@Sun.COM ic = &sc->sc_ic;
384610741SFei.Feng@Sun.COM sc->sc_dev = devinfo;
384710741SFei.Feng@Sun.COM
384810741SFei.Feng@Sun.COM /* PCI configuration space */
384910741SFei.Feng@Sun.COM err = ddi_regs_map_setup(devinfo, 0, (caddr_t *)&sc->sc_cfg_base, 0, 0,
385010741SFei.Feng@Sun.COM &mwl_reg_accattr, &sc->sc_cfg_handle);
385110741SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
385210741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
385310741SFei.Feng@Sun.COM "ddi_regs_map_setup() failed");
385410741SFei.Feng@Sun.COM goto attach_fail0;
385510741SFei.Feng@Sun.COM }
385610741SFei.Feng@Sun.COM csz = ddi_get8(sc->sc_cfg_handle,
385710741SFei.Feng@Sun.COM (uint8_t *)(sc->sc_cfg_base + PCI_CONF_CACHE_LINESZ));
385810741SFei.Feng@Sun.COM if (!csz)
385910741SFei.Feng@Sun.COM csz = 16;
386010741SFei.Feng@Sun.COM sc->sc_cachelsz = csz << 2;
386110741SFei.Feng@Sun.COM sc->sc_dmabuf_size = roundup(IEEE80211_MAX_LEN, sc->sc_cachelsz);
386210741SFei.Feng@Sun.COM vendor_id = ddi_get16(sc->sc_cfg_handle,
386310741SFei.Feng@Sun.COM (uint16_t *)(sc->sc_cfg_base + PCI_CONF_VENID));
386410741SFei.Feng@Sun.COM device_id = ddi_get16(sc->sc_cfg_handle,
386510741SFei.Feng@Sun.COM (uint16_t *)(sc->sc_cfg_base + PCI_CONF_DEVID));
386610741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
386710741SFei.Feng@Sun.COM "vendor 0x%x, device id 0x%x, cache size %d\n",
386810741SFei.Feng@Sun.COM vendor_id, device_id, csz);
386910741SFei.Feng@Sun.COM
387010741SFei.Feng@Sun.COM /*
387110741SFei.Feng@Sun.COM * Enable response to memory space accesses,
387210741SFei.Feng@Sun.COM * and enabe bus master.
387310741SFei.Feng@Sun.COM */
387410741SFei.Feng@Sun.COM command = PCI_COMM_MAE | PCI_COMM_ME;
387510741SFei.Feng@Sun.COM ddi_put16(sc->sc_cfg_handle,
387610741SFei.Feng@Sun.COM (uint16_t *)((uintptr_t)(sc->sc_cfg_base) + PCI_CONF_COMM),
387710741SFei.Feng@Sun.COM command);
387810741SFei.Feng@Sun.COM ddi_put8(sc->sc_cfg_handle,
387910741SFei.Feng@Sun.COM (uint8_t *)(sc->sc_cfg_base + PCI_CONF_LATENCY_TIMER), 0xa8);
388010741SFei.Feng@Sun.COM ddi_put8(sc->sc_cfg_handle,
388110741SFei.Feng@Sun.COM (uint8_t *)(sc->sc_cfg_base + PCI_CONF_ILINE), 0x10);
388210741SFei.Feng@Sun.COM
388310741SFei.Feng@Sun.COM /* BAR0 */
388410741SFei.Feng@Sun.COM err = ddi_regs_map_setup(devinfo, 1,
388510741SFei.Feng@Sun.COM &sc->sc_mem_base, 0, 0, &mwl_reg_accattr, &sc->sc_mem_handle);
388610741SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
388710741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
388810741SFei.Feng@Sun.COM "i/o space failed");
388910741SFei.Feng@Sun.COM goto attach_fail1;
389010741SFei.Feng@Sun.COM }
389110741SFei.Feng@Sun.COM
389210741SFei.Feng@Sun.COM /* BAR1 */
389310741SFei.Feng@Sun.COM err = ddi_regs_map_setup(devinfo, 2,
389410741SFei.Feng@Sun.COM &sc->sc_io_base, 0, 0, &mwl_reg_accattr, &sc->sc_io_handle);
389510741SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
389610741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
389710741SFei.Feng@Sun.COM "memory space failed");
389810741SFei.Feng@Sun.COM goto attach_fail2;
389910741SFei.Feng@Sun.COM }
390010741SFei.Feng@Sun.COM
390110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
390210741SFei.Feng@Sun.COM "PCI configuration is done successfully\n");
390310741SFei.Feng@Sun.COM
390410741SFei.Feng@Sun.COM /*
390510741SFei.Feng@Sun.COM * Alloc cmd DMA buffer for firmware download
390610741SFei.Feng@Sun.COM */
390710741SFei.Feng@Sun.COM err = mwl_alloc_cmdbuf(sc);
390810741SFei.Feng@Sun.COM if (err != 0) {
390910741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
391010741SFei.Feng@Sun.COM "could not alloc cmd dma buffer\n");
391110741SFei.Feng@Sun.COM goto attach_fail3;
391210741SFei.Feng@Sun.COM }
391310741SFei.Feng@Sun.COM
391410741SFei.Feng@Sun.COM sc->sc_imask = 0;
391510741SFei.Feng@Sun.COM sc->sc_hw_flags = 0;
391610741SFei.Feng@Sun.COM sc->sc_flags = 0;
391710741SFei.Feng@Sun.COM
391810741SFei.Feng@Sun.COM /*
391910741SFei.Feng@Sun.COM * Some cards have SDRAM. When loading firmware we need
392010741SFei.Feng@Sun.COM * to reset the SDRAM controller prior to doing this.
392110741SFei.Feng@Sun.COM * When the SDRAMSIZE is non-zero we do that work in
392210741SFei.Feng@Sun.COM * mwl_hal_fwload.
392310741SFei.Feng@Sun.COM */
392410741SFei.Feng@Sun.COM switch (device_id) {
392510741SFei.Feng@Sun.COM case 0x2a02: /* CB82 */
392610741SFei.Feng@Sun.COM case 0x2a03: /* CB85 */
392710741SFei.Feng@Sun.COM case 0x2a08: /* MC85_B1 */
392810741SFei.Feng@Sun.COM case 0x2a0b: /* CB85AP */
392910741SFei.Feng@Sun.COM case 0x2a24:
393010741SFei.Feng@Sun.COM sc->sc_SDRAMSIZE_Addr = 0x40fe70b7; /* 8M SDRAM */
393110741SFei.Feng@Sun.COM break;
393210741SFei.Feng@Sun.COM case 0x2a04: /* MC85 */
393310741SFei.Feng@Sun.COM sc->sc_SDRAMSIZE_Addr = 0x40fc70b7; /* 16M SDRAM */
393410741SFei.Feng@Sun.COM break;
393510741SFei.Feng@Sun.COM default:
393610741SFei.Feng@Sun.COM break;
393710741SFei.Feng@Sun.COM }
393810741SFei.Feng@Sun.COM
393910741SFei.Feng@Sun.COM err = mwl_fwload(sc, NULL);
394010741SFei.Feng@Sun.COM if (err != 0) {
394110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
394210741SFei.Feng@Sun.COM "firmware download failed\n");
394310741SFei.Feng@Sun.COM goto attach_fail4;
394410741SFei.Feng@Sun.COM }
394510741SFei.Feng@Sun.COM
394610741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
394710741SFei.Feng@Sun.COM "firmware download successfully\n");
394810741SFei.Feng@Sun.COM
394910741SFei.Feng@Sun.COM err = mwl_gethwspecs(sc);
395010741SFei.Feng@Sun.COM if (err != 0) {
395110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
395210741SFei.Feng@Sun.COM "failed to get hw spec\n");
395310741SFei.Feng@Sun.COM goto attach_fail4;
395410741SFei.Feng@Sun.COM }
395510741SFei.Feng@Sun.COM
395610741SFei.Feng@Sun.COM err = mwl_getchannels(sc);
395710741SFei.Feng@Sun.COM if (err != 0) {
395810741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
395910741SFei.Feng@Sun.COM "failed to get channels\n");
396010741SFei.Feng@Sun.COM goto attach_fail4;
396110741SFei.Feng@Sun.COM }
396210741SFei.Feng@Sun.COM
396310741SFei.Feng@Sun.COM /*
396410741SFei.Feng@Sun.COM * Alloc rx DMA buffer
396510741SFei.Feng@Sun.COM */
396610741SFei.Feng@Sun.COM err = mwl_alloc_rx_ring(sc, MWL_RX_RING_COUNT);
396710741SFei.Feng@Sun.COM if (err != 0) {
396810741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
396910741SFei.Feng@Sun.COM "could not alloc cmd dma buffer\n");
397010741SFei.Feng@Sun.COM goto attach_fail5;
397110741SFei.Feng@Sun.COM }
397210741SFei.Feng@Sun.COM
397310741SFei.Feng@Sun.COM /*
397410741SFei.Feng@Sun.COM * Alloc rx DMA buffer
397510741SFei.Feng@Sun.COM */
397610741SFei.Feng@Sun.COM for (qid = 0; qid < MWL_NUM_TX_QUEUES; qid++) {
397710741SFei.Feng@Sun.COM err = mwl_alloc_tx_ring(sc,
397810741SFei.Feng@Sun.COM &sc->sc_txring[qid], MWL_TX_RING_COUNT);
397910741SFei.Feng@Sun.COM if (err != 0) {
398010741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
398110741SFei.Feng@Sun.COM "could not alloc tx ring %d\n", qid);
398210741SFei.Feng@Sun.COM goto attach_fail6;
398310741SFei.Feng@Sun.COM }
398410741SFei.Feng@Sun.COM }
398510741SFei.Feng@Sun.COM
398610741SFei.Feng@Sun.COM err = mwl_setupdma(sc);
398710741SFei.Feng@Sun.COM if (err != 0) {
398810741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
398910741SFei.Feng@Sun.COM "could not setup dma\n");
399010741SFei.Feng@Sun.COM goto attach_fail6;
399110741SFei.Feng@Sun.COM }
399210741SFei.Feng@Sun.COM
399310741SFei.Feng@Sun.COM err = mwl_setup_txq(sc);
399410741SFei.Feng@Sun.COM if (err != 0) {
399510741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
399610741SFei.Feng@Sun.COM "could not setup txq\n");
399710741SFei.Feng@Sun.COM goto attach_fail6;
399810741SFei.Feng@Sun.COM }
399910741SFei.Feng@Sun.COM
400010741SFei.Feng@Sun.COM IEEE80211_ADDR_COPY(ic->ic_macaddr, sc->sc_hwspecs.macAddr);
400110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
400210741SFei.Feng@Sun.COM "mwl MAC:%2x:%2x:%2x:%2x:%2x:%2x\n",
400310741SFei.Feng@Sun.COM ic->ic_macaddr[0],
400410741SFei.Feng@Sun.COM ic->ic_macaddr[1],
400510741SFei.Feng@Sun.COM ic->ic_macaddr[2],
400610741SFei.Feng@Sun.COM ic->ic_macaddr[3],
400710741SFei.Feng@Sun.COM ic->ic_macaddr[4],
400810741SFei.Feng@Sun.COM ic->ic_macaddr[5]);
400910741SFei.Feng@Sun.COM
401010741SFei.Feng@Sun.COM err = mwl_hal_setmac_locked(sc, ic->ic_macaddr);
401110741SFei.Feng@Sun.COM if (err != 0) { /* NB: mwl_setupdma prints msg */
401210741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: attach(): "
401310741SFei.Feng@Sun.COM "could not set mac\n");
401410741SFei.Feng@Sun.COM goto attach_fail6;
401510741SFei.Feng@Sun.COM }
401610741SFei.Feng@Sun.COM
401710741SFei.Feng@Sun.COM mutex_init(&sc->sc_glock, NULL, MUTEX_DRIVER, NULL);
401810741SFei.Feng@Sun.COM mutex_init(&sc->sc_rxlock, NULL, MUTEX_DRIVER, NULL);
401910741SFei.Feng@Sun.COM mutex_init(&sc->sc_txlock, NULL, MUTEX_DRIVER, NULL);
402010741SFei.Feng@Sun.COM
402110741SFei.Feng@Sun.COM
402210741SFei.Feng@Sun.COM /* set supported rates */
402310741SFei.Feng@Sun.COM ic->ic_sup_rates[IEEE80211_MODE_11B] = mwl_rateset_11b;
402410741SFei.Feng@Sun.COM ic->ic_sup_rates[IEEE80211_MODE_11G] = mwl_rateset_11g;
402510741SFei.Feng@Sun.COM
402610741SFei.Feng@Sun.COM /* set supported .11b and .11g channels (1 through 14) */
402710741SFei.Feng@Sun.COM for (i = 1; i <= 14; i++) {
402810741SFei.Feng@Sun.COM ic->ic_sup_channels[i].ich_freq =
402910741SFei.Feng@Sun.COM ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
403010741SFei.Feng@Sun.COM ic->ic_sup_channels[i].ich_flags =
403110741SFei.Feng@Sun.COM IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
403210741SFei.Feng@Sun.COM }
403310741SFei.Feng@Sun.COM
403410741SFei.Feng@Sun.COM ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
403510741SFei.Feng@Sun.COM ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
403610741SFei.Feng@Sun.COM ic->ic_state = IEEE80211_S_INIT;
403710741SFei.Feng@Sun.COM
403810741SFei.Feng@Sun.COM /* set device capabilities */
403910741SFei.Feng@Sun.COM ic->ic_caps =
404010741SFei.Feng@Sun.COM IEEE80211_C_TXPMGT | /* tx power management */
404110741SFei.Feng@Sun.COM IEEE80211_C_SHPREAMBLE | /* short preamble supported */
404210741SFei.Feng@Sun.COM IEEE80211_C_SHSLOT; /* short slot time supported */
404310741SFei.Feng@Sun.COM
404410741SFei.Feng@Sun.COM /* WPA/WPA2 support */
404510741SFei.Feng@Sun.COM ic->ic_caps |= IEEE80211_C_WPA; /* Support WPA/WPA2 */
404610741SFei.Feng@Sun.COM
404710741SFei.Feng@Sun.COM /* Enable hardware encryption */
404810741SFei.Feng@Sun.COM ic->ic_caps |= IEEE80211_C_WEP | IEEE80211_C_TKIP | IEEE80211_C_AES_CCM;
404910741SFei.Feng@Sun.COM
405010741SFei.Feng@Sun.COM ic->ic_xmit = mwl_send;
405110741SFei.Feng@Sun.COM
405210741SFei.Feng@Sun.COM ieee80211_attach(ic);
405310741SFei.Feng@Sun.COM
405410741SFei.Feng@Sun.COM /* register WPA door */
405510741SFei.Feng@Sun.COM ieee80211_register_door(ic, ddi_driver_name(devinfo),
405610741SFei.Feng@Sun.COM ddi_get_instance(devinfo));
405710741SFei.Feng@Sun.COM
405810741SFei.Feng@Sun.COM /* override state transition machine */
405910741SFei.Feng@Sun.COM sc->sc_newstate = ic->ic_newstate;
406010741SFei.Feng@Sun.COM ic->ic_newstate = mwl_newstate;
406110741SFei.Feng@Sun.COM ic->ic_node_alloc = mwl_node_alloc;
406210741SFei.Feng@Sun.COM ic->ic_node_free = mwl_node_free;
406310741SFei.Feng@Sun.COM ic->ic_crypto.cs_max_keyix = 0;
406410741SFei.Feng@Sun.COM ic->ic_crypto.cs_key_alloc = mwl_key_alloc;
406510741SFei.Feng@Sun.COM ic->ic_crypto.cs_key_delete = mwl_key_delete;
406610741SFei.Feng@Sun.COM ic->ic_crypto.cs_key_set = mwl_key_set;
406710741SFei.Feng@Sun.COM
406810741SFei.Feng@Sun.COM ieee80211_media_init(ic);
406910741SFei.Feng@Sun.COM
407010741SFei.Feng@Sun.COM ic->ic_def_txkey = 0;
407110741SFei.Feng@Sun.COM
407210741SFei.Feng@Sun.COM err = mwl_hal_newstation(sc, ic->ic_macaddr, 0, 0, NULL, 0, 0);
407310741SFei.Feng@Sun.COM if (err != 0) {
407410741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: attach(): "
407510741SFei.Feng@Sun.COM "could not create new station\n");
407610741SFei.Feng@Sun.COM goto attach_fail7;
407710741SFei.Feng@Sun.COM }
407810741SFei.Feng@Sun.COM
407910741SFei.Feng@Sun.COM IEEE80211_ADDR_COPY(ic->ic_bss->in_bssid, ic->ic_macaddr);
408010741SFei.Feng@Sun.COM // mwl_setglobalkeys(ic);
408110741SFei.Feng@Sun.COM
408210741SFei.Feng@Sun.COM err = ddi_intr_get_supported_types(devinfo, &intr_type);
408310741SFei.Feng@Sun.COM if ((err != DDI_SUCCESS) || (!(intr_type & DDI_INTR_TYPE_FIXED))) {
408410741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
408510741SFei.Feng@Sun.COM "fixed type interrupt is not supported\n");
408610741SFei.Feng@Sun.COM goto attach_fail7;
408710741SFei.Feng@Sun.COM }
408810741SFei.Feng@Sun.COM
408910741SFei.Feng@Sun.COM err = ddi_intr_get_nintrs(devinfo, DDI_INTR_TYPE_FIXED, &intr_count);
409010741SFei.Feng@Sun.COM if ((err != DDI_SUCCESS) || (intr_count != 1)) {
409110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
409210741SFei.Feng@Sun.COM "no fixed interrupts\n");
409310741SFei.Feng@Sun.COM goto attach_fail7;
409410741SFei.Feng@Sun.COM }
409510741SFei.Feng@Sun.COM
409610741SFei.Feng@Sun.COM sc->sc_intr_htable = kmem_zalloc(sizeof (ddi_intr_handle_t), KM_SLEEP);
409710741SFei.Feng@Sun.COM
409810741SFei.Feng@Sun.COM err = ddi_intr_alloc(devinfo, sc->sc_intr_htable,
409910741SFei.Feng@Sun.COM DDI_INTR_TYPE_FIXED, 0, intr_count, &intr_actual, 0);
410010741SFei.Feng@Sun.COM if ((err != DDI_SUCCESS) || (intr_actual != 1)) {
410110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
410210741SFei.Feng@Sun.COM "ddi_intr_alloc() failed 0x%x\n", err);
410310741SFei.Feng@Sun.COM goto attach_fail8;
410410741SFei.Feng@Sun.COM }
410510741SFei.Feng@Sun.COM
410610741SFei.Feng@Sun.COM err = ddi_intr_get_pri(sc->sc_intr_htable[0], &sc->sc_intr_pri);
410710741SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
410810741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
410910741SFei.Feng@Sun.COM "ddi_intr_get_pri() failed 0x%x\n", err);
411010741SFei.Feng@Sun.COM goto attach_fail9;
411110741SFei.Feng@Sun.COM }
411210741SFei.Feng@Sun.COM
411310741SFei.Feng@Sun.COM err = ddi_intr_add_softint(devinfo, &sc->sc_softintr_hdl,
411410741SFei.Feng@Sun.COM DDI_INTR_SOFTPRI_MAX, mwl_softintr, (caddr_t)sc);
411510741SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
411610741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
411710741SFei.Feng@Sun.COM "ddi_add_softintr() failed");
411810741SFei.Feng@Sun.COM goto attach_fail9;
411910741SFei.Feng@Sun.COM }
412010741SFei.Feng@Sun.COM
412110741SFei.Feng@Sun.COM err = ddi_intr_add_handler(sc->sc_intr_htable[0], mwl_intr,
412210741SFei.Feng@Sun.COM (caddr_t)sc, NULL);
412310741SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
412410741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
412510741SFei.Feng@Sun.COM "ddi_intr_addr_handle() failed\n");
412610741SFei.Feng@Sun.COM goto attach_fail10;
412710741SFei.Feng@Sun.COM }
412810741SFei.Feng@Sun.COM
412910741SFei.Feng@Sun.COM err = ddi_intr_enable(sc->sc_intr_htable[0]);
413010741SFei.Feng@Sun.COM if (err != DDI_SUCCESS) {
413110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
413210741SFei.Feng@Sun.COM "ddi_intr_enable() failed\n");
413310741SFei.Feng@Sun.COM goto attach_fail11;
413410741SFei.Feng@Sun.COM }
413510741SFei.Feng@Sun.COM
413610741SFei.Feng@Sun.COM /*
413710741SFei.Feng@Sun.COM * Provide initial settings for the WiFi plugin; whenever this
413810741SFei.Feng@Sun.COM * information changes, we need to call mac_plugindata_update()
413910741SFei.Feng@Sun.COM */
414010741SFei.Feng@Sun.COM wd.wd_opmode = ic->ic_opmode;
414110741SFei.Feng@Sun.COM wd.wd_secalloc = WIFI_SEC_NONE;
414210741SFei.Feng@Sun.COM IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
414310741SFei.Feng@Sun.COM
414410741SFei.Feng@Sun.COM if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
414510741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
414610741SFei.Feng@Sun.COM "MAC version mismatch\n");
414710741SFei.Feng@Sun.COM goto attach_fail12;
414810741SFei.Feng@Sun.COM }
414910741SFei.Feng@Sun.COM
415010741SFei.Feng@Sun.COM macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI;
415110741SFei.Feng@Sun.COM macp->m_driver = sc;
415210741SFei.Feng@Sun.COM macp->m_dip = devinfo;
415310741SFei.Feng@Sun.COM macp->m_src_addr = ic->ic_macaddr;
415410741SFei.Feng@Sun.COM macp->m_callbacks = &mwl_m_callbacks;
415510741SFei.Feng@Sun.COM macp->m_min_sdu = 0;
415610741SFei.Feng@Sun.COM macp->m_max_sdu = IEEE80211_MTU;
415710741SFei.Feng@Sun.COM macp->m_pdata = &wd;
415810741SFei.Feng@Sun.COM macp->m_pdata_size = sizeof (wd);
415910741SFei.Feng@Sun.COM
416010741SFei.Feng@Sun.COM err = mac_register(macp, &ic->ic_mach);
416110741SFei.Feng@Sun.COM mac_free(macp);
416210741SFei.Feng@Sun.COM if (err != 0) {
416310741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
416410741SFei.Feng@Sun.COM "mac_register err %x\n", err);
416510741SFei.Feng@Sun.COM goto attach_fail12;
416610741SFei.Feng@Sun.COM }
416710741SFei.Feng@Sun.COM
416810741SFei.Feng@Sun.COM /*
416910741SFei.Feng@Sun.COM * Create minor node of type DDI_NT_NET_WIFI
417010741SFei.Feng@Sun.COM */
417110741SFei.Feng@Sun.COM (void) snprintf(strbuf, sizeof (strbuf), "%s%d",
417210741SFei.Feng@Sun.COM "mwl", instance);
417310741SFei.Feng@Sun.COM err = ddi_create_minor_node(devinfo, strbuf, S_IFCHR,
417410741SFei.Feng@Sun.COM instance + 1, DDI_NT_NET_WIFI, 0);
417510741SFei.Feng@Sun.COM if (err != 0) {
417610741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
417710741SFei.Feng@Sun.COM "create minor node error\n");
417810741SFei.Feng@Sun.COM goto attach_fail13;
417910741SFei.Feng@Sun.COM }
418010741SFei.Feng@Sun.COM
418110741SFei.Feng@Sun.COM /*
418210741SFei.Feng@Sun.COM * Notify link is down now
418310741SFei.Feng@Sun.COM */
418410741SFei.Feng@Sun.COM mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
418510741SFei.Feng@Sun.COM
418610741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): "
418710741SFei.Feng@Sun.COM "driver attach successfully\n");
418810741SFei.Feng@Sun.COM return (DDI_SUCCESS);
418910741SFei.Feng@Sun.COM
419010741SFei.Feng@Sun.COM attach_fail13:
419110741SFei.Feng@Sun.COM (void) mac_disable(ic->ic_mach);
419210741SFei.Feng@Sun.COM (void) mac_unregister(ic->ic_mach);
419310741SFei.Feng@Sun.COM attach_fail12:
419410741SFei.Feng@Sun.COM (void) ddi_intr_disable(sc->sc_intr_htable[0]);
419510741SFei.Feng@Sun.COM attach_fail11:
419610741SFei.Feng@Sun.COM (void) ddi_intr_remove_handler(sc->sc_intr_htable[0]);
419710741SFei.Feng@Sun.COM attach_fail10:
419810741SFei.Feng@Sun.COM (void) ddi_intr_remove_softint(sc->sc_softintr_hdl);
419910741SFei.Feng@Sun.COM sc->sc_softintr_hdl = NULL;
420010741SFei.Feng@Sun.COM attach_fail9:
420110741SFei.Feng@Sun.COM (void) ddi_intr_free(sc->sc_intr_htable[0]);
420210741SFei.Feng@Sun.COM attach_fail8:
420310741SFei.Feng@Sun.COM kmem_free(sc->sc_intr_htable, sizeof (ddi_intr_handle_t));
420410741SFei.Feng@Sun.COM attach_fail7:
420510741SFei.Feng@Sun.COM mutex_destroy(&sc->sc_txlock);
420610741SFei.Feng@Sun.COM mutex_destroy(&sc->sc_rxlock);
420710741SFei.Feng@Sun.COM mutex_destroy(&sc->sc_glock);
420810741SFei.Feng@Sun.COM attach_fail6:
420910741SFei.Feng@Sun.COM while (--qid >= 0)
421010741SFei.Feng@Sun.COM mwl_free_tx_ring(sc, &sc->sc_txring[qid]);
421110741SFei.Feng@Sun.COM attach_fail5:
421210741SFei.Feng@Sun.COM mwl_free_rx_ring(sc);
421310741SFei.Feng@Sun.COM attach_fail4:
421410741SFei.Feng@Sun.COM mwl_free_cmdbuf(sc);
421510741SFei.Feng@Sun.COM attach_fail3:
421610741SFei.Feng@Sun.COM ddi_regs_map_free(&sc->sc_mem_handle);
421710741SFei.Feng@Sun.COM attach_fail2:
421810741SFei.Feng@Sun.COM ddi_regs_map_free(&sc->sc_io_handle);
421910741SFei.Feng@Sun.COM attach_fail1:
422010741SFei.Feng@Sun.COM ddi_regs_map_free(&sc->sc_cfg_handle);
422110741SFei.Feng@Sun.COM attach_fail0:
422210741SFei.Feng@Sun.COM ddi_soft_state_free(mwl_soft_state_p, ddi_get_instance(devinfo));
422310741SFei.Feng@Sun.COM return (DDI_FAILURE);
422410741SFei.Feng@Sun.COM }
422510741SFei.Feng@Sun.COM
422610741SFei.Feng@Sun.COM static int32_t
mwl_detach(dev_info_t * devinfo,ddi_detach_cmd_t cmd)422710741SFei.Feng@Sun.COM mwl_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
422810741SFei.Feng@Sun.COM {
422910741SFei.Feng@Sun.COM struct mwl_softc *sc;
423010741SFei.Feng@Sun.COM int qid;
423110741SFei.Feng@Sun.COM
423210741SFei.Feng@Sun.COM sc = ddi_get_soft_state(mwl_soft_state_p, ddi_get_instance(devinfo));
423310741SFei.Feng@Sun.COM ASSERT(sc != NULL);
423410741SFei.Feng@Sun.COM
423510741SFei.Feng@Sun.COM switch (cmd) {
423610741SFei.Feng@Sun.COM case DDI_DETACH:
423710741SFei.Feng@Sun.COM break;
423810741SFei.Feng@Sun.COM case DDI_SUSPEND:
423910741SFei.Feng@Sun.COM if (MWL_IS_RUNNING(sc))
424010741SFei.Feng@Sun.COM mwl_stop(sc);
424110741SFei.Feng@Sun.COM for (qid = 0; qid < MWL_NUM_TX_QUEUES; qid++)
424210741SFei.Feng@Sun.COM mwl_free_tx_ring(sc, &sc->sc_txring[qid]);
424310741SFei.Feng@Sun.COM mwl_free_rx_ring(sc);
424410741SFei.Feng@Sun.COM MWL_GLOCK(sc);
424510741SFei.Feng@Sun.COM sc->sc_flags |= MWL_F_SUSPEND;
424610741SFei.Feng@Sun.COM MWL_GUNLOCK(sc);
424710741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_SR, "mwl: mwl_detach(): "
424810741SFei.Feng@Sun.COM "suspend now\n");
424910741SFei.Feng@Sun.COM return (DDI_SUCCESS);
425010741SFei.Feng@Sun.COM default:
425110741SFei.Feng@Sun.COM return (DDI_FAILURE);
425210741SFei.Feng@Sun.COM }
425310741SFei.Feng@Sun.COM
425410741SFei.Feng@Sun.COM if (mac_disable(sc->sc_ic.ic_mach) != 0)
425510741SFei.Feng@Sun.COM return (DDI_FAILURE);
425610741SFei.Feng@Sun.COM
425710741SFei.Feng@Sun.COM /*
425810741SFei.Feng@Sun.COM * Unregister from the MAC layer subsystem
425910741SFei.Feng@Sun.COM */
426010741SFei.Feng@Sun.COM (void) mac_unregister(sc->sc_ic.ic_mach);
426110741SFei.Feng@Sun.COM
426210741SFei.Feng@Sun.COM (void) ddi_intr_remove_softint(sc->sc_softintr_hdl);
426310741SFei.Feng@Sun.COM sc->sc_softintr_hdl = NULL;
426410741SFei.Feng@Sun.COM (void) ddi_intr_disable(sc->sc_intr_htable[0]);
426510741SFei.Feng@Sun.COM (void) ddi_intr_remove_handler(sc->sc_intr_htable[0]);
426610741SFei.Feng@Sun.COM (void) ddi_intr_free(sc->sc_intr_htable[0]);
426710741SFei.Feng@Sun.COM kmem_free(sc->sc_intr_htable, sizeof (ddi_intr_handle_t));
426810741SFei.Feng@Sun.COM
426910741SFei.Feng@Sun.COM /*
427010741SFei.Feng@Sun.COM * detach ieee80211 layer
427110741SFei.Feng@Sun.COM */
427210741SFei.Feng@Sun.COM ieee80211_detach(&sc->sc_ic);
427310741SFei.Feng@Sun.COM
427410741SFei.Feng@Sun.COM
427510741SFei.Feng@Sun.COM for (qid = 0; qid < MWL_NUM_TX_QUEUES; qid++)
427610741SFei.Feng@Sun.COM mwl_free_tx_ring(sc, &sc->sc_txring[qid]);
427710741SFei.Feng@Sun.COM mwl_free_rx_ring(sc);
427810741SFei.Feng@Sun.COM mwl_free_cmdbuf(sc);
427910741SFei.Feng@Sun.COM
428010741SFei.Feng@Sun.COM mutex_destroy(&sc->sc_txlock);
428110741SFei.Feng@Sun.COM mutex_destroy(&sc->sc_rxlock);
428210741SFei.Feng@Sun.COM mutex_destroy(&sc->sc_glock);
428310741SFei.Feng@Sun.COM
428410741SFei.Feng@Sun.COM ddi_regs_map_free(&sc->sc_mem_handle);
428510741SFei.Feng@Sun.COM ddi_regs_map_free(&sc->sc_io_handle);
428610741SFei.Feng@Sun.COM ddi_regs_map_free(&sc->sc_cfg_handle);
428710741SFei.Feng@Sun.COM
428810741SFei.Feng@Sun.COM ddi_remove_minor_node(devinfo, NULL);
428910741SFei.Feng@Sun.COM ddi_soft_state_free(mwl_soft_state_p, ddi_get_instance(devinfo));
429010741SFei.Feng@Sun.COM
429110741SFei.Feng@Sun.COM MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_detach(): "
429210741SFei.Feng@Sun.COM "detach successfully\n");
429310741SFei.Feng@Sun.COM return (DDI_SUCCESS);
429410741SFei.Feng@Sun.COM }
429510741SFei.Feng@Sun.COM
429610741SFei.Feng@Sun.COM /*
429710741SFei.Feng@Sun.COM * quiesce(9E) entry point.
429810741SFei.Feng@Sun.COM *
429910741SFei.Feng@Sun.COM * This function is called when the system is single-threaded at high
430010741SFei.Feng@Sun.COM * PIL with preemption disabled. Therefore, this function must not be
430110741SFei.Feng@Sun.COM * blocked.
430210741SFei.Feng@Sun.COM *
430310741SFei.Feng@Sun.COM * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
430410741SFei.Feng@Sun.COM * DDI_FAILURE indicates an error condition and should almost never happen.
430510741SFei.Feng@Sun.COM */
430610741SFei.Feng@Sun.COM int
mwl_quiesce(dev_info_t * dip)430710741SFei.Feng@Sun.COM mwl_quiesce(dev_info_t *dip)
430810741SFei.Feng@Sun.COM {
430910741SFei.Feng@Sun.COM struct mwl_softc *sc;
431010741SFei.Feng@Sun.COM
431110741SFei.Feng@Sun.COM sc = ddi_get_soft_state(mwl_soft_state_p, ddi_get_instance(dip));
431210741SFei.Feng@Sun.COM if (sc == NULL)
431310741SFei.Feng@Sun.COM return (DDI_FAILURE);
431410741SFei.Feng@Sun.COM
431510741SFei.Feng@Sun.COM #ifdef DEBUG
431610741SFei.Feng@Sun.COM mwl_dbg_flags = 0;
431710741SFei.Feng@Sun.COM #endif
431810741SFei.Feng@Sun.COM
431910741SFei.Feng@Sun.COM /*
432010741SFei.Feng@Sun.COM * No more blocking is allowed while we are in quiesce(9E) entry point
432110741SFei.Feng@Sun.COM */
432210741SFei.Feng@Sun.COM sc->sc_flags |= MWL_F_QUIESCE;
432310741SFei.Feng@Sun.COM
432410741SFei.Feng@Sun.COM /*
432510741SFei.Feng@Sun.COM * Disable all interrupts
432610741SFei.Feng@Sun.COM */
432710741SFei.Feng@Sun.COM mwl_stop(sc);
432810741SFei.Feng@Sun.COM return (DDI_SUCCESS);
432910741SFei.Feng@Sun.COM }
433010741SFei.Feng@Sun.COM
433110741SFei.Feng@Sun.COM int
_init(void)433210741SFei.Feng@Sun.COM _init(void)
433310741SFei.Feng@Sun.COM {
433410741SFei.Feng@Sun.COM int status;
433510741SFei.Feng@Sun.COM
433610741SFei.Feng@Sun.COM status = ddi_soft_state_init(&mwl_soft_state_p,
433710741SFei.Feng@Sun.COM sizeof (struct mwl_softc), 1);
433810741SFei.Feng@Sun.COM if (status != 0)
433910741SFei.Feng@Sun.COM return (status);
434010741SFei.Feng@Sun.COM
434110741SFei.Feng@Sun.COM mac_init_ops(&mwl_dev_ops, "mwl");
434210741SFei.Feng@Sun.COM status = mod_install(&modlinkage);
434310741SFei.Feng@Sun.COM if (status != 0) {
434410741SFei.Feng@Sun.COM mac_fini_ops(&mwl_dev_ops);
434510741SFei.Feng@Sun.COM ddi_soft_state_fini(&mwl_soft_state_p);
434610741SFei.Feng@Sun.COM }
434710741SFei.Feng@Sun.COM return (status);
434810741SFei.Feng@Sun.COM }
434910741SFei.Feng@Sun.COM
435010741SFei.Feng@Sun.COM int
_info(struct modinfo * modinfop)435110741SFei.Feng@Sun.COM _info(struct modinfo *modinfop)
435210741SFei.Feng@Sun.COM {
435310741SFei.Feng@Sun.COM return (mod_info(&modlinkage, modinfop));
435410741SFei.Feng@Sun.COM }
435510741SFei.Feng@Sun.COM
435610741SFei.Feng@Sun.COM int
_fini(void)435710741SFei.Feng@Sun.COM _fini(void)
435810741SFei.Feng@Sun.COM {
435910741SFei.Feng@Sun.COM int status;
436010741SFei.Feng@Sun.COM
436110741SFei.Feng@Sun.COM status = mod_remove(&modlinkage);
436210741SFei.Feng@Sun.COM if (status == 0) {
436310741SFei.Feng@Sun.COM mac_fini_ops(&mwl_dev_ops);
436410741SFei.Feng@Sun.COM ddi_soft_state_fini(&mwl_soft_state_p);
436510741SFei.Feng@Sun.COM }
436610741SFei.Feng@Sun.COM return (status);
436710741SFei.Feng@Sun.COM }
4368