/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer,
 * without modification.
 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
 * redistribution must be conditioned upon including a substantially
 * similar Disclaimer requirement for further binary redistribution.
 * 3. Neither the names of the above-listed copyright holders nor the names
 * of any contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * NO WARRANTY
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGES.
 *
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * Driver for the Atheros Wireless LAN controller.
 *
 * The Atheros driver calls into net80211 module for IEEE80211 protocol
 * management functionalities. The driver includes a LLD(Low Level Driver)
 * part to implement H/W related operations.
 * The following is the high level structure of ath driver.
 * (The arrows between modules indicate function call direction.)
 *
 *
 *                                                  |
 *                                                  | GLD thread
 *                                                  V
 *         ==================  =========================================
 *         |                |  |[1]                                    |
 *         |                |  |  GLDv3 Callback functions registered  |
 *         |   Net80211     |  =========================       by      |
 *         |    module      |          |               |     driver    |
 *         |                |          V               |               |
 *         |                |========================  |               |
 *         |   Functions exported by net80211       |  |               |
 *         |                                        |  |               |
 *         ==========================================  =================
 *                         |                                  |
 *                         V                                  |
 *         +----------------------------------+               |
 *         |[2]                               |               |
 *         |    Net80211 Callback functions   |               |
 *         |      registered by LLD           |               |
 *         +----------------------------------+               |
 *                         |                                  |
 *                         V                                  v
 *         +-----------------------------------------------------------+
 *         |[3]                                                        |
 *         |                LLD Internal functions                     |
 *         |                                                           |
 *         +-----------------------------------------------------------+
 *                                    ^
 *                                    | Software interrupt thread
 *                                    |
 *
 * The short description of each module is as below:
 *      Module 1: GLD callback functions, which are intercepting the calls from
 *                GLD to LLD.
 *      Module 2: Net80211 callback functions registered by LLD, which
 *                calls into LLD for H/W related functions needed by net80211.
 *      Module 3: LLD Internal functions, which are responsible for allocing
 *                descriptor/buffer, handling interrupt and other H/W
 *                operations.
 *
 * All functions are running in 3 types of thread:
 * 1. GLD callbacks threads, such as ioctl, intr, etc.
 * 2. Clock interruptt thread which is responsible for scan, rate control and
 *    calibration.
 * 3. Software Interrupt thread originated in LLD.
 *
 * The lock strategy is as below:
 * There have 4 queues for tx, each queue has one asc_txqlock[i] to
 *      prevent conflicts access to queue resource from different thread.
 *
 * All the transmit buffers are contained in asc_txbuf which are
 *      protected by asc_txbuflock.
 *
 * Each receive buffers are contained in asc_rxbuf which are protected
 *      by asc_rxbuflock.
 *
 * In ath struct, asc_genlock is a general lock, protecting most other
 *      operational data in ath_softc struct and HAL accesses.
 *      It is acquired by the interupt handler and most "mode-ctrl" routines.
 *
 * Any of the locks can be acquired singly, but where multiple
 * locks are acquired, they *must* be in the order:
 *    asc_genlock >> asc_txqlock[i] >> asc_txbuflock >> asc_rxbuflock
 */

#include <sys/param.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <sys/stream.h>
#include <sys/termio.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/cmn_err.h>
#include <sys/stropts.h>
#include <sys/strsubr.h>
#include <sys/strtty.h>
#include <sys/kbio.h>
#include <sys/cred.h>
#include <sys/stat.h>
#include <sys/consdev.h>
#include <sys/kmem.h>
#include <sys/modctl.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/pci.h>
#include <sys/errno.h>
#include <sys/mac.h>
#include <sys/dlpi.h>
#include <sys/ethernet.h>
#include <sys/list.h>
#include <sys/byteorder.h>
#include <sys/strsun.h>
#include <sys/policy.h>
#include <inet/common.h>
#include <inet/nd.h>
#include <inet/mi.h>
#include <inet/wifi_ioctl.h>
#include <sys/mac_wifi.h>
#include "ath_hal.h"
#include "ath_impl.h"
#include "ath_aux.h"
#include "ath_rate.h"

#define	ATH_MAX_RSSI	63	/* max rssi */

extern void ath_halfix_init(void);
extern void ath_halfix_finit(void);
extern int32_t ath_getset(ath_t *asc, mblk_t *mp, uint32_t cmd);

/*
 * PIO access attributes for registers
 */
static ddi_device_acc_attr_t ath_reg_accattr = {
	DDI_DEVICE_ATTR_V0,
	DDI_STRUCTURE_LE_ACC,
	DDI_STRICTORDER_ACC
};

/*
 * DMA access attributes for descriptors: NOT to be byte swapped.
 */
static ddi_device_acc_attr_t ath_desc_accattr = {
	DDI_DEVICE_ATTR_V0,
	DDI_STRUCTURE_LE_ACC,
	DDI_STRICTORDER_ACC
};

/*
 * Describes the chip's DMA engine
 */
static ddi_dma_attr_t ath_dma_attr = {
	DMA_ATTR_V0,		/* version number */
	0,			/* low address */
	0xffffffffU,		/* high address */
	0x3ffffU,		/* counter register max */
	1,			/* alignment */
	0xFFF,			/* burst sizes */
	1,			/* minimum transfer size */
	0x3ffffU,		/* max transfer size */
	0xffffffffU,		/* address register max */
	1,			/* no scatter-gather */
	1,			/* granularity of device */
	0,			/* DMA flags */
};

static ddi_dma_attr_t ath_desc_dma_attr = {
	DMA_ATTR_V0,		/* version number */
	0,			/* low address */
	0xffffffffU,		/* high address */
	0xffffffffU,		/* counter register max */
	0x1000,			/* alignment */
	0xFFF,			/* burst sizes */
	1,			/* minimum transfer size */
	0xffffffffU,		/* max transfer size */
	0xffffffffU,		/* address register max */
	1,			/* no scatter-gather */
	1,			/* granularity of device */
	0,			/* DMA flags */
};

static kmutex_t ath_loglock;
static void *ath_soft_state_p = NULL;
static int ath_dwelltime = 150;		/* scan interval, ms */

static int	ath_m_stat(void *,  uint_t, uint64_t *);
static int	ath_m_start(void *);
static void	ath_m_stop(void *);
static int	ath_m_promisc(void *, boolean_t);
static int	ath_m_multicst(void *, boolean_t, const uint8_t *);
static int	ath_m_unicst(void *, const uint8_t *);
static mblk_t	*ath_m_tx(void *, mblk_t *);
static void	ath_m_ioctl(void *, queue_t *, mblk_t *);
static mac_callbacks_t ath_m_callbacks = {
	MC_IOCTL,
	ath_m_stat,
	ath_m_start,
	ath_m_stop,
	ath_m_promisc,
	ath_m_multicst,
	ath_m_unicst,
	ath_m_tx,
	NULL,		/* mc_resources; */
	ath_m_ioctl,
	NULL		/* mc_getcapab */
};

/*
 * Available debug flags:
 * ATH_DBG_INIT, ATH_DBG_GLD, ATH_DBG_HAL, ATH_DBG_INT, ATH_DBG_ATTACH,
 * ATH_DBG_DETACH, ATH_DBG_AUX, ATH_DBG_WIFICFG, ATH_DBG_OSDEP
 */
uint32_t ath_dbg_flags = 0;

/*
 * Exception/warning cases not leading to panic.
 */
void
ath_problem(const int8_t *fmt, ...)
{
	va_list args;

	mutex_enter(&ath_loglock);

	va_start(args, fmt);
	vcmn_err(CE_WARN, fmt, args);
	va_end(args);

	mutex_exit(&ath_loglock);
}

/*
 * Normal log information independent of debug.
 */
void
ath_log(const int8_t *fmt, ...)
{
	va_list args;

	mutex_enter(&ath_loglock);

	va_start(args, fmt);
	vcmn_err(CE_CONT, fmt, args);
	va_end(args);

	mutex_exit(&ath_loglock);
}

void
ath_dbg(uint32_t dbg_flags, const int8_t *fmt, ...)
{
	va_list args;

	if (dbg_flags & ath_dbg_flags) {
		mutex_enter(&ath_loglock);
		va_start(args, fmt);
		vcmn_err(CE_CONT, fmt, args);
		va_end(args);
		mutex_exit(&ath_loglock);
	}
}

void
ath_setup_desc(ath_t *asc, struct ath_buf *bf)
{
	struct ath_desc *ds;

	ds = bf->bf_desc;
	ds->ds_link = bf->bf_daddr;
	ds->ds_data = bf->bf_dma.cookie.dmac_address;
	ds->ds_vdata = bf->bf_dma.mem_va;
	ATH_HAL_SETUPRXDESC(asc->asc_ah, ds,
	    bf->bf_dma.alength,		/* buffer size */
	    0);

	if (asc->asc_rxlink != NULL)
		*asc->asc_rxlink = bf->bf_daddr;
	asc->asc_rxlink = &ds->ds_link;
}


/*
 * Allocate an area of memory and a DMA handle for accessing it
 */
static int
ath_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)
{
	int err;

	/*
	 * Allocate handle
	 */
	err = ddi_dma_alloc_handle(devinfo, dma_attr,
	    DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl);
	if (err != DDI_SUCCESS)
		return (DDI_FAILURE);

	/*
	 * Allocate memory
	 */
	err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize, attr_p,
	    alloc_flags, DDI_DMA_SLEEP, NULL, &dma_p->mem_va,
	    &dma_p->alength, &dma_p->acc_hdl);
	if (err != DDI_SUCCESS)
		return (DDI_FAILURE);

	/*
	 * Bind the two together
	 */
	err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL,
	    dma_p->mem_va, dma_p->alength, bind_flags,
	    DDI_DMA_SLEEP, NULL, &dma_p->cookie, &dma_p->ncookies);
	if (err != DDI_DMA_MAPPED)
		return (DDI_FAILURE);

	dma_p->nslots = ~0U;
	dma_p->size = ~0U;
	dma_p->token = ~0U;
	dma_p->offset = 0;
	return (DDI_SUCCESS);
}

/*
 * Free one allocated area of DMAable memory
 */
