xref: /onnv-gate/usr/src/uts/common/io/mwl/mwl.c (revision 11878:ac93462db6d7)
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