xref: /onnv-gate/usr/src/uts/common/io/arn/arn_main.c (revision 11878:ac93462db6d7)
19999SWang.Lin@Sun.COM /*
211729SWang.Lin@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
39999SWang.Lin@Sun.COM  * Use is subject to license terms.
49999SWang.Lin@Sun.COM  */
59999SWang.Lin@Sun.COM 
69999SWang.Lin@Sun.COM /*
79999SWang.Lin@Sun.COM  * Copyright (c) 2008 Atheros Communications Inc.
89999SWang.Lin@Sun.COM  *
99999SWang.Lin@Sun.COM  * Permission to use, copy, modify, and/or distribute this software for any
109999SWang.Lin@Sun.COM  * purpose with or without fee is hereby granted, provided that the above
119999SWang.Lin@Sun.COM  * copyright notice and this permission notice appear in all copies.
129999SWang.Lin@Sun.COM  *
139999SWang.Lin@Sun.COM  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
149999SWang.Lin@Sun.COM  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
159999SWang.Lin@Sun.COM  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
169999SWang.Lin@Sun.COM  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
179999SWang.Lin@Sun.COM  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
189999SWang.Lin@Sun.COM  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
199999SWang.Lin@Sun.COM  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
209999SWang.Lin@Sun.COM  */
219999SWang.Lin@Sun.COM 
229999SWang.Lin@Sun.COM #include <sys/param.h>
239999SWang.Lin@Sun.COM #include <sys/types.h>
249999SWang.Lin@Sun.COM #include <sys/signal.h>
259999SWang.Lin@Sun.COM #include <sys/stream.h>
269999SWang.Lin@Sun.COM #include <sys/termio.h>
279999SWang.Lin@Sun.COM #include <sys/errno.h>
289999SWang.Lin@Sun.COM #include <sys/file.h>
299999SWang.Lin@Sun.COM #include <sys/cmn_err.h>
309999SWang.Lin@Sun.COM #include <sys/stropts.h>
319999SWang.Lin@Sun.COM #include <sys/strsubr.h>
329999SWang.Lin@Sun.COM #include <sys/strtty.h>
339999SWang.Lin@Sun.COM #include <sys/kbio.h>
349999SWang.Lin@Sun.COM #include <sys/cred.h>
359999SWang.Lin@Sun.COM #include <sys/stat.h>
369999SWang.Lin@Sun.COM #include <sys/consdev.h>
379999SWang.Lin@Sun.COM #include <sys/kmem.h>
389999SWang.Lin@Sun.COM #include <sys/modctl.h>
399999SWang.Lin@Sun.COM #include <sys/ddi.h>
409999SWang.Lin@Sun.COM #include <sys/sunddi.h>
419999SWang.Lin@Sun.COM #include <sys/pci.h>
429999SWang.Lin@Sun.COM #include <sys/errno.h>
439999SWang.Lin@Sun.COM #include <sys/mac_provider.h>
449999SWang.Lin@Sun.COM #include <sys/dlpi.h>
459999SWang.Lin@Sun.COM #include <sys/ethernet.h>
469999SWang.Lin@Sun.COM #include <sys/list.h>
479999SWang.Lin@Sun.COM #include <sys/byteorder.h>
489999SWang.Lin@Sun.COM #include <sys/strsun.h>
499999SWang.Lin@Sun.COM #include <sys/policy.h>
509999SWang.Lin@Sun.COM #include <inet/common.h>
519999SWang.Lin@Sun.COM #include <inet/nd.h>
529999SWang.Lin@Sun.COM #include <inet/mi.h>
539999SWang.Lin@Sun.COM #include <inet/wifi_ioctl.h>
549999SWang.Lin@Sun.COM #include <sys/mac_wifi.h>
5511729SWang.Lin@Sun.COM #include <sys/net80211.h>
5611729SWang.Lin@Sun.COM #include <sys/net80211_proto.h>
5711729SWang.Lin@Sun.COM #include <sys/net80211_ht.h>
5811729SWang.Lin@Sun.COM 
599999SWang.Lin@Sun.COM 
609999SWang.Lin@Sun.COM #include "arn_ath9k.h"
619999SWang.Lin@Sun.COM #include "arn_core.h"
629999SWang.Lin@Sun.COM #include "arn_reg.h"
639999SWang.Lin@Sun.COM #include "arn_hw.h"
649999SWang.Lin@Sun.COM 
6511377SWang.Lin@Sun.COM #define	ARN_MAX_RSSI	45	/* max rssi */
669999SWang.Lin@Sun.COM 
679999SWang.Lin@Sun.COM /*
6811729SWang.Lin@Sun.COM  * Default 11n reates supported by this station.
6911729SWang.Lin@Sun.COM  */
7011729SWang.Lin@Sun.COM extern struct ieee80211_htrateset ieee80211_rateset_11n;
7111729SWang.Lin@Sun.COM 
7211729SWang.Lin@Sun.COM /*
739999SWang.Lin@Sun.COM  * PIO access attributes for registers
749999SWang.Lin@Sun.COM  */
759999SWang.Lin@Sun.COM static ddi_device_acc_attr_t arn_reg_accattr = {
769999SWang.Lin@Sun.COM 	DDI_DEVICE_ATTR_V0,
779999SWang.Lin@Sun.COM 	DDI_STRUCTURE_LE_ACC,
7811729SWang.Lin@Sun.COM 	DDI_STRICTORDER_ACC,
7911729SWang.Lin@Sun.COM 	DDI_DEFAULT_ACC
809999SWang.Lin@Sun.COM };
819999SWang.Lin@Sun.COM 
829999SWang.Lin@Sun.COM /*
839999SWang.Lin@Sun.COM  * DMA access attributes for descriptors: NOT to be byte swapped.
849999SWang.Lin@Sun.COM  */
859999SWang.Lin@Sun.COM static ddi_device_acc_attr_t arn_desc_accattr = {
869999SWang.Lin@Sun.COM 	DDI_DEVICE_ATTR_V0,
879999SWang.Lin@Sun.COM 	DDI_STRUCTURE_LE_ACC,
8811729SWang.Lin@Sun.COM 	DDI_STRICTORDER_ACC,
8911729SWang.Lin@Sun.COM 	DDI_DEFAULT_ACC
909999SWang.Lin@Sun.COM };
919999SWang.Lin@Sun.COM 
929999SWang.Lin@Sun.COM /*
939999SWang.Lin@Sun.COM  * Describes the chip's DMA engine
949999SWang.Lin@Sun.COM  */
959999SWang.Lin@Sun.COM static ddi_dma_attr_t arn_dma_attr = {
969999SWang.Lin@Sun.COM 	DMA_ATTR_V0,	/* version number */
979999SWang.Lin@Sun.COM 	0,				/* low address */
989999SWang.Lin@Sun.COM 	0xffffffffU,	/* high address */
999999SWang.Lin@Sun.COM 	0x3ffffU,		/* counter register max */
1009999SWang.Lin@Sun.COM 	1,				/* alignment */
1019999SWang.Lin@Sun.COM 	0xFFF,			/* burst sizes */
1029999SWang.Lin@Sun.COM 	1,				/* minimum transfer size */
1039999SWang.Lin@Sun.COM 	0x3ffffU,		/* max transfer size */
1049999SWang.Lin@Sun.COM 	0xffffffffU,	/* address register max */
1059999SWang.Lin@Sun.COM 	1,				/* no scatter-gather */
1069999SWang.Lin@Sun.COM 	1,				/* granularity of device */
1079999SWang.Lin@Sun.COM 	0,				/* DMA flags */
1089999SWang.Lin@Sun.COM };
1099999SWang.Lin@Sun.COM 
1109999SWang.Lin@Sun.COM static ddi_dma_attr_t arn_desc_dma_attr = {
1119999SWang.Lin@Sun.COM 	DMA_ATTR_V0,	/* version number */
1129999SWang.Lin@Sun.COM 	0,				/* low address */
1139999SWang.Lin@Sun.COM 	0xffffffffU,	/* high address */
1149999SWang.Lin@Sun.COM 	0xffffffffU,	/* counter register max */
1159999SWang.Lin@Sun.COM 	0x1000,			/* alignment */
1169999SWang.Lin@Sun.COM 	0xFFF,			/* burst sizes */
1179999SWang.Lin@Sun.COM 	1,				/* minimum transfer size */
1189999SWang.Lin@Sun.COM 	0xffffffffU,	/* max transfer size */
1199999SWang.Lin@Sun.COM 	0xffffffffU,	/* address register max */
1209999SWang.Lin@Sun.COM 	1,				/* no scatter-gather */
1219999SWang.Lin@Sun.COM 	1,				/* granularity of device */
1229999SWang.Lin@Sun.COM 	0,				/* DMA flags */
1239999SWang.Lin@Sun.COM };
1249999SWang.Lin@Sun.COM 
1259999SWang.Lin@Sun.COM #define	ATH_DEF_CACHE_BYTES	32 /* default cache line size */
1269999SWang.Lin@Sun.COM 
1279999SWang.Lin@Sun.COM static kmutex_t arn_loglock;
1289999SWang.Lin@Sun.COM static void *arn_soft_state_p = NULL;
12911729SWang.Lin@Sun.COM static int arn_dwelltime = 200; /* scan interval */
1309999SWang.Lin@Sun.COM 
1319999SWang.Lin@Sun.COM static int	arn_m_stat(void *,  uint_t, uint64_t *);
1329999SWang.Lin@Sun.COM static int	arn_m_start(void *);
1339999SWang.Lin@Sun.COM static void	arn_m_stop(void *);
1349999SWang.Lin@Sun.COM static int	arn_m_promisc(void *, boolean_t);
1359999SWang.Lin@Sun.COM static int	arn_m_multicst(void *, boolean_t, const uint8_t *);
1369999SWang.Lin@Sun.COM static int	arn_m_unicst(void *, const uint8_t *);
1379999SWang.Lin@Sun.COM static mblk_t	*arn_m_tx(void *, mblk_t *);
1389999SWang.Lin@Sun.COM static void	arn_m_ioctl(void *, queue_t *, mblk_t *);
1399999SWang.Lin@Sun.COM static int	arn_m_setprop(void *, const char *, mac_prop_id_t,
1409999SWang.Lin@Sun.COM     uint_t, const void *);
1419999SWang.Lin@Sun.COM static int	arn_m_getprop(void *, const char *, mac_prop_id_t,
142*11878SVenu.Iyer@Sun.COM     uint_t, void *);
143*11878SVenu.Iyer@Sun.COM static void	arn_m_propinfo(void *, const char *, mac_prop_id_t,
144*11878SVenu.Iyer@Sun.COM     mac_prop_info_handle_t);
1459999SWang.Lin@Sun.COM 
1469999SWang.Lin@Sun.COM /* MAC Callcack Functions */
1479999SWang.Lin@Sun.COM static mac_callbacks_t arn_m_callbacks = {
148*11878SVenu.Iyer@Sun.COM 	MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
1499999SWang.Lin@Sun.COM 	arn_m_stat,
1509999SWang.Lin@Sun.COM 	arn_m_start,
1519999SWang.Lin@Sun.COM 	arn_m_stop,
1529999SWang.Lin@Sun.COM 	arn_m_promisc,
1539999SWang.Lin@Sun.COM 	arn_m_multicst,
1549999SWang.Lin@Sun.COM 	arn_m_unicst,
1559999SWang.Lin@Sun.COM 	arn_m_tx,
156*11878SVenu.Iyer@Sun.COM 	NULL,
1579999SWang.Lin@Sun.COM 	arn_m_ioctl,
1589999SWang.Lin@Sun.COM 	NULL,
1599999SWang.Lin@Sun.COM 	NULL,
1609999SWang.Lin@Sun.COM 	NULL,
1619999SWang.Lin@Sun.COM 	arn_m_setprop,
162*11878SVenu.Iyer@Sun.COM 	arn_m_getprop,
163*11878SVenu.Iyer@Sun.COM 	arn_m_propinfo
1649999SWang.Lin@Sun.COM };
1659999SWang.Lin@Sun.COM 
1669999SWang.Lin@Sun.COM /*
1679999SWang.Lin@Sun.COM  * ARN_DBG_HW
1689999SWang.Lin@Sun.COM  * ARN_DBG_REG_IO
1699999SWang.Lin@Sun.COM  * ARN_DBG_QUEUE
1709999SWang.Lin@Sun.COM  * ARN_DBG_EEPROM
1719999SWang.Lin@Sun.COM  * ARN_DBG_XMIT
1729999SWang.Lin@Sun.COM  * ARN_DBG_RECV
1739999SWang.Lin@Sun.COM  * ARN_DBG_CALIBRATE
1749999SWang.Lin@Sun.COM  * ARN_DBG_CHANNEL
1759999SWang.Lin@Sun.COM  * ARN_DBG_INTERRUPT
1769999SWang.Lin@Sun.COM  * ARN_DBG_REGULATORY
1779999SWang.Lin@Sun.COM  * ARN_DBG_ANI
1789999SWang.Lin@Sun.COM  * ARN_DBG_POWER_MGMT
1799999SWang.Lin@Sun.COM  * ARN_DBG_KEYCACHE
1809999SWang.Lin@Sun.COM  * ARN_DBG_BEACON
1819999SWang.Lin@Sun.COM  * ARN_DBG_RATE
1829999SWang.Lin@Sun.COM  * ARN_DBG_INIT
1839999SWang.Lin@Sun.COM  * ARN_DBG_ATTACH
1849999SWang.Lin@Sun.COM  * ARN_DBG_DEATCH
1859999SWang.Lin@Sun.COM  * ARN_DBG_AGGR
1869999SWang.Lin@Sun.COM  * ARN_DBG_RESET
1879999SWang.Lin@Sun.COM  * ARN_DBG_FATAL
1889999SWang.Lin@Sun.COM  * ARN_DBG_ANY
1899999SWang.Lin@Sun.COM  * ARN_DBG_ALL
1909999SWang.Lin@Sun.COM  */
1919999SWang.Lin@Sun.COM uint32_t arn_dbg_mask = 0;
1929999SWang.Lin@Sun.COM 
1939999SWang.Lin@Sun.COM /*
1949999SWang.Lin@Sun.COM  * Exception/warning cases not leading to panic.
1959999SWang.Lin@Sun.COM  */
1969999SWang.Lin@Sun.COM void
arn_problem(const int8_t * fmt,...)1979999SWang.Lin@Sun.COM arn_problem(const int8_t *fmt, ...)
1989999SWang.Lin@Sun.COM {
1999999SWang.Lin@Sun.COM 	va_list args;
2009999SWang.Lin@Sun.COM 
2019999SWang.Lin@Sun.COM 	mutex_enter(&arn_loglock);
2029999SWang.Lin@Sun.COM 
2039999SWang.Lin@Sun.COM 	va_start(args, fmt);
2049999SWang.Lin@Sun.COM 	vcmn_err(CE_WARN, fmt, args);
2059999SWang.Lin@Sun.COM 	va_end(args);
2069999SWang.Lin@Sun.COM 
2079999SWang.Lin@Sun.COM 	mutex_exit(&arn_loglock);
2089999SWang.Lin@Sun.COM }
2099999SWang.Lin@Sun.COM 
2109999SWang.Lin@Sun.COM /*
2119999SWang.Lin@Sun.COM  * Normal log information independent of debug.
2129999SWang.Lin@Sun.COM  */
2139999SWang.Lin@Sun.COM void
arn_log(const int8_t * fmt,...)2149999SWang.Lin@Sun.COM arn_log(const int8_t *fmt, ...)
2159999SWang.Lin@Sun.COM {
2169999SWang.Lin@Sun.COM 	va_list args;
2179999SWang.Lin@Sun.COM 
2189999SWang.Lin@Sun.COM 	mutex_enter(&arn_loglock);
2199999SWang.Lin@Sun.COM 
2209999SWang.Lin@Sun.COM 	va_start(args, fmt);
2219999SWang.Lin@Sun.COM 	vcmn_err(CE_CONT, fmt, args);
2229999SWang.Lin@Sun.COM 	va_end(args);
2239999SWang.Lin@Sun.COM 
2249999SWang.Lin@Sun.COM 	mutex_exit(&arn_loglock);
2259999SWang.Lin@Sun.COM }
2269999SWang.Lin@Sun.COM 
2279999SWang.Lin@Sun.COM void
arn_dbg(uint32_t dbg_flags,const int8_t * fmt,...)2289999SWang.Lin@Sun.COM arn_dbg(uint32_t dbg_flags, const int8_t *fmt, ...)
2299999SWang.Lin@Sun.COM {
2309999SWang.Lin@Sun.COM 	va_list args;
2319999SWang.Lin@Sun.COM 
2329999SWang.Lin@Sun.COM 	if (dbg_flags & arn_dbg_mask) {
2339999SWang.Lin@Sun.COM 		mutex_enter(&arn_loglock);
2349999SWang.Lin@Sun.COM 		va_start(args, fmt);
2359999SWang.Lin@Sun.COM 		vcmn_err(CE_CONT, fmt, args);
2369999SWang.Lin@Sun.COM 		va_end(args);
2379999SWang.Lin@Sun.COM 		mutex_exit(&arn_loglock);
2389999SWang.Lin@Sun.COM 	}
2399999SWang.Lin@Sun.COM }
2409999SWang.Lin@Sun.COM 
2419999SWang.Lin@Sun.COM /*
2429999SWang.Lin@Sun.COM  * Read and write, they both share the same lock. We do this to serialize
2439999SWang.Lin@Sun.COM  * reads and writes on Atheros 802.11n PCI devices only. This is required
2449999SWang.Lin@Sun.COM  * as the FIFO on these devices can only accept sanely 2 requests. After
2459999SWang.Lin@Sun.COM  * that the device goes bananas. Serializing the reads/writes prevents this
2469999SWang.Lin@Sun.COM  * from happening.
2479999SWang.Lin@Sun.COM  */
2489999SWang.Lin@Sun.COM void
arn_iowrite32(struct ath_hal * ah,uint32_t reg_offset,uint32_t val)2499999SWang.Lin@Sun.COM arn_iowrite32(struct ath_hal *ah, uint32_t reg_offset, uint32_t val)
2509999SWang.Lin@Sun.COM {
2519999SWang.Lin@Sun.COM 	struct arn_softc *sc = ah->ah_sc;
2529999SWang.Lin@Sun.COM 	if (ah->ah_config.serialize_regmode == SER_REG_MODE_ON) {
2539999SWang.Lin@Sun.COM 		mutex_enter(&sc->sc_serial_rw);
2549999SWang.Lin@Sun.COM 		ddi_put32(sc->sc_io_handle,
2559999SWang.Lin@Sun.COM 		    (uint32_t *)((uintptr_t)(sc->mem) + (reg_offset)), val);
2569999SWang.Lin@Sun.COM 		mutex_exit(&sc->sc_serial_rw);
2579999SWang.Lin@Sun.COM 	} else {
2589999SWang.Lin@Sun.COM 		ddi_put32(sc->sc_io_handle,
2599999SWang.Lin@Sun.COM 		    (uint32_t *)((uintptr_t)(sc->mem) + (reg_offset)), val);
2609999SWang.Lin@Sun.COM 	}
2619999SWang.Lin@Sun.COM }
2629999SWang.Lin@Sun.COM 
2639999SWang.Lin@Sun.COM unsigned int
arn_ioread32(struct ath_hal * ah,uint32_t reg_offset)2649999SWang.Lin@Sun.COM arn_ioread32(struct ath_hal *ah, uint32_t reg_offset)
2659999SWang.Lin@Sun.COM {
2669999SWang.Lin@Sun.COM 	uint32_t val;
2679999SWang.Lin@Sun.COM 	struct arn_softc *sc = ah->ah_sc;
2689999SWang.Lin@Sun.COM 	if (ah->ah_config.serialize_regmode == SER_REG_MODE_ON) {
2699999SWang.Lin@Sun.COM 		mutex_enter(&sc->sc_serial_rw);
2709999SWang.Lin@Sun.COM 		val = ddi_get32(sc->sc_io_handle,
2719999SWang.Lin@Sun.COM 		    (uint32_t *)((uintptr_t)(sc->mem) + (reg_offset)));
2729999SWang.Lin@Sun.COM 		mutex_exit(&sc->sc_serial_rw);
2739999SWang.Lin@Sun.COM 	} else {
2749999SWang.Lin@Sun.COM 		val = ddi_get32(sc->sc_io_handle,
2759999SWang.Lin@Sun.COM 		    (uint32_t *)((uintptr_t)(sc->mem) + (reg_offset)));
2769999SWang.Lin@Sun.COM 	}
2779999SWang.Lin@Sun.COM 
2789999SWang.Lin@Sun.COM 	return (val);
2799999SWang.Lin@Sun.COM }
2809999SWang.Lin@Sun.COM 
2819999SWang.Lin@Sun.COM /*
2829999SWang.Lin@Sun.COM  * Allocate an area of memory and a DMA handle for accessing it
2839999SWang.Lin@Sun.COM  */
2849999SWang.Lin@Sun.COM static int
arn_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,dma_area_t * dma_p)2859999SWang.Lin@Sun.COM arn_alloc_dma_mem(dev_info_t *devinfo, ddi_dma_attr_t *dma_attr, size_t memsize,
2869999SWang.Lin@Sun.COM     ddi_device_acc_attr_t *attr_p, uint_t alloc_flags,
2879999SWang.Lin@Sun.COM     uint_t bind_flags, dma_area_t *dma_p)
2889999SWang.Lin@Sun.COM {
2899999SWang.Lin@Sun.COM 	int err;
2909999SWang.Lin@Sun.COM 
2919999SWang.Lin@Sun.COM 	/*
2929999SWang.Lin@Sun.COM 	 * Allocate handle
2939999SWang.Lin@Sun.COM 	 */
2949999SWang.Lin@Sun.COM 	err = ddi_dma_alloc_handle(devinfo, dma_attr,
2959999SWang.Lin@Sun.COM 	    DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl);
2969999SWang.Lin@Sun.COM 	if (err != DDI_SUCCESS)
2979999SWang.Lin@Sun.COM 		return (DDI_FAILURE);
2989999SWang.Lin@Sun.COM 
2999999SWang.Lin@Sun.COM 	/*
3009999SWang.Lin@Sun.COM 	 * Allocate memory
3019999SWang.Lin@Sun.COM 	 */
3029999SWang.Lin@Sun.COM 	err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize, attr_p,
3039999SWang.Lin@Sun.COM 	    alloc_flags, DDI_DMA_SLEEP, NULL, &dma_p->mem_va,
3049999SWang.Lin@Sun.COM 	    &dma_p->alength, &dma_p->acc_hdl);
3059999SWang.Lin@Sun.COM 	if (err != DDI_SUCCESS)
3069999SWang.Lin@Sun.COM 		return (DDI_FAILURE);
3079999SWang.Lin@Sun.COM 
3089999SWang.Lin@Sun.COM 	/*
3099999SWang.Lin@Sun.COM 	 * Bind the two together
3109999SWang.Lin@Sun.COM 	 */
3119999SWang.Lin@Sun.COM 	err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL,
3129999SWang.Lin@Sun.COM 	    dma_p->mem_va, dma_p->alength, bind_flags,
3139999SWang.Lin@Sun.COM 	    DDI_DMA_SLEEP, NULL, &dma_p->cookie, &dma_p->ncookies);
3149999SWang.Lin@Sun.COM 	if (err != DDI_DMA_MAPPED)
3159999SWang.Lin@Sun.COM 		return (DDI_FAILURE);
3169999SWang.Lin@Sun.COM 
3179999SWang.Lin@Sun.COM 	dma_p->nslots = ~0U;
3189999SWang.Lin@Sun.COM 	dma_p->size = ~0U;
3199999SWang.Lin@Sun.COM 	dma_p->token = ~0U;
3209999SWang.Lin@Sun.COM 	dma_p->offset = 0;
3219999SWang.Lin@Sun.COM 	return (DDI_SUCCESS);
3229999SWang.Lin@Sun.COM }
3239999SWang.Lin@Sun.COM 
3249999SWang.Lin@Sun.COM /*
3259999SWang.Lin@Sun.COM  * Free one allocated area of DMAable memory
3269999SWang.Lin@Sun.COM  */
3279999SWang.Lin@Sun.COM static void
arn_free_dma_mem(dma_area_t * dma_p)3289999SWang.Lin@Sun.COM arn_free_dma_mem(dma_area_t *dma_p)
3299999SWang.Lin@Sun.COM {
3309999SWang.Lin@Sun.COM 	if (dma_p->dma_hdl != NULL) {
3319999SWang.Lin@Sun.COM 		(void) ddi_dma_unbind_handle(dma_p->dma_hdl);
3329999SWang.Lin@Sun.COM 		if (dma_p->acc_hdl != NULL) {
3339999SWang.Lin@Sun.COM 			ddi_dma_mem_free(&dma_p->acc_hdl);
3349999SWang.Lin@Sun.COM 			dma_p->acc_hdl = NULL;
3359999SWang.Lin@Sun.COM 		}
3369999SWang.Lin@Sun.COM 		ddi_dma_free_handle(&dma_p->dma_hdl);
3379999SWang.Lin@Sun.COM 		dma_p->ncookies = 0;
3389999SWang.Lin@Sun.COM 		dma_p->dma_hdl = NULL;
3399999SWang.Lin@Sun.COM 	}
3409999SWang.Lin@Sun.COM }
3419999SWang.Lin@Sun.COM 
3429999SWang.Lin@Sun.COM /*
3439999SWang.Lin@Sun.COM  * Initialize tx, rx. or beacon buffer list. Allocate DMA memory for
3449999SWang.Lin@Sun.COM  * each buffer.
3459999SWang.Lin@Sun.COM  */
3469999SWang.Lin@Sun.COM static int
arn_buflist_setup(dev_info_t * devinfo,struct arn_softc * sc,list_t * bflist,struct ath_buf ** pbf,struct ath_desc ** pds,int nbuf,uint_t dmabflags,uint32_t buflen)34711729SWang.Lin@Sun.COM arn_buflist_setup(dev_info_t *devinfo,
34811729SWang.Lin@Sun.COM     struct arn_softc *sc,
34911729SWang.Lin@Sun.COM     list_t *bflist,
35011729SWang.Lin@Sun.COM     struct ath_buf **pbf,
35111729SWang.Lin@Sun.COM     struct ath_desc **pds,
35211729SWang.Lin@Sun.COM     int nbuf,
35311729SWang.Lin@Sun.COM     uint_t dmabflags,
35411729SWang.Lin@Sun.COM     uint32_t buflen)
3559999SWang.Lin@Sun.COM {
3569999SWang.Lin@Sun.COM 	int i, err;
3579999SWang.Lin@Sun.COM 	struct ath_buf *bf = *pbf;
3589999SWang.Lin@Sun.COM 	struct ath_desc *ds = *pds;
3599999SWang.Lin@Sun.COM 
3609999SWang.Lin@Sun.COM 	list_create(bflist, sizeof (struct ath_buf),
3619999SWang.Lin@Sun.COM 	    offsetof(struct ath_buf, bf_node));
3629999SWang.Lin@Sun.COM 	for (i = 0; i < nbuf; i++, bf++, ds++) {
3639999SWang.Lin@Sun.COM 		bf->bf_desc = ds;
3649999SWang.Lin@Sun.COM 		bf->bf_daddr = sc->sc_desc_dma.cookie.dmac_address +
3659999SWang.Lin@Sun.COM 		    ((uintptr_t)ds - (uintptr_t)sc->sc_desc);
3669999SWang.Lin@Sun.COM 		list_insert_tail(bflist, bf);
3679999SWang.Lin@Sun.COM 
3689999SWang.Lin@Sun.COM 		/* alloc DMA memory */
3699999SWang.Lin@Sun.COM 		err = arn_alloc_dma_mem(devinfo, &arn_dma_attr,
37011729SWang.Lin@Sun.COM 		    buflen, &arn_desc_accattr, DDI_DMA_STREAMING,
3719999SWang.Lin@Sun.COM 		    dmabflags, &bf->bf_dma);
3729999SWang.Lin@Sun.COM 		if (err != DDI_SUCCESS)
3739999SWang.Lin@Sun.COM 			return (err);
3749999SWang.Lin@Sun.COM 	}
3759999SWang.Lin@Sun.COM 	*pbf = bf;
3769999SWang.Lin@Sun.COM 	*pds = ds;
3779999SWang.Lin@Sun.COM 
3789999SWang.Lin@Sun.COM 	return (DDI_SUCCESS);
3799999SWang.Lin@Sun.COM }
3809999SWang.Lin@Sun.COM 
3819999SWang.Lin@Sun.COM /*
3829999SWang.Lin@Sun.COM  * Destroy tx, rx or beacon buffer list. Free DMA memory.
3839999SWang.Lin@Sun.COM  */
3849999SWang.Lin@Sun.COM static void
arn_buflist_cleanup(list_t * buflist)3859999SWang.Lin@Sun.COM arn_buflist_cleanup(list_t *buflist)
3869999SWang.Lin@Sun.COM {
3879999SWang.Lin@Sun.COM 	struct ath_buf *bf;
3889999SWang.Lin@Sun.COM 
3899999SWang.Lin@Sun.COM 	if (!buflist)
3909999SWang.Lin@Sun.COM 		return;
3919999SWang.Lin@Sun.COM 
3929999SWang.Lin@Sun.COM 	bf = list_head(buflist);
3939999SWang.Lin@Sun.COM 	while (bf != NULL) {
3949999SWang.Lin@Sun.COM 		if (bf->bf_m != NULL) {
3959999SWang.Lin@Sun.COM 			freemsg(bf->bf_m);
3969999SWang.Lin@Sun.COM 			bf->bf_m = NULL;
3979999SWang.Lin@Sun.COM 		}
3989999SWang.Lin@Sun.COM 		/* Free DMA buffer */
3999999SWang.Lin@Sun.COM 		arn_free_dma_mem(&bf->bf_dma);
4009999SWang.Lin@Sun.COM 		if (bf->bf_in != NULL) {
4019999SWang.Lin@Sun.COM 			ieee80211_free_node(bf->bf_in);
4029999SWang.Lin@Sun.COM 			bf->bf_in = NULL;
4039999SWang.Lin@Sun.COM 		}
4049999SWang.Lin@Sun.COM 		list_remove(buflist, bf);
4059999SWang.Lin@Sun.COM 		bf = list_head(buflist);
4069999SWang.Lin@Sun.COM 	}
4079999SWang.Lin@Sun.COM 	list_destroy(buflist);
4089999SWang.Lin@Sun.COM }
4099999SWang.Lin@Sun.COM 
4109999SWang.Lin@Sun.COM static void
arn_desc_free(struct arn_softc * sc)4119999SWang.Lin@Sun.COM arn_desc_free(struct arn_softc *sc)
4129999SWang.Lin@Sun.COM {
4139999SWang.Lin@Sun.COM 	arn_buflist_cleanup(&sc->sc_txbuf_list);
4149999SWang.Lin@Sun.COM 	arn_buflist_cleanup(&sc->sc_rxbuf_list);
4159999SWang.Lin@Sun.COM #ifdef ARN_IBSS
4169999SWang.Lin@Sun.COM 	arn_buflist_cleanup(&sc->sc_bcbuf_list);
4179999SWang.Lin@Sun.COM #endif
4189999SWang.Lin@Sun.COM 
4199999SWang.Lin@Sun.COM 	/* Free descriptor DMA buffer */
4209999SWang.Lin@Sun.COM 	arn_free_dma_mem(&sc->sc_desc_dma);
4219999SWang.Lin@Sun.COM 
4229999SWang.Lin@Sun.COM 	kmem_free((void *)sc->sc_vbufptr, sc->sc_vbuflen);
4239999SWang.Lin@Sun.COM 	sc->sc_vbufptr = NULL;
4249999SWang.Lin@Sun.COM }
4259999SWang.Lin@Sun.COM 
4269999SWang.Lin@Sun.COM static int
arn_desc_alloc(dev_info_t * devinfo,struct arn_softc * sc)4279999SWang.Lin@Sun.COM arn_desc_alloc(dev_info_t *devinfo, struct arn_softc *sc)
4289999SWang.Lin@Sun.COM {
4299999SWang.Lin@Sun.COM 	int err;
4309999SWang.Lin@Sun.COM 	size_t size;
4319999SWang.Lin@Sun.COM 	struct ath_desc *ds;
4329999SWang.Lin@Sun.COM 	struct ath_buf *bf;
4339999SWang.Lin@Sun.COM 
4349999SWang.Lin@Sun.COM #ifdef ARN_IBSS
4359999SWang.Lin@Sun.COM 	size = sizeof (struct ath_desc) * (ATH_TXBUF + ATH_RXBUF + ATH_BCBUF);
4369999SWang.Lin@Sun.COM #else
4379999SWang.Lin@Sun.COM 	size = sizeof (struct ath_desc) * (ATH_TXBUF + ATH_RXBUF);
4389999SWang.Lin@Sun.COM #endif
4399999SWang.Lin@Sun.COM 
4409999SWang.Lin@Sun.COM 	err = arn_alloc_dma_mem(devinfo, &arn_desc_dma_attr, size,
4419999SWang.Lin@Sun.COM 	    &arn_desc_accattr, DDI_DMA_CONSISTENT,
4429999SWang.Lin@Sun.COM 	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, &sc->sc_desc_dma);
4439999SWang.Lin@Sun.COM 
4449999SWang.Lin@Sun.COM 	/* virtual address of the first descriptor */
4459999SWang.Lin@Sun.COM 	sc->sc_desc = (struct ath_desc *)sc->sc_desc_dma.mem_va;
4469999SWang.Lin@Sun.COM 
4479999SWang.Lin@Sun.COM 	ds = sc->sc_desc;
4489999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_INIT, "arn: arn_desc_alloc(): DMA map: "
4499999SWang.Lin@Sun.COM 	    "%p (%d) -> %p\n",
4509999SWang.Lin@Sun.COM 	    sc->sc_desc, sc->sc_desc_dma.alength,
4519999SWang.Lin@Sun.COM 	    sc->sc_desc_dma.cookie.dmac_address));
4529999SWang.Lin@Sun.COM 
4539999SWang.Lin@Sun.COM 	/* allocate data structures to describe TX/RX DMA buffers */
4549999SWang.Lin@Sun.COM #ifdef ARN_IBSS
4559999SWang.Lin@Sun.COM 	sc->sc_vbuflen = sizeof (struct ath_buf) * (ATH_TXBUF + ATH_RXBUF +
4569999SWang.Lin@Sun.COM 	    ATH_BCBUF);
4579999SWang.Lin@Sun.COM #else
4589999SWang.Lin@Sun.COM 	sc->sc_vbuflen = sizeof (struct ath_buf) * (ATH_TXBUF + ATH_RXBUF);
4599999SWang.Lin@Sun.COM #endif
4609999SWang.Lin@Sun.COM 	bf = (struct ath_buf *)kmem_zalloc(sc->sc_vbuflen, KM_SLEEP);
4619999SWang.Lin@Sun.COM 	sc->sc_vbufptr = bf;
4629999SWang.Lin@Sun.COM 
4639999SWang.Lin@Sun.COM 	/* DMA buffer size for each TX/RX packet */
46411729SWang.Lin@Sun.COM #ifdef ARN_TX_AGGREGRATION
46511729SWang.Lin@Sun.COM 	sc->tx_dmabuf_size =
46611729SWang.Lin@Sun.COM 	    roundup((IEEE80211_MAX_MPDU_LEN + 3840 * 2),
46711729SWang.Lin@Sun.COM 	    min(sc->sc_cachelsz, (uint16_t)64));
46811729SWang.Lin@Sun.COM #else
46911729SWang.Lin@Sun.COM 	sc->tx_dmabuf_size =
47011729SWang.Lin@Sun.COM 	    roundup(IEEE80211_MAX_MPDU_LEN, min(sc->sc_cachelsz, (uint16_t)64));
47111729SWang.Lin@Sun.COM #endif
47211729SWang.Lin@Sun.COM 	sc->rx_dmabuf_size =
47311729SWang.Lin@Sun.COM 	    roundup(IEEE80211_MAX_MPDU_LEN, min(sc->sc_cachelsz, (uint16_t)64));
4749999SWang.Lin@Sun.COM 
4759999SWang.Lin@Sun.COM 	/* create RX buffer list */
4769999SWang.Lin@Sun.COM 	err = arn_buflist_setup(devinfo, sc, &sc->sc_rxbuf_list, &bf, &ds,
47711729SWang.Lin@Sun.COM 	    ATH_RXBUF, DDI_DMA_READ | DDI_DMA_STREAMING, sc->rx_dmabuf_size);
4789999SWang.Lin@Sun.COM 	if (err != DDI_SUCCESS) {
4799999SWang.Lin@Sun.COM 		arn_desc_free(sc);
4809999SWang.Lin@Sun.COM 		return (err);
4819999SWang.Lin@Sun.COM 	}
4829999SWang.Lin@Sun.COM 
4839999SWang.Lin@Sun.COM 	/* create TX buffer list */
4849999SWang.Lin@Sun.COM 	err = arn_buflist_setup(devinfo, sc, &sc->sc_txbuf_list, &bf, &ds,
48511729SWang.Lin@Sun.COM 	    ATH_TXBUF, DDI_DMA_STREAMING, sc->tx_dmabuf_size);
4869999SWang.Lin@Sun.COM 	if (err != DDI_SUCCESS) {
4879999SWang.Lin@Sun.COM 		arn_desc_free(sc);
4889999SWang.Lin@Sun.COM 		return (err);
4899999SWang.Lin@Sun.COM 	}
4909999SWang.Lin@Sun.COM 
4919999SWang.Lin@Sun.COM 	/* create beacon buffer list */
4929999SWang.Lin@Sun.COM #ifdef ARN_IBSS
4939999SWang.Lin@Sun.COM 	err = arn_buflist_setup(devinfo, sc, &sc->sc_bcbuf_list, &bf, &ds,
4949999SWang.Lin@Sun.COM 	    ATH_BCBUF, DDI_DMA_STREAMING);
4959999SWang.Lin@Sun.COM 	if (err != DDI_SUCCESS) {
4969999SWang.Lin@Sun.COM 		arn_desc_free(sc);
4979999SWang.Lin@Sun.COM 		return (err);
4989999SWang.Lin@Sun.COM 	}
4999999SWang.Lin@Sun.COM #endif
5009999SWang.Lin@Sun.COM 
5019999SWang.Lin@Sun.COM 	return (DDI_SUCCESS);
5029999SWang.Lin@Sun.COM }
5039999SWang.Lin@Sun.COM 
5049999SWang.Lin@Sun.COM static struct ath_rate_table *
5059999SWang.Lin@Sun.COM /* LINTED E_STATIC_UNUSED */
arn_get_ratetable(struct arn_softc * sc,uint32_t mode)5069999SWang.Lin@Sun.COM arn_get_ratetable(struct arn_softc *sc, uint32_t mode)
5079999SWang.Lin@Sun.COM {
5089999SWang.Lin@Sun.COM 	struct ath_rate_table *rate_table = NULL;
5099999SWang.Lin@Sun.COM 
5109999SWang.Lin@Sun.COM 	switch (mode) {
5119999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11A:
5129999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11A];
5139999SWang.Lin@Sun.COM 		break;
5149999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11B:
5159999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11B];
5169999SWang.Lin@Sun.COM 		break;
5179999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11G:
5189999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11G];
5199999SWang.Lin@Sun.COM 		break;
5209999SWang.Lin@Sun.COM #ifdef ARB_11N
5219999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NA_HT20:
5229999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT20];
5239999SWang.Lin@Sun.COM 		break;
5249999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NG_HT20:
5259999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT20];
5269999SWang.Lin@Sun.COM 		break;
5279999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NA_HT40PLUS:
5289999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS];
5299999SWang.Lin@Sun.COM 		break;
5309999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NA_HT40MINUS:
5319999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS];
5329999SWang.Lin@Sun.COM 		break;
5339999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NG_HT40PLUS:
5349999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS];
5359999SWang.Lin@Sun.COM 		break;
5369999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NG_HT40MINUS:
5379999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS];
5389999SWang.Lin@Sun.COM 		break;
5399999SWang.Lin@Sun.COM #endif
5409999SWang.Lin@Sun.COM 	default:
5419999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_FATAL, "arn: arn_get_ratetable(): "
5429999SWang.Lin@Sun.COM 		    "invalid mode %u\n", mode));
5439999SWang.Lin@Sun.COM 		return (NULL);
5449999SWang.Lin@Sun.COM 	}
5459999SWang.Lin@Sun.COM 
5469999SWang.Lin@Sun.COM 	return (rate_table);
5479999SWang.Lin@Sun.COM 
5489999SWang.Lin@Sun.COM }
5499999SWang.Lin@Sun.COM 
5509999SWang.Lin@Sun.COM static void
arn_setcurmode(struct arn_softc * sc,enum wireless_mode mode)5519999SWang.Lin@Sun.COM arn_setcurmode(struct arn_softc *sc, enum wireless_mode mode)
5529999SWang.Lin@Sun.COM {
5539999SWang.Lin@Sun.COM 	struct ath_rate_table *rt;
5549999SWang.Lin@Sun.COM 	int i;
5559999SWang.Lin@Sun.COM 
5569999SWang.Lin@Sun.COM 	for (i = 0; i < sizeof (sc->asc_rixmap); i++)
5579999SWang.Lin@Sun.COM 		sc->asc_rixmap[i] = 0xff;
5589999SWang.Lin@Sun.COM 
5599999SWang.Lin@Sun.COM 	rt = sc->hw_rate_table[mode];
5609999SWang.Lin@Sun.COM 	ASSERT(rt != NULL);
5619999SWang.Lin@Sun.COM 
5629999SWang.Lin@Sun.COM 	for (i = 0; i < rt->rate_cnt; i++)
5639999SWang.Lin@Sun.COM 		sc->asc_rixmap[rt->info[i].dot11rate &
5649999SWang.Lin@Sun.COM 		    IEEE80211_RATE_VAL] = (uint8_t)i; /* LINT */
5659999SWang.Lin@Sun.COM 
5669999SWang.Lin@Sun.COM 	sc->sc_currates = rt;
5679999SWang.Lin@Sun.COM 	sc->sc_curmode = mode;
5689999SWang.Lin@Sun.COM 
5699999SWang.Lin@Sun.COM 	/*
5709999SWang.Lin@Sun.COM 	 * All protection frames are transmited at 2Mb/s for
5719999SWang.Lin@Sun.COM 	 * 11g, otherwise at 1Mb/s.
5729999SWang.Lin@Sun.COM 	 * XXX select protection rate index from rate table.
5739999SWang.Lin@Sun.COM 	 */
5749999SWang.Lin@Sun.COM 	sc->sc_protrix = (mode == ATH9K_MODE_11G ? 1 : 0);
5759999SWang.Lin@Sun.COM }
5769999SWang.Lin@Sun.COM 
5779999SWang.Lin@Sun.COM static enum wireless_mode
arn_chan2mode(struct ath9k_channel * chan)5789999SWang.Lin@Sun.COM arn_chan2mode(struct ath9k_channel *chan)
5799999SWang.Lin@Sun.COM {
5809999SWang.Lin@Sun.COM 	if (chan->chanmode == CHANNEL_A)
5819999SWang.Lin@Sun.COM 		return (ATH9K_MODE_11A);
5829999SWang.Lin@Sun.COM 	else if (chan->chanmode == CHANNEL_G)
5839999SWang.Lin@Sun.COM 		return (ATH9K_MODE_11G);
5849999SWang.Lin@Sun.COM 	else if (chan->chanmode == CHANNEL_B)
5859999SWang.Lin@Sun.COM 		return (ATH9K_MODE_11B);
5869999SWang.Lin@Sun.COM 	else if (chan->chanmode == CHANNEL_A_HT20)
5879999SWang.Lin@Sun.COM 		return (ATH9K_MODE_11NA_HT20);
5889999SWang.Lin@Sun.COM 	else if (chan->chanmode == CHANNEL_G_HT20)
5899999SWang.Lin@Sun.COM 		return (ATH9K_MODE_11NG_HT20);
5909999SWang.Lin@Sun.COM 	else if (chan->chanmode == CHANNEL_A_HT40PLUS)
5919999SWang.Lin@Sun.COM 		return (ATH9K_MODE_11NA_HT40PLUS);
5929999SWang.Lin@Sun.COM 	else if (chan->chanmode == CHANNEL_A_HT40MINUS)
5939999SWang.Lin@Sun.COM 		return (ATH9K_MODE_11NA_HT40MINUS);
5949999SWang.Lin@Sun.COM 	else if (chan->chanmode == CHANNEL_G_HT40PLUS)
5959999SWang.Lin@Sun.COM 		return (ATH9K_MODE_11NG_HT40PLUS);
5969999SWang.Lin@Sun.COM 	else if (chan->chanmode == CHANNEL_G_HT40MINUS)
5979999SWang.Lin@Sun.COM 		return (ATH9K_MODE_11NG_HT40MINUS);
5989999SWang.Lin@Sun.COM 
5999999SWang.Lin@Sun.COM 	return (ATH9K_MODE_11B);
6009999SWang.Lin@Sun.COM }
6019999SWang.Lin@Sun.COM 
6029999SWang.Lin@Sun.COM static void
arn_update_txpow(struct arn_softc * sc)6039999SWang.Lin@Sun.COM arn_update_txpow(struct arn_softc *sc)
6049999SWang.Lin@Sun.COM {
6059999SWang.Lin@Sun.COM 	struct ath_hal 	*ah = sc->sc_ah;
6069999SWang.Lin@Sun.COM 	uint32_t txpow;
6079999SWang.Lin@Sun.COM 
6089999SWang.Lin@Sun.COM 	if (sc->sc_curtxpow != sc->sc_config.txpowlimit) {
6099999SWang.Lin@Sun.COM 		(void) ath9k_hw_set_txpowerlimit(ah, sc->sc_config.txpowlimit);
6109999SWang.Lin@Sun.COM 		/* read back in case value is clamped */
6119999SWang.Lin@Sun.COM 		(void) ath9k_hw_getcapability(ah, ATH9K_CAP_TXPOW, 1, &txpow);
6129999SWang.Lin@Sun.COM 		sc->sc_curtxpow = (uint32_t)txpow;
6139999SWang.Lin@Sun.COM 	}
6149999SWang.Lin@Sun.COM }
6159999SWang.Lin@Sun.COM 
61611729SWang.Lin@Sun.COM uint8_t
parse_mpdudensity(uint8_t mpdudensity)61711729SWang.Lin@Sun.COM parse_mpdudensity(uint8_t mpdudensity)
61811729SWang.Lin@Sun.COM {
61911729SWang.Lin@Sun.COM 	/*
62011729SWang.Lin@Sun.COM 	 * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing":
62111729SWang.Lin@Sun.COM 	 *   0 for no restriction
62211729SWang.Lin@Sun.COM 	 *   1 for 1/4 us
62311729SWang.Lin@Sun.COM 	 *   2 for 1/2 us
62411729SWang.Lin@Sun.COM 	 *   3 for 1 us
62511729SWang.Lin@Sun.COM 	 *   4 for 2 us
62611729SWang.Lin@Sun.COM 	 *   5 for 4 us
62711729SWang.Lin@Sun.COM 	 *   6 for 8 us
62811729SWang.Lin@Sun.COM 	 *   7 for 16 us
62911729SWang.Lin@Sun.COM 	 */
63011729SWang.Lin@Sun.COM 	switch (mpdudensity) {
63111729SWang.Lin@Sun.COM 	case 0:
63211729SWang.Lin@Sun.COM 		return (0);
63311729SWang.Lin@Sun.COM 	case 1:
63411729SWang.Lin@Sun.COM 	case 2:
63511729SWang.Lin@Sun.COM 	case 3:
63611729SWang.Lin@Sun.COM 		/*
63711729SWang.Lin@Sun.COM 		 * Our lower layer calculations limit our
63811729SWang.Lin@Sun.COM 		 * precision to 1 microsecond
63911729SWang.Lin@Sun.COM 		 */
64011729SWang.Lin@Sun.COM 		return (1);
64111729SWang.Lin@Sun.COM 	case 4:
64211729SWang.Lin@Sun.COM 		return (2);
64311729SWang.Lin@Sun.COM 	case 5:
64411729SWang.Lin@Sun.COM 		return (4);
64511729SWang.Lin@Sun.COM 	case 6:
64611729SWang.Lin@Sun.COM 		return (8);
64711729SWang.Lin@Sun.COM 	case 7:
64811729SWang.Lin@Sun.COM 		return (16);
64911729SWang.Lin@Sun.COM 	default:
65011729SWang.Lin@Sun.COM 		return (0);
65111729SWang.Lin@Sun.COM 	}
65211729SWang.Lin@Sun.COM }
65311729SWang.Lin@Sun.COM 
6549999SWang.Lin@Sun.COM static void
arn_setup_rates(struct arn_softc * sc,uint32_t mode)6559999SWang.Lin@Sun.COM arn_setup_rates(struct arn_softc *sc, uint32_t mode)
6569999SWang.Lin@Sun.COM {
6579999SWang.Lin@Sun.COM 	int i, maxrates;
6589999SWang.Lin@Sun.COM 	struct ath_rate_table *rate_table = NULL;
6599999SWang.Lin@Sun.COM 	struct ieee80211_rateset *rateset;
6609999SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
6619999SWang.Lin@Sun.COM 
6629999SWang.Lin@Sun.COM 	/* rate_table = arn_get_ratetable(sc, mode); */
6639999SWang.Lin@Sun.COM 	switch (mode) {
6649999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11A:
6659999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11A];
6669999SWang.Lin@Sun.COM 		break;
6679999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11B:
6689999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11B];
6699999SWang.Lin@Sun.COM 		break;
6709999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11G:
6719999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11G];
6729999SWang.Lin@Sun.COM 		break;
6739999SWang.Lin@Sun.COM #ifdef ARN_11N
6749999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NA_HT20:
6759999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT20];
6769999SWang.Lin@Sun.COM 		break;
6779999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NG_HT20:
6789999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT20];
6799999SWang.Lin@Sun.COM 		break;
6809999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NA_HT40PLUS:
6819999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS];
6829999SWang.Lin@Sun.COM 		break;
6839999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NA_HT40MINUS:
6849999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS];
6859999SWang.Lin@Sun.COM 		break;
6869999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NG_HT40PLUS:
6879999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS];
6889999SWang.Lin@Sun.COM 		break;
6899999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NG_HT40MINUS:
6909999SWang.Lin@Sun.COM 		rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS];
6919999SWang.Lin@Sun.COM 		break;
6929999SWang.Lin@Sun.COM #endif
6939999SWang.Lin@Sun.COM 	default:
6949999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RATE, "arn: arn_get_ratetable(): "
6959999SWang.Lin@Sun.COM 		    "invalid mode %u\n", mode));
6969999SWang.Lin@Sun.COM 		break;
6979999SWang.Lin@Sun.COM 	}
6989999SWang.Lin@Sun.COM 	if (rate_table == NULL)
6999999SWang.Lin@Sun.COM 		return;
7009999SWang.Lin@Sun.COM 	if (rate_table->rate_cnt > ATH_RATE_MAX) {
7019999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RATE, "arn: arn_rate_setup(): "
7029999SWang.Lin@Sun.COM 		    "rate table too small (%u > %u)\n",
7039999SWang.Lin@Sun.COM 		    rate_table->rate_cnt, IEEE80211_RATE_MAXSIZE));
7049999SWang.Lin@Sun.COM 		maxrates = ATH_RATE_MAX;
7059999SWang.Lin@Sun.COM 	} else
7069999SWang.Lin@Sun.COM 		maxrates = rate_table->rate_cnt;
7079999SWang.Lin@Sun.COM 
7089999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_RATE, "arn: arn_rate_setup(): "
7099999SWang.Lin@Sun.COM 	    "maxrates is %d\n", maxrates));
7109999SWang.Lin@Sun.COM 
7119999SWang.Lin@Sun.COM 	rateset = &ic->ic_sup_rates[mode];
7129999SWang.Lin@Sun.COM 	for (i = 0; i < maxrates; i++) {
7139999SWang.Lin@Sun.COM 		rateset->ir_rates[i] = rate_table->info[i].dot11rate;
7149999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RATE, "arn: arn_rate_setup(): "
7159999SWang.Lin@Sun.COM 		    "%d\n", rate_table->info[i].dot11rate));
7169999SWang.Lin@Sun.COM 	}
7179999SWang.Lin@Sun.COM 	rateset->ir_nrates = (uint8_t)maxrates; /* ??? */
7189999SWang.Lin@Sun.COM }
7199999SWang.Lin@Sun.COM 
7209999SWang.Lin@Sun.COM static int
arn_setup_channels(struct arn_softc * sc)7219999SWang.Lin@Sun.COM arn_setup_channels(struct arn_softc *sc)
7229999SWang.Lin@Sun.COM {
7239999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
7249999SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
7259999SWang.Lin@Sun.COM 	int nchan, i, index;
7269999SWang.Lin@Sun.COM 	uint8_t regclassids[ATH_REGCLASSIDS_MAX];
7279999SWang.Lin@Sun.COM 	uint32_t nregclass = 0;
7289999SWang.Lin@Sun.COM 	struct ath9k_channel *c;
7299999SWang.Lin@Sun.COM 
7309999SWang.Lin@Sun.COM 	/* Fill in ah->ah_channels */
7319999SWang.Lin@Sun.COM 	if (!ath9k_regd_init_channels(ah, ATH_CHAN_MAX, (uint32_t *)&nchan,
7329999SWang.Lin@Sun.COM 	    regclassids, ATH_REGCLASSIDS_MAX, &nregclass, CTRY_DEFAULT,
7339999SWang.Lin@Sun.COM 	    B_FALSE, 1)) {
7349999SWang.Lin@Sun.COM 		uint32_t rd = ah->ah_currentRD;
7359999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CHANNEL, "arn: arn_setup_channels(): "
7369999SWang.Lin@Sun.COM 		    "unable to collect channel list; "
7379999SWang.Lin@Sun.COM 		    "regdomain likely %u country code %u\n",
7389999SWang.Lin@Sun.COM 		    rd, CTRY_DEFAULT));
7399999SWang.Lin@Sun.COM 		return (EINVAL);
7409999SWang.Lin@Sun.COM 	}
7419999SWang.Lin@Sun.COM 
7429999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_CHANNEL, "arn: arn_setup_channels(): "
7439999SWang.Lin@Sun.COM 	    "number of channel is %d\n", nchan));
7449999SWang.Lin@Sun.COM 
7459999SWang.Lin@Sun.COM 	for (i = 0; i < nchan; i++) {
7469999SWang.Lin@Sun.COM 		c = &ah->ah_channels[i];
74711729SWang.Lin@Sun.COM 		uint32_t flags;
7489999SWang.Lin@Sun.COM 		index = ath9k_hw_mhz2ieee(ah, c->channel, c->channelFlags);
7499999SWang.Lin@Sun.COM 
7509999SWang.Lin@Sun.COM 		if (index > IEEE80211_CHAN_MAX) {
7519999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CHANNEL,
7529999SWang.Lin@Sun.COM 			    "arn: arn_setup_channels(): "
7539999SWang.Lin@Sun.COM 			    "bad hal channel %d (%u/%x) ignored\n",
7549999SWang.Lin@Sun.COM 			    index, c->channel, c->channelFlags));
7559999SWang.Lin@Sun.COM 			continue;
7569999SWang.Lin@Sun.COM 		}
7579999SWang.Lin@Sun.COM 		/* NB: flags are known to be compatible */
7589999SWang.Lin@Sun.COM 		if (index < 0) {
7599999SWang.Lin@Sun.COM 			/*
7609999SWang.Lin@Sun.COM 			 * can't handle frequency <2400MHz (negative
7619999SWang.Lin@Sun.COM 			 * channels) right now
7629999SWang.Lin@Sun.COM 			 */
7639999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CHANNEL,
7649999SWang.Lin@Sun.COM 			    "arn: arn_setup_channels(): "
7659999SWang.Lin@Sun.COM 			    "hal channel %d (%u/%x) "
7669999SWang.Lin@Sun.COM 			    "cannot be handled, ignored\n",
7679999SWang.Lin@Sun.COM 			    index, c->channel, c->channelFlags));
7689999SWang.Lin@Sun.COM 			continue;
7699999SWang.Lin@Sun.COM 		}
7709999SWang.Lin@Sun.COM 
7719999SWang.Lin@Sun.COM 		/*
7729999SWang.Lin@Sun.COM 		 * Calculate net80211 flags; most are compatible
7739999SWang.Lin@Sun.COM 		 * but some need massaging.  Note the static turbo
7749999SWang.Lin@Sun.COM 		 * conversion can be removed once net80211 is updated
7759999SWang.Lin@Sun.COM 		 * to understand static vs. dynamic turbo.
7769999SWang.Lin@Sun.COM 		 */
7779999SWang.Lin@Sun.COM 
7789999SWang.Lin@Sun.COM 		flags = c->channelFlags & (CHANNEL_ALL | CHANNEL_PASSIVE);
7799999SWang.Lin@Sun.COM 
7809999SWang.Lin@Sun.COM 		if (ic->ic_sup_channels[index].ich_freq == 0) {
7819999SWang.Lin@Sun.COM 			ic->ic_sup_channels[index].ich_freq = c->channel;
7829999SWang.Lin@Sun.COM 			ic->ic_sup_channels[index].ich_flags = flags;
7839999SWang.Lin@Sun.COM 		} else {
7849999SWang.Lin@Sun.COM 			/* channels overlap; e.g. 11g and 11b */
7859999SWang.Lin@Sun.COM 			ic->ic_sup_channels[index].ich_flags |= flags;
7869999SWang.Lin@Sun.COM 		}
7879999SWang.Lin@Sun.COM 		if ((c->channelFlags & CHANNEL_G) == CHANNEL_G) {
7889999SWang.Lin@Sun.COM 			sc->sc_have11g = 1;
7899999SWang.Lin@Sun.COM 			ic->ic_caps |= IEEE80211_C_SHPREAMBLE |
7909999SWang.Lin@Sun.COM 			    IEEE80211_C_SHSLOT;	/* short slot time */
7919999SWang.Lin@Sun.COM 		}
7929999SWang.Lin@Sun.COM 	}
7939999SWang.Lin@Sun.COM 
7949999SWang.Lin@Sun.COM 	return (0);
7959999SWang.Lin@Sun.COM }
7969999SWang.Lin@Sun.COM 
7979999SWang.Lin@Sun.COM uint32_t
arn_chan2flags(ieee80211com_t * isc,struct ieee80211_channel * chan)7989999SWang.Lin@Sun.COM arn_chan2flags(ieee80211com_t *isc, struct ieee80211_channel *chan)
7999999SWang.Lin@Sun.COM {
80011729SWang.Lin@Sun.COM 	uint32_t channel_mode;
80111729SWang.Lin@Sun.COM 	switch (ieee80211_chan2mode(isc, chan)) {
80211729SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NA:
80311729SWang.Lin@Sun.COM 		if (chan->ich_flags & IEEE80211_CHAN_HT40U)
80411729SWang.Lin@Sun.COM 			channel_mode = CHANNEL_A_HT40PLUS;
80511729SWang.Lin@Sun.COM 		else if (chan->ich_flags & IEEE80211_CHAN_HT40D)
80611729SWang.Lin@Sun.COM 			channel_mode = CHANNEL_A_HT40MINUS;
80711729SWang.Lin@Sun.COM 		else
80811729SWang.Lin@Sun.COM 			channel_mode = CHANNEL_A_HT20;
80911729SWang.Lin@Sun.COM 		break;
81011729SWang.Lin@Sun.COM 	case IEEE80211_MODE_11NG:
81111729SWang.Lin@Sun.COM 		if (chan->ich_flags & IEEE80211_CHAN_HT40U)
81211729SWang.Lin@Sun.COM 			channel_mode = CHANNEL_G_HT40PLUS;
81311729SWang.Lin@Sun.COM 		else if (chan->ich_flags & IEEE80211_CHAN_HT40D)
81411729SWang.Lin@Sun.COM 			channel_mode = CHANNEL_G_HT40MINUS;
81511729SWang.Lin@Sun.COM 		else
81611729SWang.Lin@Sun.COM 			channel_mode = CHANNEL_G_HT20;
81711729SWang.Lin@Sun.COM 		break;
81811729SWang.Lin@Sun.COM 	case IEEE80211_MODE_TURBO_G:
81911729SWang.Lin@Sun.COM 	case IEEE80211_MODE_STURBO_A:
82011729SWang.Lin@Sun.COM 	case IEEE80211_MODE_TURBO_A:
82111729SWang.Lin@Sun.COM 		channel_mode = 0;
82211729SWang.Lin@Sun.COM 		break;
82311729SWang.Lin@Sun.COM 	case IEEE80211_MODE_11A:
82411729SWang.Lin@Sun.COM 		channel_mode = CHANNEL_A;
82511729SWang.Lin@Sun.COM 		break;
82611729SWang.Lin@Sun.COM 	case IEEE80211_MODE_11G:
82711729SWang.Lin@Sun.COM 		channel_mode = CHANNEL_B;
82811729SWang.Lin@Sun.COM 		break;
82911729SWang.Lin@Sun.COM 	case IEEE80211_MODE_11B:
83011729SWang.Lin@Sun.COM 		channel_mode = CHANNEL_G;
83111729SWang.Lin@Sun.COM 		break;
83211729SWang.Lin@Sun.COM 	case IEEE80211_MODE_FH:
83311729SWang.Lin@Sun.COM 		channel_mode = 0;
83411729SWang.Lin@Sun.COM 		break;
83511729SWang.Lin@Sun.COM 	default:
83611729SWang.Lin@Sun.COM 		break;
83711729SWang.Lin@Sun.COM 	}
83811729SWang.Lin@Sun.COM 
83911729SWang.Lin@Sun.COM 	return (channel_mode);
8409999SWang.Lin@Sun.COM }
8419999SWang.Lin@Sun.COM 
8429999SWang.Lin@Sun.COM /*
8439999SWang.Lin@Sun.COM  * Update internal state after a channel change.
8449999SWang.Lin@Sun.COM  */
8459999SWang.Lin@Sun.COM void
arn_chan_change(struct arn_softc * sc,struct ieee80211_channel * chan)8469999SWang.Lin@Sun.COM arn_chan_change(struct arn_softc *sc, struct ieee80211_channel *chan)
8479999SWang.Lin@Sun.COM {
8489999SWang.Lin@Sun.COM 	struct ieee80211com *ic = &sc->sc_isc;
8499999SWang.Lin@Sun.COM 	enum ieee80211_phymode mode;
8509999SWang.Lin@Sun.COM 	enum wireless_mode wlmode;
8519999SWang.Lin@Sun.COM 
8529999SWang.Lin@Sun.COM 	/*
8539999SWang.Lin@Sun.COM 	 * Change channels and update the h/w rate map
8549999SWang.Lin@Sun.COM 	 * if we're switching; e.g. 11a to 11b/g.
8559999SWang.Lin@Sun.COM 	 */
8569999SWang.Lin@Sun.COM 	mode = ieee80211_chan2mode(ic, chan);
8579999SWang.Lin@Sun.COM 	switch (mode) {
8589999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11A:
8599999SWang.Lin@Sun.COM 		wlmode = ATH9K_MODE_11A;
8609999SWang.Lin@Sun.COM 		break;
8619999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11B:
8629999SWang.Lin@Sun.COM 		wlmode = ATH9K_MODE_11B;
8639999SWang.Lin@Sun.COM 		break;
8649999SWang.Lin@Sun.COM 	case IEEE80211_MODE_11G:
8659999SWang.Lin@Sun.COM 		wlmode = ATH9K_MODE_11B;
8669999SWang.Lin@Sun.COM 		break;
8679999SWang.Lin@Sun.COM 	default:
8689999SWang.Lin@Sun.COM 		break;
8699999SWang.Lin@Sun.COM 	}
8709999SWang.Lin@Sun.COM 	if (wlmode != sc->sc_curmode)
8719999SWang.Lin@Sun.COM 		arn_setcurmode(sc, wlmode);
8729999SWang.Lin@Sun.COM 
8739999SWang.Lin@Sun.COM }
8749999SWang.Lin@Sun.COM 
8759999SWang.Lin@Sun.COM /*
8769999SWang.Lin@Sun.COM  * Set/change channels.  If the channel is really being changed, it's done
8779999SWang.Lin@Sun.COM  * by reseting the chip.  To accomplish this we must first cleanup any pending
8789999SWang.Lin@Sun.COM  * DMA, then restart stuff.
8799999SWang.Lin@Sun.COM  */
8809999SWang.Lin@Sun.COM static int
arn_set_channel(struct arn_softc * sc,struct ath9k_channel * hchan)8819999SWang.Lin@Sun.COM arn_set_channel(struct arn_softc *sc, struct ath9k_channel *hchan)
8829999SWang.Lin@Sun.COM {
8839999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
8849999SWang.Lin@Sun.COM 	ieee80211com_t *ic = &sc->sc_isc;
8859999SWang.Lin@Sun.COM 	boolean_t fastcc = B_TRUE;
8869999SWang.Lin@Sun.COM 	boolean_t  stopped;
8879999SWang.Lin@Sun.COM 	struct ieee80211_channel chan;
8889999SWang.Lin@Sun.COM 	enum wireless_mode curmode;
8899999SWang.Lin@Sun.COM 
8909999SWang.Lin@Sun.COM 	if (sc->sc_flags & SC_OP_INVALID)
8919999SWang.Lin@Sun.COM 		return (EIO);
8929999SWang.Lin@Sun.COM 
8939999SWang.Lin@Sun.COM 	if (hchan->channel != sc->sc_ah->ah_curchan->channel ||
8949999SWang.Lin@Sun.COM 	    hchan->channelFlags != sc->sc_ah->ah_curchan->channelFlags ||
8959999SWang.Lin@Sun.COM 	    (sc->sc_flags & SC_OP_CHAINMASK_UPDATE) ||
8969999SWang.Lin@Sun.COM 	    (sc->sc_flags & SC_OP_FULL_RESET)) {
8979999SWang.Lin@Sun.COM 		int status;
8989999SWang.Lin@Sun.COM 
8999999SWang.Lin@Sun.COM 		/*
9009999SWang.Lin@Sun.COM 		 * This is only performed if the channel settings have
9019999SWang.Lin@Sun.COM 		 * actually changed.
9029999SWang.Lin@Sun.COM 		 *
9039999SWang.Lin@Sun.COM 		 * To switch channels clear any pending DMA operations;
9049999SWang.Lin@Sun.COM 		 * wait long enough for the RX fifo to drain, reset the
9059999SWang.Lin@Sun.COM 		 * hardware at the new frequency, and then re-enable
9069999SWang.Lin@Sun.COM 		 * the relevant bits of the h/w.
9079999SWang.Lin@Sun.COM 		 */
9089999SWang.Lin@Sun.COM 		(void) ath9k_hw_set_interrupts(ah, 0);	/* disable interrupts */
9099999SWang.Lin@Sun.COM 		arn_draintxq(sc, B_FALSE);	/* clear pending tx frames */
9109999SWang.Lin@Sun.COM 		stopped = arn_stoprecv(sc);	/* turn off frame recv */
9119999SWang.Lin@Sun.COM 
9129999SWang.Lin@Sun.COM 		/*
9139999SWang.Lin@Sun.COM 		 * XXX: do not flush receive queue here. We don't want
9149999SWang.Lin@Sun.COM 		 * to flush data frames already in queue because of
9159999SWang.Lin@Sun.COM 		 * changing channel.
9169999SWang.Lin@Sun.COM 		 */
9179999SWang.Lin@Sun.COM 
9189999SWang.Lin@Sun.COM 		if (!stopped || (sc->sc_flags & SC_OP_FULL_RESET))
9199999SWang.Lin@Sun.COM 			fastcc = B_FALSE;
9209999SWang.Lin@Sun.COM 
9219999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CHANNEL, "arn: arn_set_channel(): "
9229999SWang.Lin@Sun.COM 		    "(%u MHz) -> (%u MHz), cflags:%x, chanwidth: %d\n",
9239999SWang.Lin@Sun.COM 		    sc->sc_ah->ah_curchan->channel,
9249999SWang.Lin@Sun.COM 		    hchan->channel, hchan->channelFlags, sc->tx_chan_width));
9259999SWang.Lin@Sun.COM 
9269999SWang.Lin@Sun.COM 		if (!ath9k_hw_reset(ah, hchan, sc->tx_chan_width,
9279999SWang.Lin@Sun.COM 		    sc->sc_tx_chainmask, sc->sc_rx_chainmask,
9289999SWang.Lin@Sun.COM 		    sc->sc_ht_extprotspacing, fastcc, &status)) {
9299999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_FATAL, "arn: arn_set_channel(): "
9309999SWang.Lin@Sun.COM 			    "unable to reset channel %u (%uMhz) "
9319999SWang.Lin@Sun.COM 			    "flags 0x%x hal status %u\n",
9329999SWang.Lin@Sun.COM 			    ath9k_hw_mhz2ieee(ah, hchan->channel,
9339999SWang.Lin@Sun.COM 			    hchan->channelFlags),
9349999SWang.Lin@Sun.COM 			    hchan->channel, hchan->channelFlags, status));
9359999SWang.Lin@Sun.COM 			return (EIO);
9369999SWang.Lin@Sun.COM 		}
9379999SWang.Lin@Sun.COM 
9389999SWang.Lin@Sun.COM 		sc->sc_curchan = *hchan;
9399999SWang.Lin@Sun.COM 
9409999SWang.Lin@Sun.COM 		sc->sc_flags &= ~SC_OP_CHAINMASK_UPDATE;
9419999SWang.Lin@Sun.COM 		sc->sc_flags &= ~SC_OP_FULL_RESET;
9429999SWang.Lin@Sun.COM 
9439999SWang.Lin@Sun.COM 		if (arn_startrecv(sc) != 0) {
9449999SWang.Lin@Sun.COM 			arn_problem("arn: arn_set_channel(): "
9459999SWang.Lin@Sun.COM 			    "unable to restart recv logic\n");
9469999SWang.Lin@Sun.COM 			return (EIO);
9479999SWang.Lin@Sun.COM 		}
9489999SWang.Lin@Sun.COM 
9499999SWang.Lin@Sun.COM 		chan.ich_freq = hchan->channel;
9509999SWang.Lin@Sun.COM 		chan.ich_flags = hchan->channelFlags;
9519999SWang.Lin@Sun.COM 		ic->ic_ibss_chan = &chan;
9529999SWang.Lin@Sun.COM 
9539999SWang.Lin@Sun.COM 		/*
9549999SWang.Lin@Sun.COM 		 * Change channels and update the h/w rate map
9559999SWang.Lin@Sun.COM 		 * if we're switching; e.g. 11a to 11b/g.
9569999SWang.Lin@Sun.COM 		 */
9579999SWang.Lin@Sun.COM 		curmode = arn_chan2mode(hchan);
9589999SWang.Lin@Sun.COM 		if (curmode != sc->sc_curmode)
9599999SWang.Lin@Sun.COM 			arn_setcurmode(sc, arn_chan2mode(hchan));
9609999SWang.Lin@Sun.COM 
9619999SWang.Lin@Sun.COM 		arn_update_txpow(sc);
9629999SWang.Lin@Sun.COM 
9639999SWang.Lin@Sun.COM 		(void) ath9k_hw_set_interrupts(ah, sc->sc_imask);
9649999SWang.Lin@Sun.COM 	}
9659999SWang.Lin@Sun.COM 
9669999SWang.Lin@Sun.COM 	return (0);
9679999SWang.Lin@Sun.COM }
9689999SWang.Lin@Sun.COM 
9699999SWang.Lin@Sun.COM /*
9709999SWang.Lin@Sun.COM  *  This routine performs the periodic noise floor calibration function
9719999SWang.Lin@Sun.COM  *  that is used to adjust and optimize the chip performance.  This
9729999SWang.Lin@Sun.COM  *  takes environmental changes (location, temperature) into account.
9739999SWang.Lin@Sun.COM  *  When the task is complete, it reschedules itself depending on the
9749999SWang.Lin@Sun.COM  *  appropriate interval that was calculated.
9759999SWang.Lin@Sun.COM  */
9769999SWang.Lin@Sun.COM static void
arn_ani_calibrate(void * arg)9779999SWang.Lin@Sun.COM arn_ani_calibrate(void *arg)
9789999SWang.Lin@Sun.COM 
9799999SWang.Lin@Sun.COM {
9809999SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)arg;
9819999SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)ic;
9829999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
9839999SWang.Lin@Sun.COM 	boolean_t longcal = B_FALSE;
9849999SWang.Lin@Sun.COM 	boolean_t shortcal = B_FALSE;
9859999SWang.Lin@Sun.COM 	boolean_t aniflag = B_FALSE;
9869999SWang.Lin@Sun.COM 	unsigned int timestamp = drv_hztousec(ddi_get_lbolt())/1000;
9879999SWang.Lin@Sun.COM 	uint32_t cal_interval;
9889999SWang.Lin@Sun.COM 
9899999SWang.Lin@Sun.COM 	/*
9909999SWang.Lin@Sun.COM 	 * don't calibrate when we're scanning.
9919999SWang.Lin@Sun.COM 	 * we are most likely not on our home channel.
9929999SWang.Lin@Sun.COM 	 */
9939999SWang.Lin@Sun.COM 	if (ic->ic_state != IEEE80211_S_RUN)
9949999SWang.Lin@Sun.COM 		goto settimer;
9959999SWang.Lin@Sun.COM 
9969999SWang.Lin@Sun.COM 	/* Long calibration runs independently of short calibration. */
9979999SWang.Lin@Sun.COM 	if ((timestamp - sc->sc_ani.sc_longcal_timer) >= ATH_LONG_CALINTERVAL) {
9989999SWang.Lin@Sun.COM 		longcal = B_TRUE;
9999999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_CALIBRATE, "arn: "
10009999SWang.Lin@Sun.COM 		    "%s: longcal @%lu\n", __func__, drv_hztousec));
10019999SWang.Lin@Sun.COM 		sc->sc_ani.sc_longcal_timer = timestamp;
10029999SWang.Lin@Sun.COM 	}
10039999SWang.Lin@Sun.COM 
10049999SWang.Lin@Sun.COM 	/* Short calibration applies only while sc_caldone is FALSE */
10059999SWang.Lin@Sun.COM 	if (!sc->sc_ani.sc_caldone) {
10069999SWang.Lin@Sun.COM 		if ((timestamp - sc->sc_ani.sc_shortcal_timer) >=
10079999SWang.Lin@Sun.COM 		    ATH_SHORT_CALINTERVAL) {
10089999SWang.Lin@Sun.COM 			shortcal = B_TRUE;
10099999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_CALIBRATE, "arn: "
10109999SWang.Lin@Sun.COM 			    "%s: shortcal @%lu\n",
10119999SWang.Lin@Sun.COM 			    __func__, drv_hztousec));
10129999SWang.Lin@Sun.COM 			sc->sc_ani.sc_shortcal_timer = timestamp;
10139999SWang.Lin@Sun.COM 			sc->sc_ani.sc_resetcal_timer = timestamp;
10149999SWang.Lin@Sun.COM 		}
10159999SWang.Lin@Sun.COM 	} else {
10169999SWang.Lin@Sun.COM 		if ((timestamp - sc->sc_ani.sc_resetcal_timer) >=
10179999SWang.Lin@Sun.COM 		    ATH_RESTART_CALINTERVAL) {
10189999SWang.Lin@Sun.COM 			ath9k_hw_reset_calvalid(ah, ah->ah_curchan,
10199999SWang.Lin@Sun.COM 						&sc->sc_ani.sc_caldone);
10209999SWang.Lin@Sun.COM 			if (sc->sc_ani.sc_caldone)
10219999SWang.Lin@Sun.COM 				sc->sc_ani.sc_resetcal_timer = timestamp;
10229999SWang.Lin@Sun.COM 		}
10239999SWang.Lin@Sun.COM 	}
10249999SWang.Lin@Sun.COM 
10259999SWang.Lin@Sun.COM 	/* Verify whether we must check ANI */
10269999SWang.Lin@Sun.COM 	if ((timestamp - sc->sc_ani.sc_checkani_timer) >=
10279999SWang.Lin@Sun.COM 	    ATH_ANI_POLLINTERVAL) {
10289999SWang.Lin@Sun.COM 		aniflag = B_TRUE;
10299999SWang.Lin@Sun.COM 		sc->sc_ani.sc_checkani_timer = timestamp;
10309999SWang.Lin@Sun.COM 	}
10319999SWang.Lin@Sun.COM 
10329999SWang.Lin@Sun.COM 	/* Skip all processing if there's nothing to do. */
10339999SWang.Lin@Sun.COM 	if (longcal || shortcal || aniflag) {
10349999SWang.Lin@Sun.COM 		/* Call ANI routine if necessary */
10359999SWang.Lin@Sun.COM 		if (aniflag)
10369999SWang.Lin@Sun.COM 			ath9k_hw_ani_monitor(ah, &sc->sc_halstats,
10379999SWang.Lin@Sun.COM 			    ah->ah_curchan);
10389999SWang.Lin@Sun.COM 
10399999SWang.Lin@Sun.COM 		/* Perform calibration if necessary */
10409999SWang.Lin@Sun.COM 		if (longcal || shortcal) {
10419999SWang.Lin@Sun.COM 			boolean_t iscaldone = B_FALSE;
10429999SWang.Lin@Sun.COM 
10439999SWang.Lin@Sun.COM 			if (ath9k_hw_calibrate(ah, ah->ah_curchan,
10449999SWang.Lin@Sun.COM 			    sc->sc_rx_chainmask, longcal, &iscaldone)) {
10459999SWang.Lin@Sun.COM 				if (longcal)
10469999SWang.Lin@Sun.COM 					sc->sc_ani.sc_noise_floor =
10479999SWang.Lin@Sun.COM 					    ath9k_hw_getchan_noise(ah,
10489999SWang.Lin@Sun.COM 					    ah->ah_curchan);
10499999SWang.Lin@Sun.COM 
10509999SWang.Lin@Sun.COM 				ARN_DBG((ARN_DBG_CALIBRATE, "arn: "
10519999SWang.Lin@Sun.COM 				    "%s: calibrate chan %u/%x nf: %d\n",
10529999SWang.Lin@Sun.COM 				    __func__,
10539999SWang.Lin@Sun.COM 				    ah->ah_curchan->channel,
10549999SWang.Lin@Sun.COM 				    ah->ah_curchan->channelFlags,
10559999SWang.Lin@Sun.COM 				    sc->sc_ani.sc_noise_floor));
10569999SWang.Lin@Sun.COM 			} else {
10579999SWang.Lin@Sun.COM 				ARN_DBG((ARN_DBG_CALIBRATE, "arn: "
10589999SWang.Lin@Sun.COM 				    "%s: calibrate chan %u/%x failed\n",
10599999SWang.Lin@Sun.COM 				    __func__,
10609999SWang.Lin@Sun.COM 				    ah->ah_curchan->channel,
10619999SWang.Lin@Sun.COM 				    ah->ah_curchan->channelFlags));
10629999SWang.Lin@Sun.COM 			}
10639999SWang.Lin@Sun.COM 			sc->sc_ani.sc_caldone = iscaldone;
10649999SWang.Lin@Sun.COM 		}
10659999SWang.Lin@Sun.COM 	}
10669999SWang.Lin@Sun.COM 
10679999SWang.Lin@Sun.COM settimer:
10689999SWang.Lin@Sun.COM 	/*
10699999SWang.Lin@Sun.COM 	 * Set timer interval based on previous results.
10709999SWang.Lin@Sun.COM 	 * The interval must be the shortest necessary to satisfy ANI,
10719999SWang.Lin@Sun.COM 	 * short calibration and long calibration.
10729999SWang.Lin@Sun.COM 	 */
10739999SWang.Lin@Sun.COM 	cal_interval = ATH_LONG_CALINTERVAL;
10749999SWang.Lin@Sun.COM 	if (sc->sc_ah->ah_config.enable_ani)
10759999SWang.Lin@Sun.COM 		cal_interval =
10769999SWang.Lin@Sun.COM 		    min(cal_interval, (uint32_t)ATH_ANI_POLLINTERVAL);
10779999SWang.Lin@Sun.COM 
10789999SWang.Lin@Sun.COM 	if (!sc->sc_ani.sc_caldone)
10799999SWang.Lin@Sun.COM 		cal_interval = min(cal_interval,
10809999SWang.Lin@Sun.COM 		    (uint32_t)ATH_SHORT_CALINTERVAL);
10819999SWang.Lin@Sun.COM 
10829999SWang.Lin@Sun.COM 	sc->sc_scan_timer = 0;
10839999SWang.Lin@Sun.COM 	sc->sc_scan_timer = timeout(arn_ani_calibrate, (void *)sc,
10849999SWang.Lin@Sun.COM 	    drv_usectohz(cal_interval * 1000));
10859999SWang.Lin@Sun.COM }
10869999SWang.Lin@Sun.COM 
10879999SWang.Lin@Sun.COM static void
arn_stop_caltimer(struct arn_softc * sc)10889999SWang.Lin@Sun.COM arn_stop_caltimer(struct arn_softc *sc)
10899999SWang.Lin@Sun.COM {
10909999SWang.Lin@Sun.COM 	timeout_id_t tmp_id = 0;
10919999SWang.Lin@Sun.COM 
10929999SWang.Lin@Sun.COM 	while ((sc->sc_cal_timer != 0) && (tmp_id != sc->sc_cal_timer)) {
10939999SWang.Lin@Sun.COM 		tmp_id = sc->sc_cal_timer;
10949999SWang.Lin@Sun.COM 		(void) untimeout(tmp_id);
10959999SWang.Lin@Sun.COM 	}
10969999SWang.Lin@Sun.COM 	sc->sc_cal_timer = 0;
10979999SWang.Lin@Sun.COM }
10989999SWang.Lin@Sun.COM 
10999999SWang.Lin@Sun.COM static uint_t
arn_isr(caddr_t arg)11009999SWang.Lin@Sun.COM arn_isr(caddr_t arg)
11019999SWang.Lin@Sun.COM {
11029999SWang.Lin@Sun.COM 	/* LINTED E_BAD_PTR_CAST_ALIGN */
11039999SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)arg;
11049999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
11059999SWang.Lin@Sun.COM 	enum ath9k_int status;
11069999SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
11079999SWang.Lin@Sun.COM 
11089999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
11099999SWang.Lin@Sun.COM 
11109999SWang.Lin@Sun.COM 	if (sc->sc_flags & SC_OP_INVALID) {
11119999SWang.Lin@Sun.COM 		/*
11129999SWang.Lin@Sun.COM 		 * The hardware is not ready/present, don't
11139999SWang.Lin@Sun.COM 		 * touch anything. Note this can happen early
11149999SWang.Lin@Sun.COM 		 * on if the IRQ is shared.
11159999SWang.Lin@Sun.COM 		 */
11169999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
11179999SWang.Lin@Sun.COM 		return (DDI_INTR_UNCLAIMED);
11189999SWang.Lin@Sun.COM 	}
11199999SWang.Lin@Sun.COM 	if (!ath9k_hw_intrpend(ah)) {	/* shared irq, not for us */
11209999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
11219999SWang.Lin@Sun.COM 		return (DDI_INTR_UNCLAIMED);
11229999SWang.Lin@Sun.COM 	}
11239999SWang.Lin@Sun.COM 
11249999SWang.Lin@Sun.COM 	/*
11259999SWang.Lin@Sun.COM 	 * Figure out the reason(s) for the interrupt. Note
11269999SWang.Lin@Sun.COM 	 * that the hal returns a pseudo-ISR that may include
11279999SWang.Lin@Sun.COM 	 * bits we haven't explicitly enabled so we mask the
11289999SWang.Lin@Sun.COM 	 * value to insure we only process bits we requested.
11299999SWang.Lin@Sun.COM 	 */
11309999SWang.Lin@Sun.COM 	(void) ath9k_hw_getisr(ah, &status); /* NB: clears ISR too */
11319999SWang.Lin@Sun.COM 
11329999SWang.Lin@Sun.COM 	status &= sc->sc_imask; /* discard unasked-for bits */
11339999SWang.Lin@Sun.COM 
11349999SWang.Lin@Sun.COM 	/*
11359999SWang.Lin@Sun.COM 	 * If there are no status bits set, then this interrupt was not
11369999SWang.Lin@Sun.COM 	 * for me (should have been caught above).
11379999SWang.Lin@Sun.COM 	 */
11389999SWang.Lin@Sun.COM 	if (!status) {
11399999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
11409999SWang.Lin@Sun.COM 		return (DDI_INTR_UNCLAIMED);
11419999SWang.Lin@Sun.COM 	}
11429999SWang.Lin@Sun.COM 
11439999SWang.Lin@Sun.COM 	sc->sc_intrstatus = status;
11449999SWang.Lin@Sun.COM 
11459999SWang.Lin@Sun.COM 	if (status & ATH9K_INT_FATAL) {
11469999SWang.Lin@Sun.COM 		/* need a chip reset */
11479999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
11489999SWang.Lin@Sun.COM 		    "ATH9K_INT_FATAL\n"));
11499999SWang.Lin@Sun.COM 		goto reset;
11509999SWang.Lin@Sun.COM 	} else if (status & ATH9K_INT_RXORN) {
11519999SWang.Lin@Sun.COM 		/* need a chip reset */
11529999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
11539999SWang.Lin@Sun.COM 		    "ATH9K_INT_RXORN\n"));
11549999SWang.Lin@Sun.COM 		goto reset;
11559999SWang.Lin@Sun.COM 	} else {
11569999SWang.Lin@Sun.COM 		if (status & ATH9K_INT_RXEOL) {
11579999SWang.Lin@Sun.COM 			/*
11589999SWang.Lin@Sun.COM 			 * NB: the hardware should re-read the link when
11599999SWang.Lin@Sun.COM 			 * RXE bit is written, but it doesn't work
11609999SWang.Lin@Sun.COM 			 * at least on older hardware revs.
11619999SWang.Lin@Sun.COM 			 */
11629999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
11639999SWang.Lin@Sun.COM 			    "ATH9K_INT_RXEOL\n"));
11649999SWang.Lin@Sun.COM 			sc->sc_rxlink = NULL;
11659999SWang.Lin@Sun.COM 		}
11669999SWang.Lin@Sun.COM 		if (status & ATH9K_INT_TXURN) {
11679999SWang.Lin@Sun.COM 			/* bump tx trigger level */
11689999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
11699999SWang.Lin@Sun.COM 			    "ATH9K_INT_TXURN\n"));
11709999SWang.Lin@Sun.COM 			(void) ath9k_hw_updatetxtriglevel(ah, B_TRUE);
11719999SWang.Lin@Sun.COM 		}
11729999SWang.Lin@Sun.COM 		/* XXX: optimize this */
11739999SWang.Lin@Sun.COM 		if (status & ATH9K_INT_RX) {
11749999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
11759999SWang.Lin@Sun.COM 			    "ATH9K_INT_RX\n"));
11769999SWang.Lin@Sun.COM 			sc->sc_rx_pend = 1;
11779999SWang.Lin@Sun.COM 			ddi_trigger_softintr(sc->sc_softint_id);
11789999SWang.Lin@Sun.COM 		}
11799999SWang.Lin@Sun.COM 		if (status & ATH9K_INT_TX) {
11809999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
11819999SWang.Lin@Sun.COM 			    "ATH9K_INT_TX\n"));
11829999SWang.Lin@Sun.COM 			if (ddi_taskq_dispatch(sc->sc_tq,
11839999SWang.Lin@Sun.COM 			    arn_tx_int_proc, sc, DDI_NOSLEEP) !=
11849999SWang.Lin@Sun.COM 			    DDI_SUCCESS) {
11859999SWang.Lin@Sun.COM 				arn_problem("arn: arn_isr(): "
11869999SWang.Lin@Sun.COM 				    "No memory for tx taskq\n");
11879999SWang.Lin@Sun.COM 				}
11889999SWang.Lin@Sun.COM 			}
11899999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_INT_MIB
11909999SWang.Lin@Sun.COM 		if (status & ATH9K_INT_MIB) {
11919999SWang.Lin@Sun.COM 			/*
11929999SWang.Lin@Sun.COM 			 * Disable interrupts until we service the MIB
11939999SWang.Lin@Sun.COM 			 * interrupt; otherwise it will continue to
11949999SWang.Lin@Sun.COM 			 * fire.
11959999SWang.Lin@Sun.COM 			 */
11969999SWang.Lin@Sun.COM 			(void) ath9k_hw_set_interrupts(ah, 0);
11979999SWang.Lin@Sun.COM 			/*
11989999SWang.Lin@Sun.COM 			 * Let the hal handle the event. We assume
11999999SWang.Lin@Sun.COM 			 * it will clear whatever condition caused
12009999SWang.Lin@Sun.COM 			 * the interrupt.
12019999SWang.Lin@Sun.COM 			 */
12029999SWang.Lin@Sun.COM 			ath9k_hw_procmibevent(ah, &sc->sc_halstats);
12039999SWang.Lin@Sun.COM 			(void) ath9k_hw_set_interrupts(ah, sc->sc_imask);
12049999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
12059999SWang.Lin@Sun.COM 			    "ATH9K_INT_MIB\n"));
12069999SWang.Lin@Sun.COM 		}
12079999SWang.Lin@Sun.COM #endif
12089999SWang.Lin@Sun.COM 
12099999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_INT_TIM_TIMER
12109999SWang.Lin@Sun.COM 		if (status & ATH9K_INT_TIM_TIMER) {
12119999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
12129999SWang.Lin@Sun.COM 			    "ATH9K_INT_TIM_TIMER\n"));
12139999SWang.Lin@Sun.COM 			if (!(ah->ah_caps.hw_caps &
12149999SWang.Lin@Sun.COM 			    ATH9K_HW_CAP_AUTOSLEEP)) {
12159999SWang.Lin@Sun.COM 				/*
12169999SWang.Lin@Sun.COM 				 * Clear RxAbort bit so that we can
12179999SWang.Lin@Sun.COM 				 * receive frames
12189999SWang.Lin@Sun.COM 				 */
12199999SWang.Lin@Sun.COM 				ath9k_hw_setrxabort(ah, 0);
12209999SWang.Lin@Sun.COM 				goto reset;
12219999SWang.Lin@Sun.COM 			}
12229999SWang.Lin@Sun.COM 		}
12239999SWang.Lin@Sun.COM #endif
12249999SWang.Lin@Sun.COM 
12259999SWang.Lin@Sun.COM 		if (status & ATH9K_INT_BMISS) {
12269999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
12279999SWang.Lin@Sun.COM 			    "ATH9K_INT_BMISS\n"));
122811729SWang.Lin@Sun.COM #ifdef ARN_HW_BEACON_MISS_HANDLE
122911377SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
123011377SWang.Lin@Sun.COM 			    "handle beacon mmiss by H/W mechanism\n"));
12319999SWang.Lin@Sun.COM 			if (ddi_taskq_dispatch(sc->sc_tq, arn_bmiss_proc,
12329999SWang.Lin@Sun.COM 			    sc, DDI_NOSLEEP) != DDI_SUCCESS) {
12339999SWang.Lin@Sun.COM 				arn_problem("arn: arn_isr(): "
12349999SWang.Lin@Sun.COM 				    "No memory available for bmiss taskq\n");
12359999SWang.Lin@Sun.COM 			}
123611377SWang.Lin@Sun.COM #else
123711377SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
123811377SWang.Lin@Sun.COM 			    "handle beacon mmiss by S/W mechanism\n"));
123911729SWang.Lin@Sun.COM #endif /* ARN_HW_BEACON_MISS_HANDLE */
12409999SWang.Lin@Sun.COM 		}
12419999SWang.Lin@Sun.COM 
12429999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
12439999SWang.Lin@Sun.COM 
12449999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_INT_CST
12459999SWang.Lin@Sun.COM 		/* carrier sense timeout */
12469999SWang.Lin@Sun.COM 		if (status & ATH9K_INT_CST) {
12479999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
12489999SWang.Lin@Sun.COM 			    "ATH9K_INT_CST\n"));
12499999SWang.Lin@Sun.COM 			return (DDI_INTR_CLAIMED);
12509999SWang.Lin@Sun.COM 		}
12519999SWang.Lin@Sun.COM #endif
12529999SWang.Lin@Sun.COM 
12539999SWang.Lin@Sun.COM 		if (status & ATH9K_INT_SWBA) {
12549999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
12559999SWang.Lin@Sun.COM 			    "ATH9K_INT_SWBA\n"));
12569999SWang.Lin@Sun.COM 			/* This will occur only in Host-AP or Ad-Hoc mode */
12579999SWang.Lin@Sun.COM 			return (DDI_INTR_CLAIMED);
12589999SWang.Lin@Sun.COM 		}
12599999SWang.Lin@Sun.COM 	}
12609999SWang.Lin@Sun.COM 
12619999SWang.Lin@Sun.COM 	return (DDI_INTR_CLAIMED);
12629999SWang.Lin@Sun.COM reset:
12639999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_INTERRUPT, "Rset for fatal err\n"));
12649999SWang.Lin@Sun.COM 	(void) arn_reset(ic);
12659999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
12669999SWang.Lin@Sun.COM 	return (DDI_INTR_CLAIMED);
12679999SWang.Lin@Sun.COM }
12689999SWang.Lin@Sun.COM 
12699999SWang.Lin@Sun.COM static int
arn_get_channel(struct arn_softc * sc,struct ieee80211_channel * chan)12709999SWang.Lin@Sun.COM arn_get_channel(struct arn_softc *sc, struct ieee80211_channel *chan)
12719999SWang.Lin@Sun.COM {
12729999SWang.Lin@Sun.COM 	int i;
12739999SWang.Lin@Sun.COM 
12749999SWang.Lin@Sun.COM 	for (i = 0; i < sc->sc_ah->ah_nchan; i++) {
12759999SWang.Lin@Sun.COM 		if (sc->sc_ah->ah_channels[i].channel == chan->ich_freq)
12769999SWang.Lin@Sun.COM 			return (i);
12779999SWang.Lin@Sun.COM 	}
12789999SWang.Lin@Sun.COM 
12799999SWang.Lin@Sun.COM 	return (-1);
12809999SWang.Lin@Sun.COM }
12819999SWang.Lin@Sun.COM 
12829999SWang.Lin@Sun.COM int
arn_reset(ieee80211com_t * ic)12839999SWang.Lin@Sun.COM arn_reset(ieee80211com_t *ic)
12849999SWang.Lin@Sun.COM {
12859999SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)ic;
12869999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
12879999SWang.Lin@Sun.COM 	int status;
12889999SWang.Lin@Sun.COM 	int error = 0;
12899999SWang.Lin@Sun.COM 
12909999SWang.Lin@Sun.COM 	(void) ath9k_hw_set_interrupts(ah, 0);
12919999SWang.Lin@Sun.COM 	arn_draintxq(sc, 0);
12929999SWang.Lin@Sun.COM 	(void) arn_stoprecv(sc);
12939999SWang.Lin@Sun.COM 
12949999SWang.Lin@Sun.COM 	if (!ath9k_hw_reset(ah, sc->sc_ah->ah_curchan, sc->tx_chan_width,
12959999SWang.Lin@Sun.COM 	    sc->sc_tx_chainmask, sc->sc_rx_chainmask,
12969999SWang.Lin@Sun.COM 	    sc->sc_ht_extprotspacing, B_FALSE, &status)) {
12979999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RESET, "arn: arn_reset(): "
12989999SWang.Lin@Sun.COM 		    "unable to reset hardware; hal status %u\n", status));
12999999SWang.Lin@Sun.COM 		error = EIO;
13009999SWang.Lin@Sun.COM 	}
13019999SWang.Lin@Sun.COM 
13029999SWang.Lin@Sun.COM 	if (arn_startrecv(sc) != 0)
13039999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RESET, "arn: arn_reset(): "
13049999SWang.Lin@Sun.COM 		    "unable to start recv logic\n"));
13059999SWang.Lin@Sun.COM 
13069999SWang.Lin@Sun.COM 	/*
13079999SWang.Lin@Sun.COM 	 * We may be doing a reset in response to a request
13089999SWang.Lin@Sun.COM 	 * that changes the channel so update any state that
13099999SWang.Lin@Sun.COM 	 * might change as a result.
13109999SWang.Lin@Sun.COM 	 */
13119999SWang.Lin@Sun.COM 	arn_setcurmode(sc, arn_chan2mode(sc->sc_ah->ah_curchan));
13129999SWang.Lin@Sun.COM 
13139999SWang.Lin@Sun.COM 	arn_update_txpow(sc);
13149999SWang.Lin@Sun.COM 
13159999SWang.Lin@Sun.COM 	if (sc->sc_flags & SC_OP_BEACONS)
13169999SWang.Lin@Sun.COM 		arn_beacon_config(sc);	/* restart beacons */
13179999SWang.Lin@Sun.COM 
13189999SWang.Lin@Sun.COM 	(void) ath9k_hw_set_interrupts(ah, sc->sc_imask);
13199999SWang.Lin@Sun.COM 
13209999SWang.Lin@Sun.COM 	return (error);
13219999SWang.Lin@Sun.COM }
13229999SWang.Lin@Sun.COM 
13239999SWang.Lin@Sun.COM int
arn_get_hal_qnum(uint16_t queue,struct arn_softc * sc)13249999SWang.Lin@Sun.COM arn_get_hal_qnum(uint16_t queue, struct arn_softc *sc)
13259999SWang.Lin@Sun.COM {
13269999SWang.Lin@Sun.COM 	int qnum;
13279999SWang.Lin@Sun.COM 
13289999SWang.Lin@Sun.COM 	switch (queue) {
13299999SWang.Lin@Sun.COM 	case WME_AC_VO:
13309999SWang.Lin@Sun.COM 		qnum = sc->sc_haltype2q[ATH9K_WME_AC_VO];
13319999SWang.Lin@Sun.COM 		break;
13329999SWang.Lin@Sun.COM 	case WME_AC_VI:
13339999SWang.Lin@Sun.COM 		qnum = sc->sc_haltype2q[ATH9K_WME_AC_VI];
13349999SWang.Lin@Sun.COM 		break;
13359999SWang.Lin@Sun.COM 	case WME_AC_BE:
13369999SWang.Lin@Sun.COM 		qnum = sc->sc_haltype2q[ATH9K_WME_AC_BE];
13379999SWang.Lin@Sun.COM 		break;
13389999SWang.Lin@Sun.COM 	case WME_AC_BK:
13399999SWang.Lin@Sun.COM 		qnum = sc->sc_haltype2q[ATH9K_WME_AC_BK];
13409999SWang.Lin@Sun.COM 		break;
13419999SWang.Lin@Sun.COM 	default:
13429999SWang.Lin@Sun.COM 		qnum = sc->sc_haltype2q[ATH9K_WME_AC_BE];
13439999SWang.Lin@Sun.COM 		break;
13449999SWang.Lin@Sun.COM 	}
13459999SWang.Lin@Sun.COM 
13469999SWang.Lin@Sun.COM 	return (qnum);
13479999SWang.Lin@Sun.COM }
13489999SWang.Lin@Sun.COM 
13499999SWang.Lin@Sun.COM static struct {
13509999SWang.Lin@Sun.COM 	uint32_t version;
13519999SWang.Lin@Sun.COM 	const char *name;
13529999SWang.Lin@Sun.COM } ath_mac_bb_names[] = {
13539999SWang.Lin@Sun.COM 	{ AR_SREV_VERSION_5416_PCI,	"5416" },
13549999SWang.Lin@Sun.COM 	{ AR_SREV_VERSION_5416_PCIE,	"5418" },
13559999SWang.Lin@Sun.COM 	{ AR_SREV_VERSION_9100,		"9100" },
13569999SWang.Lin@Sun.COM 	{ AR_SREV_VERSION_9160,		"9160" },
13579999SWang.Lin@Sun.COM 	{ AR_SREV_VERSION_9280,		"9280" },
13589999SWang.Lin@Sun.COM 	{ AR_SREV_VERSION_9285,		"9285" }
13599999SWang.Lin@Sun.COM };
13609999SWang.Lin@Sun.COM 
13619999SWang.Lin@Sun.COM static struct {
13629999SWang.Lin@Sun.COM 	uint16_t version;
13639999SWang.Lin@Sun.COM 	const char *name;
13649999SWang.Lin@Sun.COM } ath_rf_names[] = {
13659999SWang.Lin@Sun.COM 	{ 0,				"5133" },
13669999SWang.Lin@Sun.COM 	{ AR_RAD5133_SREV_MAJOR,	"5133" },
13679999SWang.Lin@Sun.COM 	{ AR_RAD5122_SREV_MAJOR,	"5122" },
13689999SWang.Lin@Sun.COM 	{ AR_RAD2133_SREV_MAJOR,	"2133" },
13699999SWang.Lin@Sun.COM 	{ AR_RAD2122_SREV_MAJOR,	"2122" }
13709999SWang.Lin@Sun.COM };
13719999SWang.Lin@Sun.COM 
13729999SWang.Lin@Sun.COM /*
13739999SWang.Lin@Sun.COM  * Return the MAC/BB name. "????" is returned if the MAC/BB is unknown.
13749999SWang.Lin@Sun.COM  */
13759999SWang.Lin@Sun.COM 
13769999SWang.Lin@Sun.COM static const char *
arn_mac_bb_name(uint32_t mac_bb_version)13779999SWang.Lin@Sun.COM arn_mac_bb_name(uint32_t mac_bb_version)
13789999SWang.Lin@Sun.COM {
13799999SWang.Lin@Sun.COM 	int i;
13809999SWang.Lin@Sun.COM 
13819999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(ath_mac_bb_names); i++) {
13829999SWang.Lin@Sun.COM 		if (ath_mac_bb_names[i].version == mac_bb_version) {
13839999SWang.Lin@Sun.COM 			return (ath_mac_bb_names[i].name);
13849999SWang.Lin@Sun.COM 		}
13859999SWang.Lin@Sun.COM 	}
13869999SWang.Lin@Sun.COM 
13879999SWang.Lin@Sun.COM 	return ("????");
13889999SWang.Lin@Sun.COM }
13899999SWang.Lin@Sun.COM 
13909999SWang.Lin@Sun.COM /*
13919999SWang.Lin@Sun.COM  * Return the RF name. "????" is returned if the RF is unknown.
13929999SWang.Lin@Sun.COM  */
13939999SWang.Lin@Sun.COM 
13949999SWang.Lin@Sun.COM static const char *
arn_rf_name(uint16_t rf_version)13959999SWang.Lin@Sun.COM arn_rf_name(uint16_t rf_version)
13969999SWang.Lin@Sun.COM {
13979999SWang.Lin@Sun.COM 	int i;
13989999SWang.Lin@Sun.COM 
13999999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(ath_rf_names); i++) {
14009999SWang.Lin@Sun.COM 		if (ath_rf_names[i].version == rf_version) {
14019999SWang.Lin@Sun.COM 			return (ath_rf_names[i].name);
14029999SWang.Lin@Sun.COM 		}
14039999SWang.Lin@Sun.COM 	}
14049999SWang.Lin@Sun.COM 
14059999SWang.Lin@Sun.COM 	return ("????");
14069999SWang.Lin@Sun.COM }
14079999SWang.Lin@Sun.COM 
14089999SWang.Lin@Sun.COM static void
arn_next_scan(void * arg)14099999SWang.Lin@Sun.COM arn_next_scan(void *arg)
14109999SWang.Lin@Sun.COM {
14119999SWang.Lin@Sun.COM 	ieee80211com_t *ic = arg;
14129999SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)ic;
14139999SWang.Lin@Sun.COM 
14149999SWang.Lin@Sun.COM 	sc->sc_scan_timer = 0;
14159999SWang.Lin@Sun.COM 	if (ic->ic_state == IEEE80211_S_SCAN) {
14169999SWang.Lin@Sun.COM 		sc->sc_scan_timer = timeout(arn_next_scan, (void *)sc,
14179999SWang.Lin@Sun.COM 		    drv_usectohz(arn_dwelltime * 1000));
14189999SWang.Lin@Sun.COM 		ieee80211_next_scan(ic);
14199999SWang.Lin@Sun.COM 	}
14209999SWang.Lin@Sun.COM }
14219999SWang.Lin@Sun.COM 
14229999SWang.Lin@Sun.COM static void
arn_stop_scantimer(struct arn_softc * sc)14239999SWang.Lin@Sun.COM arn_stop_scantimer(struct arn_softc *sc)
14249999SWang.Lin@Sun.COM {
14259999SWang.Lin@Sun.COM 	timeout_id_t tmp_id = 0;
14269999SWang.Lin@Sun.COM 
14279999SWang.Lin@Sun.COM 	while ((sc->sc_scan_timer != 0) && (tmp_id != sc->sc_scan_timer)) {
14289999SWang.Lin@Sun.COM 		tmp_id = sc->sc_scan_timer;
14299999SWang.Lin@Sun.COM 		(void) untimeout(tmp_id);
14309999SWang.Lin@Sun.COM 	}
14319999SWang.Lin@Sun.COM 	sc->sc_scan_timer = 0;
14329999SWang.Lin@Sun.COM }
14339999SWang.Lin@Sun.COM 
14349999SWang.Lin@Sun.COM static int32_t
arn_newstate(ieee80211com_t * ic,enum ieee80211_state nstate,int arg)14359999SWang.Lin@Sun.COM arn_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
14369999SWang.Lin@Sun.COM {
14379999SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)ic;
14389999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
14399999SWang.Lin@Sun.COM 	struct ieee80211_node *in;
14409999SWang.Lin@Sun.COM 	int32_t i, error;
14419999SWang.Lin@Sun.COM 	uint8_t *bssid;
14429999SWang.Lin@Sun.COM 	uint32_t rfilt;
14439999SWang.Lin@Sun.COM 	enum ieee80211_state ostate;
14449999SWang.Lin@Sun.COM 	struct ath9k_channel *channel;
14459999SWang.Lin@Sun.COM 	int pos;
14469999SWang.Lin@Sun.COM 
14479999SWang.Lin@Sun.COM 	/* Should set up & init LED here */
14489999SWang.Lin@Sun.COM 
14499999SWang.Lin@Sun.COM 	if (sc->sc_flags & SC_OP_INVALID)
14509999SWang.Lin@Sun.COM 		return (0);
14519999SWang.Lin@Sun.COM 
14529999SWang.Lin@Sun.COM 	ostate = ic->ic_state;
14539999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_INIT, "arn: arn_newstate(): "
14549999SWang.Lin@Sun.COM 	    "%x -> %x!\n", ostate, nstate));
14559999SWang.Lin@Sun.COM 
14569999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
14579999SWang.Lin@Sun.COM 
14589999SWang.Lin@Sun.COM 	if (nstate != IEEE80211_S_SCAN)
14599999SWang.Lin@Sun.COM 		arn_stop_scantimer(sc);
14609999SWang.Lin@Sun.COM 	if (nstate != IEEE80211_S_RUN)
14619999SWang.Lin@Sun.COM 		arn_stop_caltimer(sc);
14629999SWang.Lin@Sun.COM 
14639999SWang.Lin@Sun.COM 	/* Should set LED here */
14649999SWang.Lin@Sun.COM 
14659999SWang.Lin@Sun.COM 	if (nstate == IEEE80211_S_INIT) {
14669999SWang.Lin@Sun.COM 		sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
14679999SWang.Lin@Sun.COM 		/*
14689999SWang.Lin@Sun.COM 		 * Disable interrupts.
14699999SWang.Lin@Sun.COM 		 */
14709999SWang.Lin@Sun.COM 		(void) ath9k_hw_set_interrupts
14719999SWang.Lin@Sun.COM 		    (ah, sc->sc_imask &~ ATH9K_INT_GLOBAL);
14729999SWang.Lin@Sun.COM 
14739999SWang.Lin@Sun.COM #ifdef ARN_IBSS
14749999SWang.Lin@Sun.COM 		if (ic->ic_opmode == IEEE80211_M_IBSS) {
14759999SWang.Lin@Sun.COM 			(void) ath9k_hw_stoptxdma(ah, sc->sc_beaconq);
14769999SWang.Lin@Sun.COM 			arn_beacon_return(sc);
14779999SWang.Lin@Sun.COM 		}
14789999SWang.Lin@Sun.COM #endif
14799999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
14809999SWang.Lin@Sun.COM 		ieee80211_stop_watchdog(ic);
14819999SWang.Lin@Sun.COM 		goto done;
14829999SWang.Lin@Sun.COM 	}
14839999SWang.Lin@Sun.COM 	in = ic->ic_bss;
14849999SWang.Lin@Sun.COM 
14859999SWang.Lin@Sun.COM 	pos = arn_get_channel(sc, ic->ic_curchan);
14869999SWang.Lin@Sun.COM 
14879999SWang.Lin@Sun.COM 	if (pos == -1) {
14889999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_FATAL, "arn: "
14899999SWang.Lin@Sun.COM 		    "%s: Invalid channel\n", __func__));
14909999SWang.Lin@Sun.COM 		error = EINVAL;
14919999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
14929999SWang.Lin@Sun.COM 		goto bad;
14939999SWang.Lin@Sun.COM 	}
149411729SWang.Lin@Sun.COM 
149511729SWang.Lin@Sun.COM 	if (in->in_htcap & IEEE80211_HTCAP_CHWIDTH40) {
149611729SWang.Lin@Sun.COM 		arn_update_chainmask(sc);
149711729SWang.Lin@Sun.COM 		sc->tx_chan_width = ATH9K_HT_MACMODE_2040;
149811729SWang.Lin@Sun.COM 	} else
149911729SWang.Lin@Sun.COM 		sc->tx_chan_width = ATH9K_HT_MACMODE_20;
150011729SWang.Lin@Sun.COM 
15019999SWang.Lin@Sun.COM 	sc->sc_ah->ah_channels[pos].chanmode =
15029999SWang.Lin@Sun.COM 	    arn_chan2flags(ic, ic->ic_curchan);
15039999SWang.Lin@Sun.COM 	channel = &sc->sc_ah->ah_channels[pos];
15049999SWang.Lin@Sun.COM 	if (channel == NULL) {
15059999SWang.Lin@Sun.COM 		arn_problem("arn_newstate(): channel == NULL");
15069999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
15079999SWang.Lin@Sun.COM 		goto bad;
15089999SWang.Lin@Sun.COM 	}
15099999SWang.Lin@Sun.COM 	error = arn_set_channel(sc, channel);
15109999SWang.Lin@Sun.COM 	if (error != 0) {
15119999SWang.Lin@Sun.COM 		if (nstate != IEEE80211_S_SCAN) {
15129999SWang.Lin@Sun.COM 			ARN_UNLOCK(sc);
15139999SWang.Lin@Sun.COM 			ieee80211_reset_chan(ic);
15149999SWang.Lin@Sun.COM 			goto bad;
15159999SWang.Lin@Sun.COM 		}
15169999SWang.Lin@Sun.COM 	}
15179999SWang.Lin@Sun.COM 
15189999SWang.Lin@Sun.COM 	/*
15199999SWang.Lin@Sun.COM 	 * Get the receive filter according to the
15209999SWang.Lin@Sun.COM 	 * operating mode and state
15219999SWang.Lin@Sun.COM 	 */
15229999SWang.Lin@Sun.COM 	rfilt = arn_calcrxfilter(sc);
15239999SWang.Lin@Sun.COM 
15249999SWang.Lin@Sun.COM 	if (nstate == IEEE80211_S_SCAN)
15259999SWang.Lin@Sun.COM 		bssid = ic->ic_macaddr;
15269999SWang.Lin@Sun.COM 	else
15279999SWang.Lin@Sun.COM 		bssid = in->in_bssid;
15289999SWang.Lin@Sun.COM 
15299999SWang.Lin@Sun.COM 	ath9k_hw_setrxfilter(ah, rfilt);
15309999SWang.Lin@Sun.COM 
15319999SWang.Lin@Sun.COM 	if (nstate == IEEE80211_S_RUN && ic->ic_opmode != IEEE80211_M_IBSS)
15329999SWang.Lin@Sun.COM 		ath9k_hw_write_associd(ah, bssid, in->in_associd);
15339999SWang.Lin@Sun.COM 	else
15349999SWang.Lin@Sun.COM 		ath9k_hw_write_associd(ah, bssid, 0);
15359999SWang.Lin@Sun.COM 
15369999SWang.Lin@Sun.COM 	/* Check for WLAN_CAPABILITY_PRIVACY ? */
15379999SWang.Lin@Sun.COM 	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
15389999SWang.Lin@Sun.COM 		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
15399999SWang.Lin@Sun.COM 			if (ath9k_hw_keyisvalid(ah, (uint16_t)i))
15409999SWang.Lin@Sun.COM 				(void) ath9k_hw_keysetmac(ah, (uint16_t)i,
15419999SWang.Lin@Sun.COM 				    bssid);
15429999SWang.Lin@Sun.COM 		}
15439999SWang.Lin@Sun.COM 	}
15449999SWang.Lin@Sun.COM 
15459999SWang.Lin@Sun.COM 	if (nstate == IEEE80211_S_RUN) {
15469999SWang.Lin@Sun.COM 		switch (ic->ic_opmode) {
15479999SWang.Lin@Sun.COM #ifdef ARN_IBSS
15489999SWang.Lin@Sun.COM 		case IEEE80211_M_IBSS:
15499999SWang.Lin@Sun.COM 			/*
15509999SWang.Lin@Sun.COM 			 * Allocate and setup the beacon frame.
15519999SWang.Lin@Sun.COM 			 * Stop any previous beacon DMA.
15529999SWang.Lin@Sun.COM 			 */
15539999SWang.Lin@Sun.COM 			(void) ath9k_hw_stoptxdma(ah, sc->sc_beaconq);
15549999SWang.Lin@Sun.COM 			arn_beacon_return(sc);
15559999SWang.Lin@Sun.COM 			error = arn_beacon_alloc(sc, in);
15569999SWang.Lin@Sun.COM 			if (error != 0) {
15579999SWang.Lin@Sun.COM 				ARN_UNLOCK(sc);
15589999SWang.Lin@Sun.COM 				goto bad;
15599999SWang.Lin@Sun.COM 			}
15609999SWang.Lin@Sun.COM 			/*
15619999SWang.Lin@Sun.COM 			 * If joining an adhoc network defer beacon timer
15629999SWang.Lin@Sun.COM 			 * configuration to the next beacon frame so we
15639999SWang.Lin@Sun.COM 			 * have a current TSF to use.  Otherwise we're
15649999SWang.Lin@Sun.COM 			 * starting an ibss/bss so there's no need to delay.
15659999SWang.Lin@Sun.COM 			 */
15669999SWang.Lin@Sun.COM 			if (ic->ic_opmode == IEEE80211_M_IBSS &&
15679999SWang.Lin@Sun.COM 			    ic->ic_bss->in_tstamp.tsf != 0) {
15689999SWang.Lin@Sun.COM 				sc->sc_bsync = 1;
15699999SWang.Lin@Sun.COM 			} else {
15709999SWang.Lin@Sun.COM 				arn_beacon_config(sc);
15719999SWang.Lin@Sun.COM 			}
15729999SWang.Lin@Sun.COM 			break;
15739999SWang.Lin@Sun.COM #endif /* ARN_IBSS */
15749999SWang.Lin@Sun.COM 		case IEEE80211_M_STA:
15759999SWang.Lin@Sun.COM 			if (ostate != IEEE80211_S_RUN) {
15769999SWang.Lin@Sun.COM 				/*
15779999SWang.Lin@Sun.COM 				 * Defer beacon timer configuration to the next
15789999SWang.Lin@Sun.COM 				 * beacon frame so we have a current TSF to use.
15799999SWang.Lin@Sun.COM 				 * Any TSF collected when scanning is likely old
15809999SWang.Lin@Sun.COM 				 */
15819999SWang.Lin@Sun.COM #ifdef ARN_IBSS
15829999SWang.Lin@Sun.COM 				sc->sc_bsync = 1;
15839999SWang.Lin@Sun.COM #else
15849999SWang.Lin@Sun.COM 				/* Configure the beacon and sleep timers. */
15859999SWang.Lin@Sun.COM 				arn_beacon_config(sc);
158611729SWang.Lin@Sun.COM 				/* Reset rssi stats */
158711377SWang.Lin@Sun.COM 				sc->sc_halstats.ns_avgbrssi =
158811377SWang.Lin@Sun.COM 				    ATH_RSSI_DUMMY_MARKER;
158911377SWang.Lin@Sun.COM 				sc->sc_halstats.ns_avgrssi =
159011377SWang.Lin@Sun.COM 				    ATH_RSSI_DUMMY_MARKER;
159111377SWang.Lin@Sun.COM 				sc->sc_halstats.ns_avgtxrssi =
159211377SWang.Lin@Sun.COM 				    ATH_RSSI_DUMMY_MARKER;
159311377SWang.Lin@Sun.COM 				sc->sc_halstats.ns_avgtxrate =
159411377SWang.Lin@Sun.COM 				    ATH_RATE_DUMMY_MARKER;
159511729SWang.Lin@Sun.COM /* end */
159611729SWang.Lin@Sun.COM 
15979999SWang.Lin@Sun.COM #endif /* ARN_IBSS */
15989999SWang.Lin@Sun.COM 			}
15999999SWang.Lin@Sun.COM 			break;
16009999SWang.Lin@Sun.COM 		default:
16019999SWang.Lin@Sun.COM 			break;
16029999SWang.Lin@Sun.COM 		}
16039999SWang.Lin@Sun.COM 	} else {
16049999SWang.Lin@Sun.COM 		sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
16059999SWang.Lin@Sun.COM 		(void) ath9k_hw_set_interrupts(ah, sc->sc_imask);
16069999SWang.Lin@Sun.COM 	}
16079999SWang.Lin@Sun.COM 
16089999SWang.Lin@Sun.COM 	/*
16099999SWang.Lin@Sun.COM 	 * Reset the rate control state.
16109999SWang.Lin@Sun.COM 	 */
16119999SWang.Lin@Sun.COM 	arn_rate_ctl_reset(sc, nstate);
16129999SWang.Lin@Sun.COM 
16139999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
16149999SWang.Lin@Sun.COM done:
16159999SWang.Lin@Sun.COM 	/*
16169999SWang.Lin@Sun.COM 	 * Invoke the parent method to complete the work.
16179999SWang.Lin@Sun.COM 	 */
16189999SWang.Lin@Sun.COM 	error = sc->sc_newstate(ic, nstate, arg);
16199999SWang.Lin@Sun.COM 
16209999SWang.Lin@Sun.COM 	/*
16219999SWang.Lin@Sun.COM 	 * Finally, start any timers.
16229999SWang.Lin@Sun.COM 	 */
16239999SWang.Lin@Sun.COM 	if (nstate == IEEE80211_S_RUN) {
16249999SWang.Lin@Sun.COM 		ieee80211_start_watchdog(ic, 1);
16259999SWang.Lin@Sun.COM 		ASSERT(sc->sc_cal_timer == 0);
16269999SWang.Lin@Sun.COM 		sc->sc_cal_timer = timeout(arn_ani_calibrate, (void *)sc,
16279999SWang.Lin@Sun.COM 		    drv_usectohz(100 * 1000));
16289999SWang.Lin@Sun.COM 	} else if ((nstate == IEEE80211_S_SCAN) && (ostate != nstate)) {
16299999SWang.Lin@Sun.COM 		/* start ap/neighbor scan timer */
16309999SWang.Lin@Sun.COM 		/* ASSERT(sc->sc_scan_timer == 0); */
16319999SWang.Lin@Sun.COM 		if (sc->sc_scan_timer != 0) {
16329999SWang.Lin@Sun.COM 			(void) untimeout(sc->sc_scan_timer);
16339999SWang.Lin@Sun.COM 			sc->sc_scan_timer = 0;
16349999SWang.Lin@Sun.COM 		}
16359999SWang.Lin@Sun.COM 		sc->sc_scan_timer = timeout(arn_next_scan, (void *)sc,
16369999SWang.Lin@Sun.COM 		    drv_usectohz(arn_dwelltime * 1000));
16379999SWang.Lin@Sun.COM 	}
16389999SWang.Lin@Sun.COM 
16399999SWang.Lin@Sun.COM bad:
16409999SWang.Lin@Sun.COM 	return (error);
16419999SWang.Lin@Sun.COM }
16429999SWang.Lin@Sun.COM 
16439999SWang.Lin@Sun.COM static void
arn_watchdog(void * arg)16449999SWang.Lin@Sun.COM arn_watchdog(void *arg)
16459999SWang.Lin@Sun.COM {
16469999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
16479999SWang.Lin@Sun.COM 	ieee80211com_t *ic = &sc->sc_isc;
16489999SWang.Lin@Sun.COM 	int ntimer = 0;
16499999SWang.Lin@Sun.COM 
16509999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
16519999SWang.Lin@Sun.COM 	ic->ic_watchdog_timer = 0;
16529999SWang.Lin@Sun.COM 	if (sc->sc_flags & SC_OP_INVALID) {
16539999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
16549999SWang.Lin@Sun.COM 		return;
16559999SWang.Lin@Sun.COM 	}
16569999SWang.Lin@Sun.COM 
16579999SWang.Lin@Sun.COM 	if (ic->ic_state == IEEE80211_S_RUN) {
16589999SWang.Lin@Sun.COM 		/*
16599999SWang.Lin@Sun.COM 		 * Start the background rate control thread if we
16609999SWang.Lin@Sun.COM 		 * are not configured to use a fixed xmit rate.
16619999SWang.Lin@Sun.COM 		 */
166211729SWang.Lin@Sun.COM #ifdef ARN_LEGACY_RC
16639999SWang.Lin@Sun.COM 		if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
16649999SWang.Lin@Sun.COM 			sc->sc_stats.ast_rate_calls ++;
16659999SWang.Lin@Sun.COM 			if (ic->ic_opmode == IEEE80211_M_STA)
16669999SWang.Lin@Sun.COM 				arn_rate_ctl(ic, ic->ic_bss);
16679999SWang.Lin@Sun.COM 			else
16689999SWang.Lin@Sun.COM 				ieee80211_iterate_nodes(&ic->ic_sta,
16699999SWang.Lin@Sun.COM 				    arn_rate_ctl, sc);
16709999SWang.Lin@Sun.COM 		}
167111729SWang.Lin@Sun.COM #endif /* ARN_LEGACY_RC */
167211729SWang.Lin@Sun.COM 
167311729SWang.Lin@Sun.COM #ifdef ARN_HW_BEACON_MISS_HANDLE
167411729SWang.Lin@Sun.COM 	/* nothing to do here */
167511377SWang.Lin@Sun.COM #else
167611729SWang.Lin@Sun.COM 	/* currently set 10 seconds as beacon miss threshold */
167711729SWang.Lin@Sun.COM 	if (ic->ic_beaconmiss++ > 100) {
167811729SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_BEACON, "arn_watchdog():"
167911729SWang.Lin@Sun.COM 		    "Beacon missed for 10 seconds, run"
168011729SWang.Lin@Sun.COM 		    "ieee80211_new_state(ic, IEEE80211_S_INIT, -1)\n"));
168111729SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
168211729SWang.Lin@Sun.COM 		(void) ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
168311729SWang.Lin@Sun.COM 		return;
168411729SWang.Lin@Sun.COM 	}
168511729SWang.Lin@Sun.COM #endif /* ARN_HW_BEACON_MISS_HANDLE */
168611377SWang.Lin@Sun.COM 
16879999SWang.Lin@Sun.COM 		ntimer = 1;
16889999SWang.Lin@Sun.COM 	}
16899999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
16909999SWang.Lin@Sun.COM 
16919999SWang.Lin@Sun.COM 	ieee80211_watchdog(ic);
16929999SWang.Lin@Sun.COM 	if (ntimer != 0)
16939999SWang.Lin@Sun.COM 		ieee80211_start_watchdog(ic, ntimer);
16949999SWang.Lin@Sun.COM }
16959999SWang.Lin@Sun.COM 
169611729SWang.Lin@Sun.COM /* ARGSUSED */
16979999SWang.Lin@Sun.COM static struct ieee80211_node *
arn_node_alloc(ieee80211com_t * ic)16989999SWang.Lin@Sun.COM arn_node_alloc(ieee80211com_t *ic)
16999999SWang.Lin@Sun.COM {
17009999SWang.Lin@Sun.COM 	struct ath_node *an;
170111729SWang.Lin@Sun.COM #ifdef ARN_TX_AGGREGATION
17029999SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)ic;
170311729SWang.Lin@Sun.COM #endif
17049999SWang.Lin@Sun.COM 
17059999SWang.Lin@Sun.COM 	an = kmem_zalloc(sizeof (struct ath_node), KM_SLEEP);
170611729SWang.Lin@Sun.COM 
170711729SWang.Lin@Sun.COM 	/* legacy rate control */
170811729SWang.Lin@Sun.COM #ifdef ARN_LEGACY_RC
17099999SWang.Lin@Sun.COM 	arn_rate_update(sc, &an->an_node, 0);
171011729SWang.Lin@Sun.COM #endif
171111729SWang.Lin@Sun.COM 
171211729SWang.Lin@Sun.COM #ifdef ARN_TX_AGGREGATION
171311729SWang.Lin@Sun.COM 	if (sc->sc_flags & SC_OP_TXAGGR) {
171411729SWang.Lin@Sun.COM 		arn_tx_node_init(sc, an);
171511729SWang.Lin@Sun.COM 	}
171611729SWang.Lin@Sun.COM #endif /* ARN_TX_AGGREGATION */
171711729SWang.Lin@Sun.COM 
171811729SWang.Lin@Sun.COM 	an->last_rssi = ATH_RSSI_DUMMY_MARKER;
17199999SWang.Lin@Sun.COM 
17209999SWang.Lin@Sun.COM 	return ((an != NULL) ? &an->an_node : NULL);
17219999SWang.Lin@Sun.COM }
17229999SWang.Lin@Sun.COM 
17239999SWang.Lin@Sun.COM static void
arn_node_free(struct ieee80211_node * in)17249999SWang.Lin@Sun.COM arn_node_free(struct ieee80211_node *in)
17259999SWang.Lin@Sun.COM {
17269999SWang.Lin@Sun.COM 	ieee80211com_t *ic = in->in_ic;
17279999SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)ic;
17289999SWang.Lin@Sun.COM 	struct ath_buf *bf;
17299999SWang.Lin@Sun.COM 	struct ath_txq *txq;
17309999SWang.Lin@Sun.COM 	int32_t i;
17319999SWang.Lin@Sun.COM 
173211729SWang.Lin@Sun.COM #ifdef ARN_TX_AGGREGATION
173311729SWang.Lin@Sun.COM 	if (sc->sc_flags & SC_OP_TXAGGR)
173411729SWang.Lin@Sun.COM 		arn_tx_node_cleanup(sc, in);
173511729SWang.Lin@Sun.COM #endif /* TX_AGGREGATION */
173611729SWang.Lin@Sun.COM 
17379999SWang.Lin@Sun.COM 	for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
17389999SWang.Lin@Sun.COM 		if (ARN_TXQ_SETUP(sc, i)) {
17399999SWang.Lin@Sun.COM 			txq = &sc->sc_txq[i];
17409999SWang.Lin@Sun.COM 			mutex_enter(&txq->axq_lock);
17419999SWang.Lin@Sun.COM 			bf = list_head(&txq->axq_list);
17429999SWang.Lin@Sun.COM 			while (bf != NULL) {
17439999SWang.Lin@Sun.COM 				if (bf->bf_in == in) {
17449999SWang.Lin@Sun.COM 					bf->bf_in = NULL;
17459999SWang.Lin@Sun.COM 				}
17469999SWang.Lin@Sun.COM 				bf = list_next(&txq->axq_list, bf);
17479999SWang.Lin@Sun.COM 			}
17489999SWang.Lin@Sun.COM 			mutex_exit(&txq->axq_lock);
17499999SWang.Lin@Sun.COM 		}
17509999SWang.Lin@Sun.COM 	}
17519999SWang.Lin@Sun.COM 
17529999SWang.Lin@Sun.COM 	ic->ic_node_cleanup(in);
175311377SWang.Lin@Sun.COM 
17549999SWang.Lin@Sun.COM 	if (in->in_wpa_ie != NULL)
17559999SWang.Lin@Sun.COM 		ieee80211_free(in->in_wpa_ie);
175611377SWang.Lin@Sun.COM 
175711377SWang.Lin@Sun.COM 	if (in->in_wme_ie != NULL)
175811377SWang.Lin@Sun.COM 		ieee80211_free(in->in_wme_ie);
175911377SWang.Lin@Sun.COM 
176011377SWang.Lin@Sun.COM 	if (in->in_htcap_ie != NULL)
176111377SWang.Lin@Sun.COM 		ieee80211_free(in->in_htcap_ie);
176211377SWang.Lin@Sun.COM 
17639999SWang.Lin@Sun.COM 	kmem_free(in, sizeof (struct ath_node));
17649999SWang.Lin@Sun.COM }
17659999SWang.Lin@Sun.COM 
17669999SWang.Lin@Sun.COM /*
17679999SWang.Lin@Sun.COM  * Allocate tx/rx key slots for TKIP.  We allocate one slot for
17689999SWang.Lin@Sun.COM  * each key. MIC is right after the decrypt/encrypt key.
17699999SWang.Lin@Sun.COM  */
17709999SWang.Lin@Sun.COM static uint16_t
arn_key_alloc_pair(struct arn_softc * sc,ieee80211_keyix * txkeyix,ieee80211_keyix * rxkeyix)17719999SWang.Lin@Sun.COM arn_key_alloc_pair(struct arn_softc *sc, ieee80211_keyix *txkeyix,
17729999SWang.Lin@Sun.COM     ieee80211_keyix *rxkeyix)
17739999SWang.Lin@Sun.COM {
17749999SWang.Lin@Sun.COM 	uint16_t i, keyix;
17759999SWang.Lin@Sun.COM 
17769999SWang.Lin@Sun.COM 	ASSERT(!sc->sc_splitmic);
17779999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(sc->sc_keymap)/4; i++) {
17789999SWang.Lin@Sun.COM 		uint8_t b = sc->sc_keymap[i];
17799999SWang.Lin@Sun.COM 		if (b == 0xff)
17809999SWang.Lin@Sun.COM 			continue;
17819999SWang.Lin@Sun.COM 		for (keyix = i * NBBY; keyix < (i + 1) * NBBY;
17829999SWang.Lin@Sun.COM 		    keyix++, b >>= 1) {
17839999SWang.Lin@Sun.COM 			if ((b & 1) || is_set(keyix+64, sc->sc_keymap)) {
17849999SWang.Lin@Sun.COM 				/* full pair unavailable */
17859999SWang.Lin@Sun.COM 				continue;
17869999SWang.Lin@Sun.COM 			}
17879999SWang.Lin@Sun.COM 			set_bit(keyix, sc->sc_keymap);
17889999SWang.Lin@Sun.COM 			set_bit(keyix+64, sc->sc_keymap);
17899999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_KEYCACHE,
17909999SWang.Lin@Sun.COM 			    "arn_key_alloc_pair(): key pair %u,%u\n",
17919999SWang.Lin@Sun.COM 			    keyix, keyix+64));
17929999SWang.Lin@Sun.COM 			*txkeyix = *rxkeyix = keyix;
17939999SWang.Lin@Sun.COM 			return (1);
17949999SWang.Lin@Sun.COM 		}
17959999SWang.Lin@Sun.COM 	}
17969999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_KEYCACHE, "arn_key_alloc_pair():"
17979999SWang.Lin@Sun.COM 	    " out of pair space\n"));
17989999SWang.Lin@Sun.COM 
17999999SWang.Lin@Sun.COM 	return (0);
18009999SWang.Lin@Sun.COM }
18019999SWang.Lin@Sun.COM 
18029999SWang.Lin@Sun.COM /*
18039999SWang.Lin@Sun.COM  * Allocate tx/rx key slots for TKIP.  We allocate two slots for
18049999SWang.Lin@Sun.COM  * each key, one for decrypt/encrypt and the other for the MIC.
18059999SWang.Lin@Sun.COM  */
18069999SWang.Lin@Sun.COM static int
arn_key_alloc_2pair(struct arn_softc * sc,ieee80211_keyix * txkeyix,ieee80211_keyix * rxkeyix)18079999SWang.Lin@Sun.COM arn_key_alloc_2pair(struct arn_softc *sc, ieee80211_keyix *txkeyix,
18089999SWang.Lin@Sun.COM     ieee80211_keyix *rxkeyix)
18099999SWang.Lin@Sun.COM {
18109999SWang.Lin@Sun.COM 	uint16_t i, keyix;
18119999SWang.Lin@Sun.COM 
18129999SWang.Lin@Sun.COM 	ASSERT(sc->sc_splitmic);
18139999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(sc->sc_keymap)/4; i++) {
18149999SWang.Lin@Sun.COM 		uint8_t b = sc->sc_keymap[i];
18159999SWang.Lin@Sun.COM 		if (b != 0xff) {
18169999SWang.Lin@Sun.COM 			/*
18179999SWang.Lin@Sun.COM 			 * One or more slots in this byte are free.
18189999SWang.Lin@Sun.COM 			 */
18199999SWang.Lin@Sun.COM 			keyix = i*NBBY;
18209999SWang.Lin@Sun.COM 			while (b & 1) {
18219999SWang.Lin@Sun.COM 		again:
18229999SWang.Lin@Sun.COM 				keyix++;
18239999SWang.Lin@Sun.COM 				b >>= 1;
18249999SWang.Lin@Sun.COM 			}
18259999SWang.Lin@Sun.COM 			/* XXX IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV */
18269999SWang.Lin@Sun.COM 			if (is_set(keyix+32, sc->sc_keymap) ||
18279999SWang.Lin@Sun.COM 			    is_set(keyix+64, sc->sc_keymap) ||
18289999SWang.Lin@Sun.COM 			    is_set(keyix+32+64, sc->sc_keymap)) {
18299999SWang.Lin@Sun.COM 				/* full pair unavailable */
18309999SWang.Lin@Sun.COM 				if (keyix == (i+1)*NBBY) {
18319999SWang.Lin@Sun.COM 					/* no slots were appropriate, advance */
18329999SWang.Lin@Sun.COM 					continue;
18339999SWang.Lin@Sun.COM 				}
18349999SWang.Lin@Sun.COM 				goto again;
18359999SWang.Lin@Sun.COM 			}
18369999SWang.Lin@Sun.COM 			set_bit(keyix, sc->sc_keymap);
18379999SWang.Lin@Sun.COM 			set_bit(keyix+64, sc->sc_keymap);
18389999SWang.Lin@Sun.COM 			set_bit(keyix+32, sc->sc_keymap);
18399999SWang.Lin@Sun.COM 			set_bit(keyix+32+64, sc->sc_keymap);
18409999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_KEYCACHE,
18419999SWang.Lin@Sun.COM 			    "arn_key_alloc_2pair(): key pair %u,%u %u,%u\n",
18429999SWang.Lin@Sun.COM 			    keyix, keyix+64,
18439999SWang.Lin@Sun.COM 			    keyix+32, keyix+32+64));
18449999SWang.Lin@Sun.COM 			*txkeyix = *rxkeyix = keyix;
18459999SWang.Lin@Sun.COM 			return (1);
18469999SWang.Lin@Sun.COM 		}
18479999SWang.Lin@Sun.COM 	}
18489999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_KEYCACHE, "arn_key_alloc_2pair(): "
18499999SWang.Lin@Sun.COM 	    " out of pair space\n"));
18509999SWang.Lin@Sun.COM 
18519999SWang.Lin@Sun.COM 	return (0);
18529999SWang.Lin@Sun.COM }
18539999SWang.Lin@Sun.COM /*
18549999SWang.Lin@Sun.COM  * Allocate a single key cache slot.
18559999SWang.Lin@Sun.COM  */
18569999SWang.Lin@Sun.COM static int
arn_key_alloc_single(struct arn_softc * sc,ieee80211_keyix * txkeyix,ieee80211_keyix * rxkeyix)18579999SWang.Lin@Sun.COM arn_key_alloc_single(struct arn_softc *sc, ieee80211_keyix *txkeyix,
18589999SWang.Lin@Sun.COM     ieee80211_keyix *rxkeyix)
18599999SWang.Lin@Sun.COM {
18609999SWang.Lin@Sun.COM 	uint16_t i, keyix;
18619999SWang.Lin@Sun.COM 
18629999SWang.Lin@Sun.COM 	/* try i,i+32,i+64,i+32+64 to minimize key pair conflicts */
18639999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(sc->sc_keymap); i++) {
18649999SWang.Lin@Sun.COM 		uint8_t b = sc->sc_keymap[i];
18659999SWang.Lin@Sun.COM 
18669999SWang.Lin@Sun.COM 		if (b != 0xff) {
18679999SWang.Lin@Sun.COM 			/*
18689999SWang.Lin@Sun.COM 			 * One or more slots are free.
18699999SWang.Lin@Sun.COM 			 */
18709999SWang.Lin@Sun.COM 			keyix = i*NBBY;
18719999SWang.Lin@Sun.COM 			while (b & 1)
18729999SWang.Lin@Sun.COM 				keyix++, b >>= 1;
18739999SWang.Lin@Sun.COM 			set_bit(keyix, sc->sc_keymap);
18749999SWang.Lin@Sun.COM 			ARN_DBG((ARN_DBG_KEYCACHE, "arn_key_alloc_single(): "
18759999SWang.Lin@Sun.COM 			    "key %u\n", keyix));
18769999SWang.Lin@Sun.COM 			*txkeyix = *rxkeyix = keyix;
18779999SWang.Lin@Sun.COM 			return (1);
18789999SWang.Lin@Sun.COM 		}
18799999SWang.Lin@Sun.COM 	}
18809999SWang.Lin@Sun.COM 	return (0);
18819999SWang.Lin@Sun.COM }
18829999SWang.Lin@Sun.COM 
18839999SWang.Lin@Sun.COM /*
18849999SWang.Lin@Sun.COM  * Allocate one or more key cache slots for a unicast key.  The
18859999SWang.Lin@Sun.COM  * key itself is needed only to identify the cipher.  For hardware
18869999SWang.Lin@Sun.COM  * TKIP with split cipher+MIC keys we allocate two key cache slot
18879999SWang.Lin@Sun.COM  * pairs so that we can setup separate TX and RX MIC keys.  Note
18889999SWang.Lin@Sun.COM  * that the MIC key for a TKIP key at slot i is assumed by the
18899999SWang.Lin@Sun.COM  * hardware to be at slot i+64.  This limits TKIP keys to the first
18909999SWang.Lin@Sun.COM  * 64 entries.
18919999SWang.Lin@Sun.COM  */
18929999SWang.Lin@Sun.COM /* ARGSUSED */
18939999SWang.Lin@Sun.COM int
arn_key_alloc(ieee80211com_t * ic,const struct ieee80211_key * k,ieee80211_keyix * keyix,ieee80211_keyix * rxkeyix)18949999SWang.Lin@Sun.COM arn_key_alloc(ieee80211com_t *ic, const struct ieee80211_key *k,
18959999SWang.Lin@Sun.COM     ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
18969999SWang.Lin@Sun.COM {
18979999SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)ic;
18989999SWang.Lin@Sun.COM 
18999999SWang.Lin@Sun.COM 	/*
19009999SWang.Lin@Sun.COM 	 * We allocate two pair for TKIP when using the h/w to do
19019999SWang.Lin@Sun.COM 	 * the MIC.  For everything else, including software crypto,
19029999SWang.Lin@Sun.COM 	 * we allocate a single entry.  Note that s/w crypto requires
19039999SWang.Lin@Sun.COM 	 * a pass-through slot on the 5211 and 5212.  The 5210 does
19049999SWang.Lin@Sun.COM 	 * not support pass-through cache entries and we map all
19059999SWang.Lin@Sun.COM 	 * those requests to slot 0.
19069999SWang.Lin@Sun.COM 	 */
19079999SWang.Lin@Sun.COM 	if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
19089999SWang.Lin@Sun.COM 		return (arn_key_alloc_single(sc, keyix, rxkeyix));
19099999SWang.Lin@Sun.COM 	} else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP &&
19109999SWang.Lin@Sun.COM 	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
19119999SWang.Lin@Sun.COM 		if (sc->sc_splitmic)
19129999SWang.Lin@Sun.COM 			return (arn_key_alloc_2pair(sc, keyix, rxkeyix));
19139999SWang.Lin@Sun.COM 		else
19149999SWang.Lin@Sun.COM 			return (arn_key_alloc_pair(sc, keyix, rxkeyix));
19159999SWang.Lin@Sun.COM 	} else {
19169999SWang.Lin@Sun.COM 		return (arn_key_alloc_single(sc, keyix, rxkeyix));
19179999SWang.Lin@Sun.COM 	}
19189999SWang.Lin@Sun.COM }
19199999SWang.Lin@Sun.COM 
19209999SWang.Lin@Sun.COM /*
19219999SWang.Lin@Sun.COM  * Delete an entry in the key cache allocated by ath_key_alloc.
19229999SWang.Lin@Sun.COM  */
19239999SWang.Lin@Sun.COM int
arn_key_delete(ieee80211com_t * ic,const struct ieee80211_key * k)19249999SWang.Lin@Sun.COM arn_key_delete(ieee80211com_t *ic, const struct ieee80211_key *k)
19259999SWang.Lin@Sun.COM {
19269999SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)ic;
19279999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
19289999SWang.Lin@Sun.COM 	const struct ieee80211_cipher *cip = k->wk_cipher;
19299999SWang.Lin@Sun.COM 	ieee80211_keyix keyix = k->wk_keyix;
19309999SWang.Lin@Sun.COM 
19319999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_KEYCACHE, "arn_key_delete():"
19329999SWang.Lin@Sun.COM 	    " delete key %u ic_cipher=0x%x\n", keyix, cip->ic_cipher));
19339999SWang.Lin@Sun.COM 
19349999SWang.Lin@Sun.COM 	(void) ath9k_hw_keyreset(ah, keyix);
19359999SWang.Lin@Sun.COM 	/*
19369999SWang.Lin@Sun.COM 	 * Handle split tx/rx keying required for TKIP with h/w MIC.
19379999SWang.Lin@Sun.COM 	 */
19389999SWang.Lin@Sun.COM 	if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
19399999SWang.Lin@Sun.COM 	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic)
19409999SWang.Lin@Sun.COM 		(void) ath9k_hw_keyreset(ah, keyix+32);		/* RX key */
19419999SWang.Lin@Sun.COM 
19429999SWang.Lin@Sun.COM 	if (keyix >= IEEE80211_WEP_NKID) {
19439999SWang.Lin@Sun.COM 		/*
19449999SWang.Lin@Sun.COM 		 * Don't touch keymap entries for global keys so
19459999SWang.Lin@Sun.COM 		 * they are never considered for dynamic allocation.
19469999SWang.Lin@Sun.COM 		 */
19479999SWang.Lin@Sun.COM 		clr_bit(keyix, sc->sc_keymap);
19489999SWang.Lin@Sun.COM 		if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
19499999SWang.Lin@Sun.COM 		    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
19509999SWang.Lin@Sun.COM 			/*
19519999SWang.Lin@Sun.COM 			 * If splitmic is true +64 is TX key MIC,
19529999SWang.Lin@Sun.COM 			 * else +64 is RX key + RX key MIC.
19539999SWang.Lin@Sun.COM 			 */
19549999SWang.Lin@Sun.COM 			clr_bit(keyix+64, sc->sc_keymap);
19559999SWang.Lin@Sun.COM 			if (sc->sc_splitmic) {
19569999SWang.Lin@Sun.COM 				/* Rx key */
19579999SWang.Lin@Sun.COM 				clr_bit(keyix+32, sc->sc_keymap);
19589999SWang.Lin@Sun.COM 				/* RX key MIC */
19599999SWang.Lin@Sun.COM 				clr_bit(keyix+32+64, sc->sc_keymap);
19609999SWang.Lin@Sun.COM 			}
19619999SWang.Lin@Sun.COM 		}
19629999SWang.Lin@Sun.COM 	}
19639999SWang.Lin@Sun.COM 	return (1);
19649999SWang.Lin@Sun.COM }
19659999SWang.Lin@Sun.COM 
19669999SWang.Lin@Sun.COM /*
19679999SWang.Lin@Sun.COM  * Set a TKIP key into the hardware.  This handles the
19689999SWang.Lin@Sun.COM  * potential distribution of key state to multiple key
19699999SWang.Lin@Sun.COM  * cache slots for TKIP.
19709999SWang.Lin@Sun.COM  */
19719999SWang.Lin@Sun.COM static int
arn_keyset_tkip(struct arn_softc * sc,const struct ieee80211_key * k,struct ath9k_keyval * hk,const uint8_t mac[IEEE80211_ADDR_LEN])19729999SWang.Lin@Sun.COM arn_keyset_tkip(struct arn_softc *sc, const struct ieee80211_key *k,
19739999SWang.Lin@Sun.COM     struct ath9k_keyval *hk, const uint8_t mac[IEEE80211_ADDR_LEN])
19749999SWang.Lin@Sun.COM {
19759999SWang.Lin@Sun.COM 	uint8_t *key_rxmic = NULL;
19769999SWang.Lin@Sun.COM 	uint8_t *key_txmic = NULL;
19779999SWang.Lin@Sun.COM 	uint8_t  *key = (uint8_t *)&(k->wk_key[0]);
19789999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
19799999SWang.Lin@Sun.COM 
19809999SWang.Lin@Sun.COM 	key_txmic = key + 16;
19819999SWang.Lin@Sun.COM 	key_rxmic = key + 24;
19829999SWang.Lin@Sun.COM 
19839999SWang.Lin@Sun.COM 	if (mac == NULL) {
19849999SWang.Lin@Sun.COM 		/* Group key installation */
19859999SWang.Lin@Sun.COM 		(void) memcpy(hk->kv_mic,  key_rxmic, sizeof (hk->kv_mic));
19869999SWang.Lin@Sun.COM 		return (ath9k_hw_set_keycache_entry(ah, k->wk_keyix, hk,
19879999SWang.Lin@Sun.COM 		    mac, B_FALSE));
19889999SWang.Lin@Sun.COM 	}
19899999SWang.Lin@Sun.COM 	if (!sc->sc_splitmic) {
19909999SWang.Lin@Sun.COM 		/*
19919999SWang.Lin@Sun.COM 		 * data key goes at first index,
19929999SWang.Lin@Sun.COM 		 * the hal handles the MIC keys at index+64.
19939999SWang.Lin@Sun.COM 		 */
19949999SWang.Lin@Sun.COM 		(void) memcpy(hk->kv_mic, key_rxmic, sizeof (hk->kv_mic));
19959999SWang.Lin@Sun.COM 		(void) memcpy(hk->kv_txmic, key_txmic, sizeof (hk->kv_txmic));
19969999SWang.Lin@Sun.COM 		return (ath9k_hw_set_keycache_entry(ah, k->wk_keyix, hk,
19979999SWang.Lin@Sun.COM 		    mac, B_FALSE));
19989999SWang.Lin@Sun.COM 	}
19999999SWang.Lin@Sun.COM 	/*
20009999SWang.Lin@Sun.COM 	 * TX key goes at first index, RX key at +32.
20019999SWang.Lin@Sun.COM 	 * The hal handles the MIC keys at index+64.
20029999SWang.Lin@Sun.COM 	 */
20039999SWang.Lin@Sun.COM 	(void) memcpy(hk->kv_mic, key_txmic, sizeof (hk->kv_mic));
20049999SWang.Lin@Sun.COM 	if (!(ath9k_hw_set_keycache_entry(ah, k->wk_keyix, hk, NULL,
20059999SWang.Lin@Sun.COM 	    B_FALSE))) {
20069999SWang.Lin@Sun.COM 		/* Txmic entry failed. No need to proceed further */
20079999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_KEYCACHE,
20089999SWang.Lin@Sun.COM 		    "%s Setting TX MIC Key Failed\n", __func__));
20099999SWang.Lin@Sun.COM 		return (0);
20109999SWang.Lin@Sun.COM 	}
20119999SWang.Lin@Sun.COM 
20129999SWang.Lin@Sun.COM 	(void) memcpy(hk->kv_mic, key_rxmic, sizeof (hk->kv_mic));
20139999SWang.Lin@Sun.COM 
20149999SWang.Lin@Sun.COM 	/* XXX delete tx key on failure? */
20159999SWang.Lin@Sun.COM 	return (ath9k_hw_set_keycache_entry(ah, k->wk_keyix, hk, mac, B_FALSE));
20169999SWang.Lin@Sun.COM 
20179999SWang.Lin@Sun.COM }
20189999SWang.Lin@Sun.COM 
20199999SWang.Lin@Sun.COM int
arn_key_set(ieee80211com_t * ic,const struct ieee80211_key * k,const uint8_t mac[IEEE80211_ADDR_LEN])20209999SWang.Lin@Sun.COM arn_key_set(ieee80211com_t *ic, const struct ieee80211_key *k,
20219999SWang.Lin@Sun.COM     const uint8_t mac[IEEE80211_ADDR_LEN])
20229999SWang.Lin@Sun.COM {
20239999SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)ic;
20249999SWang.Lin@Sun.COM 	const struct ieee80211_cipher *cip = k->wk_cipher;
20259999SWang.Lin@Sun.COM 	struct ath9k_keyval hk;
20269999SWang.Lin@Sun.COM 
20279999SWang.Lin@Sun.COM 	/* cipher table */
20289999SWang.Lin@Sun.COM 	static const uint8_t ciphermap[] = {
20299999SWang.Lin@Sun.COM 		ATH9K_CIPHER_WEP,		/* IEEE80211_CIPHER_WEP */
20309999SWang.Lin@Sun.COM 		ATH9K_CIPHER_TKIP,		/* IEEE80211_CIPHER_TKIP */
20319999SWang.Lin@Sun.COM 		ATH9K_CIPHER_AES_OCB,	/* IEEE80211_CIPHER_AES_OCB */
20329999SWang.Lin@Sun.COM 		ATH9K_CIPHER_AES_CCM,	/* IEEE80211_CIPHER_AES_CCM */
20339999SWang.Lin@Sun.COM 		ATH9K_CIPHER_CKIP,		/* IEEE80211_CIPHER_CKIP */
20349999SWang.Lin@Sun.COM 		ATH9K_CIPHER_CLR,		/* IEEE80211_CIPHER_NONE */
20359999SWang.Lin@Sun.COM 	};
20369999SWang.Lin@Sun.COM 
20379999SWang.Lin@Sun.COM 	bzero(&hk, sizeof (hk));
20389999SWang.Lin@Sun.COM 
20399999SWang.Lin@Sun.COM 	/*
20409999SWang.Lin@Sun.COM 	 * Software crypto uses a "clear key" so non-crypto
20419999SWang.Lin@Sun.COM 	 * state kept in the key cache are maintainedd so that
20429999SWang.Lin@Sun.COM 	 * rx frames have an entry to match.
20439999SWang.Lin@Sun.COM 	 */
20449999SWang.Lin@Sun.COM 	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) {
20459999SWang.Lin@Sun.COM 		ASSERT(cip->ic_cipher < 6);
20469999SWang.Lin@Sun.COM 		hk.kv_type = ciphermap[cip->ic_cipher];
20479999SWang.Lin@Sun.COM 		hk.kv_len = k->wk_keylen;
20489999SWang.Lin@Sun.COM 		bcopy(k->wk_key, hk.kv_val, k->wk_keylen);
20499999SWang.Lin@Sun.COM 	} else {
20509999SWang.Lin@Sun.COM 		hk.kv_type = ATH9K_CIPHER_CLR;
20519999SWang.Lin@Sun.COM 	}
20529999SWang.Lin@Sun.COM 
20539999SWang.Lin@Sun.COM 	if (hk.kv_type == ATH9K_CIPHER_TKIP &&
20549999SWang.Lin@Sun.COM 	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
20559999SWang.Lin@Sun.COM 		return (arn_keyset_tkip(sc, k, &hk, mac));
20569999SWang.Lin@Sun.COM 	} else {
20579999SWang.Lin@Sun.COM 		return (ath9k_hw_set_keycache_entry(sc->sc_ah,
20589999SWang.Lin@Sun.COM 		    k->wk_keyix, &hk, mac, B_FALSE));
20599999SWang.Lin@Sun.COM 	}
20609999SWang.Lin@Sun.COM }
20619999SWang.Lin@Sun.COM 
20629999SWang.Lin@Sun.COM /*
20639999SWang.Lin@Sun.COM  * Enable/Disable short slot timing
20649999SWang.Lin@Sun.COM  */
20659999SWang.Lin@Sun.COM void
arn_set_shortslot(ieee80211com_t * ic,int onoff)20669999SWang.Lin@Sun.COM arn_set_shortslot(ieee80211com_t *ic, int onoff)
20679999SWang.Lin@Sun.COM {
20689999SWang.Lin@Sun.COM 	struct ath_hal *ah = ((struct arn_softc *)ic)->sc_ah;
20699999SWang.Lin@Sun.COM 
20709999SWang.Lin@Sun.COM 	if (onoff)
20719999SWang.Lin@Sun.COM 		(void) ath9k_hw_setslottime(ah, ATH9K_SLOT_TIME_9);
20729999SWang.Lin@Sun.COM 	else
20739999SWang.Lin@Sun.COM 		(void) ath9k_hw_setslottime(ah, ATH9K_SLOT_TIME_20);
20749999SWang.Lin@Sun.COM }
20759999SWang.Lin@Sun.COM 
20769999SWang.Lin@Sun.COM static int
arn_open(struct arn_softc * sc)20779999SWang.Lin@Sun.COM arn_open(struct arn_softc *sc)
20789999SWang.Lin@Sun.COM {
20799999SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
20809999SWang.Lin@Sun.COM 	struct ieee80211_channel *curchan = ic->ic_curchan;
20819999SWang.Lin@Sun.COM 	struct ath9k_channel *init_channel;
20829999SWang.Lin@Sun.COM 	int error = 0, pos, status;
20839999SWang.Lin@Sun.COM 
20849999SWang.Lin@Sun.COM 	ARN_LOCK_ASSERT(sc);
20859999SWang.Lin@Sun.COM 
20869999SWang.Lin@Sun.COM 	pos = arn_get_channel(sc, curchan);
20879999SWang.Lin@Sun.COM 	if (pos == -1) {
20889999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_FATAL, "arn: "
20899999SWang.Lin@Sun.COM 		    "%s: Invalid channel\n", __func__));
20909999SWang.Lin@Sun.COM 		error = EINVAL;
20919999SWang.Lin@Sun.COM 		goto error;
20929999SWang.Lin@Sun.COM 	}
20939999SWang.Lin@Sun.COM 
20949999SWang.Lin@Sun.COM 	sc->tx_chan_width = ATH9K_HT_MACMODE_20;
20959999SWang.Lin@Sun.COM 
20969999SWang.Lin@Sun.COM 	if (sc->sc_curmode == ATH9K_MODE_11A) {
20979999SWang.Lin@Sun.COM 		sc->sc_ah->ah_channels[pos].chanmode = CHANNEL_A;
20989999SWang.Lin@Sun.COM 	} else {
20999999SWang.Lin@Sun.COM 		sc->sc_ah->ah_channels[pos].chanmode = CHANNEL_G;
21009999SWang.Lin@Sun.COM 	}
21019999SWang.Lin@Sun.COM 
21029999SWang.Lin@Sun.COM 	init_channel = &sc->sc_ah->ah_channels[pos];
21039999SWang.Lin@Sun.COM 
21049999SWang.Lin@Sun.COM 	/* Reset SERDES registers */
21059999SWang.Lin@Sun.COM 	ath9k_hw_configpcipowersave(sc->sc_ah, 0);
21069999SWang.Lin@Sun.COM 
21079999SWang.Lin@Sun.COM 	/*
21089999SWang.Lin@Sun.COM 	 * The basic interface to setting the hardware in a good
21099999SWang.Lin@Sun.COM 	 * state is ``reset''.	On return the hardware is known to
21109999SWang.Lin@Sun.COM 	 * be powered up and with interrupts disabled.	This must
21119999SWang.Lin@Sun.COM 	 * be followed by initialization of the appropriate bits
21129999SWang.Lin@Sun.COM 	 * and then setup of the interrupt mask.
21139999SWang.Lin@Sun.COM 	 */
21149999SWang.Lin@Sun.COM 	if (!ath9k_hw_reset(sc->sc_ah, init_channel,
21159999SWang.Lin@Sun.COM 	    sc->tx_chan_width, sc->sc_tx_chainmask,
21169999SWang.Lin@Sun.COM 	    sc->sc_rx_chainmask, sc->sc_ht_extprotspacing,
21179999SWang.Lin@Sun.COM 	    B_FALSE, &status)) {
21189999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_FATAL, "arn: "
21199999SWang.Lin@Sun.COM 		    "%s: unable to reset hardware; hal status %u "
21209999SWang.Lin@Sun.COM 		    "(freq %u flags 0x%x)\n", __func__, status,
21219999SWang.Lin@Sun.COM 		    init_channel->channel, init_channel->channelFlags));
21229999SWang.Lin@Sun.COM 
21239999SWang.Lin@Sun.COM 		error = EIO;
21249999SWang.Lin@Sun.COM 		goto error;
21259999SWang.Lin@Sun.COM 	}
21269999SWang.Lin@Sun.COM 
21279999SWang.Lin@Sun.COM 	/*
21289999SWang.Lin@Sun.COM 	 * This is needed only to setup initial state
21299999SWang.Lin@Sun.COM 	 * but it's best done after a reset.
21309999SWang.Lin@Sun.COM 	 */
21319999SWang.Lin@Sun.COM 	arn_update_txpow(sc);
21329999SWang.Lin@Sun.COM 
21339999SWang.Lin@Sun.COM 	/*
21349999SWang.Lin@Sun.COM 	 * Setup the hardware after reset:
21359999SWang.Lin@Sun.COM 	 * The receive engine is set going.
21369999SWang.Lin@Sun.COM 	 * Frame transmit is handled entirely
21379999SWang.Lin@Sun.COM 	 * in the frame output path; there's nothing to do
21389999SWang.Lin@Sun.COM 	 * here except setup the interrupt mask.
21399999SWang.Lin@Sun.COM 	 */
21409999SWang.Lin@Sun.COM 	if (arn_startrecv(sc) != 0) {
21419999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_INIT, "arn: "
21429999SWang.Lin@Sun.COM 		    "%s: unable to start recv logic\n", __func__));
21439999SWang.Lin@Sun.COM 		error = EIO;
21449999SWang.Lin@Sun.COM 		goto error;
21459999SWang.Lin@Sun.COM 	}
21469999SWang.Lin@Sun.COM 
21479999SWang.Lin@Sun.COM 	/* Setup our intr mask. */
21489999SWang.Lin@Sun.COM 	sc->sc_imask = ATH9K_INT_RX | ATH9K_INT_TX |
21499999SWang.Lin@Sun.COM 	    ATH9K_INT_RXEOL | ATH9K_INT_RXORN |
21509999SWang.Lin@Sun.COM 	    ATH9K_INT_FATAL | ATH9K_INT_GLOBAL;
21519999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_HW_CAP_GTT
21529999SWang.Lin@Sun.COM 	if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_GTT)
21539999SWang.Lin@Sun.COM 		sc->sc_imask |= ATH9K_INT_GTT;
21549999SWang.Lin@Sun.COM #endif
21559999SWang.Lin@Sun.COM 
21569999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_HW_CAP_GTT
21579999SWang.Lin@Sun.COM 	if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT)
21589999SWang.Lin@Sun.COM 		sc->sc_imask |= ATH9K_INT_CST;
21599999SWang.Lin@Sun.COM #endif
21609999SWang.Lin@Sun.COM 
21619999SWang.Lin@Sun.COM 	/*
21629999SWang.Lin@Sun.COM 	 * Enable MIB interrupts when there are hardware phy counters.
21639999SWang.Lin@Sun.COM 	 * Note we only do this (at the moment) for station mode.
21649999SWang.Lin@Sun.COM 	 */
21659999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_INT_MIB
21669999SWang.Lin@Sun.COM 	if (ath9k_hw_phycounters(sc->sc_ah) &&
21679999SWang.Lin@Sun.COM 	    ((sc->sc_ah->ah_opmode == ATH9K_M_STA) ||
21689999SWang.Lin@Sun.COM 	    (sc->sc_ah->ah_opmode == ATH9K_M_IBSS)))
21699999SWang.Lin@Sun.COM 		sc->sc_imask |= ATH9K_INT_MIB;
21709999SWang.Lin@Sun.COM #endif
21719999SWang.Lin@Sun.COM 	/*
21729999SWang.Lin@Sun.COM 	 * Some hardware processes the TIM IE and fires an
21739999SWang.Lin@Sun.COM 	 * interrupt when the TIM bit is set.  For hardware
21749999SWang.Lin@Sun.COM 	 * that does, if not overridden by configuration,
21759999SWang.Lin@Sun.COM 	 * enable the TIM interrupt when operating as station.
21769999SWang.Lin@Sun.COM 	 */
21779999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_INT_TIM
21789999SWang.Lin@Sun.COM 	if ((sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_ENHANCEDPM) &&
21799999SWang.Lin@Sun.COM 	    (sc->sc_ah->ah_opmode == ATH9K_M_STA) &&
21809999SWang.Lin@Sun.COM 	    !sc->sc_config.swBeaconProcess)
21819999SWang.Lin@Sun.COM 		sc->sc_imask |= ATH9K_INT_TIM;
21829999SWang.Lin@Sun.COM #endif
21839999SWang.Lin@Sun.COM 	if (arn_chan2mode(init_channel) != sc->sc_curmode)
21849999SWang.Lin@Sun.COM 		arn_setcurmode(sc, arn_chan2mode(init_channel));
21859999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_INIT, "arn: "
21869999SWang.Lin@Sun.COM 	    "%s: current mode after arn_setcurmode is %d\n",
21879999SWang.Lin@Sun.COM 	    __func__, sc->sc_curmode));
21889999SWang.Lin@Sun.COM 
21899999SWang.Lin@Sun.COM 	sc->sc_isrunning = 1;
21909999SWang.Lin@Sun.COM 
21919999SWang.Lin@Sun.COM 	/* Disable BMISS interrupt when we're not associated */
21929999SWang.Lin@Sun.COM 	sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
21939999SWang.Lin@Sun.COM 	(void) ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask);
21949999SWang.Lin@Sun.COM 
21959999SWang.Lin@Sun.COM 	return (0);
21969999SWang.Lin@Sun.COM 
21979999SWang.Lin@Sun.COM error:
21989999SWang.Lin@Sun.COM 	return (error);
21999999SWang.Lin@Sun.COM }
22009999SWang.Lin@Sun.COM 
22019999SWang.Lin@Sun.COM static void
arn_close(struct arn_softc * sc)22029999SWang.Lin@Sun.COM arn_close(struct arn_softc *sc)
22039999SWang.Lin@Sun.COM {
22049999SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
22059999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
22069999SWang.Lin@Sun.COM 
22079999SWang.Lin@Sun.COM 	ARN_LOCK_ASSERT(sc);
22089999SWang.Lin@Sun.COM 
22099999SWang.Lin@Sun.COM 	if (!sc->sc_isrunning)
22109999SWang.Lin@Sun.COM 		return;
22119999SWang.Lin@Sun.COM 
22129999SWang.Lin@Sun.COM 	/*
22139999SWang.Lin@Sun.COM 	 * Shutdown the hardware and driver
22149999SWang.Lin@Sun.COM 	 * Note that some of this work is not possible if the
22159999SWang.Lin@Sun.COM 	 * hardware is gone (invalid).
22169999SWang.Lin@Sun.COM 	 */
22179999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
22189999SWang.Lin@Sun.COM 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
22199999SWang.Lin@Sun.COM 	ieee80211_stop_watchdog(ic);
22209999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
22219999SWang.Lin@Sun.COM 
22229999SWang.Lin@Sun.COM 	/*
22239999SWang.Lin@Sun.COM 	 * make sure h/w will not generate any interrupt
22249999SWang.Lin@Sun.COM 	 * before setting the invalid flag.
22259999SWang.Lin@Sun.COM 	 */
22269999SWang.Lin@Sun.COM 	(void) ath9k_hw_set_interrupts(ah, 0);
22279999SWang.Lin@Sun.COM 
22289999SWang.Lin@Sun.COM 	if (!(sc->sc_flags & SC_OP_INVALID)) {
22299999SWang.Lin@Sun.COM 		arn_draintxq(sc, 0);
22309999SWang.Lin@Sun.COM 		(void) arn_stoprecv(sc);
22319999SWang.Lin@Sun.COM 		(void) ath9k_hw_phy_disable(ah);
22329999SWang.Lin@Sun.COM 	} else {
22339999SWang.Lin@Sun.COM 		sc->sc_rxlink = NULL;
22349999SWang.Lin@Sun.COM 	}
22359999SWang.Lin@Sun.COM 
22369999SWang.Lin@Sun.COM 	sc->sc_isrunning = 0;
22379999SWang.Lin@Sun.COM }
22389999SWang.Lin@Sun.COM 
22399999SWang.Lin@Sun.COM /*
22409999SWang.Lin@Sun.COM  * MAC callback functions
22419999SWang.Lin@Sun.COM  */
22429999SWang.Lin@Sun.COM static int
arn_m_stat(void * arg,uint_t stat,uint64_t * val)22439999SWang.Lin@Sun.COM arn_m_stat(void *arg, uint_t stat, uint64_t *val)
22449999SWang.Lin@Sun.COM {
22459999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
22469999SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
22479999SWang.Lin@Sun.COM 	struct ieee80211_node *in;
22489999SWang.Lin@Sun.COM 	struct ieee80211_rateset *rs;
22499999SWang.Lin@Sun.COM 
22509999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
22519999SWang.Lin@Sun.COM 	switch (stat) {
22529999SWang.Lin@Sun.COM 	case MAC_STAT_IFSPEED:
22539999SWang.Lin@Sun.COM 		in = ic->ic_bss;
22549999SWang.Lin@Sun.COM 		rs = &in->in_rates;
22559999SWang.Lin@Sun.COM 		*val = (rs->ir_rates[in->in_txrate] & IEEE80211_RATE_VAL) / 2 *
22569999SWang.Lin@Sun.COM 		    1000000ull;
22579999SWang.Lin@Sun.COM 		break;
22589999SWang.Lin@Sun.COM 	case MAC_STAT_NOXMTBUF:
22599999SWang.Lin@Sun.COM 		*val = sc->sc_stats.ast_tx_nobuf +
22609999SWang.Lin@Sun.COM 		    sc->sc_stats.ast_tx_nobufmgt;
22619999SWang.Lin@Sun.COM 		break;
22629999SWang.Lin@Sun.COM 	case MAC_STAT_IERRORS:
22639999SWang.Lin@Sun.COM 		*val = sc->sc_stats.ast_rx_tooshort;
22649999SWang.Lin@Sun.COM 		break;
22659999SWang.Lin@Sun.COM 	case MAC_STAT_RBYTES:
22669999SWang.Lin@Sun.COM 		*val = ic->ic_stats.is_rx_bytes;
22679999SWang.Lin@Sun.COM 		break;
22689999SWang.Lin@Sun.COM 	case MAC_STAT_IPACKETS:
22699999SWang.Lin@Sun.COM 		*val = ic->ic_stats.is_rx_frags;
22709999SWang.Lin@Sun.COM 		break;
22719999SWang.Lin@Sun.COM 	case MAC_STAT_OBYTES:
22729999SWang.Lin@Sun.COM 		*val = ic->ic_stats.is_tx_bytes;
22739999SWang.Lin@Sun.COM 		break;
22749999SWang.Lin@Sun.COM 	case MAC_STAT_OPACKETS:
22759999SWang.Lin@Sun.COM 		*val = ic->ic_stats.is_tx_frags;
22769999SWang.Lin@Sun.COM 		break;
22779999SWang.Lin@Sun.COM 	case MAC_STAT_OERRORS:
22789999SWang.Lin@Sun.COM 	case WIFI_STAT_TX_FAILED:
22799999SWang.Lin@Sun.COM 		*val = sc->sc_stats.ast_tx_fifoerr +
22809999SWang.Lin@Sun.COM 		    sc->sc_stats.ast_tx_xretries +
22819999SWang.Lin@Sun.COM 		    sc->sc_stats.ast_tx_discard;
22829999SWang.Lin@Sun.COM 		break;
22839999SWang.Lin@Sun.COM 	case WIFI_STAT_TX_RETRANS:
22849999SWang.Lin@Sun.COM 		*val = sc->sc_stats.ast_tx_xretries;
22859999SWang.Lin@Sun.COM 		break;
22869999SWang.Lin@Sun.COM 	case WIFI_STAT_FCS_ERRORS:
22879999SWang.Lin@Sun.COM 		*val = sc->sc_stats.ast_rx_crcerr;
22889999SWang.Lin@Sun.COM 		break;
22899999SWang.Lin@Sun.COM 	case WIFI_STAT_WEP_ERRORS:
22909999SWang.Lin@Sun.COM 		*val = sc->sc_stats.ast_rx_badcrypt;
22919999SWang.Lin@Sun.COM 		break;
22929999SWang.Lin@Sun.COM 	case WIFI_STAT_TX_FRAGS:
22939999SWang.Lin@Sun.COM 	case WIFI_STAT_MCAST_TX:
22949999SWang.Lin@Sun.COM 	case WIFI_STAT_RTS_SUCCESS:
22959999SWang.Lin@Sun.COM 	case WIFI_STAT_RTS_FAILURE:
22969999SWang.Lin@Sun.COM 	case WIFI_STAT_ACK_FAILURE:
22979999SWang.Lin@Sun.COM 	case WIFI_STAT_RX_FRAGS:
22989999SWang.Lin@Sun.COM 	case WIFI_STAT_MCAST_RX:
22999999SWang.Lin@Sun.COM 	case WIFI_STAT_RX_DUPS:
23009999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
23019999SWang.Lin@Sun.COM 		return (ieee80211_stat(ic, stat, val));
23029999SWang.Lin@Sun.COM 	default:
23039999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
23049999SWang.Lin@Sun.COM 		return (ENOTSUP);
23059999SWang.Lin@Sun.COM 	}
23069999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
23079999SWang.Lin@Sun.COM 
23089999SWang.Lin@Sun.COM 	return (0);
23099999SWang.Lin@Sun.COM }
23109999SWang.Lin@Sun.COM 
23119999SWang.Lin@Sun.COM int
arn_m_start(void * arg)23129999SWang.Lin@Sun.COM arn_m_start(void *arg)
23139999SWang.Lin@Sun.COM {
23149999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
23159999SWang.Lin@Sun.COM 	int err = 0;
23169999SWang.Lin@Sun.COM 
23179999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
23189999SWang.Lin@Sun.COM 
23199999SWang.Lin@Sun.COM 	/*
23209999SWang.Lin@Sun.COM 	 * Stop anything previously setup.  This is safe
23219999SWang.Lin@Sun.COM 	 * whether this is the first time through or not.
23229999SWang.Lin@Sun.COM 	 */
23239999SWang.Lin@Sun.COM 
23249999SWang.Lin@Sun.COM 	arn_close(sc);
23259999SWang.Lin@Sun.COM 
23269999SWang.Lin@Sun.COM 	if ((err = arn_open(sc)) != 0) {
23279999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
23289999SWang.Lin@Sun.COM 		return (err);
23299999SWang.Lin@Sun.COM 	}
23309999SWang.Lin@Sun.COM 
23319999SWang.Lin@Sun.COM 	/* H/W is reday now */
23329999SWang.Lin@Sun.COM 	sc->sc_flags &= ~SC_OP_INVALID;
23339999SWang.Lin@Sun.COM 
23349999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
23359999SWang.Lin@Sun.COM 
23369999SWang.Lin@Sun.COM 	return (0);
23379999SWang.Lin@Sun.COM }
23389999SWang.Lin@Sun.COM 
23399999SWang.Lin@Sun.COM static void
arn_m_stop(void * arg)23409999SWang.Lin@Sun.COM arn_m_stop(void *arg)
23419999SWang.Lin@Sun.COM {
23429999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
23439999SWang.Lin@Sun.COM 
23449999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
23459999SWang.Lin@Sun.COM 	arn_close(sc);
23469999SWang.Lin@Sun.COM 
23479999SWang.Lin@Sun.COM 	/* disable HAL and put h/w to sleep */
23489999SWang.Lin@Sun.COM 	(void) ath9k_hw_disable(sc->sc_ah);
23499999SWang.Lin@Sun.COM 	ath9k_hw_configpcipowersave(sc->sc_ah, 1);
23509999SWang.Lin@Sun.COM 
23519999SWang.Lin@Sun.COM 	/* XXX: hardware will not be ready in suspend state */
23529999SWang.Lin@Sun.COM 	sc->sc_flags |= SC_OP_INVALID;
23539999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
23549999SWang.Lin@Sun.COM }
23559999SWang.Lin@Sun.COM 
23569999SWang.Lin@Sun.COM static int
arn_m_promisc(void * arg,boolean_t on)23579999SWang.Lin@Sun.COM arn_m_promisc(void *arg, boolean_t on)
23589999SWang.Lin@Sun.COM {
23599999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
23609999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
23619999SWang.Lin@Sun.COM 	uint32_t rfilt;
23629999SWang.Lin@Sun.COM 
23639999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
23649999SWang.Lin@Sun.COM 
23659999SWang.Lin@Sun.COM 	rfilt = ath9k_hw_getrxfilter(ah);
23669999SWang.Lin@Sun.COM 	if (on)
23679999SWang.Lin@Sun.COM 		rfilt |= ATH9K_RX_FILTER_PROM;
23689999SWang.Lin@Sun.COM 	else
23699999SWang.Lin@Sun.COM 		rfilt &= ~ATH9K_RX_FILTER_PROM;
23709999SWang.Lin@Sun.COM 	sc->sc_promisc = on;
23719999SWang.Lin@Sun.COM 	ath9k_hw_setrxfilter(ah, rfilt);
23729999SWang.Lin@Sun.COM 
23739999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
23749999SWang.Lin@Sun.COM 
23759999SWang.Lin@Sun.COM 	return (0);
23769999SWang.Lin@Sun.COM }
23779999SWang.Lin@Sun.COM 
23789999SWang.Lin@Sun.COM static int
arn_m_multicst(void * arg,boolean_t add,const uint8_t * mca)23799999SWang.Lin@Sun.COM arn_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
23809999SWang.Lin@Sun.COM {
23819999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
23829999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
23839999SWang.Lin@Sun.COM 	uint32_t val, index, bit;
23849999SWang.Lin@Sun.COM 	uint8_t pos;
23859999SWang.Lin@Sun.COM 	uint32_t *mfilt = sc->sc_mcast_hash;
23869999SWang.Lin@Sun.COM 
23879999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
23889999SWang.Lin@Sun.COM 
23899999SWang.Lin@Sun.COM 	/* calculate XOR of eight 6bit values */
23909999SWang.Lin@Sun.COM 	val = ARN_LE_READ_32(mca + 0);
23919999SWang.Lin@Sun.COM 	pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
23929999SWang.Lin@Sun.COM 	val = ARN_LE_READ_32(mca + 3);
23939999SWang.Lin@Sun.COM 	pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
23949999SWang.Lin@Sun.COM 	pos &= 0x3f;
23959999SWang.Lin@Sun.COM 	index = pos / 32;
23969999SWang.Lin@Sun.COM 	bit = 1 << (pos % 32);
23979999SWang.Lin@Sun.COM 
23989999SWang.Lin@Sun.COM 	if (add) {	/* enable multicast */
23999999SWang.Lin@Sun.COM 		sc->sc_mcast_refs[pos]++;
24009999SWang.Lin@Sun.COM 		mfilt[index] |= bit;
24019999SWang.Lin@Sun.COM 	} else {	/* disable multicast */
24029999SWang.Lin@Sun.COM 		if (--sc->sc_mcast_refs[pos] == 0)
24039999SWang.Lin@Sun.COM 			mfilt[index] &= ~bit;
24049999SWang.Lin@Sun.COM 	}
24059999SWang.Lin@Sun.COM 	ath9k_hw_setmcastfilter(ah, mfilt[0], mfilt[1]);
24069999SWang.Lin@Sun.COM 
24079999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
24089999SWang.Lin@Sun.COM 	return (0);
24099999SWang.Lin@Sun.COM }
24109999SWang.Lin@Sun.COM 
24119999SWang.Lin@Sun.COM static int
arn_m_unicst(void * arg,const uint8_t * macaddr)24129999SWang.Lin@Sun.COM arn_m_unicst(void *arg, const uint8_t *macaddr)
24139999SWang.Lin@Sun.COM {
24149999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
24159999SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
24169999SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
24179999SWang.Lin@Sun.COM 
24189999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_XMIT, "ath: ath_gld_saddr(): "
24199999SWang.Lin@Sun.COM 	    "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
24209999SWang.Lin@Sun.COM 	    macaddr[0], macaddr[1], macaddr[2],
24219999SWang.Lin@Sun.COM 	    macaddr[3], macaddr[4], macaddr[5]));
24229999SWang.Lin@Sun.COM 
24239999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
24249999SWang.Lin@Sun.COM 	IEEE80211_ADDR_COPY(sc->sc_isc.ic_macaddr, macaddr);
24259999SWang.Lin@Sun.COM 	(void) ath9k_hw_setmac(ah, sc->sc_isc.ic_macaddr);
24269999SWang.Lin@Sun.COM 	(void) arn_reset(ic);
24279999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
24289999SWang.Lin@Sun.COM 	return (0);
24299999SWang.Lin@Sun.COM }
24309999SWang.Lin@Sun.COM 
24319999SWang.Lin@Sun.COM static mblk_t *
arn_m_tx(void * arg,mblk_t * mp)24329999SWang.Lin@Sun.COM arn_m_tx(void *arg, mblk_t *mp)
24339999SWang.Lin@Sun.COM {
24349999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
24359999SWang.Lin@Sun.COM 	int error = 0;
24369999SWang.Lin@Sun.COM 	mblk_t *next;
24379999SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
24389999SWang.Lin@Sun.COM 
24399999SWang.Lin@Sun.COM 	/*
24409999SWang.Lin@Sun.COM 	 * No data frames go out unless we're associated; this
24419999SWang.Lin@Sun.COM 	 * should not happen as the 802.11 layer does not enable
24429999SWang.Lin@Sun.COM 	 * the xmit queue until we enter the RUN state.
24439999SWang.Lin@Sun.COM 	 */
24449999SWang.Lin@Sun.COM 	if (ic->ic_state != IEEE80211_S_RUN) {
24459999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_XMIT, "arn: arn_m_tx(): "
24469999SWang.Lin@Sun.COM 		    "discard, state %u\n", ic->ic_state));
24479999SWang.Lin@Sun.COM 		sc->sc_stats.ast_tx_discard++;
24489999SWang.Lin@Sun.COM 		freemsgchain(mp);
24499999SWang.Lin@Sun.COM 		return (NULL);
24509999SWang.Lin@Sun.COM 	}
24519999SWang.Lin@Sun.COM 
24529999SWang.Lin@Sun.COM 	while (mp != NULL) {
24539999SWang.Lin@Sun.COM 		next = mp->b_next;
24549999SWang.Lin@Sun.COM 		mp->b_next = NULL;
24559999SWang.Lin@Sun.COM 		error = arn_tx(ic, mp, IEEE80211_FC0_TYPE_DATA);
24569999SWang.Lin@Sun.COM 		if (error != 0) {
24579999SWang.Lin@Sun.COM 			mp->b_next = next;
24589999SWang.Lin@Sun.COM 			if (error == ENOMEM) {
24599999SWang.Lin@Sun.COM 				break;
24609999SWang.Lin@Sun.COM 			} else {
24619999SWang.Lin@Sun.COM 				freemsgchain(mp);
24629999SWang.Lin@Sun.COM 				return (NULL);
24639999SWang.Lin@Sun.COM 			}
24649999SWang.Lin@Sun.COM 		}
24659999SWang.Lin@Sun.COM 		mp = next;
24669999SWang.Lin@Sun.COM 	}
24679999SWang.Lin@Sun.COM 
24689999SWang.Lin@Sun.COM 	return (mp);
24699999SWang.Lin@Sun.COM }
24709999SWang.Lin@Sun.COM 
24719999SWang.Lin@Sun.COM static void
arn_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)24729999SWang.Lin@Sun.COM arn_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
24739999SWang.Lin@Sun.COM {
24749999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
24759999SWang.Lin@Sun.COM 	int32_t err;
24769999SWang.Lin@Sun.COM 
24779999SWang.Lin@Sun.COM 	err = ieee80211_ioctl(&sc->sc_isc, wq, mp);
24789999SWang.Lin@Sun.COM 
24799999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
24809999SWang.Lin@Sun.COM 	if (err == ENETRESET) {
24819999SWang.Lin@Sun.COM 		if (!(sc->sc_flags & SC_OP_INVALID)) {
24829999SWang.Lin@Sun.COM 			ARN_UNLOCK(sc);
24839999SWang.Lin@Sun.COM 
24849999SWang.Lin@Sun.COM 			(void) arn_m_start(sc);
24859999SWang.Lin@Sun.COM 
24869999SWang.Lin@Sun.COM 			(void) ieee80211_new_state(&sc->sc_isc,
24879999SWang.Lin@Sun.COM 			    IEEE80211_S_SCAN, -1);
24889999SWang.Lin@Sun.COM 			ARN_LOCK(sc);
24899999SWang.Lin@Sun.COM 		}
24909999SWang.Lin@Sun.COM 	}
24919999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
24929999SWang.Lin@Sun.COM }
24939999SWang.Lin@Sun.COM 
24949999SWang.Lin@Sun.COM static int
arn_m_setprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,const void * wldp_buf)24959999SWang.Lin@Sun.COM arn_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
24969999SWang.Lin@Sun.COM     uint_t wldp_length, const void *wldp_buf)
24979999SWang.Lin@Sun.COM {
24989999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
24999999SWang.Lin@Sun.COM 	int	err;
25009999SWang.Lin@Sun.COM 
25019999SWang.Lin@Sun.COM 	err = ieee80211_setprop(&sc->sc_isc, pr_name, wldp_pr_num,
25029999SWang.Lin@Sun.COM 	    wldp_length, wldp_buf);
25039999SWang.Lin@Sun.COM 
25049999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
25059999SWang.Lin@Sun.COM 
25069999SWang.Lin@Sun.COM 	if (err == ENETRESET) {
25079999SWang.Lin@Sun.COM 		if (!(sc->sc_flags & SC_OP_INVALID)) {
25089999SWang.Lin@Sun.COM 			ARN_UNLOCK(sc);
25099999SWang.Lin@Sun.COM 			(void) arn_m_start(sc);
25109999SWang.Lin@Sun.COM 			(void) ieee80211_new_state(&sc->sc_isc,
25119999SWang.Lin@Sun.COM 			    IEEE80211_S_SCAN, -1);
25129999SWang.Lin@Sun.COM 			ARN_LOCK(sc);
25139999SWang.Lin@Sun.COM 		}
25149999SWang.Lin@Sun.COM 		err = 0;
25159999SWang.Lin@Sun.COM 	}
25169999SWang.Lin@Sun.COM 
25179999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
25189999SWang.Lin@Sun.COM 
25199999SWang.Lin@Sun.COM 	return (err);
25209999SWang.Lin@Sun.COM }
25219999SWang.Lin@Sun.COM 
25229999SWang.Lin@Sun.COM /* ARGSUSED */
25239999SWang.Lin@Sun.COM static int
arn_m_getprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,void * wldp_buf)25249999SWang.Lin@Sun.COM arn_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2525*11878SVenu.Iyer@Sun.COM     uint_t wldp_length, void *wldp_buf)
25269999SWang.Lin@Sun.COM {
25279999SWang.Lin@Sun.COM 	struct arn_softc *sc = arg;
25289999SWang.Lin@Sun.COM 	int	err = 0;
25299999SWang.Lin@Sun.COM 
25309999SWang.Lin@Sun.COM 	err = ieee80211_getprop(&sc->sc_isc, pr_name, wldp_pr_num,
2531*11878SVenu.Iyer@Sun.COM 	    wldp_length, wldp_buf);
25329999SWang.Lin@Sun.COM 
25339999SWang.Lin@Sun.COM 	return (err);
25349999SWang.Lin@Sun.COM }
25359999SWang.Lin@Sun.COM 
2536*11878SVenu.Iyer@Sun.COM static void
arn_m_propinfo(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,mac_prop_info_handle_t prh)2537*11878SVenu.Iyer@Sun.COM arn_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2538*11878SVenu.Iyer@Sun.COM     mac_prop_info_handle_t prh)
2539*11878SVenu.Iyer@Sun.COM {
2540*11878SVenu.Iyer@Sun.COM 	struct arn_softc *sc = arg;
2541*11878SVenu.Iyer@Sun.COM 
2542*11878SVenu.Iyer@Sun.COM 	ieee80211_propinfo(&sc->sc_isc, pr_name, wldp_pr_num, prh);
2543*11878SVenu.Iyer@Sun.COM }
2544*11878SVenu.Iyer@Sun.COM 
25459999SWang.Lin@Sun.COM /* return bus cachesize in 4B word units */
25469999SWang.Lin@Sun.COM static void
arn_pci_config_cachesize(struct arn_softc * sc)25479999SWang.Lin@Sun.COM arn_pci_config_cachesize(struct arn_softc *sc)
25489999SWang.Lin@Sun.COM {
25499999SWang.Lin@Sun.COM 	uint8_t csz;
25509999SWang.Lin@Sun.COM 
25519999SWang.Lin@Sun.COM 	/*
25529999SWang.Lin@Sun.COM 	 * Cache line size is used to size and align various
25539999SWang.Lin@Sun.COM 	 * structures used to communicate with the hardware.
25549999SWang.Lin@Sun.COM 	 */
25559999SWang.Lin@Sun.COM 	csz = pci_config_get8(sc->sc_cfg_handle, PCI_CONF_CACHE_LINESZ);
25569999SWang.Lin@Sun.COM 	if (csz == 0) {
25579999SWang.Lin@Sun.COM 		/*
25589999SWang.Lin@Sun.COM 		 * We must have this setup properly for rx buffer
25599999SWang.Lin@Sun.COM 		 * DMA to work so force a reasonable value here if it
25609999SWang.Lin@Sun.COM 		 * comes up zero.
25619999SWang.Lin@Sun.COM 		 */
25629999SWang.Lin@Sun.COM 		csz = ATH_DEF_CACHE_BYTES / sizeof (uint32_t);
25639999SWang.Lin@Sun.COM 		pci_config_put8(sc->sc_cfg_handle, PCI_CONF_CACHE_LINESZ,
25649999SWang.Lin@Sun.COM 		    csz);
25659999SWang.Lin@Sun.COM 	}
25669999SWang.Lin@Sun.COM 	sc->sc_cachelsz = csz << 2;
25679999SWang.Lin@Sun.COM }
25689999SWang.Lin@Sun.COM 
25699999SWang.Lin@Sun.COM static int
arn_pci_setup(struct arn_softc * sc)25709999SWang.Lin@Sun.COM arn_pci_setup(struct arn_softc *sc)
25719999SWang.Lin@Sun.COM {
25729999SWang.Lin@Sun.COM 	uint16_t command;
25739999SWang.Lin@Sun.COM 
25749999SWang.Lin@Sun.COM 	/*
25759999SWang.Lin@Sun.COM 	 * Enable memory mapping and bus mastering
25769999SWang.Lin@Sun.COM 	 */
25779999SWang.Lin@Sun.COM 	ASSERT(sc != NULL);
25789999SWang.Lin@Sun.COM 	command = pci_config_get16(sc->sc_cfg_handle, PCI_CONF_COMM);
25799999SWang.Lin@Sun.COM 	command	|= PCI_COMM_MAE | PCI_COMM_ME;
25809999SWang.Lin@Sun.COM 	pci_config_put16(sc->sc_cfg_handle, PCI_CONF_COMM, command);
25819999SWang.Lin@Sun.COM 	command = pci_config_get16(sc->sc_cfg_handle, PCI_CONF_COMM);
25829999SWang.Lin@Sun.COM 	if ((command & PCI_COMM_MAE) == 0) {
25839999SWang.Lin@Sun.COM 		arn_problem("arn: arn_pci_setup(): "
25849999SWang.Lin@Sun.COM 		    "failed to enable memory mapping\n");
25859999SWang.Lin@Sun.COM 		return (EIO);
25869999SWang.Lin@Sun.COM 	}
25879999SWang.Lin@Sun.COM 	if ((command & PCI_COMM_ME) == 0) {
25889999SWang.Lin@Sun.COM 		arn_problem("arn: arn_pci_setup(): "
25899999SWang.Lin@Sun.COM 		    "failed to enable bus mastering\n");
25909999SWang.Lin@Sun.COM 		return (EIO);
25919999SWang.Lin@Sun.COM 	}
25929999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_INIT, "arn: arn_pci_setup(): "
25939999SWang.Lin@Sun.COM 	    "set command reg to 0x%x \n", command));
25949999SWang.Lin@Sun.COM 
25959999SWang.Lin@Sun.COM 	return (0);
25969999SWang.Lin@Sun.COM }
25979999SWang.Lin@Sun.COM 
25989999SWang.Lin@Sun.COM static void
arn_get_hw_encap(struct arn_softc * sc)25999999SWang.Lin@Sun.COM arn_get_hw_encap(struct arn_softc *sc)
26009999SWang.Lin@Sun.COM {
26019999SWang.Lin@Sun.COM 	ieee80211com_t *ic;
26029999SWang.Lin@Sun.COM 	struct ath_hal *ah;
26039999SWang.Lin@Sun.COM 
26049999SWang.Lin@Sun.COM 	ic = (ieee80211com_t *)sc;
26059999SWang.Lin@Sun.COM 	ah = sc->sc_ah;
26069999SWang.Lin@Sun.COM 
26079999SWang.Lin@Sun.COM 	if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
26089999SWang.Lin@Sun.COM 	    ATH9K_CIPHER_AES_CCM, NULL))
26099999SWang.Lin@Sun.COM 		ic->ic_caps |= IEEE80211_C_AES_CCM;
26109999SWang.Lin@Sun.COM 	if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
26119999SWang.Lin@Sun.COM 	    ATH9K_CIPHER_AES_OCB, NULL))
26129999SWang.Lin@Sun.COM 		ic->ic_caps |= IEEE80211_C_AES;
26139999SWang.Lin@Sun.COM 	if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
26149999SWang.Lin@Sun.COM 	    ATH9K_CIPHER_TKIP, NULL))
26159999SWang.Lin@Sun.COM 		ic->ic_caps |= IEEE80211_C_TKIP;
26169999SWang.Lin@Sun.COM 	if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
26179999SWang.Lin@Sun.COM 	    ATH9K_CIPHER_WEP, NULL))
26189999SWang.Lin@Sun.COM 		ic->ic_caps |= IEEE80211_C_WEP;
26199999SWang.Lin@Sun.COM 	if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
26209999SWang.Lin@Sun.COM 	    ATH9K_CIPHER_MIC, NULL))
26219999SWang.Lin@Sun.COM 		ic->ic_caps |= IEEE80211_C_TKIPMIC;
26229999SWang.Lin@Sun.COM }
26239999SWang.Lin@Sun.COM 
262411729SWang.Lin@Sun.COM static void
arn_setup_ht_cap(struct arn_softc * sc)262511729SWang.Lin@Sun.COM arn_setup_ht_cap(struct arn_softc *sc)
262611729SWang.Lin@Sun.COM {
262711729SWang.Lin@Sun.COM #define	ATH9K_HT_CAP_MAXRXAMPDU_65536 0x3	/* 2 ^ 16 */
262811729SWang.Lin@Sun.COM #define	ATH9K_HT_CAP_MPDUDENSITY_8 0x6		/* 8 usec */
262911729SWang.Lin@Sun.COM 
263011729SWang.Lin@Sun.COM 	/* LINTED E_FUNC_SET_NOT_USED */
263111729SWang.Lin@Sun.COM 	uint8_t tx_streams;
263211729SWang.Lin@Sun.COM 	uint8_t rx_streams;
263311729SWang.Lin@Sun.COM 
263411729SWang.Lin@Sun.COM 	arn_ht_conf *ht_info = &sc->sc_ht_conf;
263511729SWang.Lin@Sun.COM 
263611729SWang.Lin@Sun.COM 	ht_info->ht_supported = B_TRUE;
263711729SWang.Lin@Sun.COM 
263811729SWang.Lin@Sun.COM 	/* Todo: IEEE80211_HTCAP_SMPS */
263911729SWang.Lin@Sun.COM 	ht_info->cap = IEEE80211_HTCAP_CHWIDTH40|
264011729SWang.Lin@Sun.COM 	    IEEE80211_HTCAP_SHORTGI40 |
264111729SWang.Lin@Sun.COM 	    IEEE80211_HTCAP_DSSSCCK40;
264211729SWang.Lin@Sun.COM 
264311729SWang.Lin@Sun.COM 	ht_info->ampdu_factor = ATH9K_HT_CAP_MAXRXAMPDU_65536;
264411729SWang.Lin@Sun.COM 	ht_info->ampdu_density = ATH9K_HT_CAP_MPDUDENSITY_8;
264511729SWang.Lin@Sun.COM 
264611729SWang.Lin@Sun.COM 	/* set up supported mcs set */
264711729SWang.Lin@Sun.COM 	(void) memset(&ht_info->rx_mcs_mask, 0, sizeof (ht_info->rx_mcs_mask));
264811729SWang.Lin@Sun.COM 	tx_streams =
264911729SWang.Lin@Sun.COM 	    !(sc->sc_ah->ah_caps.tx_chainmask &
265011729SWang.Lin@Sun.COM 	    (sc->sc_ah->ah_caps.tx_chainmask - 1)) ? 1 : 2;
265111729SWang.Lin@Sun.COM 	rx_streams =
265211729SWang.Lin@Sun.COM 	    !(sc->sc_ah->ah_caps.rx_chainmask &
265311729SWang.Lin@Sun.COM 	    (sc->sc_ah->ah_caps.rx_chainmask - 1)) ? 1 : 2;
265411729SWang.Lin@Sun.COM 
265511729SWang.Lin@Sun.COM 	ht_info->rx_mcs_mask[0] = 0xff;
265611729SWang.Lin@Sun.COM 	if (rx_streams >= 2)
265711729SWang.Lin@Sun.COM 		ht_info->rx_mcs_mask[1] = 0xff;
265811729SWang.Lin@Sun.COM }
265911729SWang.Lin@Sun.COM 
266011729SWang.Lin@Sun.COM /* xxx should be used for ht rate set negotiating ? */
266111729SWang.Lin@Sun.COM static void
arn_overwrite_11n_rateset(struct arn_softc * sc)266211729SWang.Lin@Sun.COM arn_overwrite_11n_rateset(struct arn_softc *sc)
266311729SWang.Lin@Sun.COM {
266411729SWang.Lin@Sun.COM 	uint8_t *ht_rs = sc->sc_ht_conf.rx_mcs_mask;
266511729SWang.Lin@Sun.COM 	int mcs_idx, mcs_count = 0;
266611729SWang.Lin@Sun.COM 	int i, j;
266711729SWang.Lin@Sun.COM 
266811729SWang.Lin@Sun.COM 	(void) memset(&ieee80211_rateset_11n, 0,
266911729SWang.Lin@Sun.COM 	    sizeof (ieee80211_rateset_11n));
267011729SWang.Lin@Sun.COM 	for (i = 0; i < 10; i++) {
267111729SWang.Lin@Sun.COM 		for (j = 0; j < 8; j++) {
267211729SWang.Lin@Sun.COM 			if (ht_rs[i] & (1 << j)) {
267311729SWang.Lin@Sun.COM 				mcs_idx = i * 8 + j;
267411729SWang.Lin@Sun.COM 				if (mcs_idx >= IEEE80211_HTRATE_MAXSIZE) {
267511729SWang.Lin@Sun.COM 					break;
267611729SWang.Lin@Sun.COM 				}
267711729SWang.Lin@Sun.COM 
267811729SWang.Lin@Sun.COM 				ieee80211_rateset_11n.rs_rates[mcs_idx] =
267911729SWang.Lin@Sun.COM 				    (uint8_t)mcs_idx;
268011729SWang.Lin@Sun.COM 				mcs_count++;
268111729SWang.Lin@Sun.COM 			}
268211729SWang.Lin@Sun.COM 		}
268311729SWang.Lin@Sun.COM 	}
268411729SWang.Lin@Sun.COM 
268511729SWang.Lin@Sun.COM 	ieee80211_rateset_11n.rs_nrates = (uint8_t)mcs_count;
268611729SWang.Lin@Sun.COM 
268711729SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_RATE, "arn_overwrite_11n_rateset(): "
268811729SWang.Lin@Sun.COM 	    "MCS rate set supported by this station is as follows:\n"));
268911729SWang.Lin@Sun.COM 
269011729SWang.Lin@Sun.COM 	for (i = 0; i < ieee80211_rateset_11n.rs_nrates; i++) {
269111729SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_RATE, "MCS rate %d is %d\n",
269211729SWang.Lin@Sun.COM 		    i, ieee80211_rateset_11n.rs_rates[i]));
269311729SWang.Lin@Sun.COM 	}
269411729SWang.Lin@Sun.COM 
269511729SWang.Lin@Sun.COM }
269611729SWang.Lin@Sun.COM 
269711729SWang.Lin@Sun.COM /*
269811729SWang.Lin@Sun.COM  * Update WME parameters for a transmit queue.
269911729SWang.Lin@Sun.COM  */
270011729SWang.Lin@Sun.COM static int
arn_tx_queue_update(struct arn_softc * sc,int ac)270111729SWang.Lin@Sun.COM arn_tx_queue_update(struct arn_softc *sc, int ac)
270211729SWang.Lin@Sun.COM {
270311729SWang.Lin@Sun.COM #define	ATH_EXPONENT_TO_VALUE(v)	((1<<v)-1)
270411729SWang.Lin@Sun.COM #define	ATH_TXOP_TO_US(v)		(v<<5)
270511729SWang.Lin@Sun.COM 	ieee80211com_t *ic = (ieee80211com_t *)sc;
270611729SWang.Lin@Sun.COM 	struct ath_txq *txq;
270711729SWang.Lin@Sun.COM 	struct wmeParams *wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac];
270811729SWang.Lin@Sun.COM 	struct ath_hal *ah = sc->sc_ah;
270911729SWang.Lin@Sun.COM 	struct ath9k_tx_queue_info qi;
271011729SWang.Lin@Sun.COM 
271111729SWang.Lin@Sun.COM 	txq = &sc->sc_txq[arn_get_hal_qnum(ac, sc)];
271211729SWang.Lin@Sun.COM 	(void) ath9k_hw_get_txq_props(ah, txq->axq_qnum, &qi);
271311729SWang.Lin@Sun.COM 
271411729SWang.Lin@Sun.COM 	/*
271511729SWang.Lin@Sun.COM 	 * TXQ_FLAG_TXOKINT_ENABLE = 0x0001
271611729SWang.Lin@Sun.COM 	 * TXQ_FLAG_TXERRINT_ENABLE = 0x0001
271711729SWang.Lin@Sun.COM 	 * TXQ_FLAG_TXDESCINT_ENABLE = 0x0002
271811729SWang.Lin@Sun.COM 	 * TXQ_FLAG_TXEOLINT_ENABLE = 0x0004
271911729SWang.Lin@Sun.COM 	 * TXQ_FLAG_TXURNINT_ENABLE = 0x0008
272011729SWang.Lin@Sun.COM 	 * TXQ_FLAG_BACKOFF_DISABLE = 0x0010
272111729SWang.Lin@Sun.COM 	 * TXQ_FLAG_COMPRESSION_ENABLE = 0x0020
272211729SWang.Lin@Sun.COM 	 * TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE = 0x0040
272311729SWang.Lin@Sun.COM 	 * TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE = 0x0080
272411729SWang.Lin@Sun.COM 	 */
272511729SWang.Lin@Sun.COM 
272611729SWang.Lin@Sun.COM 	/* xxx should update these flags here? */
272711729SWang.Lin@Sun.COM #if 0
272811729SWang.Lin@Sun.COM 	qi.tqi_qflags = TXQ_FLAG_TXOKINT_ENABLE |
272911729SWang.Lin@Sun.COM 	    TXQ_FLAG_TXERRINT_ENABLE |
273011729SWang.Lin@Sun.COM 	    TXQ_FLAG_TXDESCINT_ENABLE |
273111729SWang.Lin@Sun.COM 	    TXQ_FLAG_TXURNINT_ENABLE;
273211729SWang.Lin@Sun.COM #endif
273311729SWang.Lin@Sun.COM 
273411729SWang.Lin@Sun.COM 	qi.tqi_aifs = wmep->wmep_aifsn;
273511729SWang.Lin@Sun.COM 	qi.tqi_cwmin = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin);
273611729SWang.Lin@Sun.COM 	qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax);
273711729SWang.Lin@Sun.COM 	qi.tqi_readyTime = 0;
273811729SWang.Lin@Sun.COM 	qi.tqi_burstTime = ATH_TXOP_TO_US(wmep->wmep_txopLimit);
273911729SWang.Lin@Sun.COM 
274011729SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_INIT,
274111729SWang.Lin@Sun.COM 	    "%s:"
274211729SWang.Lin@Sun.COM 	    "Q%u"
274311729SWang.Lin@Sun.COM 	    "qflags 0x%x"
274411729SWang.Lin@Sun.COM 	    "aifs %u"
274511729SWang.Lin@Sun.COM 	    "cwmin %u"
274611729SWang.Lin@Sun.COM 	    "cwmax %u"
274711729SWang.Lin@Sun.COM 	    "burstTime %u\n",
274811729SWang.Lin@Sun.COM 	    __func__,
274911729SWang.Lin@Sun.COM 	    txq->axq_qnum,
275011729SWang.Lin@Sun.COM 	    qi.tqi_qflags,
275111729SWang.Lin@Sun.COM 	    qi.tqi_aifs,
275211729SWang.Lin@Sun.COM 	    qi.tqi_cwmin,
275311729SWang.Lin@Sun.COM 	    qi.tqi_cwmax,
275411729SWang.Lin@Sun.COM 	    qi.tqi_burstTime));
275511729SWang.Lin@Sun.COM 
275611729SWang.Lin@Sun.COM 	if (!ath9k_hw_set_txq_props(ah, txq->axq_qnum, &qi)) {
275711729SWang.Lin@Sun.COM 		arn_problem("unable to update hardware queue "
275811729SWang.Lin@Sun.COM 		    "parameters for %s traffic!\n",
275911729SWang.Lin@Sun.COM 		    ieee80211_wme_acnames[ac]);
276011729SWang.Lin@Sun.COM 		return (0);
276111729SWang.Lin@Sun.COM 	} else {
276211729SWang.Lin@Sun.COM 		/* push to H/W */
276311729SWang.Lin@Sun.COM 		(void) ath9k_hw_resettxqueue(ah, txq->axq_qnum);
276411729SWang.Lin@Sun.COM 		return (1);
276511729SWang.Lin@Sun.COM 	}
276611729SWang.Lin@Sun.COM 
276711729SWang.Lin@Sun.COM #undef ATH_TXOP_TO_US
276811729SWang.Lin@Sun.COM #undef ATH_EXPONENT_TO_VALUE
276911729SWang.Lin@Sun.COM }
277011729SWang.Lin@Sun.COM 
277111729SWang.Lin@Sun.COM /* Update WME parameters */
277211729SWang.Lin@Sun.COM static int
arn_wme_update(ieee80211com_t * ic)277311729SWang.Lin@Sun.COM arn_wme_update(ieee80211com_t *ic)
277411729SWang.Lin@Sun.COM {
277511729SWang.Lin@Sun.COM 	struct arn_softc *sc = (struct arn_softc *)ic;
277611729SWang.Lin@Sun.COM 
277711729SWang.Lin@Sun.COM 	/* updateing */
277811729SWang.Lin@Sun.COM 	return (!arn_tx_queue_update(sc, WME_AC_BE) ||
277911729SWang.Lin@Sun.COM 	    !arn_tx_queue_update(sc, WME_AC_BK) ||
278011729SWang.Lin@Sun.COM 	    !arn_tx_queue_update(sc, WME_AC_VI) ||
278111729SWang.Lin@Sun.COM 	    !arn_tx_queue_update(sc, WME_AC_VO) ? EIO : 0);
278211729SWang.Lin@Sun.COM }
278311729SWang.Lin@Sun.COM 
278411729SWang.Lin@Sun.COM /*
278511729SWang.Lin@Sun.COM  * Update tx/rx chainmask. For legacy association,
278611729SWang.Lin@Sun.COM  * hard code chainmask to 1x1, for 11n association, use
278711729SWang.Lin@Sun.COM  * the chainmask configuration.
278811729SWang.Lin@Sun.COM  */
278911729SWang.Lin@Sun.COM void
arn_update_chainmask(struct arn_softc * sc)279011729SWang.Lin@Sun.COM arn_update_chainmask(struct arn_softc *sc)
279111729SWang.Lin@Sun.COM {
279211729SWang.Lin@Sun.COM 	boolean_t is_ht = B_FALSE;
279311729SWang.Lin@Sun.COM 	sc->sc_flags |= SC_OP_CHAINMASK_UPDATE;
279411729SWang.Lin@Sun.COM 
279511729SWang.Lin@Sun.COM 	is_ht = sc->sc_ht_conf.ht_supported;
279611729SWang.Lin@Sun.COM 	if (is_ht) {
279711729SWang.Lin@Sun.COM 		sc->sc_tx_chainmask = sc->sc_ah->ah_caps.tx_chainmask;
279811729SWang.Lin@Sun.COM 		sc->sc_rx_chainmask = sc->sc_ah->ah_caps.rx_chainmask;
279911729SWang.Lin@Sun.COM 	} else {
280011729SWang.Lin@Sun.COM 		sc->sc_tx_chainmask = 1;
280111729SWang.Lin@Sun.COM 		sc->sc_rx_chainmask = 1;
280211729SWang.Lin@Sun.COM 	}
280311729SWang.Lin@Sun.COM 
280411729SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
280511729SWang.Lin@Sun.COM 	    "tx_chainmask = %d, rx_chainmask = %d\n",
280611729SWang.Lin@Sun.COM 	    sc->sc_tx_chainmask, sc->sc_rx_chainmask));
280711729SWang.Lin@Sun.COM }
280811729SWang.Lin@Sun.COM 
28099999SWang.Lin@Sun.COM static int
arn_resume(dev_info_t * devinfo)28109999SWang.Lin@Sun.COM arn_resume(dev_info_t *devinfo)
28119999SWang.Lin@Sun.COM {
28129999SWang.Lin@Sun.COM 	struct arn_softc *sc;
28139999SWang.Lin@Sun.COM 	int ret = DDI_SUCCESS;
28149999SWang.Lin@Sun.COM 
28159999SWang.Lin@Sun.COM 	sc = ddi_get_soft_state(arn_soft_state_p, ddi_get_instance(devinfo));
28169999SWang.Lin@Sun.COM 	if (sc == NULL) {
28179999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_INIT, "ath: ath_resume(): "
28189999SWang.Lin@Sun.COM 		    "failed to get soft state\n"));
28199999SWang.Lin@Sun.COM 		return (DDI_FAILURE);
28209999SWang.Lin@Sun.COM 	}
28219999SWang.Lin@Sun.COM 
28229999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
28239999SWang.Lin@Sun.COM 	/*
28249999SWang.Lin@Sun.COM 	 * Set up config space command register(s). Refuse
28259999SWang.Lin@Sun.COM 	 * to resume on failure.
28269999SWang.Lin@Sun.COM 	 */
28279999SWang.Lin@Sun.COM 	if (arn_pci_setup(sc) != 0) {
28289999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_INIT, "ath: ath_resume(): "
28299999SWang.Lin@Sun.COM 		    "ath_pci_setup() failed\n"));
28309999SWang.Lin@Sun.COM 		ARN_UNLOCK(sc);
28319999SWang.Lin@Sun.COM 		return (DDI_FAILURE);
28329999SWang.Lin@Sun.COM 	}
28339999SWang.Lin@Sun.COM 
28349999SWang.Lin@Sun.COM 	if (!(sc->sc_flags & SC_OP_INVALID))
28359999SWang.Lin@Sun.COM 		ret = arn_open(sc);
28369999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
28379999SWang.Lin@Sun.COM 
28389999SWang.Lin@Sun.COM 	return (ret);
28399999SWang.Lin@Sun.COM }
28409999SWang.Lin@Sun.COM 
28419999SWang.Lin@Sun.COM static int
arn_attach(dev_info_t * devinfo,ddi_attach_cmd_t cmd)28429999SWang.Lin@Sun.COM arn_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
28439999SWang.Lin@Sun.COM {
28449999SWang.Lin@Sun.COM 	struct arn_softc *sc;
28459999SWang.Lin@Sun.COM 	int		instance;
28469999SWang.Lin@Sun.COM 	int		status;
28479999SWang.Lin@Sun.COM 	int32_t		err;
28489999SWang.Lin@Sun.COM 	uint16_t	vendor_id;
28499999SWang.Lin@Sun.COM 	uint16_t	device_id;
28509999SWang.Lin@Sun.COM 	uint32_t	i;
28519999SWang.Lin@Sun.COM 	uint32_t	val;
28529999SWang.Lin@Sun.COM 	char		strbuf[32];
28539999SWang.Lin@Sun.COM 	ieee80211com_t *ic;
28549999SWang.Lin@Sun.COM 	struct ath_hal *ah;
28559999SWang.Lin@Sun.COM 	wifi_data_t wd = { 0 };
28569999SWang.Lin@Sun.COM 	mac_register_t *macp;
28579999SWang.Lin@Sun.COM 
28589999SWang.Lin@Sun.COM 	switch (cmd) {
28599999SWang.Lin@Sun.COM 	case DDI_ATTACH:
28609999SWang.Lin@Sun.COM 		break;
28619999SWang.Lin@Sun.COM 	case DDI_RESUME:
28629999SWang.Lin@Sun.COM 		return (arn_resume(devinfo));
28639999SWang.Lin@Sun.COM 	default:
28649999SWang.Lin@Sun.COM 		return (DDI_FAILURE);
28659999SWang.Lin@Sun.COM 	}
28669999SWang.Lin@Sun.COM 
28679999SWang.Lin@Sun.COM 	instance = ddi_get_instance(devinfo);
28689999SWang.Lin@Sun.COM 	if (ddi_soft_state_zalloc(arn_soft_state_p, instance) != DDI_SUCCESS) {
28699999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: "
28709999SWang.Lin@Sun.COM 		    "%s: Unable to alloc softstate\n", __func__));
28719999SWang.Lin@Sun.COM 		return (DDI_FAILURE);
28729999SWang.Lin@Sun.COM 	}
28739999SWang.Lin@Sun.COM 
28749999SWang.Lin@Sun.COM 	sc = ddi_get_soft_state(arn_soft_state_p, ddi_get_instance(devinfo));
28759999SWang.Lin@Sun.COM 	ic = (ieee80211com_t *)sc;
28769999SWang.Lin@Sun.COM 	sc->sc_dev = devinfo;
28779999SWang.Lin@Sun.COM 
28789999SWang.Lin@Sun.COM 	mutex_init(&sc->sc_genlock, NULL, MUTEX_DRIVER, NULL);
28799999SWang.Lin@Sun.COM 	mutex_init(&sc->sc_serial_rw, NULL, MUTEX_DRIVER, NULL);
28809999SWang.Lin@Sun.COM 	mutex_init(&sc->sc_txbuflock, NULL, MUTEX_DRIVER, NULL);
28819999SWang.Lin@Sun.COM 	mutex_init(&sc->sc_rxbuflock, NULL, MUTEX_DRIVER, NULL);
28829999SWang.Lin@Sun.COM 	mutex_init(&sc->sc_resched_lock, NULL, MUTEX_DRIVER, NULL);
28839999SWang.Lin@Sun.COM #ifdef ARN_IBSS
28849999SWang.Lin@Sun.COM 	mutex_init(&sc->sc_bcbuflock, NULL, MUTEX_DRIVER, NULL);
28859999SWang.Lin@Sun.COM #endif
28869999SWang.Lin@Sun.COM 
28879999SWang.Lin@Sun.COM 	sc->sc_flags |= SC_OP_INVALID;
28889999SWang.Lin@Sun.COM 
28899999SWang.Lin@Sun.COM 	err = pci_config_setup(devinfo, &sc->sc_cfg_handle);
28909999SWang.Lin@Sun.COM 	if (err != DDI_SUCCESS) {
28919999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
28929999SWang.Lin@Sun.COM 		    "pci_config_setup() failed"));
28939999SWang.Lin@Sun.COM 		goto attach_fail0;
28949999SWang.Lin@Sun.COM 	}
28959999SWang.Lin@Sun.COM 
28969999SWang.Lin@Sun.COM 	if (arn_pci_setup(sc) != 0)
28979999SWang.Lin@Sun.COM 		goto attach_fail1;
28989999SWang.Lin@Sun.COM 
28999999SWang.Lin@Sun.COM 	/* Cache line size set up */
29009999SWang.Lin@Sun.COM 	arn_pci_config_cachesize(sc);
29019999SWang.Lin@Sun.COM 
29029999SWang.Lin@Sun.COM 	vendor_id = pci_config_get16(sc->sc_cfg_handle, PCI_CONF_VENID);
29039999SWang.Lin@Sun.COM 	device_id = pci_config_get16(sc->sc_cfg_handle, PCI_CONF_DEVID);
29049999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): vendor 0x%x, "
29059999SWang.Lin@Sun.COM 	    "device id 0x%x, cache size %d\n",
29069999SWang.Lin@Sun.COM 	    vendor_id, device_id,
29079999SWang.Lin@Sun.COM 	    pci_config_get8(sc->sc_cfg_handle, PCI_CONF_CACHE_LINESZ)));
29089999SWang.Lin@Sun.COM 
29099999SWang.Lin@Sun.COM 	pci_config_put8(sc->sc_cfg_handle, PCI_CONF_LATENCY_TIMER, 0xa8);
29109999SWang.Lin@Sun.COM 	val = pci_config_get32(sc->sc_cfg_handle, 0x40);
29119999SWang.Lin@Sun.COM 	if ((val & 0x0000ff00) != 0)
29129999SWang.Lin@Sun.COM 		pci_config_put32(sc->sc_cfg_handle, 0x40, val & 0xffff00ff);
29139999SWang.Lin@Sun.COM 
29149999SWang.Lin@Sun.COM 	err = ddi_regs_map_setup(devinfo, 1,
29159999SWang.Lin@Sun.COM 	    &sc->mem, 0, 0, &arn_reg_accattr, &sc->sc_io_handle);
29169999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
29179999SWang.Lin@Sun.COM 	    "regs map1 = %x err=%d\n", sc->mem, err));
29189999SWang.Lin@Sun.COM 	if (err != DDI_SUCCESS) {
29199999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
29209999SWang.Lin@Sun.COM 		    "ddi_regs_map_setup() failed"));
29219999SWang.Lin@Sun.COM 		goto attach_fail1;
29229999SWang.Lin@Sun.COM 	}
29239999SWang.Lin@Sun.COM 
29249999SWang.Lin@Sun.COM 	ah = ath9k_hw_attach(device_id, sc, sc->mem, &status);
29259999SWang.Lin@Sun.COM 	if (ah == NULL) {
29269999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
29279999SWang.Lin@Sun.COM 		    "unable to attach hw: H/W status %u\n",
29289999SWang.Lin@Sun.COM 		    status));
29299999SWang.Lin@Sun.COM 		goto attach_fail2;
29309999SWang.Lin@Sun.COM 	}
29319999SWang.Lin@Sun.COM 	sc->sc_ah = ah;
29329999SWang.Lin@Sun.COM 
29339999SWang.Lin@Sun.COM 	ath9k_hw_getmac(ah, ic->ic_macaddr);
29349999SWang.Lin@Sun.COM 
29359999SWang.Lin@Sun.COM 	/* Get the hardware key cache size. */
29369999SWang.Lin@Sun.COM 	sc->sc_keymax = ah->ah_caps.keycache_size;
29379999SWang.Lin@Sun.COM 	if (sc->sc_keymax > ATH_KEYMAX) {
29389999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
29399999SWang.Lin@Sun.COM 		    "Warning, using only %u entries in %u key cache\n",
29409999SWang.Lin@Sun.COM 		    ATH_KEYMAX, sc->sc_keymax));
29419999SWang.Lin@Sun.COM 		sc->sc_keymax = ATH_KEYMAX;
29429999SWang.Lin@Sun.COM 	}
29439999SWang.Lin@Sun.COM 
29449999SWang.Lin@Sun.COM 	/*
29459999SWang.Lin@Sun.COM 	 * Reset the key cache since some parts do not
29469999SWang.Lin@Sun.COM 	 * reset the contents on initial power up.
29479999SWang.Lin@Sun.COM 	 */
29489999SWang.Lin@Sun.COM 	for (i = 0; i < sc->sc_keymax; i++)
29499999SWang.Lin@Sun.COM 		(void) ath9k_hw_keyreset(ah, (uint16_t)i);
29509999SWang.Lin@Sun.COM 	/*
29519999SWang.Lin@Sun.COM 	 * Mark key cache slots associated with global keys
29529999SWang.Lin@Sun.COM 	 * as in use.  If we knew TKIP was not to be used we
29539999SWang.Lin@Sun.COM 	 * could leave the +32, +64, and +32+64 slots free.
29549999SWang.Lin@Sun.COM 	 * XXX only for splitmic.
29559999SWang.Lin@Sun.COM 	 */
29569999SWang.Lin@Sun.COM 	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
29579999SWang.Lin@Sun.COM 		set_bit(i, sc->sc_keymap);
29589999SWang.Lin@Sun.COM 		set_bit(i + 32, sc->sc_keymap);
29599999SWang.Lin@Sun.COM 		set_bit(i + 64, sc->sc_keymap);
29609999SWang.Lin@Sun.COM 		set_bit(i + 32 + 64, sc->sc_keymap);
29619999SWang.Lin@Sun.COM 	}
29629999SWang.Lin@Sun.COM 
29639999SWang.Lin@Sun.COM 	/* Collect the channel list using the default country code */
29649999SWang.Lin@Sun.COM 	err = arn_setup_channels(sc);
29659999SWang.Lin@Sun.COM 	if (err == EINVAL) {
29669999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
29679999SWang.Lin@Sun.COM 		    "ERR:arn_setup_channels\n"));
29689999SWang.Lin@Sun.COM 		goto attach_fail3;
29699999SWang.Lin@Sun.COM 	}
29709999SWang.Lin@Sun.COM 
29719999SWang.Lin@Sun.COM 	/* default to STA mode */
29729999SWang.Lin@Sun.COM 	sc->sc_ah->ah_opmode = ATH9K_M_STA;
29739999SWang.Lin@Sun.COM 
29749999SWang.Lin@Sun.COM 	/* Setup rate tables */
29759999SWang.Lin@Sun.COM 	arn_rate_attach(sc);
29769999SWang.Lin@Sun.COM 	arn_setup_rates(sc, IEEE80211_MODE_11A);
29779999SWang.Lin@Sun.COM 	arn_setup_rates(sc, IEEE80211_MODE_11B);
29789999SWang.Lin@Sun.COM 	arn_setup_rates(sc, IEEE80211_MODE_11G);
29799999SWang.Lin@Sun.COM 
29809999SWang.Lin@Sun.COM 	/* Setup current mode here */
29819999SWang.Lin@Sun.COM 	arn_setcurmode(sc, ATH9K_MODE_11G);
29829999SWang.Lin@Sun.COM 
29839999SWang.Lin@Sun.COM 	/* 802.11g features */
29849999SWang.Lin@Sun.COM 	if (sc->sc_have11g)
29859999SWang.Lin@Sun.COM 		ic->ic_caps |= IEEE80211_C_SHPREAMBLE |
29869999SWang.Lin@Sun.COM 		    IEEE80211_C_SHSLOT;		/* short slot time */
29879999SWang.Lin@Sun.COM 
298811729SWang.Lin@Sun.COM 	/* Temp workaround */
29899999SWang.Lin@Sun.COM 	sc->sc_mrretry = 1;
299011729SWang.Lin@Sun.COM 	sc->sc_config.ath_aggr_prot = 0;
29919999SWang.Lin@Sun.COM 
29929999SWang.Lin@Sun.COM 	/* Setup tx/rx descriptors */
29939999SWang.Lin@Sun.COM 	err = arn_desc_alloc(devinfo, sc);
29949999SWang.Lin@Sun.COM 	if (err != DDI_SUCCESS) {
29959999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
29969999SWang.Lin@Sun.COM 		    "failed to allocate descriptors: %d\n", err));
29979999SWang.Lin@Sun.COM 		goto attach_fail3;
29989999SWang.Lin@Sun.COM 	}
29999999SWang.Lin@Sun.COM 
30009999SWang.Lin@Sun.COM 	if ((sc->sc_tq = ddi_taskq_create(devinfo, "ath_taskq", 1,
30019999SWang.Lin@Sun.COM 	    TASKQ_DEFAULTPRI, 0)) == NULL) {
30029999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
30039999SWang.Lin@Sun.COM 		    "ERR:ddi_taskq_create\n"));
30049999SWang.Lin@Sun.COM 		goto attach_fail4;
30059999SWang.Lin@Sun.COM 	}
30069999SWang.Lin@Sun.COM 
30079999SWang.Lin@Sun.COM 	/*
30089999SWang.Lin@Sun.COM 	 * Allocate hardware transmit queues: one queue for
30099999SWang.Lin@Sun.COM 	 * beacon frames and one data queue for each QoS
30109999SWang.Lin@Sun.COM 	 * priority.  Note that the hal handles reseting
30119999SWang.Lin@Sun.COM 	 * these queues at the needed time.
30129999SWang.Lin@Sun.COM 	 */
30139999SWang.Lin@Sun.COM #ifdef ARN_IBSS
30149999SWang.Lin@Sun.COM 	sc->sc_beaconq = arn_beaconq_setup(ah);
30159999SWang.Lin@Sun.COM 	if (sc->sc_beaconq == (-1)) {
30169999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
30179999SWang.Lin@Sun.COM 		    "unable to setup a beacon xmit queue\n"));
30189999SWang.Lin@Sun.COM 		goto attach_fail4;
30199999SWang.Lin@Sun.COM 	}
30209999SWang.Lin@Sun.COM #endif
30219999SWang.Lin@Sun.COM #ifdef ARN_HOSTAP
30229999SWang.Lin@Sun.COM 	sc->sc_cabq = arn_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0);
30239999SWang.Lin@Sun.COM 	if (sc->sc_cabq == NULL) {
30249999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
30259999SWang.Lin@Sun.COM 		    "unable to setup CAB xmit queue\n"));
30269999SWang.Lin@Sun.COM 		goto attach_fail4;
30279999SWang.Lin@Sun.COM 	}
30289999SWang.Lin@Sun.COM 
30299999SWang.Lin@Sun.COM 	sc->sc_config.cabqReadytime = ATH_CABQ_READY_TIME;
30309999SWang.Lin@Sun.COM 	ath_cabq_update(sc);
30319999SWang.Lin@Sun.COM #endif
30329999SWang.Lin@Sun.COM 
30339999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(sc->sc_haltype2q); i++)
30349999SWang.Lin@Sun.COM 		sc->sc_haltype2q[i] = -1;
30359999SWang.Lin@Sun.COM 
30369999SWang.Lin@Sun.COM 	/* Setup data queues */
30379999SWang.Lin@Sun.COM 	/* NB: ensure BK queue is the lowest priority h/w queue */
30389999SWang.Lin@Sun.COM 	if (!arn_tx_setup(sc, ATH9K_WME_AC_BK)) {
30399999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
30409999SWang.Lin@Sun.COM 		    "unable to setup xmit queue for BK traffic\n"));
30419999SWang.Lin@Sun.COM 		goto attach_fail4;
30429999SWang.Lin@Sun.COM 	}
30439999SWang.Lin@Sun.COM 	if (!arn_tx_setup(sc, ATH9K_WME_AC_BE)) {
30449999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
30459999SWang.Lin@Sun.COM 		    "unable to setup xmit queue for BE traffic\n"));
30469999SWang.Lin@Sun.COM 		goto attach_fail4;
30479999SWang.Lin@Sun.COM 	}
30489999SWang.Lin@Sun.COM 	if (!arn_tx_setup(sc, ATH9K_WME_AC_VI)) {
30499999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
30509999SWang.Lin@Sun.COM 		    "unable to setup xmit queue for VI traffic\n"));
30519999SWang.Lin@Sun.COM 		goto attach_fail4;
30529999SWang.Lin@Sun.COM 	}
30539999SWang.Lin@Sun.COM 	if (!arn_tx_setup(sc, ATH9K_WME_AC_VO)) {
30549999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
30559999SWang.Lin@Sun.COM 		    "unable to setup xmit queue for VO traffic\n"));
30569999SWang.Lin@Sun.COM 		goto attach_fail4;
30579999SWang.Lin@Sun.COM 	}
30589999SWang.Lin@Sun.COM 
30599999SWang.Lin@Sun.COM 	/*
30609999SWang.Lin@Sun.COM 	 * Initializes the noise floor to a reasonable default value.
30619999SWang.Lin@Sun.COM 	 * Later on this will be updated during ANI processing.
30629999SWang.Lin@Sun.COM 	 */
30639999SWang.Lin@Sun.COM 
30649999SWang.Lin@Sun.COM 	sc->sc_ani.sc_noise_floor = ATH_DEFAULT_NOISE_FLOOR;
30659999SWang.Lin@Sun.COM 
30669999SWang.Lin@Sun.COM 
30679999SWang.Lin@Sun.COM 	if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
30689999SWang.Lin@Sun.COM 	    ATH9K_CIPHER_TKIP, NULL)) {
30699999SWang.Lin@Sun.COM 		/*
30709999SWang.Lin@Sun.COM 		 * Whether we should enable h/w TKIP MIC.
30719999SWang.Lin@Sun.COM 		 * XXX: if we don't support WME TKIP MIC, then we wouldn't
30729999SWang.Lin@Sun.COM 		 * report WMM capable, so it's always safe to turn on
30739999SWang.Lin@Sun.COM 		 * TKIP MIC in this case.
30749999SWang.Lin@Sun.COM 		 */
30759999SWang.Lin@Sun.COM 		(void) ath9k_hw_setcapability(sc->sc_ah, ATH9K_CAP_TKIP_MIC,
30769999SWang.Lin@Sun.COM 		    0, 1, NULL);
30779999SWang.Lin@Sun.COM 	}
30789999SWang.Lin@Sun.COM 
30799999SWang.Lin@Sun.COM 	/* Get cipher releated capability information */
30809999SWang.Lin@Sun.COM 	arn_get_hw_encap(sc);
30819999SWang.Lin@Sun.COM 
30829999SWang.Lin@Sun.COM 	/*
30839999SWang.Lin@Sun.COM 	 * Check whether the separate key cache entries
30849999SWang.Lin@Sun.COM 	 * are required to handle both tx+rx MIC keys.
30859999SWang.Lin@Sun.COM 	 * With split mic keys the number of stations is limited
30869999SWang.Lin@Sun.COM 	 * to 27 otherwise 59.
30879999SWang.Lin@Sun.COM 	 */
30889999SWang.Lin@Sun.COM 	if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
30899999SWang.Lin@Sun.COM 	    ATH9K_CIPHER_TKIP, NULL) &&
30909999SWang.Lin@Sun.COM 	    ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
30919999SWang.Lin@Sun.COM 	    ATH9K_CIPHER_MIC, NULL) &&
30929999SWang.Lin@Sun.COM 	    ath9k_hw_getcapability(ah, ATH9K_CAP_TKIP_SPLIT,
30939999SWang.Lin@Sun.COM 	    0, NULL))
30949999SWang.Lin@Sun.COM 		sc->sc_splitmic = 1;
30959999SWang.Lin@Sun.COM 
30969999SWang.Lin@Sun.COM 	/* turn on mcast key search if possible */
30979999SWang.Lin@Sun.COM 	if (!ath9k_hw_getcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL))
30989999SWang.Lin@Sun.COM 		(void) ath9k_hw_setcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 1,
30999999SWang.Lin@Sun.COM 		    1, NULL);
31009999SWang.Lin@Sun.COM 
31019999SWang.Lin@Sun.COM 	sc->sc_config.txpowlimit = ATH_TXPOWER_MAX;
31029999SWang.Lin@Sun.COM 	sc->sc_config.txpowlimit_override = 0;
31039999SWang.Lin@Sun.COM 
31049999SWang.Lin@Sun.COM 	/* 11n Capabilities */
31059999SWang.Lin@Sun.COM 	if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) {
31069999SWang.Lin@Sun.COM 		sc->sc_flags |= SC_OP_TXAGGR;
31079999SWang.Lin@Sun.COM 		sc->sc_flags |= SC_OP_RXAGGR;
310811729SWang.Lin@Sun.COM 		arn_setup_ht_cap(sc);
310911729SWang.Lin@Sun.COM 		arn_overwrite_11n_rateset(sc);
31109999SWang.Lin@Sun.COM 	}
311111729SWang.Lin@Sun.COM 
31129999SWang.Lin@Sun.COM 	sc->sc_tx_chainmask = 1;
31139999SWang.Lin@Sun.COM 	sc->sc_rx_chainmask = 1;
31149999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
31159999SWang.Lin@Sun.COM 	    "tx_chainmask = %d, rx_chainmask = %d\n",
31169999SWang.Lin@Sun.COM 	    sc->sc_tx_chainmask, sc->sc_rx_chainmask));
31179999SWang.Lin@Sun.COM 
311811729SWang.Lin@Sun.COM 	/* arn_update_chainmask(sc); */
311911729SWang.Lin@Sun.COM 
31209999SWang.Lin@Sun.COM 	(void) ath9k_hw_setcapability(ah, ATH9K_CAP_DIVERSITY, 1, B_TRUE, NULL);
31219999SWang.Lin@Sun.COM 	sc->sc_defant = ath9k_hw_getdefantenna(ah);
31229999SWang.Lin@Sun.COM 
31239999SWang.Lin@Sun.COM 	ath9k_hw_getmac(ah, sc->sc_myaddr);
31249999SWang.Lin@Sun.COM 	if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) {
31259999SWang.Lin@Sun.COM 		ath9k_hw_getbssidmask(ah, sc->sc_bssidmask);
31269999SWang.Lin@Sun.COM 		ATH_SET_VAP_BSSID_MASK(sc->sc_bssidmask);
31279999SWang.Lin@Sun.COM 		(void) ath9k_hw_setbssidmask(ah, sc->sc_bssidmask);
31289999SWang.Lin@Sun.COM 	}
31299999SWang.Lin@Sun.COM 
31309999SWang.Lin@Sun.COM 	/* set default value to short slot time */
31319999SWang.Lin@Sun.COM 	sc->sc_slottime = ATH9K_SLOT_TIME_9;
31329999SWang.Lin@Sun.COM 	(void) ath9k_hw_setslottime(ah, ATH9K_SLOT_TIME_9);
31339999SWang.Lin@Sun.COM 
31349999SWang.Lin@Sun.COM 	/* initialize beacon slots */
31359999SWang.Lin@Sun.COM 	for (i = 0; i < ARRAY_SIZE(sc->sc_bslot); i++)
31369999SWang.Lin@Sun.COM 		sc->sc_bslot[i] = ATH_IF_ID_ANY;
31379999SWang.Lin@Sun.COM 
313811729SWang.Lin@Sun.COM 	/* Save MISC configurations */
31399999SWang.Lin@Sun.COM 	sc->sc_config.swBeaconProcess = 1;
31409999SWang.Lin@Sun.COM 
314111729SWang.Lin@Sun.COM 	/* Support QoS/WME */
314211729SWang.Lin@Sun.COM 	ic->ic_caps |= IEEE80211_C_WME;
314311729SWang.Lin@Sun.COM 	ic->ic_wme.wme_update = arn_wme_update;
314411729SWang.Lin@Sun.COM 
314511729SWang.Lin@Sun.COM 	/* Support 802.11n/HT */
314611729SWang.Lin@Sun.COM 	if (sc->sc_ht_conf.ht_supported) {
314711729SWang.Lin@Sun.COM 		ic->ic_htcaps =
314811729SWang.Lin@Sun.COM 		    IEEE80211_HTCAP_CHWIDTH40 |
314911729SWang.Lin@Sun.COM 		    IEEE80211_HTCAP_SHORTGI40 |
315011729SWang.Lin@Sun.COM 		    IEEE80211_HTCAP_DSSSCCK40 |
315111729SWang.Lin@Sun.COM 		    IEEE80211_HTCAP_MAXAMSDU_7935 |
315211729SWang.Lin@Sun.COM 		    IEEE80211_HTC_HT |
315311729SWang.Lin@Sun.COM 		    IEEE80211_HTC_AMSDU |
315411729SWang.Lin@Sun.COM 		    IEEE80211_HTCAP_RXSTBC_2STREAM;
315511729SWang.Lin@Sun.COM 
315611729SWang.Lin@Sun.COM #ifdef ARN_TX_AGGREGATION
315711729SWang.Lin@Sun.COM 	ic->ic_htcaps |= IEEE80211_HTC_AMPDU;
315811729SWang.Lin@Sun.COM #endif
315911729SWang.Lin@Sun.COM 	}
316011729SWang.Lin@Sun.COM 
316111729SWang.Lin@Sun.COM 	/* Header padding requested by driver */
316211729SWang.Lin@Sun.COM 	ic->ic_flags |= IEEE80211_F_DATAPAD;
316311729SWang.Lin@Sun.COM 	/* Support WPA/WPA2 */
316411729SWang.Lin@Sun.COM 	ic->ic_caps |= IEEE80211_C_WPA;
316511729SWang.Lin@Sun.COM #if 0
316611729SWang.Lin@Sun.COM 	ic->ic_caps |= IEEE80211_C_TXFRAG; /* handle tx frags */
316711729SWang.Lin@Sun.COM 	ic->ic_caps |= IEEE80211_C_BGSCAN; /* capable of bg scanning */
316811729SWang.Lin@Sun.COM #endif
316911729SWang.Lin@Sun.COM 	ic->ic_phytype = IEEE80211_T_HT;
31709999SWang.Lin@Sun.COM 	ic->ic_opmode = IEEE80211_M_STA;
31719999SWang.Lin@Sun.COM 	ic->ic_state = IEEE80211_S_INIT;
31729999SWang.Lin@Sun.COM 	ic->ic_maxrssi = ARN_MAX_RSSI;
31739999SWang.Lin@Sun.COM 	ic->ic_set_shortslot = arn_set_shortslot;
31749999SWang.Lin@Sun.COM 	ic->ic_xmit = arn_tx;
31759999SWang.Lin@Sun.COM 	ieee80211_attach(ic);
31769999SWang.Lin@Sun.COM 
31779999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
31789999SWang.Lin@Sun.COM 	    "ic->ic_curchan->ich_freq: %d\n", ic->ic_curchan->ich_freq));
31799999SWang.Lin@Sun.COM 
31809999SWang.Lin@Sun.COM 	/* different instance has different WPA door */
31819999SWang.Lin@Sun.COM 	(void) snprintf(ic->ic_wpadoor, MAX_IEEE80211STR, "%s_%s%d", WPA_DOOR,
31829999SWang.Lin@Sun.COM 	    ddi_driver_name(devinfo),
31839999SWang.Lin@Sun.COM 	    ddi_get_instance(devinfo));
31849999SWang.Lin@Sun.COM 
318511729SWang.Lin@Sun.COM 	if (sc->sc_ht_conf.ht_supported) {
318611729SWang.Lin@Sun.COM 		sc->sc_recv_action = ic->ic_recv_action;
318711729SWang.Lin@Sun.COM 		ic->ic_recv_action = arn_ampdu_recv_action;
318811729SWang.Lin@Sun.COM 		// sc->sc_send_action = ic->ic_send_action;
318911729SWang.Lin@Sun.COM 		// ic->ic_send_action = arn_ampdu_send_action;
319011729SWang.Lin@Sun.COM 
319111729SWang.Lin@Sun.COM 		ic->ic_ampdu_rxmax = sc->sc_ht_conf.ampdu_factor;
319211729SWang.Lin@Sun.COM 		ic->ic_ampdu_density = sc->sc_ht_conf.ampdu_density;
319311729SWang.Lin@Sun.COM 		ic->ic_ampdu_limit = ic->ic_ampdu_rxmax;
319411729SWang.Lin@Sun.COM 	}
319511729SWang.Lin@Sun.COM 
31969999SWang.Lin@Sun.COM 	/* Override 80211 default routines */
31979999SWang.Lin@Sun.COM 	sc->sc_newstate = ic->ic_newstate;
31989999SWang.Lin@Sun.COM 	ic->ic_newstate = arn_newstate;
31999999SWang.Lin@Sun.COM #ifdef ARN_IBSS
32009999SWang.Lin@Sun.COM 	sc->sc_recv_mgmt = ic->ic_recv_mgmt;
32019999SWang.Lin@Sun.COM 	ic->ic_recv_mgmt = arn_recv_mgmt;
32029999SWang.Lin@Sun.COM #endif
32039999SWang.Lin@Sun.COM 	ic->ic_watchdog = arn_watchdog;
32049999SWang.Lin@Sun.COM 	ic->ic_node_alloc = arn_node_alloc;
32059999SWang.Lin@Sun.COM 	ic->ic_node_free = arn_node_free;
32069999SWang.Lin@Sun.COM 	ic->ic_crypto.cs_key_alloc = arn_key_alloc;
32079999SWang.Lin@Sun.COM 	ic->ic_crypto.cs_key_delete = arn_key_delete;
32089999SWang.Lin@Sun.COM 	ic->ic_crypto.cs_key_set = arn_key_set;
32099999SWang.Lin@Sun.COM 
32109999SWang.Lin@Sun.COM 	ieee80211_media_init(ic);
32119999SWang.Lin@Sun.COM 
32129999SWang.Lin@Sun.COM 	/*
32139999SWang.Lin@Sun.COM 	 * initialize default tx key
32149999SWang.Lin@Sun.COM 	 */
32159999SWang.Lin@Sun.COM 	ic->ic_def_txkey = 0;
32169999SWang.Lin@Sun.COM 
32179999SWang.Lin@Sun.COM 	sc->sc_rx_pend = 0;
32189999SWang.Lin@Sun.COM 	(void) ath9k_hw_set_interrupts(sc->sc_ah, 0);
32199999SWang.Lin@Sun.COM 	err = ddi_add_softintr(devinfo, DDI_SOFTINT_LOW,
32209999SWang.Lin@Sun.COM 	    &sc->sc_softint_id, NULL, 0, arn_softint_handler, (caddr_t)sc);
32219999SWang.Lin@Sun.COM 	if (err != DDI_SUCCESS) {
32229999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
32239999SWang.Lin@Sun.COM 		    "ddi_add_softintr() failed....\n"));
32249999SWang.Lin@Sun.COM 		goto attach_fail5;
32259999SWang.Lin@Sun.COM 	}
32269999SWang.Lin@Sun.COM 
32279999SWang.Lin@Sun.COM 	if (ddi_get_iblock_cookie(devinfo, 0, &sc->sc_iblock)
32289999SWang.Lin@Sun.COM 	    != DDI_SUCCESS) {
32299999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
32309999SWang.Lin@Sun.COM 		    "Can not get iblock cookie for INT\n"));
32319999SWang.Lin@Sun.COM 		goto attach_fail6;
32329999SWang.Lin@Sun.COM 	}
32339999SWang.Lin@Sun.COM 
32349999SWang.Lin@Sun.COM 	if (ddi_add_intr(devinfo, 0, NULL, NULL, arn_isr,
32359999SWang.Lin@Sun.COM 	    (caddr_t)sc) != DDI_SUCCESS) {
32369999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
32379999SWang.Lin@Sun.COM 		    "Can not set intr for ARN driver\n"));
32389999SWang.Lin@Sun.COM 		goto attach_fail6;
32399999SWang.Lin@Sun.COM 	}
32409999SWang.Lin@Sun.COM 
32419999SWang.Lin@Sun.COM 	/*
32429999SWang.Lin@Sun.COM 	 * Provide initial settings for the WiFi plugin; whenever this
32439999SWang.Lin@Sun.COM 	 * information changes, we need to call mac_plugindata_update()
32449999SWang.Lin@Sun.COM 	 */
32459999SWang.Lin@Sun.COM 	wd.wd_opmode = ic->ic_opmode;
32469999SWang.Lin@Sun.COM 	wd.wd_secalloc = WIFI_SEC_NONE;
32479999SWang.Lin@Sun.COM 	IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
32489999SWang.Lin@Sun.COM 
32499999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
32509999SWang.Lin@Sun.COM 	    "IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid)"
32519999SWang.Lin@Sun.COM 	    "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
32529999SWang.Lin@Sun.COM 	    wd.wd_bssid[0], wd.wd_bssid[1], wd.wd_bssid[2],
32539999SWang.Lin@Sun.COM 	    wd.wd_bssid[3], wd.wd_bssid[4], wd.wd_bssid[5]));
32549999SWang.Lin@Sun.COM 
32559999SWang.Lin@Sun.COM 	if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
32569999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
32579999SWang.Lin@Sun.COM 		    "MAC version mismatch\n"));
32589999SWang.Lin@Sun.COM 		goto attach_fail7;
32599999SWang.Lin@Sun.COM 	}
32609999SWang.Lin@Sun.COM 
32619999SWang.Lin@Sun.COM 	macp->m_type_ident	= MAC_PLUGIN_IDENT_WIFI;
32629999SWang.Lin@Sun.COM 	macp->m_driver		= sc;
32639999SWang.Lin@Sun.COM 	macp->m_dip		= devinfo;
32649999SWang.Lin@Sun.COM 	macp->m_src_addr	= ic->ic_macaddr;
32659999SWang.Lin@Sun.COM 	macp->m_callbacks	= &arn_m_callbacks;
32669999SWang.Lin@Sun.COM 	macp->m_min_sdu		= 0;
32679999SWang.Lin@Sun.COM 	macp->m_max_sdu		= IEEE80211_MTU;
32689999SWang.Lin@Sun.COM 	macp->m_pdata		= &wd;
32699999SWang.Lin@Sun.COM 	macp->m_pdata_size	= sizeof (wd);
32709999SWang.Lin@Sun.COM 
32719999SWang.Lin@Sun.COM 	err = mac_register(macp, &ic->ic_mach);
32729999SWang.Lin@Sun.COM 	mac_free(macp);
32739999SWang.Lin@Sun.COM 	if (err != 0) {
32749999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
32759999SWang.Lin@Sun.COM 		    "mac_register err %x\n", err));
32769999SWang.Lin@Sun.COM 		goto attach_fail7;
32779999SWang.Lin@Sun.COM 	}
32789999SWang.Lin@Sun.COM 
32799999SWang.Lin@Sun.COM 	/* Create minor node of type DDI_NT_NET_WIFI */
32809999SWang.Lin@Sun.COM 	(void) snprintf(strbuf, sizeof (strbuf), "%s%d",
32819999SWang.Lin@Sun.COM 	    ARN_NODENAME, instance);
32829999SWang.Lin@Sun.COM 	err = ddi_create_minor_node(devinfo, strbuf, S_IFCHR,
32839999SWang.Lin@Sun.COM 	    instance + 1, DDI_NT_NET_WIFI, 0);
32849999SWang.Lin@Sun.COM 	if (err != DDI_SUCCESS)
32859999SWang.Lin@Sun.COM 		ARN_DBG((ARN_DBG_ATTACH, "WARN: arn: arn_attach(): "
32869999SWang.Lin@Sun.COM 		    "Create minor node failed - %d\n", err));
32879999SWang.Lin@Sun.COM 
328811729SWang.Lin@Sun.COM 	/* Notify link is down now */
32899999SWang.Lin@Sun.COM 	mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
32909999SWang.Lin@Sun.COM 
32919999SWang.Lin@Sun.COM 	sc->sc_promisc = B_FALSE;
32929999SWang.Lin@Sun.COM 	bzero(sc->sc_mcast_refs, sizeof (sc->sc_mcast_refs));
32939999SWang.Lin@Sun.COM 	bzero(sc->sc_mcast_hash, sizeof (sc->sc_mcast_hash));
32949999SWang.Lin@Sun.COM 
32959999SWang.Lin@Sun.COM 	ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
32969999SWang.Lin@Sun.COM 	    "Atheros AR%s MAC/BB Rev:%x "
32979999SWang.Lin@Sun.COM 	    "AR%s RF Rev:%x: mem=0x%lx\n",
32989999SWang.Lin@Sun.COM 	    arn_mac_bb_name(ah->ah_macVersion),
32999999SWang.Lin@Sun.COM 	    ah->ah_macRev,
33009999SWang.Lin@Sun.COM 	    arn_rf_name((ah->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR)),
33019999SWang.Lin@Sun.COM 	    ah->ah_phyRev,
33029999SWang.Lin@Sun.COM 	    (unsigned long)sc->mem));
33039999SWang.Lin@Sun.COM 
33049999SWang.Lin@Sun.COM 	/* XXX: hardware will not be ready until arn_open() being called */
33059999SWang.Lin@Sun.COM 	sc->sc_flags |= SC_OP_INVALID;
33069999SWang.Lin@Sun.COM 	sc->sc_isrunning = 0;
33079999SWang.Lin@Sun.COM 
33089999SWang.Lin@Sun.COM 	return (DDI_SUCCESS);
33099999SWang.Lin@Sun.COM 
33109999SWang.Lin@Sun.COM attach_fail7:
33119999SWang.Lin@Sun.COM 	ddi_remove_intr(devinfo, 0, sc->sc_iblock);
33129999SWang.Lin@Sun.COM attach_fail6:
33139999SWang.Lin@Sun.COM 	ddi_remove_softintr(sc->sc_softint_id);
33149999SWang.Lin@Sun.COM attach_fail5:
33159999SWang.Lin@Sun.COM 	(void) ieee80211_detach(ic);
33169999SWang.Lin@Sun.COM attach_fail4:
33179999SWang.Lin@Sun.COM 	arn_desc_free(sc);
33189999SWang.Lin@Sun.COM 	if (sc->sc_tq)
33199999SWang.Lin@Sun.COM 		ddi_taskq_destroy(sc->sc_tq);
33209999SWang.Lin@Sun.COM attach_fail3:
33219999SWang.Lin@Sun.COM 	ath9k_hw_detach(ah);
33229999SWang.Lin@Sun.COM attach_fail2:
33239999SWang.Lin@Sun.COM 	ddi_regs_map_free(&sc->sc_io_handle);
33249999SWang.Lin@Sun.COM attach_fail1:
33259999SWang.Lin@Sun.COM 	pci_config_teardown(&sc->sc_cfg_handle);
33269999SWang.Lin@Sun.COM attach_fail0:
33279999SWang.Lin@Sun.COM 	sc->sc_flags |= SC_OP_INVALID;
33289999SWang.Lin@Sun.COM 	/* cleanup tx queues */
33299999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_txbuflock);
33309999SWang.Lin@Sun.COM 	for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
33319999SWang.Lin@Sun.COM 		if (ARN_TXQ_SETUP(sc, i)) {
33329999SWang.Lin@Sun.COM 			/* arn_tx_cleanupq(asc, &asc->sc_txq[i]); */
33339999SWang.Lin@Sun.COM 			mutex_destroy(&((&sc->sc_txq[i])->axq_lock));
33349999SWang.Lin@Sun.COM 		}
33359999SWang.Lin@Sun.COM 	}
33369999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_rxbuflock);
33379999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_serial_rw);
33389999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_genlock);
33399999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_resched_lock);
33409999SWang.Lin@Sun.COM #ifdef ARN_IBSS
33419999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_bcbuflock);
33429999SWang.Lin@Sun.COM #endif
33439999SWang.Lin@Sun.COM 
33449999SWang.Lin@Sun.COM 	ddi_soft_state_free(arn_soft_state_p, instance);
33459999SWang.Lin@Sun.COM 
33469999SWang.Lin@Sun.COM 	return (DDI_FAILURE);
33479999SWang.Lin@Sun.COM 
33489999SWang.Lin@Sun.COM }
33499999SWang.Lin@Sun.COM 
33509999SWang.Lin@Sun.COM /*
33519999SWang.Lin@Sun.COM  * Suspend transmit/receive for powerdown
33529999SWang.Lin@Sun.COM  */
33539999SWang.Lin@Sun.COM static int
arn_suspend(struct arn_softc * sc)33549999SWang.Lin@Sun.COM arn_suspend(struct arn_softc *sc)
33559999SWang.Lin@Sun.COM {
33569999SWang.Lin@Sun.COM 	ARN_LOCK(sc);
33579999SWang.Lin@Sun.COM 	arn_close(sc);
33589999SWang.Lin@Sun.COM 	ARN_UNLOCK(sc);
33599999SWang.Lin@Sun.COM 
33609999SWang.Lin@Sun.COM 	return (DDI_SUCCESS);
33619999SWang.Lin@Sun.COM }
33629999SWang.Lin@Sun.COM 
33639999SWang.Lin@Sun.COM static int32_t
arn_detach(dev_info_t * devinfo,ddi_detach_cmd_t cmd)33649999SWang.Lin@Sun.COM arn_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
33659999SWang.Lin@Sun.COM {
33669999SWang.Lin@Sun.COM 	struct arn_softc *sc;
33679999SWang.Lin@Sun.COM 	int i;
33689999SWang.Lin@Sun.COM 
33699999SWang.Lin@Sun.COM 	sc = ddi_get_soft_state(arn_soft_state_p, ddi_get_instance(devinfo));
33709999SWang.Lin@Sun.COM 	ASSERT(sc != NULL);
33719999SWang.Lin@Sun.COM 
33729999SWang.Lin@Sun.COM 	switch (cmd) {
33739999SWang.Lin@Sun.COM 	case DDI_DETACH:
33749999SWang.Lin@Sun.COM 		break;
33759999SWang.Lin@Sun.COM 
33769999SWang.Lin@Sun.COM 	case DDI_SUSPEND:
33779999SWang.Lin@Sun.COM 		return (arn_suspend(sc));
33789999SWang.Lin@Sun.COM 
33799999SWang.Lin@Sun.COM 	default:
33809999SWang.Lin@Sun.COM 		return (DDI_FAILURE);
33819999SWang.Lin@Sun.COM 	}
33829999SWang.Lin@Sun.COM 
33839999SWang.Lin@Sun.COM 	if (mac_disable(sc->sc_isc.ic_mach) != 0)
33849999SWang.Lin@Sun.COM 		return (DDI_FAILURE);
33859999SWang.Lin@Sun.COM 
33869999SWang.Lin@Sun.COM 	arn_stop_scantimer(sc);
33879999SWang.Lin@Sun.COM 	arn_stop_caltimer(sc);
33889999SWang.Lin@Sun.COM 
33899999SWang.Lin@Sun.COM 	/* disable interrupts */
33909999SWang.Lin@Sun.COM 	(void) ath9k_hw_set_interrupts(sc->sc_ah, 0);
33919999SWang.Lin@Sun.COM 
33929999SWang.Lin@Sun.COM 	/*
33939999SWang.Lin@Sun.COM 	 * Unregister from the MAC layer subsystem
33949999SWang.Lin@Sun.COM 	 */
33959999SWang.Lin@Sun.COM 	(void) mac_unregister(sc->sc_isc.ic_mach);
33969999SWang.Lin@Sun.COM 
33979999SWang.Lin@Sun.COM 	/* free intterrupt resources */
33989999SWang.Lin@Sun.COM 	ddi_remove_intr(devinfo, 0, sc->sc_iblock);
33999999SWang.Lin@Sun.COM 	ddi_remove_softintr(sc->sc_softint_id);
34009999SWang.Lin@Sun.COM 
34019999SWang.Lin@Sun.COM 	/*
34029999SWang.Lin@Sun.COM 	 * NB: the order of these is important:
34039999SWang.Lin@Sun.COM 	 * o call the 802.11 layer before detaching the hal to
34049999SWang.Lin@Sun.COM 	 *   insure callbacks into the driver to delete global
34059999SWang.Lin@Sun.COM 	 *   key cache entries can be handled
34069999SWang.Lin@Sun.COM 	 * o reclaim the tx queue data structures after calling
34079999SWang.Lin@Sun.COM 	 *   the 802.11 layer as we'll get called back to reclaim
34089999SWang.Lin@Sun.COM 	 *   node state and potentially want to use them
34099999SWang.Lin@Sun.COM 	 * o to cleanup the tx queues the hal is called, so detach
34109999SWang.Lin@Sun.COM 	 *   it last
34119999SWang.Lin@Sun.COM 	 */
34129999SWang.Lin@Sun.COM 	ieee80211_detach(&sc->sc_isc);
34139999SWang.Lin@Sun.COM 
34149999SWang.Lin@Sun.COM 	arn_desc_free(sc);
34159999SWang.Lin@Sun.COM 
34169999SWang.Lin@Sun.COM 	ddi_taskq_destroy(sc->sc_tq);
34179999SWang.Lin@Sun.COM 
34189999SWang.Lin@Sun.COM 	if (!(sc->sc_flags & SC_OP_INVALID))
34199999SWang.Lin@Sun.COM 		(void) ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
34209999SWang.Lin@Sun.COM 
34219999SWang.Lin@Sun.COM 	/* cleanup tx queues */
34229999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_txbuflock);
34239999SWang.Lin@Sun.COM 	for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
34249999SWang.Lin@Sun.COM 		if (ARN_TXQ_SETUP(sc, i)) {
34259999SWang.Lin@Sun.COM 			arn_tx_cleanupq(sc, &sc->sc_txq[i]);
34269999SWang.Lin@Sun.COM 			mutex_destroy(&((&sc->sc_txq[i])->axq_lock));
34279999SWang.Lin@Sun.COM 		}
34289999SWang.Lin@Sun.COM 	}
34299999SWang.Lin@Sun.COM 
34309999SWang.Lin@Sun.COM 	ath9k_hw_detach(sc->sc_ah);
34319999SWang.Lin@Sun.COM 
34329999SWang.Lin@Sun.COM 	/* free io handle */
34339999SWang.Lin@Sun.COM 	ddi_regs_map_free(&sc->sc_io_handle);
34349999SWang.Lin@Sun.COM 	pci_config_teardown(&sc->sc_cfg_handle);
34359999SWang.Lin@Sun.COM 
34369999SWang.Lin@Sun.COM 	/* destroy locks */
34379999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_genlock);
34389999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_serial_rw);
34399999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_rxbuflock);
34409999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_resched_lock);
34419999SWang.Lin@Sun.COM #ifdef ARN_IBSS
34429999SWang.Lin@Sun.COM 	mutex_destroy(&sc->sc_bcbuflock);
34439999SWang.Lin@Sun.COM #endif
34449999SWang.Lin@Sun.COM 
34459999SWang.Lin@Sun.COM 	ddi_remove_minor_node(devinfo, NULL);
34469999SWang.Lin@Sun.COM 	ddi_soft_state_free(arn_soft_state_p, ddi_get_instance(devinfo));
34479999SWang.Lin@Sun.COM 
34489999SWang.Lin@Sun.COM 	return (DDI_SUCCESS);
34499999SWang.Lin@Sun.COM }
34509999SWang.Lin@Sun.COM 
34519999SWang.Lin@Sun.COM /*
34529999SWang.Lin@Sun.COM  * quiesce(9E) entry point.
34539999SWang.Lin@Sun.COM  *
34549999SWang.Lin@Sun.COM  * This function is called when the system is single-threaded at high
34559999SWang.Lin@Sun.COM  * PIL with preemption disabled. Therefore, this function must not be
34569999SWang.Lin@Sun.COM  * blocked.
34579999SWang.Lin@Sun.COM  *
34589999SWang.Lin@Sun.COM  * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
34599999SWang.Lin@Sun.COM  * DDI_FAILURE indicates an error condition and should almost never happen.
34609999SWang.Lin@Sun.COM  */
34619999SWang.Lin@Sun.COM static int32_t
arn_quiesce(dev_info_t * devinfo)34629999SWang.Lin@Sun.COM arn_quiesce(dev_info_t *devinfo)
34639999SWang.Lin@Sun.COM {
34649999SWang.Lin@Sun.COM 	struct arn_softc *sc;
34659999SWang.Lin@Sun.COM 	int i;
34669999SWang.Lin@Sun.COM 	struct ath_hal *ah;
34679999SWang.Lin@Sun.COM 
34689999SWang.Lin@Sun.COM 	sc = ddi_get_soft_state(arn_soft_state_p, ddi_get_instance(devinfo));
34699999SWang.Lin@Sun.COM 
34709999SWang.Lin@Sun.COM 	if (sc == NULL || (ah = sc->sc_ah) == NULL)
34719999SWang.Lin@Sun.COM 		return (DDI_FAILURE);
34729999SWang.Lin@Sun.COM 
34739999SWang.Lin@Sun.COM 	/*
34749999SWang.Lin@Sun.COM 	 * Disable interrupts
34759999SWang.Lin@Sun.COM 	 */
34769999SWang.Lin@Sun.COM 	(void) ath9k_hw_set_interrupts(ah, 0);
34779999SWang.Lin@Sun.COM 
34789999SWang.Lin@Sun.COM 	/*
34799999SWang.Lin@Sun.COM 	 * Disable TX HW
34809999SWang.Lin@Sun.COM 	 */
34819999SWang.Lin@Sun.COM 	for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
34829999SWang.Lin@Sun.COM 		if (ARN_TXQ_SETUP(sc, i))
34839999SWang.Lin@Sun.COM 			(void) ath9k_hw_stoptxdma(ah, sc->sc_txq[i].axq_qnum);
34849999SWang.Lin@Sun.COM 	}
34859999SWang.Lin@Sun.COM 
34869999SWang.Lin@Sun.COM 	/*
34879999SWang.Lin@Sun.COM 	 * Disable RX HW
34889999SWang.Lin@Sun.COM 	 */
34899999SWang.Lin@Sun.COM 	ath9k_hw_stoppcurecv(ah);
34909999SWang.Lin@Sun.COM 	ath9k_hw_setrxfilter(ah, 0);
34919999SWang.Lin@Sun.COM 	(void) ath9k_hw_stopdmarecv(ah);
34929999SWang.Lin@Sun.COM 	drv_usecwait(3000);
34939999SWang.Lin@Sun.COM 
34949999SWang.Lin@Sun.COM 	/*
34959999SWang.Lin@Sun.COM 	 * Power down HW
34969999SWang.Lin@Sun.COM 	 */
34979999SWang.Lin@Sun.COM 	(void) ath9k_hw_phy_disable(ah);
34989999SWang.Lin@Sun.COM 
34999999SWang.Lin@Sun.COM 	return (DDI_SUCCESS);
35009999SWang.Lin@Sun.COM }
35019999SWang.Lin@Sun.COM 
35029999SWang.Lin@Sun.COM DDI_DEFINE_STREAM_OPS(arn_dev_ops, nulldev, nulldev, arn_attach, arn_detach,
35039999SWang.Lin@Sun.COM     nodev, NULL, D_MP, NULL, arn_quiesce);
35049999SWang.Lin@Sun.COM 
35059999SWang.Lin@Sun.COM static struct modldrv arn_modldrv = {
35069999SWang.Lin@Sun.COM 	&mod_driverops, /* Type of module.  This one is a driver */
350711729SWang.Lin@Sun.COM 	"arn-Atheros 9000 series driver:2.0", /* short description */
35089999SWang.Lin@Sun.COM 	&arn_dev_ops /* driver specific ops */
35099999SWang.Lin@Sun.COM };
35109999SWang.Lin@Sun.COM 
35119999SWang.Lin@Sun.COM static struct modlinkage modlinkage = {
35129999SWang.Lin@Sun.COM 	MODREV_1, (void *)&arn_modldrv, NULL
35139999SWang.Lin@Sun.COM };
35149999SWang.Lin@Sun.COM 
35159999SWang.Lin@Sun.COM int
_info(struct modinfo * modinfop)35169999SWang.Lin@Sun.COM _info(struct modinfo *modinfop)
35179999SWang.Lin@Sun.COM {
35189999SWang.Lin@Sun.COM 	return (mod_info(&modlinkage, modinfop));
35199999SWang.Lin@Sun.COM }
35209999SWang.Lin@Sun.COM 
35219999SWang.Lin@Sun.COM int
_init(void)35229999SWang.Lin@Sun.COM _init(void)
35239999SWang.Lin@Sun.COM {
35249999SWang.Lin@Sun.COM 	int status;
35259999SWang.Lin@Sun.COM 
35269999SWang.Lin@Sun.COM 	status = ddi_soft_state_init
35279999SWang.Lin@Sun.COM 	    (&arn_soft_state_p, sizeof (struct arn_softc), 1);
35289999SWang.Lin@Sun.COM 	if (status != 0)
35299999SWang.Lin@Sun.COM 		return (status);
35309999SWang.Lin@Sun.COM 
35319999SWang.Lin@Sun.COM 	mutex_init(&arn_loglock, NULL, MUTEX_DRIVER, NULL);
35329999SWang.Lin@Sun.COM 	mac_init_ops(&arn_dev_ops, "arn");
35339999SWang.Lin@Sun.COM 	status = mod_install(&modlinkage);
35349999SWang.Lin@Sun.COM 	if (status != 0) {
35359999SWang.Lin@Sun.COM 		mac_fini_ops(&arn_dev_ops);
35369999SWang.Lin@Sun.COM 		mutex_destroy(&arn_loglock);
35379999SWang.Lin@Sun.COM 		ddi_soft_state_fini(&arn_soft_state_p);
35389999SWang.Lin@Sun.COM 	}
35399999SWang.Lin@Sun.COM 
35409999SWang.Lin@Sun.COM 	return (status);
35419999SWang.Lin@Sun.COM }
35429999SWang.Lin@Sun.COM 
35439999SWang.Lin@Sun.COM int
_fini(void)35449999SWang.Lin@Sun.COM _fini(void)
35459999SWang.Lin@Sun.COM {
35469999SWang.Lin@Sun.COM 	int status;
35479999SWang.Lin@Sun.COM 
35489999SWang.Lin@Sun.COM 	status = mod_remove(&modlinkage);
35499999SWang.Lin@Sun.COM 	if (status == 0) {
35509999SWang.Lin@Sun.COM 		mac_fini_ops(&arn_dev_ops);
35519999SWang.Lin@Sun.COM 		mutex_destroy(&arn_loglock);
35529999SWang.Lin@Sun.COM 		ddi_soft_state_fini(&arn_soft_state_p);
35539999SWang.Lin@Sun.COM 	}
35549999SWang.Lin@Sun.COM 	return (status);
35559999SWang.Lin@Sun.COM }
3556