static void
ath_free_dma_mem(dma_area_t *dma_p)
{
	if (dma_p->dma_hdl != NULL) {
		(void) ddi_dma_unbind_handle(dma_p->dma_hdl);
		if (dma_p->acc_hdl != NULL) {
			ddi_dma_mem_free(&dma_p->acc_hdl);
			dma_p->acc_hdl = NULL;
		}
		ddi_dma_free_handle(&dma_p->dma_hdl);
		dma_p->ncookies = 0;
		dma_p->dma_hdl = NULL;
	}
}


static int
ath_desc_alloc(dev_info_t *devinfo, ath_t *asc)
{
	int i, err;
	size_t size;
	struct ath_desc *ds;
	struct ath_buf *bf;

	size = sizeof (struct ath_desc) * (ATH_TXBUF + ATH_RXBUF);

	err = ath_alloc_dma_mem(devinfo, &ath_desc_dma_attr, size,
	    &ath_desc_accattr, DDI_DMA_CONSISTENT,
	    DDI_DMA_RDWR | DDI_DMA_CONSISTENT, &asc->asc_desc_dma);

	/* virtual address of the first descriptor */
	asc->asc_desc = (struct ath_desc *)asc->asc_desc_dma.mem_va;

	ds = asc->asc_desc;
	ATH_DEBUG((ATH_DBG_INIT, "ath: ath_desc_alloc(): DMA map: "
	    "%p (%d) -> %p\n",
	    asc->asc_desc, asc->asc_desc_dma.alength,
	    asc->asc_desc_dma.cookie.dmac_address));

	/* allocate data structures to describe TX/RX DMA buffers */
	asc->asc_vbuflen = sizeof (struct ath_buf) * (ATH_TXBUF + ATH_RXBUF);
	bf = (struct ath_buf *)kmem_zalloc(asc->asc_vbuflen, KM_SLEEP);
	asc->asc_vbufptr = bf;

	/* DMA buffer size for each TX/RX packet */
	asc->asc_dmabuf_size = roundup(1000 + sizeof (struct ieee80211_frame) +
	    IEEE80211_MTU + IEEE80211_CRC_LEN +
	    (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
	    IEEE80211_WEP_CRCLEN), asc->asc_cachelsz);

	/* create RX buffer list and allocate DMA memory */
	list_create(&asc->asc_rxbuf_list, sizeof (struct ath_buf),
	    offsetof(struct ath_buf, bf_node));
	for (i = 0; i < ATH_RXBUF; i++, bf++, ds++) {
		bf->bf_desc = ds;
		bf->bf_daddr = asc->asc_desc_dma.cookie.dmac_address +
		    ((uintptr_t)ds - (uintptr_t)asc->asc_desc);
		list_insert_tail(&asc->asc_rxbuf_list, bf);

		/* alloc DMA memory */
		err = ath_alloc_dma_mem(devinfo, &ath_dma_attr,
		    asc->asc_dmabuf_size, &ath_desc_accattr,
		    DDI_DMA_STREAMING, DDI_DMA_READ | DDI_DMA_STREAMING,
		    &bf->bf_dma);
		if (err != DDI_SUCCESS)
			return (err);
	}

	/* create TX buffer list and allocate DMA memory */
	list_create(&asc->asc_txbuf_list, sizeof (struct ath_buf),
	    offsetof(struct ath_buf, bf_node));
	for (i = 0; i < ATH_TXBUF; i++, bf++, ds++) {
		bf->bf_desc = ds;
		bf->bf_daddr = asc->asc_desc_dma.cookie.dmac_address +
		    ((uintptr_t)ds - (uintptr_t)asc->asc_desc);
		list_insert_tail(&asc->asc_txbuf_list, bf);

		/* alloc DMA memory */
		err = ath_alloc_dma_mem(devinfo, &ath_dma_attr,
		    asc->asc_dmabuf_size, &ath_desc_accattr,
		    DDI_DMA_STREAMING, DDI_DMA_STREAMING, &bf->bf_dma);
		if (err != DDI_SUCCESS)
			return (err);
	}

	return (DDI_SUCCESS);
}

static void
ath_desc_free(ath_t *asc)
{
	struct ath_buf *bf;

	/* Free TX DMA buffer */
	bf = list_head(&asc->asc_txbuf_list);
	while (bf != NULL) {
		ath_free_dma_mem(&bf->bf_dma);
		list_remove(&asc->asc_txbuf_list, bf);
		bf = list_head(&asc->asc_txbuf_list);
	}
	list_destroy(&asc->asc_txbuf_list);

	/* Free RX DMA uffer */
	bf = list_head(&asc->asc_rxbuf_list);
	while (bf != NULL) {
		ath_free_dma_mem(&bf->bf_dma);
		list_remove(&asc->asc_rxbuf_list, bf);
		bf = list_head(&asc->asc_rxbuf_list);
	}
	list_destroy(&asc->asc_rxbuf_list);

	/* Free descriptor DMA buffer */
	ath_free_dma_mem(&asc->asc_desc_dma);

	kmem_free((void *)asc->asc_vbufptr, asc->asc_vbuflen);
	asc->asc_vbufptr = NULL;
}

static void
ath_printrxbuf(struct ath_buf *bf, int32_t done)
{
	struct ath_desc *ds = bf->bf_desc;

	ATH_DEBUG((ATH_DBG_RECV, "ath: R (%p %p) %08x %08x %08x "
	    "%08x %08x %08x %c\n",
	    ds, bf->bf_daddr,
	    ds->ds_link, ds->ds_data,
	    ds->ds_ctl0, ds->ds_ctl1,
	    ds->ds_hw[0], ds->ds_hw[1],
	    !done ? ' ' : (ds->ds_rxstat.rs_status == 0) ? '*' : '!'));
}

static void
ath_rx_handler(ath_t *asc)
{
	ieee80211com_t *ic = (ieee80211com_t *)asc;
	struct ath_buf *bf;
	struct ath_hal *ah = asc->asc_ah;
	struct ath_desc *ds;
	mblk_t *rx_mp;
	struct ieee80211_frame *wh;
	int32_t len, loop = 1;
	uint8_t phyerr;
	HAL_STATUS status;
	HAL_NODE_STATS hal_node_stats;
	struct ieee80211_node *in;

	do {
		mutex_enter(&asc->asc_rxbuflock);
		bf = list_head(&asc->asc_rxbuf_list);
		if (bf == NULL) {
			ATH_DEBUG((ATH_DBG_RECV, "ath: ath_rx_handler(): "
			    "no buffer\n"));
			mutex_exit(&asc->asc_rxbuflock);
			break;
		}
		ASSERT(bf->bf_dma.cookie.dmac_address != NULL);
		ds = bf->bf_desc;
		if (ds->ds_link == bf->bf_daddr) {
			/*
			 * Never process the self-linked entry at the end,
			 * this may be met at heavy load.
			 */
			mutex_exit(&asc->asc_rxbuflock);
			break;
		}

		status = ATH_HAL_RXPROCDESC(ah, ds,
		    bf->bf_daddr,
		    ATH_PA2DESC(asc, ds->ds_link));
		if (status == HAL_EINPROGRESS) {
			mutex_exit(&asc->asc_rxbuflock);
			break;
		}
		list_remove(&asc->asc_rxbuf_list, bf);
		mutex_exit(&asc->asc_rxbuflock);

		if (ds->ds_rxstat.rs_status != 0) {
			if (ds->ds_rxstat.rs_status & HAL_RXERR_CRC)
				asc->asc_stats.ast_rx_crcerr++;
			if (ds->ds_rxstat.rs_status & HAL_RXERR_FIFO)
				asc->asc_stats.ast_rx_fifoerr++;
			if (ds->ds_rxstat.rs_status & HAL_RXERR_DECRYPT)
				asc->asc_stats.ast_rx_badcrypt++;
			if (ds->ds_rxstat.rs_status & HAL_RXERR_PHY) {
				asc->asc_stats.ast_rx_phyerr++;
				phyerr = ds->ds_rxstat.rs_phyerr & 0x1f;
				asc->asc_stats.ast_rx_phy[phyerr]++;
			}
			goto rx_next;
		}
		len = ds->ds_rxstat.rs_datalen;

		/* less than sizeof(struct ieee80211_frame) */
		if (len < 20) {
			asc->asc_stats.ast_rx_tooshort++;
			goto rx_next;
		}

		if ((rx_mp = allocb(asc->asc_dmabuf_size, BPRI_MED)) == NULL) {
			ath_problem("ath: ath_rx_handler(): "
			    "allocing mblk buffer failed.\n");
			return;
		}

		ATH_DMA_SYNC(bf->bf_dma, DDI_DMA_SYNC_FORCPU);
		bcopy(bf->bf_dma.mem_va, rx_mp->b_rptr, len);

		rx_mp->b_wptr += len;
		wh = (struct ieee80211_frame *)rx_mp->b_rptr;
		if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
		    IEEE80211_FC0_TYPE_CTL) {
			/*
			 * Ignore control frame received in promisc mode.
			 */
			freemsg(rx_mp);
			goto rx_next;
		}
		/* Remove the CRC at the end of IEEE80211 frame */
		rx_mp->b_wptr -= IEEE80211_CRC_LEN;
#ifdef DEBUG
		ath_printrxbuf(bf, status == HAL_OK);
#endif /* DEBUG */
		/*
		 * Locate the node for sender, track state, and then
		 * pass the (referenced) node up to the 802.11 layer
		 * for its use.
		 */
		in = ieee80211_find_rxnode(ic, wh);

		/*
		 * Send frame up for processing.
		 */
		(void) ieee80211_input(ic, rx_mp, in,
		    ds->ds_rxstat.rs_rssi,
		    ds->ds_rxstat.rs_tstamp);

		ieee80211_free_node(in);

rx_next:
		mutex_enter(&asc->asc_rxbuflock);
		list_insert_tail(&asc->asc_rxbuf_list, bf);
		mutex_exit(&asc->asc_rxbuflock);
		ath_setup_desc(asc, bf);
	} while (loop);

	/* rx signal state monitoring */
	ATH_HAL_RXMONITOR(ah, &hal_node_stats, &asc->asc_curchan);
}

static void
ath_printtxbuf(struct ath_buf *bf, int done)
{
	struct ath_desc *ds = bf->bf_desc;

	ATH_DEBUG((ATH_DBG_SEND, "ath: T(%p %p) %08x %08x %08x %08x %08x"
	    " %08x %08x %08x %c\n",
	    ds, bf->bf_daddr,
	    ds->ds_link, ds->ds_data,
	    ds->ds_ctl0, ds->ds_ctl1,
	    ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3],
	    !done ? ' ' : (ds->ds_txstat.ts_status == 0) ? '*' : '!'));
}

/*
 * The input parameter mp has following assumption:
 * For data packets, GLDv3 mac_wifi plugin allocates and fills the
 * ieee80211 header. For management packets, net80211 allocates and
 * fills the ieee80211 header. In both cases, enough spaces in the
 * header are left for encryption option.
 */
static int32_t
ath_tx_start(ath_t *asc, struct ieee80211_node *in, struct ath_buf *bf,
    mblk_t *mp)
{
	ieee80211com_t *ic = (ieee80211com_t *)asc;
	struct ieee80211_frame *wh;
	struct ath_hal *ah = asc->asc_ah;
	uint32_t subtype, flags, ctsduration;
	int32_t keyix, iswep, hdrlen, pktlen, mblen, mbslen, try0;
	uint8_t rix, cix, txrate, ctsrate;
	struct ath_desc *ds;
	struct ath_txq *txq;
	HAL_PKT_TYPE atype;
	const HAL_RATE_TABLE *rt;
	HAL_BOOL shortPreamble;
	struct ath_node *an;
	caddr_t dest;

	/*
	 * CRC are added by H/W, not encaped by driver,
	 * but we must count it in pkt length.
	 */
	pktlen = IEEE80211_CRC_LEN;

	wh = (struct ieee80211_frame *)mp->b_rptr;
	iswep = wh->i_fc[1] & IEEE80211_FC1_WEP;
	keyix = HAL_TXKEYIX_INVALID;
	hdrlen = sizeof (struct ieee80211_frame);
	if (iswep != 0) {
		const struct ieee80211_cipher *cip;
		struct ieee80211_key *k;

		/*
		 * Construct the 802.11 header+trailer for an encrypted
		 * frame. The only reason this can fail is because of an
		 * unknown or unsupported cipher/key type.
		 */
		k = ieee80211_crypto_encap(ic, mp);
		if (k == NULL) {
			ATH_DEBUG((ATH_DBG_AUX, "crypto_encap failed\n"));
			/*
			 * This can happen when the key is yanked after the
			 * frame was queued.  Just discard the frame; the
			 * 802.11 layer counts failures and provides
			 * debugging/diagnostics.
			 */
			return (EIO);
		}
		cip = k->wk_cipher;
		/*
		 * Adjust the packet + header lengths for the crypto
		 * additions and calculate the h/w key index.  When
		 * a s/w mic is done the frame will have had any mic
		 * added to it prior to entry so m0->m_pkthdr.len above will
		 * account for it. Otherwise we need to add it to the
		 * packet length.
		 */
		hdrlen += cip->ic_header;
		pktlen += cip->ic_trailer;
		if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0)
			pktlen += cip->ic_miclen;
		keyix = k->wk_keyix;

		/* packet header may have moved, reset our local pointer */
		wh = (struct ieee80211_frame *)mp->b_rptr;
	}

	dest = bf->bf_dma.mem_va;
	for (; mp != NULL; mp = mp->b_cont) {
		mblen = MBLKL(mp);
		bcopy(mp->b_rptr, dest, mblen);
		dest += mblen;
	}
	mbslen = (uintptr_t)dest - (uintptr_t)bf->bf_dma.mem_va;
	pktlen += mbslen;

	bf->bf_in = in;

	/* setup descriptors */
	ds = bf->bf_desc;
	rt = asc->asc_currates;
	ASSERT(rt != NULL);

	/*
	 * The 802.11 layer marks whether or not we should
	 * use short preamble based on the current mode and
	 * negotiated parameters.
	 */
	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
	    (in->in_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
		shortPreamble = AH_TRUE;
		asc->asc_stats.ast_tx_shortpre++;
	} else {
		shortPreamble = AH_FALSE;
	}

	an = ATH_NODE(in);

	/*
	 * Calculate Atheros packet type from IEEE80211 packet header
	 * and setup for rate calculations.
	 */
	switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
	case IEEE80211_FC0_TYPE_MGT:
		subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
		if (subtype == IEEE80211_FC0_SUBTYPE_BEACON)
			atype = HAL_PKT_TYPE_BEACON;
		else if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
			atype = HAL_PKT_TYPE_PROBE_RESP;
		else if (subtype == IEEE80211_FC0_SUBTYPE_ATIM)
			atype = HAL_PKT_TYPE_ATIM;
		else
			atype = HAL_PKT_TYPE_NORMAL;
		rix = 0;	/* lowest rate */
		try0 = ATH_TXMAXTRY;
		if (shortPreamble)
			txrate = an->an_tx_mgtratesp;
		else
			txrate = an->an_tx_mgtrate;
		/* force all ctl frames to highest queue */
		txq = asc->asc_ac2q[WME_AC_VO];
		break;
	case IEEE80211_FC0_TYPE_CTL:
		atype = HAL_PKT_TYPE_PSPOLL;
		subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
		rix = 0;	/* lowest rate */
		try0 = ATH_TXMAXTRY;
		if (shortPreamble)
			txrate = an->an_tx_mgtratesp;
		else
			txrate = an->an_tx_mgtrate;
		/* force all ctl frames to highest queue */
		txq = asc->asc_ac2q[WME_AC_VO];
		break;
	case IEEE80211_FC0_TYPE_DATA:
		atype = HAL_PKT_TYPE_NORMAL;
		rix = an->an_tx_rix0;
		try0 = an->an_tx_try0;
		if (shortPreamble)
			txrate = an->an_tx_rate0sp;
		else
			txrate = an->an_tx_rate0;
		/* Always use background queue */
		txq = asc->asc_ac2q[WME_AC_BK];
		break;
	default:
		/* Unknown 802.11 frame */
		asc->asc_stats.ast_tx_invalid++;
		return (1);
	}
	/*
	 * Calculate miscellaneous flags.
	 */
	flags = HAL_TXDESC_CLRDMASK;
	if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
		flags |= HAL_TXDESC_NOACK;	/* no ack on broad/multicast */
		asc->asc_stats.ast_tx_noack++;
	} else if (pktlen > ic->ic_rtsthreshold) {
		flags |= HAL_TXDESC_RTSENA;	/* RTS based on frame length */
		asc->asc_stats.ast_tx_rts++;
	}

	/*
	 * Calculate duration.  This logically belongs in the 802.11
	 * layer but it lacks sufficient information to calculate it.
	 */
	if ((flags & HAL_TXDESC_NOACK) == 0 &&
	    (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) !=
	    IEEE80211_FC0_TYPE_CTL) {
		uint16_t dur;
		dur = ath_hal_computetxtime(ah, rt, IEEE80211_ACK_SIZE,
		    rix, shortPreamble);
		*(uint16_t *)wh->i_dur = LE_16(dur);
	}

	/*
	 * Calculate RTS/CTS rate and duration if needed.
	 */
	ctsduration = 0;
	if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)) {
		/*
		 * CTS transmit rate is derived from the transmit rate
		 * by looking in the h/w rate table.  We must also factor
		 * in whether or not a short preamble is to be used.
		 */
		cix = rt->info[rix].controlRate;
		ctsrate = rt->info[cix].rateCode;
		if (shortPreamble)
			ctsrate |= rt->info[cix].shortPreamble;
		/*
		 * Compute the transmit duration based on the size
		 * of an ACK frame.  We call into the HAL to do the
		 * computation since it depends on the characteristics
		 * of the actual PHY being used.
		 */
		if (flags & HAL_TXDESC_RTSENA) {	/* SIFS + CTS */
			ctsduration += ath_hal_computetxtime(ah,
			    rt, IEEE80211_ACK_SIZE, cix, shortPreamble);
		}
		/* SIFS + data */
		ctsduration += ath_hal_computetxtime(ah,
		    rt, pktlen, rix, shortPreamble);
		if ((flags & HAL_TXDESC_NOACK) == 0) {	/* SIFS + ACK */
			ctsduration += ath_hal_computetxtime(ah,
			    rt, IEEE80211_ACK_SIZE, cix, shortPreamble);
		}
	} else
		ctsrate = 0;

	if (++txq->axq_intrcnt >= ATH_TXINTR_PERIOD) {
		flags |= HAL_TXDESC_INTREQ;
		txq->axq_intrcnt = 0;
	}

	/*
	 * Formulate first tx descriptor with tx controls.
	 */
	ATH_HAL_SETUPTXDESC(ah, ds,
	    pktlen,			/* packet length */
	    hdrlen,			/* header length */
	    atype,			/* Atheros packet type */
	    MIN(in->in_txpower, 60),	/* txpower */
	    txrate, try0,		/* series 0 rate/tries */
	    keyix,			/* key cache index */
	    an->an_tx_antenna,		/* antenna mode */
	    flags,			/* flags */
	    ctsrate,			/* rts/cts rate */
	    ctsduration);		/* rts/cts duration */
	bf->bf_flags = flags;

	ATH_DEBUG((ATH_DBG_SEND, "ath: ath_xmit(): to %s totlen=%d "
	    "an->an_tx_rate1sp=%d tx_rate2sp=%d tx_rate3sp=%d "
	    "qnum=%d rix=%d sht=%d dur = %d\n",
	    ieee80211_macaddr_sprintf(wh->i_addr1), mbslen, an->an_tx_rate1sp,
	    an->an_tx_rate2sp, an->an_tx_rate3sp,
	    txq->axq_qnum, rix, shortPreamble, *(uint16_t *)wh->i_dur));

	/*
	 * Setup the multi-rate retry state only when we're
	 * going to use it.  This assumes ath_hal_setuptxdesc
	 * initializes the descriptors (so we don't have to)
	 * when the hardware supports multi-rate retry and
	 * we don't use it.
	 */
	if (try0 != ATH_TXMAXTRY)
		ATH_HAL_SETUPXTXDESC(ah, ds,
		    an->an_tx_rate1sp, 2,	/* series 1 */
		    an->an_tx_rate2sp, 2,	/* series 2 */
		    an->an_tx_rate3sp, 2);	/* series 3 */

	ds->ds_link = 0;
	ds->ds_data = bf->bf_dma.cookie.dmac_address;
	ATH_HAL_FILLTXDESC(ah, ds,
	    mbslen,		/* segment length */
	    AH_TRUE,		/* first segment */
	    AH_TRUE,		/* last segment */
	    ds);		/* first descriptor */

	ATH_DMA_SYNC(bf->bf_dma, DDI_DMA_SYNC_FORDEV);

	mutex_enter(&txq->axq_lock);
	list_insert_tail(&txq->axq_list, bf);
	if (txq->axq_link == NULL) {
		ATH_HAL_PUTTXBUF(ah, txq->axq_qnum, bf->bf_daddr);
	} else {
		*txq->axq_link = bf->bf_daddr;
	}
	txq->axq_link = &ds->ds_link;
	mutex_exit(&txq->axq_lock);

	ATH_HAL_TXSTART(ah, txq->axq_qnum);

	ic->ic_stats.is_tx_frags++;
	ic->ic_stats.is_tx_bytes += pktlen;

	return (0);
}

/*
 * Transmit a management frame.  On failure we reclaim the skbuff.
 * Note that management frames come directly from the 802.11 layer
 * and do not honor the send queue flow control.  Need to investigate
 * using priority queueing so management frames can bypass data.
 */
static int
ath_xmit(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
{
	ath_t *asc = (ath_t *)ic;
	struct ath_hal *ah = asc->asc_ah;
	struct ieee80211_node *in = NULL;
	struct ath_buf *bf = NULL;
	struct ieee80211_frame *wh;
	int error = 0;

	ASSERT(mp->b_next == NULL);

	if (!ATH_IS_RUNNING(asc)) {
		if ((type & IEEE80211_FC0_TYPE_MASK) !=
		    IEEE80211_FC0_TYPE_DATA) {
			freemsg(mp);
		}
		return (ENXIO);
	}

	/* Grab a TX buffer */
	mutex_enter(&asc->asc_txbuflock);
	bf = list_head(&asc->asc_txbuf_list);
	if (bf != NULL)
		list_remove(&asc->asc_txbuf_list, bf);
	if (list_empty(&asc->asc_txbuf_list)) {
		ATH_DEBUG((ATH_DBG_SEND, "ath: ath_mgmt_send(): "
		    "stop queue\n"));
		asc->asc_stats.ast_tx_qstop++;
	}
	mutex_exit(&asc->asc_txbuflock);
	if (bf == NULL) {
		ATH_DEBUG((ATH_DBG_SEND, "ath: ath_mgmt_send(): discard, "
		    "no xmit buf\n"));
		ic->ic_stats.is_tx_nobuf++;
		if ((type & IEEE80211_FC0_TYPE_MASK) ==
		    IEEE80211_FC0_TYPE_DATA) {
			asc->asc_stats.ast_tx_nobuf++;
			mutex_enter(&asc->asc_resched_lock);
			asc->asc_resched_needed = B_TRUE;
			mutex_exit(&asc->asc_resched_lock);
		} else {
			asc->asc_stats.ast_tx_nobufmgt++;
			freemsg(mp);
		}
		return (ENOMEM);
	}

	wh = (struct ieee80211_frame *)mp->b_rptr;

	/* Locate node */
	in = ieee80211_find_txnode(ic,  wh->i_addr1);
	if (in == NULL) {
		error = EIO;
		goto bad;
	}

	in->in_inact = 0;
	switch (type & IEEE80211_FC0_TYPE_MASK) {
	case IEEE80211_FC0_TYPE_DATA:
		(void) ieee80211_encap(ic, mp, in);
		break;
	default:
		if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
		    IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
			/* fill time stamp */
			uint64_t tsf;
			uint32_t *tstamp;

			tsf = ATH_HAL_GETTSF64(ah);
			/* adjust 100us delay to xmit */
			tsf += 100;
			tstamp = (uint32_t *)&wh[1];
			tstamp[0] = LE_32(tsf & 0xffffffff);
			tstamp[1] = LE_32(tsf >> 32);
		}
		asc->asc_stats.ast_tx_mgmt++;
		break;
	}

	error = ath_tx_start(asc, in, bf, mp);
	if (error != 0) {
bad:
		ic->ic_stats.is_tx_failed++;
		if (bf != NULL) {
			mutex_enter(&asc->asc_txbuflock);
			list_insert_tail(&asc->asc_txbuf_list, bf);
			mutex_exit(&asc->asc_txbuflock);
		}
	}
	if (in != NULL)
		ieee80211_free_node(in);
	if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA ||
	    error == 0) {
		freemsg(mp);
	}

	return (error);
}

static mblk_t *
ath_m_tx(void *arg, mblk_t *mp)
{
	ath_t *asc = arg;
	ieee80211com_t *ic = (ieee80211com_t *)asc;
	mblk_t *next;
	int error = 0;

	/*
	 * No data frames go out unless we're associated; this
	 * should not happen as the 802.11 layer does not enable
	 * the xmit queue until we enter the RUN state.
	 */
	if (ic->ic_state != IEEE80211_S_RUN) {
		ATH_DEBUG((ATH_DBG_SEND, "ath: ath_m_tx(): "
		    "discard, state %u\n", ic->ic_state));
		asc->asc_stats.ast_tx_discard++;
		freemsgchain(mp);
		return (NULL);
	}

	while (mp != NULL) {
		next = mp->b_next;
		mp->b_next = NULL;
		error = ath_xmit(ic, mp, IEEE80211_FC0_TYPE_DATA);
		if (error != 0) {
			mp->b_next = next;
			if (error == ENOMEM) {
				break;
			} else {
				freemsgchain(mp);	/* CR6501759 issues */
				return (NULL);
			}
		}
		mp = next;
	}

	return (mp);

}

static int
ath_tx_processq(ath_t *asc, struct ath_txq *txq)
{
	ieee80211com_t *ic = (ieee80211com_t *)asc;
	struct ath_hal *ah = asc->asc_ah;
	struct ath_buf *bf;
	struct ath_desc *ds;
	struct ieee80211_node *in;
	int32_t sr, lr, nacked = 0;
	HAL_STATUS status;
	struct ath_node *an;

	for (;;) {
		mutex_enter(&txq->axq_lock);
		bf = list_head(&txq->axq_list);
		if (bf == NULL) {
			txq->axq_link = NULL;
			mutex_exit(&txq->axq_lock);
			break;
		}
		ds = bf->bf_desc;	/* last decriptor */
		status = ATH_HAL_TXPROCDESC(ah, ds);
#ifdef DEBUG
		ath_printtxbuf(bf, status == HAL_OK);
#endif
		if (status == HAL_EINPROGRESS) {
			mutex_exit(&txq->axq_lock);
			break;
		}
		list_remove(&txq->axq_list, bf);
		mutex_exit(&txq->axq_lock);
		in = bf->bf_in;
		if (in != NULL) {
			an = ATH_NODE(in);
			/* Successful transmition */
			if (ds->ds_txstat.ts_status == 0) {
				an->an_tx_ok++;
				an->an_tx_antenna =
				    ds->ds_txstat.ts_antenna;
				if (ds->ds_txstat.ts_rate &
				    HAL_TXSTAT_ALTRATE)
					asc->asc_stats.ast_tx_altrate++;
				asc->asc_stats.ast_tx_rssidelta =
				    ds->ds_txstat.ts_rssi -
				    asc->asc_stats.ast_tx_rssi;
				asc->asc_stats.ast_tx_rssi =
				    ds->ds_txstat.ts_rssi;
			} else {
				an->an_tx_err++;
				if (ds->ds_txstat.ts_status &
				    HAL_TXERR_XRETRY)
					asc->asc_stats.
					    ast_tx_xretries++;
				if (ds->ds_txstat.ts_status &
				    HAL_TXERR_FIFO)
					asc->asc_stats.ast_tx_fifoerr++;
				if (ds->ds_txstat.ts_status &
				    HAL_TXERR_FILT)
					asc->asc_stats.
					    ast_tx_filtered++;
				an->an_tx_antenna = 0;	/* invalidate */
			}
			sr = ds->ds_txstat.ts_shortretry;
			lr = ds->ds_txstat.ts_longretry;
			asc->asc_stats.ast_tx_shortretry += sr;
			asc->asc_stats.ast_tx_longretry += lr;
			/*
			 * Hand the descriptor to the rate control algorithm.
			 */
			if ((ds->ds_txstat.ts_status & HAL_TXERR_FILT) == 0 &&
			    (bf->bf_flags & HAL_TXDESC_NOACK) == 0) {
				/*
				 * If frame was ack'd update the last rx time
				 * used to workaround phantom bmiss interrupts.
				 */
				if (ds->ds_txstat.ts_status == 0) {
					nacked++;
					an->an_tx_ok++;
				} else {
					an->an_tx_err++;
				}
				an->an_tx_retr += sr + lr;
			}
		}
		bf->bf_in = NULL;
		mutex_enter(&asc->asc_txbuflock);
		list_insert_tail(&asc->asc_txbuf_list, bf);
		mutex_exit(&asc->asc_txbuflock);
		/*
		 * Reschedule stalled outbound packets
		 */
		mutex_enter(&asc->asc_resched_lock);
		if (asc->asc_resched_needed) {
			asc->asc_resched_needed = B_FALSE;
			mac_tx_update(ic->ic_mach);
		}
		mutex_exit(&asc->asc_resched_lock);
	}
	return (nacked);
}


static void
ath_tx_handler(ath_t *asc)
{
	int i;

	/*
	 * Process each active queue.
	 */
	for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
		if (ATH_TXQ_SETUP(asc, i)) {
			(void) ath_tx_processq(asc, &asc->asc_txq[i]);
		}
	}
}

static struct ieee80211_node *
ath_node_alloc(ieee80211com_t *ic)
{
	struct ath_node *an;
	ath_t *asc = (ath_t *)ic;

	an = kmem_zalloc(sizeof (struct ath_node), KM_SLEEP);
	ath_rate_update(asc, &an->an_node, 0);
	return (&an->an_node);
}

static void
ath_node_free(struct ieee80211_node *in)
{
	ieee80211com_t *ic = in->in_ic;
	ath_t *asc = (ath_t *)ic;
	struct ath_buf *bf;
	struct ath_txq *txq;
	int32_t i;

	for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
		if (ATH_TXQ_SETUP(asc, i)) {
			txq = &asc->asc_txq[i];
			mutex_enter(&txq->axq_lock);
			bf = list_head(&txq->axq_list);
			while (bf != NULL) {
				if (bf->bf_in == in) {
					bf->bf_in = NULL;
				}
				bf = list_next(&txq->axq_list, bf);
			}
			mutex_exit(&txq->axq_lock);
		}
	}
	ic->ic_node_cleanup(in);
	if (in->in_wpa_ie != NULL)
		ieee80211_free(in->in_wpa_ie);
	kmem_free(in, sizeof (struct ath_node));
}

static void
ath_next_scan(void *arg)
{
	ieee80211com_t *ic = arg;
	ath_t *asc = (ath_t *)ic;

	asc->asc_scan_timer = 0;
	if (ic->ic_state == IEEE80211_S_SCAN) {
		asc->asc_scan_timer = timeout(ath_next_scan, (void *)asc,
		    drv_usectohz(ath_dwelltime * 1000));
		ieee80211_next_scan(ic);
	}
}

static void
ath_stop_scantimer(ath_t *asc)
{
	timeout_id_t tmp_id = 0;

	while ((asc->asc_scan_timer != 0) && (tmp_id != asc->asc_scan_timer)) {
		tmp_id = asc->asc_scan_timer;
		(void) untimeout(tmp_id);
	}
	asc->asc_scan_timer = 0;
}

static int32_t
ath_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
{
	ath_t *asc = (ath_t *)ic;
	struct ath_hal *ah = asc->asc_ah;
	struct ieee80211_node *in;
	int32_t i, error;
	uint8_t *bssid;
	uint32_t rfilt;
	enum ieee80211_state ostate;

	static const HAL_LED_STATE leds[] = {
	    HAL_LED_INIT,	/* IEEE80211_S_INIT */
	    HAL_LED_SCAN,	/* IEEE80211_S_SCAN */
	    HAL_LED_AUTH,	/* IEEE80211_S_AUTH */
	    HAL_LED_ASSOC, 	/* IEEE80211_S_ASSOC */
	    HAL_LED_RUN, 	/* IEEE80211_S_RUN */
	};
	if (!ATH_IS_RUNNING(asc))
		return (0);

	ostate = ic->ic_state;
	if (nstate != IEEE80211_S_SCAN)
		ath_stop_scantimer(asc);

	ATH_LOCK(asc);
	ATH_HAL_SETLEDSTATE(ah, leds[nstate]);	/* set LED */

	if (nstate == IEEE80211_S_INIT) {
		asc->asc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
		ATH_HAL_INTRSET(ah, asc->asc_imask &~ HAL_INT_GLOBAL);
		ATH_UNLOCK(asc);
		goto done;
	}
	in = ic->ic_bss;
	error = ath_chan_set(asc, ic->ic_curchan);
	if (error != 0) {
		if (nstate != IEEE80211_S_SCAN) {
			ATH_UNLOCK(asc);
			ieee80211_reset_chan(ic);
			goto bad;
		}
	}

	rfilt = ath_calcrxfilter(asc);
	if (nstate == IEEE80211_S_SCAN)
		bssid = ic->ic_macaddr;
	else
		bssid = in->in_bssid;
	ATH_HAL_SETRXFILTER(ah, rfilt);

	if (nstate == IEEE80211_S_RUN && ic->ic_opmode != IEEE80211_M_IBSS)
		ATH_HAL_SETASSOCID(ah, bssid, in->in_associd);
	else
		ATH_HAL_SETASSOCID(ah, bssid, 0);
	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
			if (ATH_HAL_KEYISVALID(ah, i))
				ATH_HAL_KEYSETMAC(ah, i, bssid);
		}
	}

	if ((nstate == IEEE80211_S_RUN) &&
	    (ostate != IEEE80211_S_RUN)) {
		/* Configure the beacon and sleep timers. */
		ath_beacon_config(asc);
	} else {
		asc->asc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
		ATH_HAL_INTRSET(ah, asc->asc_imask);
	}
	/*
	 * Reset the rate control state.
	 */
	ath_rate_ctl_reset(asc, nstate);

	if (nstate == IEEE80211_S_RUN && (ostate != IEEE80211_S_RUN)) {
		nvlist_t *attr_list = NULL;
		sysevent_id_t eid;
		int32_t err = 0;
		char *str_name = "ATH";
		char str_value[256] = {0};

		ATH_DEBUG((ATH_DBG_80211, "ath: ath new state(RUN): "
		    "ic_flags=0x%08x iv=%d"
		    " bssid=%s capinfo=0x%04x chan=%d\n",
		    ic->ic_flags,
		    in->in_intval,
		    ieee80211_macaddr_sprintf(in->in_bssid),
		    in->in_capinfo,
		    ieee80211_chan2ieee(ic, in->in_chan)));

		(void) sprintf(str_value, "%s%s%d", "-i ",
		    ddi_driver_name(asc->asc_dev),
		    ddi_get_instance(asc->asc_dev));
		if (nvlist_alloc(&attr_list,
		    NV_UNIQUE_NAME_TYPE, KM_SLEEP) == 0) {
			err = nvlist_add_string(attr_list,
			    str_name, str_value);
			if (err != DDI_SUCCESS)
				ATH_DEBUG((ATH_DBG_80211, "ath: "
				    "ath_new_state: error log event\n"));
			err = ddi_log_sysevent(asc->asc_dev,
			    DDI_VENDOR_SUNW, "class",
			    "subclass", attr_list,
			    &eid, DDI_NOSLEEP);
			if (err != DDI_SUCCESS)
				ATH_DEBUG((ATH_DBG_80211, "ath: "
				    "ath_new_state(): error log event\n"));
			nvlist_free(attr_list);
		}
	}

	ATH_UNLOCK(asc);
done:
	/*
	 * Invoke the parent method to complete the work.
	 */
	error = asc->asc_newstate(ic, nstate, arg);
	/*
	 * Finally, start any timers.
	 */
	if (nstate == IEEE80211_S_RUN) {
		ieee80211_start_watchdog(ic, 1);
	} else if ((nstate == IEEE80211_S_SCAN) && (ostate != nstate)) {
		/* start ap/neighbor scan timer */
		ASSERT(asc->asc_scan_timer == 0);
		asc->asc_scan_timer = timeout(ath_next_scan, (void *)asc,
		    drv_usectohz(ath_dwelltime * 1000));
	}
bad:
	return (error);
}

/*
 * Periodically recalibrate the PHY to account
 * for temperature/environment changes.
 */
static void
ath_calibrate(ath_t *asc)
{
	struct ath_hal *ah = asc->asc_ah;
	HAL_BOOL iqcaldone;

	asc->asc_stats.ast_per_cal++;

	if (ATH_HAL_GETRFGAIN(ah) == HAL_RFGAIN_NEED_CHANGE) {
		/*
		 * Rfgain is out of bounds, reset the chip
		 * to load new gain values.
		 */
		ATH_DEBUG((ATH_DBG_HAL, "ath: ath_calibrate(): "
		    "Need change RFgain\n"));
		asc->asc_stats.ast_per_rfgain++;
		(void) ath_reset(&asc->asc_isc);
	}
	if (!ATH_HAL_CALIBRATE(ah, &asc->asc_curchan, &iqcaldone)) {
		ATH_DEBUG((ATH_DBG_HAL, "ath: ath_calibrate(): "
		    "calibration of channel %u failed\n",
		    asc->asc_curchan.channel));
		asc->asc_stats.ast_per_calfail++;
	}
}

static void
ath_watchdog(void *arg)
{
	ath_t *asc = arg;
	ieee80211com_t *ic = &asc->asc_isc;
	int ntimer = 0;

	ATH_LOCK(asc);
	ic->ic_watchdog_timer = 0;
	if (!ATH_IS_RUNNING(asc)) {
		ATH_UNLOCK(asc);
		return;
	}

	if (ic->ic_state == IEEE80211_S_RUN) {
		/* periodic recalibration */
		ath_calibrate(asc);

		/*
		 * Start the background rate control thread if we
		 * are not configured to use a fixed xmit rate.
		 */
		if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
			asc->asc_stats.ast_rate_calls ++;
			if (ic->ic_opmode == IEEE80211_M_STA)
				ath_rate_ctl(ic, ic->ic_bss);
			else
				ieee80211_iterate_nodes(&ic->ic_sta,
				    ath_rate_cb, asc);
		}

		ntimer = 1;
	}
	ATH_UNLOCK(asc);

	ieee80211_watchdog(ic);
	if (ntimer != 0)
		ieee80211_start_watchdog(ic, ntimer);
}

static uint_t
ath_intr(caddr_t arg)
{
	ath_t *asc = (ath_t *)arg;
	struct ath_hal *ah = asc->asc_ah;
	HAL_INT status;
	ieee80211com_t *ic = (ieee80211com_t *)asc;

	ATH_LOCK(asc);

	if (!ATH_IS_RUNNING(asc)) {
		/*
		 * The hardware is not ready/present, don't touch anything.
		 * Note this can happen early on if the IRQ is shared.
		 */
		ATH_UNLOCK(asc);
		return (DDI_INTR_UNCLAIMED);
	}

	if (!ATH_HAL_INTRPEND(ah)) {	/* shared irq, not for us */
		ATH_UNLOCK(asc);
		return (DDI_INTR_UNCLAIMED);
	}

	ATH_HAL_GETISR(ah, &status);
	status &= asc->asc_imask;
	if (status & HAL_INT_FATAL) {
		asc->asc_stats.ast_hardware++;
		goto reset;
	} else if (status & HAL_INT_RXORN) {
		asc->asc_stats.ast_rxorn++;
		goto reset;
	} else {
		if (status & HAL_INT_RXEOL) {
			asc->asc_stats.ast_rxeol++;
			asc->asc_rxlink = NULL;
		}
		if (status & HAL_INT_TXURN) {
			asc->asc_stats.ast_txurn++;
			ATH_HAL_UPDATETXTRIGLEVEL(ah, AH_TRUE);
		}

		if (status & HAL_INT_RX) {
			asc->asc_rx_pend = 1;
			ddi_trigger_softintr(asc->asc_softint_id);
		}
		if (status & HAL_INT_TX) {
			ath_tx_handler(asc);
		}
		ATH_UNLOCK(asc);

		if (status & HAL_INT_SWBA) {
			/* This will occur only in Host-AP or Ad-Hoc mode */
			return (DDI_INTR_CLAIMED);
		}
		if (status & HAL_INT_BMISS) {
			if (ic->ic_state == IEEE80211_S_RUN) {
				(void) ieee80211_new_state(ic,
				    IEEE80211_S_ASSOC, -1);
			}
		}
	}

	return (DDI_INTR_CLAIMED);
reset:
	(void) ath_reset(ic);
	ATH_UNLOCK(asc);
	return (DDI_INTR_CLAIMED);
}

static uint_t
ath_softint_handler(caddr_t data)
{
	ath_t *asc = (ath_t *)data;

	/*
	 * Check if the soft interrupt is triggered by another
	 * driver at the same level.
	 */
	ATH_LOCK(asc);
	if (asc->asc_rx_pend) { /* Soft interrupt for this driver */
		asc->asc_rx_pend = 0;
		ATH_UNLOCK(asc);
		ath_rx_handler(asc);
		return (DDI_INTR_CLAIMED);
	}
	ATH_UNLOCK(asc);
	return (DDI_INTR_UNCLAIMED);
}

/*
 * following are gld callback routine
 * ath_gld_send, ath_gld_ioctl, ath_gld_gstat
 * are listed in other corresponding sections.
 * reset the hardware w/o losing operational state.  this is
 * basically a more efficient way of doing ath_gld_stop, ath_gld_start,
 * followed by state transitions to the current 802.11
 * operational state.  used to recover from errors rx overrun
 * and to reset the hardware when rf gain settings must be reset.
 */

static void
ath_stop_locked(ath_t *asc)
{
	ieee80211com_t *ic = (ieee80211com_t *)asc;
	struct ath_hal *ah = asc->asc_ah;

	ATH_LOCK_ASSERT(asc);
	if (!asc->asc_isrunning)
		return;

	/*
	 * Shutdown the hardware and driver:
	 *    reset 802.11 state machine
	 *    turn off timers
	 *    disable interrupts
	 *    turn off the radio
	 *    clear transmit machinery
	 *    clear receive machinery
	 *    drain and release tx queues
	 *    reclaim beacon resources
	 *    power down hardware
	 *
	 * Note that some of this work is not possible if the
	 * hardware is gone (invalid).
	 */
	ATH_UNLOCK(asc);
	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
	ieee80211_stop_watchdog(ic);
	ATH_LOCK(asc);
	ATH_HAL_INTRSET(ah, 0);
	ath_draintxq(asc);
	if (!asc->asc_invalid) {
		ath_stoprecv(asc);
		ATH_HAL_PHYDISABLE(ah);
	} else {
		asc->asc_rxlink = NULL;
	}
	asc->asc_isrunning = 0;
}

static void
ath_m_stop(void *arg)
{
	ath_t *asc = arg;
	struct ath_hal *ah = asc->asc_ah;

	ATH_LOCK(asc);
	ath_stop_locked(asc);
	ATH_HAL_SETPOWER(ah, HAL_PM_AWAKE);
	asc->asc_invalid = 1;
	ATH_UNLOCK(asc);
}

static int
ath_start_locked(ath_t *asc)
{
	ieee80211com_t *ic = (ieee80211com_t *)asc;
	struct ath_hal *ah = asc->asc_ah;
	HAL_STATUS status;

	ATH_LOCK_ASSERT(asc);

	/*
	 * The basic interface to setting the hardware in a good
	 * state is ``reset''.  On return the hardware is known to
	 * be powered up and with interrupts disabled.  This must
	 * be followed by initialization of the appropriate bits
	 * and then setup of the interrupt mask.
	 */
	asc->asc_curchan.channel = ic->ic_curchan->ich_freq;
	asc->asc_curchan.channelFlags = ath_chan2flags(ic, ic->ic_curchan);
	if (!ATH_HAL_RESET(ah, (HAL_OPMODE)ic->ic_opmode,
	    &asc->asc_curchan, AH_FALSE, &status)) {
		ATH_DEBUG((ATH_DBG_HAL, "ath: ath_m_start(): "
		    "reset hardware failed: '%s' (HAL status %u)\n",
		    ath_get_hal_status_desc(status), status));
		return (ENOTACTIVE);
	}

	(void) ath_startrecv(asc);

	/*
	 * Enable interrupts.
	 */
	asc->asc_imask = HAL_INT_RX | HAL_INT_TX
	    | HAL_INT_RXEOL | HAL_INT_RXORN
	    | HAL_INT_FATAL | HAL_INT_GLOBAL;
	ATH_HAL_INTRSET(ah, asc->asc_imask);

	/*
	 * The hardware should be ready to go now so it's safe
	 * to kick the 802.11 state machine as it's likely to
	 * immediately call back to us to send mgmt frames.
	 */
	ath_chan_change(asc, ic->ic_curchan);

	asc->asc_isrunning = 1;

	return (0);
}

int
ath_m_start(void *arg)
{
	ath_t *asc = arg;
	int err;

	ATH_LOCK(asc);
	/*
	 * Stop anything previously setup.  This is safe
	 * whether this is the first time through or not.
	 */
	ath_stop_locked(asc);

	if ((err = ath_start_locked(asc)) != 0) {
		ATH_UNLOCK(asc);
		return (err);
	}

	asc->asc_invalid = 0;
	ATH_UNLOCK(asc);

	return (0);
}


static int
ath_m_unicst(void *arg, const uint8_t *macaddr)
{
	ath_t *asc = arg;
	struct ath_hal *ah = asc->asc_ah;

	ATH_DEBUG((ATH_DBG_GLD, "ath: ath_gld_saddr(): "
	    "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
	    macaddr[0], macaddr[1], macaddr[2],
	    macaddr[3], macaddr[4], macaddr[5]));

	ATH_LOCK(asc);
	IEEE80211_ADDR_COPY(asc->asc_isc.ic_macaddr, macaddr);
	ATH_HAL_SETMAC(ah, asc->asc_isc.ic_macaddr);

	(void) ath_reset(&asc->asc_isc);
	ATH_UNLOCK(asc);
	return (0);
}

static int
ath_m_promisc(void *arg, boolean_t on)
{
	ath_t *asc = arg;
	struct ath_hal *ah = asc->asc_ah;
	uint32_t rfilt;

	ATH_LOCK(asc);
	rfilt = ATH_HAL_GETRXFILTER(ah);
	if (on)
		rfilt |= HAL_RX_FILTER_PROM;
	else
		rfilt &= ~HAL_RX_FILTER_PROM;
	asc->asc_promisc = on;
	ATH_HAL_SETRXFILTER(ah, rfilt);
	ATH_UNLOCK(asc);

	return (0);
}

static int
ath_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
{
	ath_t *asc = arg;
	struct ath_hal *ah = asc->asc_ah;
	uint32_t val, index, bit;
	uint8_t pos;
	uint32_t *mfilt = asc->asc_mcast_hash;

	ATH_LOCK(asc);
	/* calculate XOR of eight 6bit values */
	val = ATH_LE_READ_4(mca + 0);
	pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
	val = ATH_LE_READ_4(mca + 3);
	pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
	pos &= 0x3f;
	index = pos / 32;
	bit = 1 << (pos % 32);

	if (add) {	/* enable multicast */
		asc->asc_mcast_refs[pos]++;
		mfilt[index] |= bit;
	} else {	/* disable multicast */
		if (--asc->asc_mcast_refs[pos] == 0)
			mfilt[index] &= ~bit;
	}
	ATH_HAL_SETMCASTFILTER(ah, mfilt[0], mfilt[1]);

	ATH_UNLOCK(asc);
	return (0);
}

static void
ath_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
{
	ath_t *asc = arg;
	int32_t err;

	err = ieee80211_ioctl(&asc->asc_isc, wq, mp);
	ATH_LOCK(asc);
	if (err == ENETRESET) {
		if (ATH_IS_RUNNING(asc)) {
			ATH_UNLOCK(asc);
			(void) ath_m_start(asc);
			(void) ieee80211_new_state(&asc->asc_isc,
			    IEEE80211_S_SCAN, -1);
			ATH_LOCK(asc);
		}
	}
	ATH_UNLOCK(asc);
}

static int
ath_m_stat(void *arg, uint_t stat, uint64_t *val)
{
	ath_t *asc = arg;
	ieee80211com_t *ic = (ieee80211com_t *)asc;
	struct ieee80211_node *in = ic->ic_bss;
	struct ieee80211_rateset *rs = &in->in_rates;

	ATH_LOCK(asc);
	switch (stat) {
	case MAC_STAT_IFSPEED:
		*val = (rs->ir_rates[in->in_txrate] & IEEE80211_RATE_VAL) / 2 *
		    1000000ull;
		break;
	case MAC_STAT_NOXMTBUF:
		*val = asc->asc_stats.ast_tx_nobuf +
		    asc->asc_stats.ast_tx_nobufmgt;
		break;
	case MAC_STAT_IERRORS:
		*val = asc->asc_stats.ast_rx_tooshort;
		break;
	case MAC_STAT_RBYTES:
		*val = ic->ic_stats.is_rx_bytes;
		break;
	case MAC_STAT_IPACKETS:
		*val = ic->ic_stats.is_rx_frags;
		break;
	case MAC_STAT_OBYTES:
		*val = ic->ic_stats.is_tx_bytes;
		break;
	case MAC_STAT_OPACKETS:
		*val = ic->ic_stats.is_tx_frags;
		break;
	case MAC_STAT_OERRORS:
	case WIFI_STAT_TX_FAILED:
		*val = asc->asc_stats.ast_tx_fifoerr +
		    asc->asc_stats.ast_tx_xretries +
		    asc->asc_stats.ast_tx_discard;
		break;
	case WIFI_STAT_TX_RETRANS:
		*val = asc->asc_stats.ast_tx_xretries;
		break;
	case WIFI_STAT_FCS_ERRORS:
		*val = asc->asc_stats.ast_rx_crcerr;
		break;
	case WIFI_STAT_WEP_ERRORS:
		*val = asc->asc_stats.ast_rx_badcrypt;
		break;
	case WIFI_STAT_TX_FRAGS:
	case WIFI_STAT_MCAST_TX:
	case WIFI_STAT_RTS_SUCCESS:
	case WIFI_STAT_RTS_FAILURE:
	case WIFI_STAT_ACK_FAILURE:
	case WIFI_STAT_RX_FRAGS:
	case WIFI_STAT_MCAST_RX:
	case WIFI_STAT_RX_DUPS:
		ATH_UNLOCK(asc);
		return (ieee80211_stat(ic, stat, val));
	default:
		ATH_UNLOCK(asc);
		return (ENOTSUP);
	}
	ATH_UNLOCK(asc);

	return (0);
}

static int
ath_pci_setup(ath_t *asc)
{
	uint16_t command;

	/*
	 * Enable memory mapping and bus mastering
	 */
	ASSERT(asc != NULL);
	command = pci_config_get16(asc->asc_cfg_handle, PCI_CONF_COMM);
	command |= PCI_COMM_MAE | PCI_COMM_ME;
	pci_config_put16(asc->asc_cfg_handle, PCI_CONF_COMM, command);
	command = pci_config_get16(asc->asc_cfg_handle, PCI_CONF_COMM);
	if ((command & PCI_COMM_MAE) == 0) {
		ath_problem("ath: ath_pci_setup(): "
		    "failed to enable memory mapping\n");
		return (EIO);
	}
	if ((command & PCI_COMM_ME) == 0) {
		ath_problem("ath: ath_pci_setup(): "
		    "failed to enable bus mastering\n");
		return (EIO);
	}
	ATH_DEBUG((ATH_DBG_INIT, "ath: ath_pci_setup(): "
	    "set command reg to 0x%x \n", command));

	return (0);
}

static int
ath_resume(dev_info_t *devinfo)
{
	ath_t *asc;
	int ret = DDI_SUCCESS;

	asc = ddi_get_soft_state(ath_soft_state_p, ddi_get_instance(devinfo));
	if (asc == NULL) {
		ATH_DEBUG((ATH_DBG_SUSPEND, "ath: ath_resume(): "
		    "failed to get soft state\n"));
		return (DDI_FAILURE);
	}

	ATH_LOCK(asc);
	/*
	 * Set up config space command register(s). Refuse
	 * to resume on failure.
	 */
	if (ath_pci_setup(asc) != 0) {
		ATH_DEBUG((ATH_DBG_SUSPEND, "ath: ath_resume(): "
		    "ath_pci_setup() failed\n"));
		ATH_UNLOCK(asc);
		return (DDI_FAILURE);
	}

	if (!asc->asc_invalid)
		ret = ath_start_locked(asc);
	ATH_UNLOCK(asc);

	return (ret);
}

static int
ath_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
{
	ath_t *asc;
	ieee80211com_t *ic;
	struct ath_hal *ah;
	uint8_t csz;
	HAL_STATUS status;
	caddr_t regs;
	uint32_t i, val;
	uint16_t vendor_id, device_id;
	const char *athname;
	int32_t ath_countrycode = CTRY_DEFAULT;	/* country code */
	int32_t err, ath_regdomain = 0; /* regulatory domain */
	char strbuf[32];
	int instance;
	wifi_data_t wd = { 0 };
	mac_register_t *macp;

	switch (cmd) {
	case DDI_ATTACH:
		break;

	case DDI_RESUME:
		return (ath_resume(devinfo));

	default:
		return (DDI_FAILURE);
	}

	instance = ddi_get_instance(devinfo);
	if (ddi_soft_state_zalloc(ath_soft_state_p, instance) != DDI_SUCCESS) {
		ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
		    "Unable to alloc softstate\n"));
		return (DDI_FAILURE);
	}

	asc = ddi_get_soft_state(ath_soft_state_p, ddi_get_instance(devinfo));
	ic = (ieee80211com_t *)asc;
	asc->asc_dev = devinfo;

	mutex_init(&asc->asc_genlock, NULL, MUTEX_DRIVER, NULL);
	mutex_init(&asc->asc_txbuflock, NULL, MUTEX_DRIVER, NULL);
	mutex_init(&asc->asc_rxbuflock, NULL, MUTEX_DRIVER, NULL);
	mutex_init(&asc->asc_resched_lock, NULL, MUTEX_DRIVER, NULL);

	err = pci_config_setup(devinfo, &asc->asc_cfg_handle);
	if (err != DDI_SUCCESS) {
		ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
		    "pci_config_setup() failed"));
		goto attach_fail0;
	}

	if (ath_pci_setup(asc) != 0)
		goto attach_fail1;

	/*
	 * Cache line size is used to size and align various
	 * structures used to communicate with the hardware.
	 */
	csz = pci_config_get8(asc->asc_cfg_handle, PCI_CONF_CACHE_LINESZ);
	if (csz == 0) {
		/*
		 * We must have this setup properly for rx buffer
		 * DMA to work so force a reasonable value here if it
		 * comes up zero.
		 */
		csz = ATH_DEF_CACHE_BYTES / sizeof (uint32_t);
		pci_config_put8(asc->asc_cfg_handle, PCI_CONF_CACHE_LINESZ,
		    csz);
	}
	asc->asc_cachelsz = csz << 2;
	vendor_id = pci_config_get16(asc->asc_cfg_handle, PCI_CONF_VENID);
	device_id = pci_config_get16(asc->asc_cfg_handle, PCI_CONF_DEVID);
	ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): vendor 0x%x, "
	    "device id 0x%x, cache size %d\n", vendor_id, device_id, csz));

	athname = ath_hal_probe(vendor_id, device_id);
	ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): athname: %s\n",
	    athname ? athname : "Atheros ???"));

	pci_config_put8(asc->asc_cfg_handle, PCI_CONF_LATENCY_TIMER, 0xa8);
	val = pci_config_get32(asc->asc_cfg_handle, 0x40);
	if ((val & 0x0000ff00) != 0)
		pci_config_put32(asc->asc_cfg_handle, 0x40, val & 0xffff00ff);

	err = ddi_regs_map_setup(devinfo, 1,
	    &regs, 0, 0, &ath_reg_accattr, &asc->asc_io_handle);
	ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
	    "regs map1 = %x err=%d\n", regs, err));
	if (err != DDI_SUCCESS) {
		ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
		    "ddi_regs_map_setup() failed"));
		goto attach_fail1;
	}

	ah = ath_hal_attach(device_id, asc, 0, regs, &status);
	if (ah == NULL) {
		ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
		    "unable to attach hw: '%s' (HAL status %u)\n",
		    ath_get_hal_status_desc(status), status));
		goto attach_fail2;
	}
	ATH_HAL_INTRSET(ah, 0);
	asc->asc_ah = ah;

	if (ah->ah_abi != HAL_ABI_VERSION) {
		ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
		    "HAL ABI mismatch detected (0x%x != 0x%x)\n",
		    ah->ah_abi, HAL_ABI_VERSION));
		goto attach_fail3;
	}

	ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
	    "HAL ABI version 0x%x\n", ah->ah_abi));
	ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
	    "HAL mac version %d.%d, phy version %d.%d\n",
	    ah->ah_macVersion, ah->ah_macRev,
	    ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf));
	if (ah->ah_analog5GhzRev)
		ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
		    "HAL 5ghz radio version %d.%d\n",
		    ah->ah_analog5GhzRev >> 4,
		    ah->ah_analog5GhzRev & 0xf));
	if (ah->ah_analog2GhzRev)
		ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
		    "HAL 2ghz radio version %d.%d\n",
		    ah->ah_analog2GhzRev >> 4,
		    ah->ah_analog2GhzRev & 0xf));

	/*
	 * Check if the MAC has multi-rate retry support.
	 * We do this by trying to setup a fake extended
	 * descriptor.  MAC's that don't have support will
	 * return false w/o doing anything.  MAC's that do
	 * support it will return true w/o doing anything.
	 */
	asc->asc_mrretry = ATH_HAL_SETUPXTXDESC(ah, NULL, 0, 0, 0, 0, 0, 0);
	ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
	    "multi rate retry support=%x\n",
	    asc->asc_mrretry));

	/*
	 * Get the hardware key cache size.
	 */
	asc->asc_keymax = ATH_HAL_KEYCACHESIZE(ah);
	if (asc->asc_keymax > sizeof (asc->asc_keymap) * NBBY) {
		ATH_DEBUG((ATH_DBG_ATTACH, "ath_attach:"
		    " Warning, using only %u entries in %u key cache\n",
		    sizeof (asc->asc_keymap) * NBBY, asc->asc_keymax));
		asc->asc_keymax = sizeof (asc->asc_keymap) * NBBY;
	}
	/*
	 * Reset the key cache since some parts do not
	 * reset the contents on initial power up.
	 */
	for (i = 0; i < asc->asc_keymax; i++)
		ATH_HAL_KEYRESET(ah, i);

	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
		setbit(asc->asc_keymap, i);
		setbit(asc->asc_keymap, i+32);
		setbit(asc->asc_keymap, i+64);
		setbit(asc->asc_keymap, i+32+64);
	}

	ATH_HAL_GETREGDOMAIN(ah, (uint32_t *)&ath_regdomain);
	ATH_HAL_GETCOUNTRYCODE(ah, &ath_countrycode);
	/*
	 * Collect the channel list using the default country
	 * code and including outdoor channels.  The 802.11 layer
	 * is resposible for filtering this list to a set of
	 * channels that it considers ok to use.
	 */
	asc->asc_have11g = 0;

	/* enable outdoor use, enable extended channels */
	err = ath_getchannels(asc, ath_countrycode, AH_FALSE, AH_TRUE);
	if (err != 0)
		goto attach_fail3;

	/*
	 * Setup rate tables for all potential media types.
	 */
	ath_rate_setup(asc, IEEE80211_MODE_11A);
	ath_rate_setup(asc, IEEE80211_MODE_11B);
	ath_rate_setup(asc, IEEE80211_MODE_11G);
	ath_rate_setup(asc, IEEE80211_MODE_TURBO_A);

	/* Setup here so ath_rate_update is happy */
	ath_setcurmode(asc, IEEE80211_MODE_11A);

	err = ath_desc_alloc(devinfo, asc);
	if (err != DDI_SUCCESS) {
		ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
		    "failed to allocate descriptors: %d\n", err));
		goto attach_fail3;
	}

	/* Setup transmit queues in the HAL */
	if (ath_txq_setup(asc))
		goto attach_fail4;

	ATH_HAL_GETMAC(ah, ic->ic_macaddr);

	/*
	 * Initialize pointers to device specific functions which
	 * will be used by the generic layer.
	 */
	/* 11g support is identified when we fetch the channel set */
	if (asc->asc_have11g)
		ic->ic_caps |= IEEE80211_C_SHPREAMBLE |
		    IEEE80211_C_SHSLOT;		/* short slot time */
	/*
	 * Query the hal to figure out h/w crypto support.
	 */
	if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_WEP))
		ic->ic_caps |= IEEE80211_C_WEP;
	if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_AES_OCB))
		ic->ic_caps |= IEEE80211_C_AES;
	if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_AES_CCM)) {
		ATH_DEBUG((ATH_DBG_ATTACH, "Atheros support H/W CCMP\n"));
		ic->ic_caps |= IEEE80211_C_AES_CCM;
	}
	if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_CKIP))
		ic->ic_caps |= IEEE80211_C_CKIP;
	if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_TKIP)) {
		ATH_DEBUG((ATH_DBG_ATTACH, "Atheros support H/W TKIP\n"));
		ic->ic_caps |= IEEE80211_C_TKIP;
		/*
		 * Check if h/w does the MIC and/or whether the
		 * separate key cache entries are required to
		 * handle both tx+rx MIC keys.
		 */
		if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_MIC)) {
			ATH_DEBUG((ATH_DBG_ATTACH, "Support H/W TKIP MIC\n"));
			ic->ic_caps |= IEEE80211_C_TKIPMIC;
		}
		if (ATH_HAL_TKIPSPLIT(ah))
			asc->asc_splitmic = 1;
	}
	ic->ic_caps |= IEEE80211_C_WPA;	/* Support WPA/WPA2 */

	asc->asc_hasclrkey = ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_CLR);
	ic->ic_phytype = IEEE80211_T_OFDM;
	ic->ic_opmode = IEEE80211_M_STA;
	ic->ic_state = IEEE80211_S_INIT;
	ic->ic_maxrssi = ATH_MAX_RSSI;
	ic->ic_set_shortslot = ath_set_shortslot;
	ic->ic_xmit = ath_xmit;
	ieee80211_attach(ic);

	/* different instance has different WPA door */
	(void) snprintf(ic->ic_wpadoor, MAX_IEEE80211STR, "%s_%s%d", WPA_DOOR,
	    ddi_driver_name(devinfo),
	    ddi_get_instance(devinfo));

	/* Override 80211 default routines */
	ic->ic_reset = ath_reset;
	asc->asc_newstate = ic->ic_newstate;
	ic->ic_newstate = ath_newstate;
	ic->ic_watchdog = ath_watchdog;
	ic->ic_node_alloc = ath_node_alloc;
	ic->ic_node_free = ath_node_free;
	ic->ic_crypto.cs_key_alloc = ath_key_alloc;
	ic->ic_crypto.cs_key_delete = ath_key_delete;
	ic->ic_crypto.cs_key_set = ath_key_set;
	ieee80211_media_init(ic);
	/*
	 * initialize default tx key
	 */
	ic->ic_def_txkey = 0;

	asc->asc_rx_pend = 0;
	ATH_HAL_INTRSET(ah, 0);
	err = ddi_add_softintr(devinfo, DDI_SOFTINT_LOW,
	    &asc->asc_softint_id, NULL, 0, ath_softint_handler, (caddr_t)asc);
	if (err != DDI_SUCCESS) {
		ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
		    "ddi_add_softintr() failed\n"));
		goto attach_fail5;
	}

	if (ddi_get_iblock_cookie(devinfo, 0, &asc->asc_iblock)
	    != DDI_SUCCESS) {
		ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
		    "Can not get iblock cookie for INT\n"));
		goto attach_fail6;
	}

	if (ddi_add_intr(devinfo, 0, NULL, NULL, ath_intr,
	    (caddr_t)asc) != DDI_SUCCESS) {
		ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
		    "Can not set intr for ATH driver\n"));
		goto attach_fail6;
	}

	/*
	 * Provide initial settings for the WiFi plugin; whenever this
	 * information changes, we need to call mac_plugindata_update()
	 */
	wd.wd_opmode = ic->ic_opmode;
	wd.wd_secalloc = WIFI_SEC_NONE;
	IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);

	if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
		ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
		    "MAC version mismatch\n"));
		goto attach_fail7;
	}

	macp->m_type_ident	= MAC_PLUGIN_IDENT_WIFI;
	macp->m_driver		= asc;
	macp->m_dip		= devinfo;
	macp->m_src_addr	= ic->ic_macaddr;
	macp->m_callbacks	= &ath_m_callbacks;
	macp->m_min_sdu		= 0;
	macp->m_max_sdu		= IEEE80211_MTU;
	macp->m_pdata		= &wd;
	macp->m_pdata_size	= sizeof (wd);

	err = mac_register(macp, &ic->ic_mach);
	mac_free(macp);
	if (err != 0) {
		ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
		    "mac_register err %x\n", err));
		goto attach_fail7;
	}

	/* Create minor node of type DDI_NT_NET_WIFI */
	(void) snprintf(strbuf, sizeof (strbuf), "%s%d",
	    ATH_NODENAME, instance);
	err = ddi_create_minor_node(devinfo, strbuf, S_IFCHR,
	    instance + 1, DDI_NT_NET_WIFI, 0);
	if (err != DDI_SUCCESS)
		ATH_DEBUG((ATH_DBG_ATTACH, "WARN: ath: ath_attach(): "
		    "Create minor node failed - %d\n", err));

	mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
	asc->asc_invalid = 1;
	asc->asc_isrunning = 0;
	asc->asc_promisc = B_FALSE;
	bzero(asc->asc_mcast_refs, sizeof (asc->asc_mcast_refs));
	bzero(asc->asc_mcast_hash, sizeof (asc->asc_mcast_hash));
	return (DDI_SUCCESS);
attach_fail7:
	ddi_remove_intr(devinfo, 0, asc->asc_iblock);
attach_fail6:
	ddi_remove_softintr(asc->asc_softint_id);
attach_fail5:
	(void) ieee80211_detach(ic);
attach_fail4:
	ath_desc_free(asc);
attach_fail3:
	ah->ah_detach(asc->asc_ah);
attach_fail2:
	ddi_regs_map_free(&asc->asc_io_handle);
attach_fail1:
	pci_config_teardown(&asc->asc_cfg_handle);
attach_fail0:
	asc->asc_invalid = 1;
	mutex_destroy(&asc->asc_txbuflock);
	for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
		if (ATH_TXQ_SETUP(asc, i)) {
			struct ath_txq *txq = &asc->asc_txq[i];
			mutex_destroy(&txq->axq_lock);
		}
	}
	mutex_destroy(&asc->asc_rxbuflock);
	mutex_destroy(&asc->asc_genlock);
	mutex_destroy(&asc->asc_resched_lock);
	ddi_soft_state_free(ath_soft_state_p, instance);

	return (DDI_FAILURE);
}

/*
 * Suspend transmit/receive for powerdown
 */
static int
ath_suspend(ath_t *asc)
{
	ATH_LOCK(asc);
	ath_stop_locked(asc);
	ATH_UNLOCK(asc);
	ATH_DEBUG((ATH_DBG_SUSPEND, "ath: suspended.\n"));

	return (DDI_SUCCESS);
}

static int32_t
ath_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
{
	ath_t *asc;

	asc = ddi_get_soft_state(ath_soft_state_p, ddi_get_instance(devinfo));
	ASSERT(asc != NULL);

	switch (cmd) {
	case DDI_DETACH:
		break;

	case DDI_SUSPEND:
		return (ath_suspend(asc));

	default:
		return (DDI_FAILURE);
	}

	ath_stop_scantimer(asc);

	/* disable interrupts */
	ATH_HAL_INTRSET(asc->asc_ah, 0);

	/*
	 * Unregister from the MAC layer subsystem
	 */
	if (mac_unregister(asc->asc_isc.ic_mach) != 0)
		return (DDI_FAILURE);

	/* free intterrupt resources */
	ddi_remove_intr(devinfo, 0, asc->asc_iblock);
	ddi_remove_softintr(asc->asc_softint_id);

	/*
	 * NB: the order of these is important:
	 * o call the 802.11 layer before detaching the hal to
	 *   insure callbacks into the driver to delete global
	 *   key cache entries can be handled
	 * o reclaim the tx queue data structures after calling
	 *   the 802.11 layer as we'll get called back to reclaim
	 *   node state and potentially want to use them
	 * o to cleanup the tx queues the hal is called, so detach
	 *   it last
	 */
	ieee80211_detach(&asc->asc_isc);
	ath_desc_free(asc);
	ath_txq_cleanup(asc);
	asc->asc_ah->ah_detach(asc->asc_ah);

	/* free io handle */
	ddi_regs_map_free(&asc->asc_io_handle);
	pci_config_teardown(&asc->asc_cfg_handle);

	/* destroy locks */
	mutex_destroy(&asc->asc_rxbuflock);
	mutex_destroy(&asc->asc_genlock);
	mutex_destroy(&asc->asc_resched_lock);

	ddi_remove_minor_node(devinfo, NULL);
	ddi_soft_state_free(ath_soft_state_p, ddi_get_instance(devinfo));

	return (DDI_SUCCESS);
}

DDI_DEFINE_STREAM_OPS(ath_dev_ops, nulldev, nulldev, ath_attach, ath_detach,
    nodev, NULL, D_MP, NULL);

static struct modldrv ath_modldrv = {
	&mod_driverops,		/* Type of module.  This one is a driver */
	"ath driver 1.3.1/HAL 0.9.17.2",	/* short description */
	&ath_dev_ops		/* driver specific ops */
};

static struct modlinkage modlinkage = {
	MODREV_1, (void *)&ath_modldrv, NULL
};


int
_info(struct modinfo *modinfop)
{
	return (mod_info(&modlinkage, modinfop));
}

int
_init(void)
{
	int status;

	status = ddi_soft_state_init(&ath_soft_state_p, sizeof (ath_t), 1);
	if (status != 0)
		return (status);

	mutex_init(&ath_loglock, NULL, MUTEX_DRIVER, NULL);
	ath_halfix_init();
	mac_init_ops(&ath_dev_ops, "ath");
	status = mod_install(&modlinkage);
	if (status != 0) {
		mac_fini_ops(&ath_dev_ops);
		ath_halfix_finit();
		mutex_destroy(&ath_loglock);
		ddi_soft_state_fini(&ath_soft_state_p);
	}

	return (status);
}

int
_fini(void)
{
	int status;

	status = mod_remove(&modlinkage);
	if (status == 0) {
		mac_fini_ops(&ath_dev_ops);
		ath_halfix_finit();
		mutex_destroy(&ath_loglock);
		ddi_soft_state_fini(&ath_soft_state_p);
	}
	return (status);
}