11000Sxc151355 /*
2*11878SVenu.Iyer@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
31000Sxc151355 * Use is subject to license terms.
41000Sxc151355 */
51000Sxc151355
61000Sxc151355 /*
71000Sxc151355 * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
81000Sxc151355 * All rights reserved.
91000Sxc151355 *
101000Sxc151355 * Redistribution and use in source and binary forms, with or without
111000Sxc151355 * modification, are permitted provided that the following conditions
121000Sxc151355 * are met:
131000Sxc151355 * 1. Redistributions of source code must retain the above copyright
141000Sxc151355 * notice, this list of conditions and the following disclaimer,
151000Sxc151355 * without modification.
161000Sxc151355 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
171000Sxc151355 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
181000Sxc151355 * redistribution must be conditioned upon including a substantially
191000Sxc151355 * similar Disclaimer requirement for further binary redistribution.
201000Sxc151355 * 3. Neither the names of the above-listed copyright holders nor the names
211000Sxc151355 * of any contributors may be used to endorse or promote products derived
221000Sxc151355 * from this software without specific prior written permission.
231000Sxc151355 *
241000Sxc151355 * NO WARRANTY
251000Sxc151355 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
261000Sxc151355 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
271000Sxc151355 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
281000Sxc151355 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
291000Sxc151355 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
301000Sxc151355 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
311000Sxc151355 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
321000Sxc151355 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
331000Sxc151355 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
341000Sxc151355 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
351000Sxc151355 * THE POSSIBILITY OF SUCH DAMAGES.
361000Sxc151355 *
371000Sxc151355 */
381000Sxc151355
391000Sxc151355 /*
401000Sxc151355 * Driver for the Atheros Wireless LAN controller.
411000Sxc151355 *
423147Sxc151355 * The Atheros driver calls into net80211 module for IEEE80211 protocol
433147Sxc151355 * management functionalities. The driver includes a LLD(Low Level Driver)
443147Sxc151355 * part to implement H/W related operations.
451000Sxc151355 * The following is the high level structure of ath driver.
461000Sxc151355 * (The arrows between modules indicate function call direction.)
471000Sxc151355 *
481000Sxc151355 *
493147Sxc151355 * |
503147Sxc151355 * | GLD thread
513147Sxc151355 * V
523147Sxc151355 * ================== =========================================
533147Sxc151355 * | | |[1] |
543147Sxc151355 * | | | GLDv3 Callback functions registered |
553147Sxc151355 * | Net80211 | ========================= by |
563147Sxc151355 * | module | | | driver |
573147Sxc151355 * | | V | |
583147Sxc151355 * | |======================== | |
593147Sxc151355 * | Functions exported by net80211 | | |
603147Sxc151355 * | | | |
613147Sxc151355 * ========================================== =================
623147Sxc151355 * | |
633147Sxc151355 * V |
643147Sxc151355 * +----------------------------------+ |
653147Sxc151355 * |[2] | |
663147Sxc151355 * | Net80211 Callback functions | |
673147Sxc151355 * | registered by LLD | |
683147Sxc151355 * +----------------------------------+ |
693147Sxc151355 * | |
703147Sxc151355 * V v
713147Sxc151355 * +-----------------------------------------------------------+
723147Sxc151355 * |[3] |
733147Sxc151355 * | LLD Internal functions |
743147Sxc151355 * | |
753147Sxc151355 * +-----------------------------------------------------------+
763147Sxc151355 * ^
773147Sxc151355 * | Software interrupt thread
783147Sxc151355 * |
791000Sxc151355 *
801000Sxc151355 * The short description of each module is as below:
813147Sxc151355 * Module 1: GLD callback functions, which are intercepting the calls from
823147Sxc151355 * GLD to LLD.
833147Sxc151355 * Module 2: Net80211 callback functions registered by LLD, which
843147Sxc151355 * calls into LLD for H/W related functions needed by net80211.
853147Sxc151355 * Module 3: LLD Internal functions, which are responsible for allocing
861000Sxc151355 * descriptor/buffer, handling interrupt and other H/W
871000Sxc151355 * operations.
881000Sxc151355 *
891000Sxc151355 * All functions are running in 3 types of thread:
901000Sxc151355 * 1. GLD callbacks threads, such as ioctl, intr, etc.
913147Sxc151355 * 2. Clock interruptt thread which is responsible for scan, rate control and
923147Sxc151355 * calibration.
931000Sxc151355 * 3. Software Interrupt thread originated in LLD.
941000Sxc151355 *
951000Sxc151355 * The lock strategy is as below:
961000Sxc151355 * There have 4 queues for tx, each queue has one asc_txqlock[i] to
971000Sxc151355 * prevent conflicts access to queue resource from different thread.
981000Sxc151355 *
991000Sxc151355 * All the transmit buffers are contained in asc_txbuf which are
1001000Sxc151355 * protected by asc_txbuflock.
1011000Sxc151355 *
1021000Sxc151355 * Each receive buffers are contained in asc_rxbuf which are protected
1031000Sxc151355 * by asc_rxbuflock.
1041000Sxc151355 *
1051000Sxc151355 * In ath struct, asc_genlock is a general lock, protecting most other
1061000Sxc151355 * operational data in ath_softc struct and HAL accesses.
1071000Sxc151355 * It is acquired by the interupt handler and most "mode-ctrl" routines.
1081000Sxc151355 *
1091000Sxc151355 * Any of the locks can be acquired singly, but where multiple
1101000Sxc151355 * locks are acquired, they *must* be in the order:
1113147Sxc151355 * asc_genlock >> asc_txqlock[i] >> asc_txbuflock >> asc_rxbuflock
1121000Sxc151355 */
1131000Sxc151355
1141000Sxc151355 #include <sys/param.h>
1151000Sxc151355 #include <sys/types.h>
1161000Sxc151355 #include <sys/signal.h>
1171000Sxc151355 #include <sys/stream.h>
1181000Sxc151355 #include <sys/termio.h>
1191000Sxc151355 #include <sys/errno.h>
1201000Sxc151355 #include <sys/file.h>
1211000Sxc151355 #include <sys/cmn_err.h>
1221000Sxc151355 #include <sys/stropts.h>
1231000Sxc151355 #include <sys/strsubr.h>
1241000Sxc151355 #include <sys/strtty.h>
1251000Sxc151355 #include <sys/kbio.h>
1261000Sxc151355 #include <sys/cred.h>
1271000Sxc151355 #include <sys/stat.h>
1281000Sxc151355 #include <sys/consdev.h>
1291000Sxc151355 #include <sys/kmem.h>
1301000Sxc151355 #include <sys/modctl.h>
1311000Sxc151355 #include <sys/ddi.h>
1321000Sxc151355 #include <sys/sunddi.h>
1331000Sxc151355 #include <sys/pci.h>
1341000Sxc151355 #include <sys/errno.h>
1358275SEric Cheng #include <sys/mac_provider.h>
1361000Sxc151355 #include <sys/dlpi.h>
1371000Sxc151355 #include <sys/ethernet.h>
1381000Sxc151355 #include <sys/list.h>
1391000Sxc151355 #include <sys/byteorder.h>
1401000Sxc151355 #include <sys/strsun.h>
1411000Sxc151355 #include <sys/policy.h>
1421000Sxc151355 #include <inet/common.h>
1431000Sxc151355 #include <inet/nd.h>
1441000Sxc151355 #include <inet/mi.h>
1451000Sxc151355 #include <inet/wifi_ioctl.h>
1463147Sxc151355 #include <sys/mac_wifi.h>
1471000Sxc151355 #include "ath_hal.h"
1481000Sxc151355 #include "ath_impl.h"
1491000Sxc151355 #include "ath_aux.h"
1501000Sxc151355 #include "ath_rate.h"
1511000Sxc151355
1523147Sxc151355 #define ATH_MAX_RSSI 63 /* max rssi */
1533147Sxc151355
1541000Sxc151355 extern void ath_halfix_init(void);
1551000Sxc151355 extern void ath_halfix_finit(void);
1561000Sxc151355 extern int32_t ath_getset(ath_t *asc, mblk_t *mp, uint32_t cmd);
1571000Sxc151355
1581000Sxc151355 /*
1591000Sxc151355 * PIO access attributes for registers
1601000Sxc151355 */
1611000Sxc151355 static ddi_device_acc_attr_t ath_reg_accattr = {
1621000Sxc151355 DDI_DEVICE_ATTR_V0,
1631000Sxc151355 DDI_STRUCTURE_LE_ACC,
1641000Sxc151355 DDI_STRICTORDER_ACC
1651000Sxc151355 };
1661000Sxc151355
1671000Sxc151355 /*
1681000Sxc151355 * DMA access attributes for descriptors: NOT to be byte swapped.
1691000Sxc151355 */
1701000Sxc151355 static ddi_device_acc_attr_t ath_desc_accattr = {
1711000Sxc151355 DDI_DEVICE_ATTR_V0,
1721000Sxc151355 DDI_STRUCTURE_LE_ACC,
1731000Sxc151355 DDI_STRICTORDER_ACC
1741000Sxc151355 };
1751000Sxc151355
1761000Sxc151355 /*
1778033SWang.Lin@Sun.COM * DMA attributes for rx/tx buffers
1781000Sxc151355 */
1796235Sxc151355 static ddi_dma_attr_t ath_dma_attr = {
1806235Sxc151355 DMA_ATTR_V0, /* version number */
1816235Sxc151355 0, /* low address */
1826235Sxc151355 0xffffffffU, /* high address */
1836235Sxc151355 0x3ffffU, /* counter register max */
1846235Sxc151355 1, /* alignment */
1856235Sxc151355 0xFFF, /* burst sizes */
1866235Sxc151355 1, /* minimum transfer size */
1876235Sxc151355 0x3ffffU, /* max transfer size */
1886235Sxc151355 0xffffffffU, /* address register max */
1896235Sxc151355 1, /* no scatter-gather */
1906235Sxc151355 1, /* granularity of device */
1916235Sxc151355 0, /* DMA flags */
1926235Sxc151355 };
1936235Sxc151355
1946235Sxc151355 static ddi_dma_attr_t ath_desc_dma_attr = {
1956235Sxc151355 DMA_ATTR_V0, /* version number */
1966235Sxc151355 0, /* low address */
1976235Sxc151355 0xffffffffU, /* high address */
1986235Sxc151355 0xffffffffU, /* counter register max */
1996235Sxc151355 0x1000, /* alignment */
2006235Sxc151355 0xFFF, /* burst sizes */
2016235Sxc151355 1, /* minimum transfer size */
2026235Sxc151355 0xffffffffU, /* max transfer size */
2036235Sxc151355 0xffffffffU, /* address register max */
2046235Sxc151355 1, /* no scatter-gather */
2056235Sxc151355 1, /* granularity of device */
2066235Sxc151355 0, /* DMA flags */
2071000Sxc151355 };
2081000Sxc151355
2091000Sxc151355 static kmutex_t ath_loglock;
2101000Sxc151355 static void *ath_soft_state_p = NULL;
2113147Sxc151355 static int ath_dwelltime = 150; /* scan interval, ms */
2123147Sxc151355
2133147Sxc151355 static int ath_m_stat(void *, uint_t, uint64_t *);
2143147Sxc151355 static int ath_m_start(void *);
2153147Sxc151355 static void ath_m_stop(void *);
2163147Sxc151355 static int ath_m_promisc(void *, boolean_t);
2173147Sxc151355 static int ath_m_multicst(void *, boolean_t, const uint8_t *);
2183147Sxc151355 static int ath_m_unicst(void *, const uint8_t *);
2193147Sxc151355 static mblk_t *ath_m_tx(void *, mblk_t *);
2203147Sxc151355 static void ath_m_ioctl(void *, queue_t *, mblk_t *);
2217663SSowmini.Varadhan@Sun.COM static int ath_m_setprop(void *, const char *, mac_prop_id_t,
2227663SSowmini.Varadhan@Sun.COM uint_t, const void *);
2237663SSowmini.Varadhan@Sun.COM static int ath_m_getprop(void *, const char *, mac_prop_id_t,
224*11878SVenu.Iyer@Sun.COM uint_t, void *);
225*11878SVenu.Iyer@Sun.COM static void ath_m_propinfo(void *, const char *, mac_prop_id_t,
226*11878SVenu.Iyer@Sun.COM mac_prop_info_handle_t);
2277663SSowmini.Varadhan@Sun.COM
2283147Sxc151355 static mac_callbacks_t ath_m_callbacks = {
229*11878SVenu.Iyer@Sun.COM MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
2303147Sxc151355 ath_m_stat,
2313147Sxc151355 ath_m_start,
2323147Sxc151355 ath_m_stop,
2333147Sxc151355 ath_m_promisc,
2343147Sxc151355 ath_m_multicst,
2353147Sxc151355 ath_m_unicst,
2363147Sxc151355 ath_m_tx,
237*11878SVenu.Iyer@Sun.COM NULL,
2383147Sxc151355 ath_m_ioctl,
2397663SSowmini.Varadhan@Sun.COM NULL, /* mc_getcapab */
2407663SSowmini.Varadhan@Sun.COM NULL,
2417663SSowmini.Varadhan@Sun.COM NULL,
2427663SSowmini.Varadhan@Sun.COM ath_m_setprop,
243*11878SVenu.Iyer@Sun.COM ath_m_getprop,
244*11878SVenu.Iyer@Sun.COM ath_m_propinfo
2453147Sxc151355 };
2461000Sxc151355
2471000Sxc151355 /*
2481000Sxc151355 * Available debug flags:
2491000Sxc151355 * ATH_DBG_INIT, ATH_DBG_GLD, ATH_DBG_HAL, ATH_DBG_INT, ATH_DBG_ATTACH,
2501000Sxc151355 * ATH_DBG_DETACH, ATH_DBG_AUX, ATH_DBG_WIFICFG, ATH_DBG_OSDEP
2511000Sxc151355 */
2521000Sxc151355 uint32_t ath_dbg_flags = 0;
2531000Sxc151355
2541000Sxc151355 /*
2551000Sxc151355 * Exception/warning cases not leading to panic.
2561000Sxc151355 */
2571000Sxc151355 void
ath_problem(const int8_t * fmt,...)2581000Sxc151355 ath_problem(const int8_t *fmt, ...)
2591000Sxc151355 {
2601000Sxc151355 va_list args;
2611000Sxc151355
2621000Sxc151355 mutex_enter(&ath_loglock);
2631000Sxc151355
2641000Sxc151355 va_start(args, fmt);
2651000Sxc151355 vcmn_err(CE_WARN, fmt, args);
2661000Sxc151355 va_end(args);
2671000Sxc151355
2681000Sxc151355 mutex_exit(&ath_loglock);
2691000Sxc151355 }
2701000Sxc151355
2711000Sxc151355 /*
2721000Sxc151355 * Normal log information independent of debug.
2731000Sxc151355 */
2741000Sxc151355 void
ath_log(const int8_t * fmt,...)2751000Sxc151355 ath_log(const int8_t *fmt, ...)
2761000Sxc151355 {
2771000Sxc151355 va_list args;
2781000Sxc151355
2791000Sxc151355 mutex_enter(&ath_loglock);
2801000Sxc151355
2811000Sxc151355 va_start(args, fmt);
2821000Sxc151355 vcmn_err(CE_CONT, fmt, args);
2831000Sxc151355 va_end(args);
2841000Sxc151355
2851000Sxc151355 mutex_exit(&ath_loglock);
2861000Sxc151355 }
2871000Sxc151355
2881000Sxc151355 void
ath_dbg(uint32_t dbg_flags,const int8_t * fmt,...)2891000Sxc151355 ath_dbg(uint32_t dbg_flags, const int8_t *fmt, ...)
2901000Sxc151355 {
2911000Sxc151355 va_list args;
2921000Sxc151355
2931000Sxc151355 if (dbg_flags & ath_dbg_flags) {
2941000Sxc151355 mutex_enter(&ath_loglock);
2951000Sxc151355 va_start(args, fmt);
2961000Sxc151355 vcmn_err(CE_CONT, fmt, args);
2971000Sxc151355 va_end(args);
2981000Sxc151355 mutex_exit(&ath_loglock);
2991000Sxc151355 }
3001000Sxc151355 }
3011000Sxc151355
3021000Sxc151355 void
ath_setup_desc(ath_t * asc,struct ath_buf * bf)3031000Sxc151355 ath_setup_desc(ath_t *asc, struct ath_buf *bf)
3041000Sxc151355 {
3051000Sxc151355 struct ath_desc *ds;
3061000Sxc151355
3071000Sxc151355 ds = bf->bf_desc;
3081000Sxc151355 ds->ds_link = bf->bf_daddr;
3091000Sxc151355 ds->ds_data = bf->bf_dma.cookie.dmac_address;
3101000Sxc151355 ATH_HAL_SETUPRXDESC(asc->asc_ah, ds,
3111000Sxc151355 bf->bf_dma.alength, /* buffer size */
3121000Sxc151355 0);
3131000Sxc151355
3141000Sxc151355 if (asc->asc_rxlink != NULL)
3151000Sxc151355 *asc->asc_rxlink = bf->bf_daddr;
3161000Sxc151355 asc->asc_rxlink = &ds->ds_link;
3171000Sxc151355 }
3181000Sxc151355
3191000Sxc151355
3201000Sxc151355 /*
3211000Sxc151355 * Allocate an area of memory and a DMA handle for accessing it
3221000Sxc151355 */
3231000Sxc151355 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)3246235Sxc151355 ath_alloc_dma_mem(dev_info_t *devinfo, ddi_dma_attr_t *dma_attr, size_t memsize,
3256235Sxc151355 ddi_device_acc_attr_t *attr_p, uint_t alloc_flags,
3266235Sxc151355 uint_t bind_flags, dma_area_t *dma_p)
3271000Sxc151355 {
3281000Sxc151355 int err;
3291000Sxc151355
3301000Sxc151355 /*
3311000Sxc151355 * Allocate handle
3321000Sxc151355 */
3336235Sxc151355 err = ddi_dma_alloc_handle(devinfo, dma_attr,
3345420Sxc151355 DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl);
3351000Sxc151355 if (err != DDI_SUCCESS)
3361000Sxc151355 return (DDI_FAILURE);
3371000Sxc151355
3381000Sxc151355 /*
3391000Sxc151355 * Allocate memory
3401000Sxc151355 */
3411000Sxc151355 err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize, attr_p,
3421000Sxc151355 alloc_flags, DDI_DMA_SLEEP, NULL, &dma_p->mem_va,
3431000Sxc151355 &dma_p->alength, &dma_p->acc_hdl);
3441000Sxc151355 if (err != DDI_SUCCESS)
3451000Sxc151355 return (DDI_FAILURE);
3461000Sxc151355
3471000Sxc151355 /*
3481000Sxc151355 * Bind the two together
3491000Sxc151355 */
3501000Sxc151355 err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL,
3515420Sxc151355 dma_p->mem_va, dma_p->alength, bind_flags,
3525420Sxc151355 DDI_DMA_SLEEP, NULL, &dma_p->cookie, &dma_p->ncookies);
3531000Sxc151355 if (err != DDI_DMA_MAPPED)
3541000Sxc151355 return (DDI_FAILURE);
3551000Sxc151355
3561000Sxc151355 dma_p->nslots = ~0U;
3571000Sxc151355 dma_p->size = ~0U;
3581000Sxc151355 dma_p->token = ~0U;
3591000Sxc151355 dma_p->offset = 0;
3601000Sxc151355 return (DDI_SUCCESS);
3611000Sxc151355 }
3621000Sxc151355
3631000Sxc151355 /*
3641000Sxc151355 * Free one allocated area of DMAable memory
3651000Sxc151355 */
3661000Sxc151355 static void
ath_free_dma_mem(dma_area_t * dma_p)3671000Sxc151355 ath_free_dma_mem(dma_area_t *dma_p)
3681000Sxc151355 {
3691000Sxc151355 if (dma_p->dma_hdl != NULL) {
3701000Sxc151355 (void) ddi_dma_unbind_handle(dma_p->dma_hdl);
3711000Sxc151355 if (dma_p->acc_hdl != NULL) {
3721000Sxc151355 ddi_dma_mem_free(&dma_p->acc_hdl);
3731000Sxc151355 dma_p->acc_hdl = NULL;
3741000Sxc151355 }
3751000Sxc151355 ddi_dma_free_handle(&dma_p->dma_hdl);
3761000Sxc151355 dma_p->ncookies = 0;
3771000Sxc151355 dma_p->dma_hdl = NULL;
3781000Sxc151355 }
3791000Sxc151355 }
3801000Sxc151355
3811000Sxc151355
3828033SWang.Lin@Sun.COM /*
3838033SWang.Lin@Sun.COM * Initialize tx/rx buffer list. Allocate DMA memory for
3848033SWang.Lin@Sun.COM * each buffer.
3858033SWang.Lin@Sun.COM */
3868033SWang.Lin@Sun.COM static int
ath_buflist_setup(dev_info_t * devinfo,ath_t * asc,list_t * bflist,struct ath_buf ** pbf,struct ath_desc ** pds,int nbuf,uint_t dmabflags)3878033SWang.Lin@Sun.COM ath_buflist_setup(dev_info_t *devinfo, ath_t *asc, list_t *bflist,
3888033SWang.Lin@Sun.COM struct ath_buf **pbf, struct ath_desc **pds, int nbuf, uint_t dmabflags)
3898033SWang.Lin@Sun.COM {
3908033SWang.Lin@Sun.COM int i, err;
3918033SWang.Lin@Sun.COM struct ath_buf *bf = *pbf;
3928033SWang.Lin@Sun.COM struct ath_desc *ds = *pds;
3938033SWang.Lin@Sun.COM
3948033SWang.Lin@Sun.COM list_create(bflist, sizeof (struct ath_buf),
3958033SWang.Lin@Sun.COM offsetof(struct ath_buf, bf_node));
3968033SWang.Lin@Sun.COM for (i = 0; i < nbuf; i++, bf++, ds++) {
3978033SWang.Lin@Sun.COM bf->bf_desc = ds;
3988033SWang.Lin@Sun.COM bf->bf_daddr = asc->asc_desc_dma.cookie.dmac_address +
3998033SWang.Lin@Sun.COM ((uintptr_t)ds - (uintptr_t)asc->asc_desc);
4008033SWang.Lin@Sun.COM list_insert_tail(bflist, bf);
4018033SWang.Lin@Sun.COM
4028033SWang.Lin@Sun.COM /* alloc DMA memory */
4038033SWang.Lin@Sun.COM err = ath_alloc_dma_mem(devinfo, &ath_dma_attr,
4048033SWang.Lin@Sun.COM asc->asc_dmabuf_size, &ath_desc_accattr, DDI_DMA_STREAMING,
4058033SWang.Lin@Sun.COM dmabflags, &bf->bf_dma);
4068033SWang.Lin@Sun.COM if (err != DDI_SUCCESS)
4078033SWang.Lin@Sun.COM return (err);
4088033SWang.Lin@Sun.COM }
4098033SWang.Lin@Sun.COM *pbf = bf;
4108033SWang.Lin@Sun.COM *pds = ds;
4118033SWang.Lin@Sun.COM
4128033SWang.Lin@Sun.COM return (DDI_SUCCESS);
4138033SWang.Lin@Sun.COM }
4148033SWang.Lin@Sun.COM
4158033SWang.Lin@Sun.COM /*
4168033SWang.Lin@Sun.COM * Destroy tx/rx buffer list. Free DMA memory.
4178033SWang.Lin@Sun.COM */
4188033SWang.Lin@Sun.COM static void
ath_buflist_cleanup(list_t * buflist)4198033SWang.Lin@Sun.COM ath_buflist_cleanup(list_t *buflist)
4208033SWang.Lin@Sun.COM {
4218033SWang.Lin@Sun.COM struct ath_buf *bf;
4228033SWang.Lin@Sun.COM
4238033SWang.Lin@Sun.COM if (!buflist)
4248033SWang.Lin@Sun.COM return;
4258033SWang.Lin@Sun.COM
4268033SWang.Lin@Sun.COM bf = list_head(buflist);
4278033SWang.Lin@Sun.COM while (bf != NULL) {
4288033SWang.Lin@Sun.COM if (bf->bf_m != NULL) {
4298033SWang.Lin@Sun.COM freemsg(bf->bf_m);
4308033SWang.Lin@Sun.COM bf->bf_m = NULL;
4318033SWang.Lin@Sun.COM }
4328033SWang.Lin@Sun.COM /* Free DMA buffer */
4338033SWang.Lin@Sun.COM ath_free_dma_mem(&bf->bf_dma);
4348033SWang.Lin@Sun.COM if (bf->bf_in != NULL) {
4358033SWang.Lin@Sun.COM ieee80211_free_node(bf->bf_in);
4368033SWang.Lin@Sun.COM bf->bf_in = NULL;
4378033SWang.Lin@Sun.COM }
4388033SWang.Lin@Sun.COM list_remove(buflist, bf);
4398033SWang.Lin@Sun.COM bf = list_head(buflist);
4408033SWang.Lin@Sun.COM }
4418033SWang.Lin@Sun.COM list_destroy(buflist);
4428033SWang.Lin@Sun.COM }
4438033SWang.Lin@Sun.COM
4448033SWang.Lin@Sun.COM
4458033SWang.Lin@Sun.COM static void
ath_desc_free(ath_t * asc)4468033SWang.Lin@Sun.COM ath_desc_free(ath_t *asc)
4478033SWang.Lin@Sun.COM {
4488033SWang.Lin@Sun.COM ath_buflist_cleanup(&asc->asc_txbuf_list);
4498033SWang.Lin@Sun.COM ath_buflist_cleanup(&asc->asc_rxbuf_list);
4508033SWang.Lin@Sun.COM
4518033SWang.Lin@Sun.COM /* Free descriptor DMA buffer */
4528033SWang.Lin@Sun.COM ath_free_dma_mem(&asc->asc_desc_dma);
4538033SWang.Lin@Sun.COM
4548033SWang.Lin@Sun.COM kmem_free((void *)asc->asc_vbufptr, asc->asc_vbuflen);
4558033SWang.Lin@Sun.COM asc->asc_vbufptr = NULL;
4568033SWang.Lin@Sun.COM }
4578033SWang.Lin@Sun.COM
4581000Sxc151355 static int
ath_desc_alloc(dev_info_t * devinfo,ath_t * asc)4591000Sxc151355 ath_desc_alloc(dev_info_t *devinfo, ath_t *asc)
4601000Sxc151355 {
4618033SWang.Lin@Sun.COM int err;
4621000Sxc151355 size_t size;
4631000Sxc151355 struct ath_desc *ds;
4641000Sxc151355 struct ath_buf *bf;
4651000Sxc151355
4661000Sxc151355 size = sizeof (struct ath_desc) * (ATH_TXBUF + ATH_RXBUF);
4671000Sxc151355
4686235Sxc151355 err = ath_alloc_dma_mem(devinfo, &ath_desc_dma_attr, size,
4696235Sxc151355 &ath_desc_accattr, DDI_DMA_CONSISTENT,
4706235Sxc151355 DDI_DMA_RDWR | DDI_DMA_CONSISTENT, &asc->asc_desc_dma);
4711000Sxc151355
4721000Sxc151355 /* virtual address of the first descriptor */
4731000Sxc151355 asc->asc_desc = (struct ath_desc *)asc->asc_desc_dma.mem_va;
4741000Sxc151355
4751000Sxc151355 ds = asc->asc_desc;
4761000Sxc151355 ATH_DEBUG((ATH_DBG_INIT, "ath: ath_desc_alloc(): DMA map: "
4771000Sxc151355 "%p (%d) -> %p\n",
4781000Sxc151355 asc->asc_desc, asc->asc_desc_dma.alength,
4791000Sxc151355 asc->asc_desc_dma.cookie.dmac_address));
4801000Sxc151355
4811000Sxc151355 /* allocate data structures to describe TX/RX DMA buffers */
4821000Sxc151355 asc->asc_vbuflen = sizeof (struct ath_buf) * (ATH_TXBUF + ATH_RXBUF);
4831000Sxc151355 bf = (struct ath_buf *)kmem_zalloc(asc->asc_vbuflen, KM_SLEEP);
4841000Sxc151355 asc->asc_vbufptr = bf;
4851000Sxc151355
4861000Sxc151355 /* DMA buffer size for each TX/RX packet */
4871000Sxc151355 asc->asc_dmabuf_size = roundup(1000 + sizeof (struct ieee80211_frame) +
4881000Sxc151355 IEEE80211_MTU + IEEE80211_CRC_LEN +
4891000Sxc151355 (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
4901000Sxc151355 IEEE80211_WEP_CRCLEN), asc->asc_cachelsz);
4911000Sxc151355
4928033SWang.Lin@Sun.COM /* create RX buffer list */
4938033SWang.Lin@Sun.COM err = ath_buflist_setup(devinfo, asc, &asc->asc_rxbuf_list, &bf, &ds,
4948033SWang.Lin@Sun.COM ATH_RXBUF, DDI_DMA_READ | DDI_DMA_STREAMING);
4958033SWang.Lin@Sun.COM if (err != DDI_SUCCESS) {
4968033SWang.Lin@Sun.COM ath_desc_free(asc);
4978033SWang.Lin@Sun.COM return (err);
4981000Sxc151355 }
4991000Sxc151355
5008033SWang.Lin@Sun.COM /* create TX buffer list */
5018033SWang.Lin@Sun.COM err = ath_buflist_setup(devinfo, asc, &asc->asc_txbuf_list, &bf, &ds,
5028033SWang.Lin@Sun.COM ATH_TXBUF, DDI_DMA_STREAMING);
5038033SWang.Lin@Sun.COM if (err != DDI_SUCCESS) {
5048033SWang.Lin@Sun.COM ath_desc_free(asc);
5058033SWang.Lin@Sun.COM return (err);
5068033SWang.Lin@Sun.COM }
5071000Sxc151355
5081000Sxc151355
5091000Sxc151355 return (DDI_SUCCESS);
5101000Sxc151355 }
5111000Sxc151355
5121000Sxc151355 static void
ath_printrxbuf(struct ath_buf * bf,int32_t done)5131000Sxc151355 ath_printrxbuf(struct ath_buf *bf, int32_t done)
5141000Sxc151355 {
5151000Sxc151355 struct ath_desc *ds = bf->bf_desc;
5168033SWang.Lin@Sun.COM const struct ath_rx_status *rs = &bf->bf_status.ds_rxstat;
5171000Sxc151355
5181000Sxc151355 ATH_DEBUG((ATH_DBG_RECV, "ath: R (%p %p) %08x %08x %08x "
5191000Sxc151355 "%08x %08x %08x %c\n",
5201000Sxc151355 ds, bf->bf_daddr,
5211000Sxc151355 ds->ds_link, ds->ds_data,
5221000Sxc151355 ds->ds_ctl0, ds->ds_ctl1,
5231000Sxc151355 ds->ds_hw[0], ds->ds_hw[1],
5248033SWang.Lin@Sun.COM !done ? ' ' : (rs->rs_status == 0) ? '*' : '!'));
5251000Sxc151355 }
5261000Sxc151355
5271000Sxc151355 static void
ath_rx_handler(ath_t * asc)5281000Sxc151355 ath_rx_handler(ath_t *asc)
5291000Sxc151355 {
5303147Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc;
5311000Sxc151355 struct ath_buf *bf;
5321000Sxc151355 struct ath_hal *ah = asc->asc_ah;
5331000Sxc151355 struct ath_desc *ds;
5348033SWang.Lin@Sun.COM struct ath_rx_status *rs;
5351000Sxc151355 mblk_t *rx_mp;
5363147Sxc151355 struct ieee80211_frame *wh;
5371000Sxc151355 int32_t len, loop = 1;
5381000Sxc151355 uint8_t phyerr;
5391000Sxc151355 HAL_STATUS status;
5401000Sxc151355 HAL_NODE_STATS hal_node_stats;
5413147Sxc151355 struct ieee80211_node *in;
5421000Sxc151355
5431000Sxc151355 do {
5441000Sxc151355 mutex_enter(&asc->asc_rxbuflock);
5451000Sxc151355 bf = list_head(&asc->asc_rxbuf_list);
5461000Sxc151355 if (bf == NULL) {
5471000Sxc151355 ATH_DEBUG((ATH_DBG_RECV, "ath: ath_rx_handler(): "
5481000Sxc151355 "no buffer\n"));
5491000Sxc151355 mutex_exit(&asc->asc_rxbuflock);
5501000Sxc151355 break;
5511000Sxc151355 }
5521000Sxc151355 ASSERT(bf->bf_dma.cookie.dmac_address != NULL);
5531000Sxc151355 ds = bf->bf_desc;
5541000Sxc151355 if (ds->ds_link == bf->bf_daddr) {
5551000Sxc151355 /*
5561000Sxc151355 * Never process the self-linked entry at the end,
5571000Sxc151355 * this may be met at heavy load.
5581000Sxc151355 */
5591000Sxc151355 mutex_exit(&asc->asc_rxbuflock);
5601000Sxc151355 break;
5611000Sxc151355 }
5621000Sxc151355
5638033SWang.Lin@Sun.COM rs = &bf->bf_status.ds_rxstat;
5641000Sxc151355 status = ATH_HAL_RXPROCDESC(ah, ds,
5651000Sxc151355 bf->bf_daddr,
5668033SWang.Lin@Sun.COM ATH_PA2DESC(asc, ds->ds_link), rs);
5671000Sxc151355 if (status == HAL_EINPROGRESS) {
5681000Sxc151355 mutex_exit(&asc->asc_rxbuflock);
5691000Sxc151355 break;
5701000Sxc151355 }
5711000Sxc151355 list_remove(&asc->asc_rxbuf_list, bf);
5721000Sxc151355 mutex_exit(&asc->asc_rxbuflock);
5731000Sxc151355
5748033SWang.Lin@Sun.COM if (rs->rs_status != 0) {
5758033SWang.Lin@Sun.COM if (rs->rs_status & HAL_RXERR_CRC)
5761000Sxc151355 asc->asc_stats.ast_rx_crcerr++;
5778033SWang.Lin@Sun.COM if (rs->rs_status & HAL_RXERR_FIFO)
5781000Sxc151355 asc->asc_stats.ast_rx_fifoerr++;
5798033SWang.Lin@Sun.COM if (rs->rs_status & HAL_RXERR_DECRYPT)
5801000Sxc151355 asc->asc_stats.ast_rx_badcrypt++;
5818033SWang.Lin@Sun.COM if (rs->rs_status & HAL_RXERR_PHY) {
5821000Sxc151355 asc->asc_stats.ast_rx_phyerr++;
5838033SWang.Lin@Sun.COM phyerr = rs->rs_phyerr & 0x1f;
5841000Sxc151355 asc->asc_stats.ast_rx_phy[phyerr]++;
5851000Sxc151355 }
5861000Sxc151355 goto rx_next;
5871000Sxc151355 }
5888033SWang.Lin@Sun.COM len = rs->rs_datalen;
5891000Sxc151355
5901000Sxc151355 /* less than sizeof(struct ieee80211_frame) */
5911000Sxc151355 if (len < 20) {
5921000Sxc151355 asc->asc_stats.ast_rx_tooshort++;
5931000Sxc151355 goto rx_next;
5941000Sxc151355 }
5951000Sxc151355
5961000Sxc151355 if ((rx_mp = allocb(asc->asc_dmabuf_size, BPRI_MED)) == NULL) {
5971000Sxc151355 ath_problem("ath: ath_rx_handler(): "
5981000Sxc151355 "allocing mblk buffer failed.\n");
5991000Sxc151355 return;
6001000Sxc151355 }
6011000Sxc151355
6021000Sxc151355 ATH_DMA_SYNC(bf->bf_dma, DDI_DMA_SYNC_FORCPU);
6031000Sxc151355 bcopy(bf->bf_dma.mem_va, rx_mp->b_rptr, len);
6041000Sxc151355
6051000Sxc151355 rx_mp->b_wptr += len;
6061000Sxc151355 wh = (struct ieee80211_frame *)rx_mp->b_rptr;
6073147Sxc151355 if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
6081000Sxc151355 IEEE80211_FC0_TYPE_CTL) {
6091000Sxc151355 /*
6101000Sxc151355 * Ignore control frame received in promisc mode.
6111000Sxc151355 */
6121000Sxc151355 freemsg(rx_mp);
6131000Sxc151355 goto rx_next;
6141000Sxc151355 }
6151000Sxc151355 /* Remove the CRC at the end of IEEE80211 frame */
6161000Sxc151355 rx_mp->b_wptr -= IEEE80211_CRC_LEN;
6171000Sxc151355 #ifdef DEBUG
6181000Sxc151355 ath_printrxbuf(bf, status == HAL_OK);
6191000Sxc151355 #endif /* DEBUG */
6203147Sxc151355 /*
6213147Sxc151355 * Locate the node for sender, track state, and then
6223147Sxc151355 * pass the (referenced) node up to the 802.11 layer
6233147Sxc151355 * for its use.
6243147Sxc151355 */
6253147Sxc151355 in = ieee80211_find_rxnode(ic, wh);
6263147Sxc151355
6273147Sxc151355 /*
6283147Sxc151355 * Send frame up for processing.
6293147Sxc151355 */
6303147Sxc151355 (void) ieee80211_input(ic, rx_mp, in,
6318033SWang.Lin@Sun.COM rs->rs_rssi, rs->rs_tstamp);
6323147Sxc151355
6333147Sxc151355 ieee80211_free_node(in);
6343147Sxc151355
6351000Sxc151355 rx_next:
6361000Sxc151355 mutex_enter(&asc->asc_rxbuflock);
6371000Sxc151355 list_insert_tail(&asc->asc_rxbuf_list, bf);
6381000Sxc151355 mutex_exit(&asc->asc_rxbuflock);
6391000Sxc151355 ath_setup_desc(asc, bf);
6401000Sxc151355 } while (loop);
6411000Sxc151355
6421000Sxc151355 /* rx signal state monitoring */
6433147Sxc151355 ATH_HAL_RXMONITOR(ah, &hal_node_stats, &asc->asc_curchan);
6441000Sxc151355 }
6451000Sxc151355
6461000Sxc151355 static void
ath_printtxbuf(struct ath_buf * bf,int done)6471000Sxc151355 ath_printtxbuf(struct ath_buf *bf, int done)
6481000Sxc151355 {
6491000Sxc151355 struct ath_desc *ds = bf->bf_desc;
6508033SWang.Lin@Sun.COM const struct ath_tx_status *ts = &bf->bf_status.ds_txstat;
6511000Sxc151355
6521000Sxc151355 ATH_DEBUG((ATH_DBG_SEND, "ath: T(%p %p) %08x %08x %08x %08x %08x"
6531000Sxc151355 " %08x %08x %08x %c\n",
6541000Sxc151355 ds, bf->bf_daddr,
6551000Sxc151355 ds->ds_link, ds->ds_data,
6561000Sxc151355 ds->ds_ctl0, ds->ds_ctl1,
6571000Sxc151355 ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3],
6588033SWang.Lin@Sun.COM !done ? ' ' : (ts->ts_status == 0) ? '*' : '!'));
6591000Sxc151355 }
6601000Sxc151355
6611000Sxc151355 /*
6621000Sxc151355 * The input parameter mp has following assumption:
6633147Sxc151355 * For data packets, GLDv3 mac_wifi plugin allocates and fills the
6643147Sxc151355 * ieee80211 header. For management packets, net80211 allocates and
6653147Sxc151355 * fills the ieee80211 header. In both cases, enough spaces in the
6663147Sxc151355 * header are left for encryption option.
6671000Sxc151355 */
6681000Sxc151355 static int32_t
ath_tx_start(ath_t * asc,struct ieee80211_node * in,struct ath_buf * bf,mblk_t * mp)6693147Sxc151355 ath_tx_start(ath_t *asc, struct ieee80211_node *in, struct ath_buf *bf,
6703147Sxc151355 mblk_t *mp)
6711000Sxc151355 {
6723147Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc;
6731000Sxc151355 struct ieee80211_frame *wh;
6741000Sxc151355 struct ath_hal *ah = asc->asc_ah;
6753147Sxc151355 uint32_t subtype, flags, ctsduration;
6761000Sxc151355 int32_t keyix, iswep, hdrlen, pktlen, mblen, mbslen, try0;
6773147Sxc151355 uint8_t rix, cix, txrate, ctsrate;
6781000Sxc151355 struct ath_desc *ds;
6791000Sxc151355 struct ath_txq *txq;
6801000Sxc151355 HAL_PKT_TYPE atype;
6811000Sxc151355 const HAL_RATE_TABLE *rt;
6821000Sxc151355 HAL_BOOL shortPreamble;
6831000Sxc151355 struct ath_node *an;
6843147Sxc151355 caddr_t dest;
6851000Sxc151355
6861000Sxc151355 /*
6871000Sxc151355 * CRC are added by H/W, not encaped by driver,
6881000Sxc151355 * but we must count it in pkt length.
6891000Sxc151355 */
6901000Sxc151355 pktlen = IEEE80211_CRC_LEN;
6911000Sxc151355
6923147Sxc151355 wh = (struct ieee80211_frame *)mp->b_rptr;
6933147Sxc151355 iswep = wh->i_fc[1] & IEEE80211_FC1_WEP;
6941000Sxc151355 keyix = HAL_TXKEYIX_INVALID;
6951000Sxc151355 hdrlen = sizeof (struct ieee80211_frame);
6963147Sxc151355 if (iswep != 0) {
6973147Sxc151355 const struct ieee80211_cipher *cip;
6983147Sxc151355 struct ieee80211_key *k;
6991000Sxc151355
7003147Sxc151355 /*
7013147Sxc151355 * Construct the 802.11 header+trailer for an encrypted
7023147Sxc151355 * frame. The only reason this can fail is because of an
7033147Sxc151355 * unknown or unsupported cipher/key type.
7043147Sxc151355 */
7053147Sxc151355 k = ieee80211_crypto_encap(ic, mp);
7063147Sxc151355 if (k == NULL) {
7073147Sxc151355 ATH_DEBUG((ATH_DBG_AUX, "crypto_encap failed\n"));
7083147Sxc151355 /*
7093147Sxc151355 * This can happen when the key is yanked after the
7103147Sxc151355 * frame was queued. Just discard the frame; the
7113147Sxc151355 * 802.11 layer counts failures and provides
7123147Sxc151355 * debugging/diagnostics.
7133147Sxc151355 */
7143147Sxc151355 return (EIO);
7153147Sxc151355 }
7163147Sxc151355 cip = k->wk_cipher;
7171000Sxc151355 /*
7183147Sxc151355 * Adjust the packet + header lengths for the crypto
7193147Sxc151355 * additions and calculate the h/w key index. When
7203147Sxc151355 * a s/w mic is done the frame will have had any mic
7213147Sxc151355 * added to it prior to entry so m0->m_pkthdr.len above will
7223147Sxc151355 * account for it. Otherwise we need to add it to the
7233147Sxc151355 * packet length.
7241000Sxc151355 */
7253147Sxc151355 hdrlen += cip->ic_header;
7264126Szf162725 pktlen += cip->ic_trailer;
7273147Sxc151355 if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0)
7283147Sxc151355 pktlen += cip->ic_miclen;
7293147Sxc151355 keyix = k->wk_keyix;
7301000Sxc151355
7313147Sxc151355 /* packet header may have moved, reset our local pointer */
7323147Sxc151355 wh = (struct ieee80211_frame *)mp->b_rptr;
7331000Sxc151355 }
7341000Sxc151355
7353147Sxc151355 dest = bf->bf_dma.mem_va;
7363147Sxc151355 for (; mp != NULL; mp = mp->b_cont) {
7373147Sxc151355 mblen = MBLKL(mp);
7383147Sxc151355 bcopy(mp->b_rptr, dest, mblen);
7393147Sxc151355 dest += mblen;
7403147Sxc151355 }
7416990Sgd78059 mbslen = (uintptr_t)dest - (uintptr_t)bf->bf_dma.mem_va;
7423147Sxc151355 pktlen += mbslen;
7433147Sxc151355
7441000Sxc151355 bf->bf_in = in;
7451000Sxc151355
7461000Sxc151355 /* setup descriptors */
7471000Sxc151355 ds = bf->bf_desc;
7481000Sxc151355 rt = asc->asc_currates;
7493147Sxc151355 ASSERT(rt != NULL);
7501000Sxc151355
7511000Sxc151355 /*
7521000Sxc151355 * The 802.11 layer marks whether or not we should
7531000Sxc151355 * use short preamble based on the current mode and
7541000Sxc151355 * negotiated parameters.
7551000Sxc151355 */
7563147Sxc151355 if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
7571000Sxc151355 (in->in_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
7581000Sxc151355 shortPreamble = AH_TRUE;
7591000Sxc151355 asc->asc_stats.ast_tx_shortpre++;
7601000Sxc151355 } else {
7611000Sxc151355 shortPreamble = AH_FALSE;
7621000Sxc151355 }
7631000Sxc151355
7641000Sxc151355 an = ATH_NODE(in);
7651000Sxc151355
7661000Sxc151355 /*
7671000Sxc151355 * Calculate Atheros packet type from IEEE80211 packet header
7681000Sxc151355 * and setup for rate calculations.
7691000Sxc151355 */
7703147Sxc151355 switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
7711000Sxc151355 case IEEE80211_FC0_TYPE_MGT:
7723147Sxc151355 subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
7731000Sxc151355 if (subtype == IEEE80211_FC0_SUBTYPE_BEACON)
7741000Sxc151355 atype = HAL_PKT_TYPE_BEACON;
7751000Sxc151355 else if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
7761000Sxc151355 atype = HAL_PKT_TYPE_PROBE_RESP;
7771000Sxc151355 else if (subtype == IEEE80211_FC0_SUBTYPE_ATIM)
7781000Sxc151355 atype = HAL_PKT_TYPE_ATIM;
7791000Sxc151355 else
7801000Sxc151355 atype = HAL_PKT_TYPE_NORMAL;
7811000Sxc151355 rix = 0; /* lowest rate */
7821000Sxc151355 try0 = ATH_TXMAXTRY;
7831000Sxc151355 if (shortPreamble)
7841000Sxc151355 txrate = an->an_tx_mgtratesp;
7851000Sxc151355 else
7861000Sxc151355 txrate = an->an_tx_mgtrate;
7871000Sxc151355 /* force all ctl frames to highest queue */
7881000Sxc151355 txq = asc->asc_ac2q[WME_AC_VO];
7891000Sxc151355 break;
7901000Sxc151355 case IEEE80211_FC0_TYPE_CTL:
7911000Sxc151355 atype = HAL_PKT_TYPE_PSPOLL;
7923147Sxc151355 subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
7931000Sxc151355 rix = 0; /* lowest rate */
7941000Sxc151355 try0 = ATH_TXMAXTRY;
7951000Sxc151355 if (shortPreamble)
7961000Sxc151355 txrate = an->an_tx_mgtratesp;
7971000Sxc151355 else
7981000Sxc151355 txrate = an->an_tx_mgtrate;
7991000Sxc151355 /* force all ctl frames to highest queue */
8001000Sxc151355 txq = asc->asc_ac2q[WME_AC_VO];
8011000Sxc151355 break;
8021000Sxc151355 case IEEE80211_FC0_TYPE_DATA:
8031000Sxc151355 atype = HAL_PKT_TYPE_NORMAL;
8041000Sxc151355 rix = an->an_tx_rix0;
8051000Sxc151355 try0 = an->an_tx_try0;
8061000Sxc151355 if (shortPreamble)
8071000Sxc151355 txrate = an->an_tx_rate0sp;
8081000Sxc151355 else
8091000Sxc151355 txrate = an->an_tx_rate0;
8101000Sxc151355 /* Always use background queue */
8111000Sxc151355 txq = asc->asc_ac2q[WME_AC_BK];
8121000Sxc151355 break;
8131000Sxc151355 default:
8141000Sxc151355 /* Unknown 802.11 frame */
8151000Sxc151355 asc->asc_stats.ast_tx_invalid++;
8161000Sxc151355 return (1);
8171000Sxc151355 }
8181000Sxc151355 /*
8191000Sxc151355 * Calculate miscellaneous flags.
8201000Sxc151355 */
8211000Sxc151355 flags = HAL_TXDESC_CLRDMASK;
8223147Sxc151355 if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
8231000Sxc151355 flags |= HAL_TXDESC_NOACK; /* no ack on broad/multicast */
8241000Sxc151355 asc->asc_stats.ast_tx_noack++;
8253147Sxc151355 } else if (pktlen > ic->ic_rtsthreshold) {
8261000Sxc151355 flags |= HAL_TXDESC_RTSENA; /* RTS based on frame length */
8271000Sxc151355 asc->asc_stats.ast_tx_rts++;
8281000Sxc151355 }
8291000Sxc151355
8301000Sxc151355 /*
8311000Sxc151355 * Calculate duration. This logically belongs in the 802.11
8321000Sxc151355 * layer but it lacks sufficient information to calculate it.
8331000Sxc151355 */
8341000Sxc151355 if ((flags & HAL_TXDESC_NOACK) == 0 &&
8353147Sxc151355 (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) !=
8361000Sxc151355 IEEE80211_FC0_TYPE_CTL) {
8371000Sxc151355 uint16_t dur;
8381000Sxc151355 dur = ath_hal_computetxtime(ah, rt, IEEE80211_ACK_SIZE,
8391000Sxc151355 rix, shortPreamble);
8407249Sff224033 /* LINTED E_BAD_PTR_CAST_ALIGN */
8413147Sxc151355 *(uint16_t *)wh->i_dur = LE_16(dur);
8421000Sxc151355 }
8431000Sxc151355
8441000Sxc151355 /*
8451000Sxc151355 * Calculate RTS/CTS rate and duration if needed.
8461000Sxc151355 */
8471000Sxc151355 ctsduration = 0;
8481000Sxc151355 if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)) {
8491000Sxc151355 /*
8501000Sxc151355 * CTS transmit rate is derived from the transmit rate
8511000Sxc151355 * by looking in the h/w rate table. We must also factor
8521000Sxc151355 * in whether or not a short preamble is to be used.
8531000Sxc151355 */
8541000Sxc151355 cix = rt->info[rix].controlRate;
8551000Sxc151355 ctsrate = rt->info[cix].rateCode;
8561000Sxc151355 if (shortPreamble)
8571000Sxc151355 ctsrate |= rt->info[cix].shortPreamble;
8581000Sxc151355 /*
8591000Sxc151355 * Compute the transmit duration based on the size
8601000Sxc151355 * of an ACK frame. We call into the HAL to do the
8611000Sxc151355 * computation since it depends on the characteristics
8621000Sxc151355 * of the actual PHY being used.
8631000Sxc151355 */
8641000Sxc151355 if (flags & HAL_TXDESC_RTSENA) { /* SIFS + CTS */
8651000Sxc151355 ctsduration += ath_hal_computetxtime(ah,
8661000Sxc151355 rt, IEEE80211_ACK_SIZE, cix, shortPreamble);
8671000Sxc151355 }
8681000Sxc151355 /* SIFS + data */
8691000Sxc151355 ctsduration += ath_hal_computetxtime(ah,
8701000Sxc151355 rt, pktlen, rix, shortPreamble);
8718033SWang.Lin@Sun.COM if ((flags & HAL_TXDESC_NOACK) == 0) { /* SIFS + ACK */
8721000Sxc151355 ctsduration += ath_hal_computetxtime(ah,
8731000Sxc151355 rt, IEEE80211_ACK_SIZE, cix, shortPreamble);
8741000Sxc151355 }
8751000Sxc151355 } else
8761000Sxc151355 ctsrate = 0;
8771000Sxc151355
8781000Sxc151355 if (++txq->axq_intrcnt >= ATH_TXINTR_PERIOD) {
8791000Sxc151355 flags |= HAL_TXDESC_INTREQ;
8801000Sxc151355 txq->axq_intrcnt = 0;
8811000Sxc151355 }
8821000Sxc151355
8831000Sxc151355 /*
8841000Sxc151355 * Formulate first tx descriptor with tx controls.
8851000Sxc151355 */
8861000Sxc151355 ATH_HAL_SETUPTXDESC(ah, ds,
8871000Sxc151355 pktlen, /* packet length */
8881000Sxc151355 hdrlen, /* header length */
8891000Sxc151355 atype, /* Atheros packet type */
8901000Sxc151355 MIN(in->in_txpower, 60), /* txpower */
8911000Sxc151355 txrate, try0, /* series 0 rate/tries */
8923147Sxc151355 keyix, /* key cache index */
8933147Sxc151355 an->an_tx_antenna, /* antenna mode */
8941000Sxc151355 flags, /* flags */
8951000Sxc151355 ctsrate, /* rts/cts rate */
8961000Sxc151355 ctsduration); /* rts/cts duration */
8973147Sxc151355 bf->bf_flags = flags;
8981000Sxc151355
8997249Sff224033 /* LINTED E_BAD_PTR_CAST_ALIGN */
9001000Sxc151355 ATH_DEBUG((ATH_DBG_SEND, "ath: ath_xmit(): to %s totlen=%d "
9011000Sxc151355 "an->an_tx_rate1sp=%d tx_rate2sp=%d tx_rate3sp=%d "
9021000Sxc151355 "qnum=%d rix=%d sht=%d dur = %d\n",
9033147Sxc151355 ieee80211_macaddr_sprintf(wh->i_addr1), mbslen, an->an_tx_rate1sp,
9041000Sxc151355 an->an_tx_rate2sp, an->an_tx_rate3sp,
9053147Sxc151355 txq->axq_qnum, rix, shortPreamble, *(uint16_t *)wh->i_dur));
9061000Sxc151355
9071000Sxc151355 /*
9081000Sxc151355 * Setup the multi-rate retry state only when we're
9091000Sxc151355 * going to use it. This assumes ath_hal_setuptxdesc
9101000Sxc151355 * initializes the descriptors (so we don't have to)
9111000Sxc151355 * when the hardware supports multi-rate retry and
9121000Sxc151355 * we don't use it.
9131000Sxc151355 */
9141000Sxc151355 if (try0 != ATH_TXMAXTRY)
9151000Sxc151355 ATH_HAL_SETUPXTXDESC(ah, ds,
9161000Sxc151355 an->an_tx_rate1sp, 2, /* series 1 */
9171000Sxc151355 an->an_tx_rate2sp, 2, /* series 2 */
9181000Sxc151355 an->an_tx_rate3sp, 2); /* series 3 */
9191000Sxc151355
9201000Sxc151355 ds->ds_link = 0;
9211000Sxc151355 ds->ds_data = bf->bf_dma.cookie.dmac_address;
9221000Sxc151355 ATH_HAL_FILLTXDESC(ah, ds,
9231000Sxc151355 mbslen, /* segment length */
9241000Sxc151355 AH_TRUE, /* first segment */
9251000Sxc151355 AH_TRUE, /* last segment */
9261000Sxc151355 ds); /* first descriptor */
9271000Sxc151355
9281000Sxc151355 ATH_DMA_SYNC(bf->bf_dma, DDI_DMA_SYNC_FORDEV);
9291000Sxc151355
9301000Sxc151355 mutex_enter(&txq->axq_lock);
9311000Sxc151355 list_insert_tail(&txq->axq_list, bf);
9321000Sxc151355 if (txq->axq_link == NULL) {
9331000Sxc151355 ATH_HAL_PUTTXBUF(ah, txq->axq_qnum, bf->bf_daddr);
9341000Sxc151355 } else {
9351000Sxc151355 *txq->axq_link = bf->bf_daddr;
9361000Sxc151355 }
9371000Sxc151355 txq->axq_link = &ds->ds_link;
9381000Sxc151355 mutex_exit(&txq->axq_lock);
9391000Sxc151355
9401000Sxc151355 ATH_HAL_TXSTART(ah, txq->axq_qnum);
9411000Sxc151355
9423147Sxc151355 ic->ic_stats.is_tx_frags++;
9433147Sxc151355 ic->ic_stats.is_tx_bytes += pktlen;
9443147Sxc151355
9451000Sxc151355 return (0);
9461000Sxc151355 }
9471000Sxc151355
9483147Sxc151355 /*
9493147Sxc151355 * Transmit a management frame. On failure we reclaim the skbuff.
9503147Sxc151355 * Note that management frames come directly from the 802.11 layer
9513147Sxc151355 * and do not honor the send queue flow control. Need to investigate
9523147Sxc151355 * using priority queueing so management frames can bypass data.
9533147Sxc151355 */
9541000Sxc151355 static int
ath_xmit(ieee80211com_t * ic,mblk_t * mp,uint8_t type)9553147Sxc151355 ath_xmit(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
9561000Sxc151355 {
9573147Sxc151355 ath_t *asc = (ath_t *)ic;
9583147Sxc151355 struct ath_hal *ah = asc->asc_ah;
9593147Sxc151355 struct ieee80211_node *in = NULL;
9601000Sxc151355 struct ath_buf *bf = NULL;
9613147Sxc151355 struct ieee80211_frame *wh;
9623147Sxc151355 int error = 0;
9633147Sxc151355
9643147Sxc151355 ASSERT(mp->b_next == NULL);
9653147Sxc151355
9666797Sxc151355 if (!ATH_IS_RUNNING(asc)) {
9676797Sxc151355 if ((type & IEEE80211_FC0_TYPE_MASK) !=
9686797Sxc151355 IEEE80211_FC0_TYPE_DATA) {
9696797Sxc151355 freemsg(mp);
9706797Sxc151355 }
9716797Sxc151355 return (ENXIO);
9726797Sxc151355 }
9736797Sxc151355
9743147Sxc151355 /* Grab a TX buffer */
9753147Sxc151355 mutex_enter(&asc->asc_txbuflock);
9763147Sxc151355 bf = list_head(&asc->asc_txbuf_list);
9773147Sxc151355 if (bf != NULL)
9783147Sxc151355 list_remove(&asc->asc_txbuf_list, bf);
9793147Sxc151355 if (list_empty(&asc->asc_txbuf_list)) {
9803147Sxc151355 ATH_DEBUG((ATH_DBG_SEND, "ath: ath_mgmt_send(): "
9813147Sxc151355 "stop queue\n"));
9823147Sxc151355 asc->asc_stats.ast_tx_qstop++;
9833147Sxc151355 }
9843147Sxc151355 mutex_exit(&asc->asc_txbuflock);
9853147Sxc151355 if (bf == NULL) {
9863147Sxc151355 ATH_DEBUG((ATH_DBG_SEND, "ath: ath_mgmt_send(): discard, "
9873147Sxc151355 "no xmit buf\n"));
9883147Sxc151355 ic->ic_stats.is_tx_nobuf++;
9893147Sxc151355 if ((type & IEEE80211_FC0_TYPE_MASK) ==
9903147Sxc151355 IEEE80211_FC0_TYPE_DATA) {
9913147Sxc151355 asc->asc_stats.ast_tx_nobuf++;
9923147Sxc151355 mutex_enter(&asc->asc_resched_lock);
9933147Sxc151355 asc->asc_resched_needed = B_TRUE;
9943147Sxc151355 mutex_exit(&asc->asc_resched_lock);
9953147Sxc151355 } else {
9963147Sxc151355 asc->asc_stats.ast_tx_nobufmgt++;
9973147Sxc151355 freemsg(mp);
9983147Sxc151355 }
9993147Sxc151355 return (ENOMEM);
10003147Sxc151355 }
10013147Sxc151355
10023147Sxc151355 wh = (struct ieee80211_frame *)mp->b_rptr;
10033147Sxc151355
10043147Sxc151355 /* Locate node */
10053147Sxc151355 in = ieee80211_find_txnode(ic, wh->i_addr1);
10063147Sxc151355 if (in == NULL) {
10073147Sxc151355 error = EIO;
10083147Sxc151355 goto bad;
10093147Sxc151355 }
10103147Sxc151355
10113147Sxc151355 in->in_inact = 0;
10123147Sxc151355 switch (type & IEEE80211_FC0_TYPE_MASK) {
10133147Sxc151355 case IEEE80211_FC0_TYPE_DATA:
10143147Sxc151355 (void) ieee80211_encap(ic, mp, in);
10153147Sxc151355 break;
10163147Sxc151355 default:
10173147Sxc151355 if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
10183147Sxc151355 IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
10193147Sxc151355 /* fill time stamp */
10203147Sxc151355 uint64_t tsf;
10213147Sxc151355 uint32_t *tstamp;
10223147Sxc151355
10233147Sxc151355 tsf = ATH_HAL_GETTSF64(ah);
10243147Sxc151355 /* adjust 100us delay to xmit */
10253147Sxc151355 tsf += 100;
10267249Sff224033 /* LINTED E_BAD_PTR_CAST_ALIGN */
10273147Sxc151355 tstamp = (uint32_t *)&wh[1];
10283147Sxc151355 tstamp[0] = LE_32(tsf & 0xffffffff);
10293147Sxc151355 tstamp[1] = LE_32(tsf >> 32);
10303147Sxc151355 }
10313147Sxc151355 asc->asc_stats.ast_tx_mgmt++;
10323147Sxc151355 break;
10333147Sxc151355 }
10343147Sxc151355
10353147Sxc151355 error = ath_tx_start(asc, in, bf, mp);
10363147Sxc151355 if (error != 0) {
10373147Sxc151355 bad:
10383147Sxc151355 ic->ic_stats.is_tx_failed++;
10393147Sxc151355 if (bf != NULL) {
10403147Sxc151355 mutex_enter(&asc->asc_txbuflock);
10413147Sxc151355 list_insert_tail(&asc->asc_txbuf_list, bf);
10423147Sxc151355 mutex_exit(&asc->asc_txbuflock);
10433147Sxc151355 }
10443147Sxc151355 }
10453147Sxc151355 if (in != NULL)
10463147Sxc151355 ieee80211_free_node(in);
10473147Sxc151355 if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA ||
10483147Sxc151355 error == 0) {
10493147Sxc151355 freemsg(mp);
10503147Sxc151355 }
10513147Sxc151355
10523147Sxc151355 return (error);
10533147Sxc151355 }
10543147Sxc151355
10553147Sxc151355 static mblk_t *
ath_m_tx(void * arg,mblk_t * mp)10563147Sxc151355 ath_m_tx(void *arg, mblk_t *mp)
10573147Sxc151355 {
10583147Sxc151355 ath_t *asc = arg;
10593147Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc;
10603147Sxc151355 mblk_t *next;
10614126Szf162725 int error = 0;
10621000Sxc151355
10631000Sxc151355 /*
10641000Sxc151355 * No data frames go out unless we're associated; this
10651000Sxc151355 * should not happen as the 802.11 layer does not enable
10661000Sxc151355 * the xmit queue until we enter the RUN state.
10671000Sxc151355 */
10683147Sxc151355 if (ic->ic_state != IEEE80211_S_RUN) {
10693147Sxc151355 ATH_DEBUG((ATH_DBG_SEND, "ath: ath_m_tx(): "
10703147Sxc151355 "discard, state %u\n", ic->ic_state));
10713631Sxh158540 asc->asc_stats.ast_tx_discard++;
10723315Sxc151355 freemsgchain(mp);
10733315Sxc151355 return (NULL);
10741000Sxc151355 }
10751000Sxc151355
10763147Sxc151355 while (mp != NULL) {
10773147Sxc151355 next = mp->b_next;
10783147Sxc151355 mp->b_next = NULL;
10794126Szf162725 error = ath_xmit(ic, mp, IEEE80211_FC0_TYPE_DATA);
10804126Szf162725 if (error != 0) {
10813147Sxc151355 mp->b_next = next;
10824126Szf162725 if (error == ENOMEM) {
10834126Szf162725 break;
10844126Szf162725 } else {
10854126Szf162725 freemsgchain(mp); /* CR6501759 issues */
10864126Szf162725 return (NULL);
10874126Szf162725 }
10883147Sxc151355 }
10893147Sxc151355 mp = next;
10901000Sxc151355 }
10911000Sxc151355
10923147Sxc151355 return (mp);
10931000Sxc151355 }
10941000Sxc151355
10953147Sxc151355 static int
ath_tx_processq(ath_t * asc,struct ath_txq * txq)10961000Sxc151355 ath_tx_processq(ath_t *asc, struct ath_txq *txq)
10971000Sxc151355 {
10983147Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc;
10991000Sxc151355 struct ath_hal *ah = asc->asc_ah;
11001000Sxc151355 struct ath_buf *bf;
11011000Sxc151355 struct ath_desc *ds;
11021000Sxc151355 struct ieee80211_node *in;
11033147Sxc151355 int32_t sr, lr, nacked = 0;
11048033SWang.Lin@Sun.COM struct ath_tx_status *ts;
11051000Sxc151355 HAL_STATUS status;
11061000Sxc151355 struct ath_node *an;
11071000Sxc151355
11081000Sxc151355 for (;;) {
11091000Sxc151355 mutex_enter(&txq->axq_lock);
11101000Sxc151355 bf = list_head(&txq->axq_list);
11111000Sxc151355 if (bf == NULL) {
11121000Sxc151355 txq->axq_link = NULL;
11131000Sxc151355 mutex_exit(&txq->axq_lock);
11141000Sxc151355 break;
11151000Sxc151355 }
11161000Sxc151355 ds = bf->bf_desc; /* last decriptor */
11178033SWang.Lin@Sun.COM ts = &bf->bf_status.ds_txstat;
11188033SWang.Lin@Sun.COM status = ATH_HAL_TXPROCDESC(ah, ds, ts);
11191000Sxc151355 #ifdef DEBUG
11201000Sxc151355 ath_printtxbuf(bf, status == HAL_OK);
11211000Sxc151355 #endif
11221000Sxc151355 if (status == HAL_EINPROGRESS) {
11231000Sxc151355 mutex_exit(&txq->axq_lock);
11241000Sxc151355 break;
11251000Sxc151355 }
11261000Sxc151355 list_remove(&txq->axq_list, bf);
11271000Sxc151355 mutex_exit(&txq->axq_lock);
11281000Sxc151355 in = bf->bf_in;
11291000Sxc151355 if (in != NULL) {
11301000Sxc151355 an = ATH_NODE(in);
11311000Sxc151355 /* Successful transmition */
11328033SWang.Lin@Sun.COM if (ts->ts_status == 0) {
11331000Sxc151355 an->an_tx_ok++;
11348033SWang.Lin@Sun.COM an->an_tx_antenna = ts->ts_antenna;
11358033SWang.Lin@Sun.COM if (ts->ts_rate & HAL_TXSTAT_ALTRATE)
11361000Sxc151355 asc->asc_stats.ast_tx_altrate++;
11371000Sxc151355 asc->asc_stats.ast_tx_rssidelta =
11388033SWang.Lin@Sun.COM ts->ts_rssi - asc->asc_stats.ast_tx_rssi;
11398033SWang.Lin@Sun.COM asc->asc_stats.ast_tx_rssi = ts->ts_rssi;
11401000Sxc151355 } else {
11411000Sxc151355 an->an_tx_err++;
11428033SWang.Lin@Sun.COM if (ts->ts_status & HAL_TXERR_XRETRY)
11438033SWang.Lin@Sun.COM asc->asc_stats.ast_tx_xretries++;
11448033SWang.Lin@Sun.COM if (ts->ts_status & HAL_TXERR_FIFO)
11451000Sxc151355 asc->asc_stats.ast_tx_fifoerr++;
11468033SWang.Lin@Sun.COM if (ts->ts_status & HAL_TXERR_FILT)
11478033SWang.Lin@Sun.COM asc->asc_stats.ast_tx_filtered++;
11481000Sxc151355 an->an_tx_antenna = 0; /* invalidate */
11491000Sxc151355 }
11508033SWang.Lin@Sun.COM sr = ts->ts_shortretry;
11518033SWang.Lin@Sun.COM lr = ts->ts_longretry;
11521000Sxc151355 asc->asc_stats.ast_tx_shortretry += sr;
11531000Sxc151355 asc->asc_stats.ast_tx_longretry += lr;
11543147Sxc151355 /*
11553147Sxc151355 * Hand the descriptor to the rate control algorithm.
11563147Sxc151355 */
11578033SWang.Lin@Sun.COM if ((ts->ts_status & HAL_TXERR_FILT) == 0 &&
11583147Sxc151355 (bf->bf_flags & HAL_TXDESC_NOACK) == 0) {
11593147Sxc151355 /*
11603147Sxc151355 * If frame was ack'd update the last rx time
11613147Sxc151355 * used to workaround phantom bmiss interrupts.
11623147Sxc151355 */
11638033SWang.Lin@Sun.COM if (ts->ts_status == 0) {
11643147Sxc151355 nacked++;
11653147Sxc151355 an->an_tx_ok++;
11663147Sxc151355 } else {
11673147Sxc151355 an->an_tx_err++;
11683147Sxc151355 }
11693147Sxc151355 an->an_tx_retr += sr + lr;
11703147Sxc151355 }
11711000Sxc151355 }
11721000Sxc151355 bf->bf_in = NULL;
11731000Sxc151355 mutex_enter(&asc->asc_txbuflock);
11741000Sxc151355 list_insert_tail(&asc->asc_txbuf_list, bf);
11751000Sxc151355 mutex_exit(&asc->asc_txbuflock);
11761000Sxc151355 /*
11771000Sxc151355 * Reschedule stalled outbound packets
11781000Sxc151355 */
11793147Sxc151355 mutex_enter(&asc->asc_resched_lock);
11803147Sxc151355 if (asc->asc_resched_needed) {
11813147Sxc151355 asc->asc_resched_needed = B_FALSE;
11823147Sxc151355 mac_tx_update(ic->ic_mach);
11831000Sxc151355 }
11843147Sxc151355 mutex_exit(&asc->asc_resched_lock);
11851000Sxc151355 }
11863147Sxc151355 return (nacked);
11871000Sxc151355 }
11881000Sxc151355
11891000Sxc151355
11901000Sxc151355 static void
ath_tx_handler(ath_t * asc)11911000Sxc151355 ath_tx_handler(ath_t *asc)
11921000Sxc151355 {
11931000Sxc151355 int i;
11941000Sxc151355
11951000Sxc151355 /*
11961000Sxc151355 * Process each active queue.
11971000Sxc151355 */
11981000Sxc151355 for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
11991000Sxc151355 if (ATH_TXQ_SETUP(asc, i)) {
12003147Sxc151355 (void) ath_tx_processq(asc, &asc->asc_txq[i]);
12011000Sxc151355 }
12021000Sxc151355 }
12031000Sxc151355 }
12041000Sxc151355
12051000Sxc151355 static struct ieee80211_node *
ath_node_alloc(ieee80211com_t * ic)12063147Sxc151355 ath_node_alloc(ieee80211com_t *ic)
12071000Sxc151355 {
12081000Sxc151355 struct ath_node *an;
12093147Sxc151355 ath_t *asc = (ath_t *)ic;
12101000Sxc151355
12111000Sxc151355 an = kmem_zalloc(sizeof (struct ath_node), KM_SLEEP);
12121000Sxc151355 ath_rate_update(asc, &an->an_node, 0);
12131000Sxc151355 return (&an->an_node);
12141000Sxc151355 }
12151000Sxc151355
12161000Sxc151355 static void
ath_node_free(struct ieee80211_node * in)12173147Sxc151355 ath_node_free(struct ieee80211_node *in)
12181000Sxc151355 {
12193147Sxc151355 ieee80211com_t *ic = in->in_ic;
12203147Sxc151355 ath_t *asc = (ath_t *)ic;
12211000Sxc151355 struct ath_buf *bf;
12221000Sxc151355 struct ath_txq *txq;
12231000Sxc151355 int32_t i;
12241000Sxc151355
12251000Sxc151355 for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
12261000Sxc151355 if (ATH_TXQ_SETUP(asc, i)) {
12271000Sxc151355 txq = &asc->asc_txq[i];
12281000Sxc151355 mutex_enter(&txq->axq_lock);
12291000Sxc151355 bf = list_head(&txq->axq_list);
12301000Sxc151355 while (bf != NULL) {
12311000Sxc151355 if (bf->bf_in == in) {
12321000Sxc151355 bf->bf_in = NULL;
12331000Sxc151355 }
12341000Sxc151355 bf = list_next(&txq->axq_list, bf);
12351000Sxc151355 }
12361000Sxc151355 mutex_exit(&txq->axq_lock);
12371000Sxc151355 }
12381000Sxc151355 }
12393147Sxc151355 ic->ic_node_cleanup(in);
12404126Szf162725 if (in->in_wpa_ie != NULL)
12414126Szf162725 ieee80211_free(in->in_wpa_ie);
12421000Sxc151355 kmem_free(in, sizeof (struct ath_node));
12431000Sxc151355 }
12441000Sxc151355
12451000Sxc151355 static void
ath_next_scan(void * arg)12463147Sxc151355 ath_next_scan(void *arg)
12471000Sxc151355 {
12483147Sxc151355 ieee80211com_t *ic = arg;
12493147Sxc151355 ath_t *asc = (ath_t *)ic;
12503147Sxc151355
12513147Sxc151355 asc->asc_scan_timer = 0;
12523147Sxc151355 if (ic->ic_state == IEEE80211_S_SCAN) {
12533147Sxc151355 asc->asc_scan_timer = timeout(ath_next_scan, (void *)asc,
12543147Sxc151355 drv_usectohz(ath_dwelltime * 1000));
12553147Sxc151355 ieee80211_next_scan(ic);
12563147Sxc151355 }
12571000Sxc151355 }
12581000Sxc151355
12593147Sxc151355 static void
ath_stop_scantimer(ath_t * asc)12603147Sxc151355 ath_stop_scantimer(ath_t *asc)
12611000Sxc151355 {
12623147Sxc151355 timeout_id_t tmp_id = 0;
12631000Sxc151355
12643147Sxc151355 while ((asc->asc_scan_timer != 0) && (tmp_id != asc->asc_scan_timer)) {
12653147Sxc151355 tmp_id = asc->asc_scan_timer;
12663147Sxc151355 (void) untimeout(tmp_id);
12671000Sxc151355 }
12683147Sxc151355 asc->asc_scan_timer = 0;
12691000Sxc151355 }
12701000Sxc151355
12711000Sxc151355 static int32_t
ath_newstate(ieee80211com_t * ic,enum ieee80211_state nstate,int arg)12723147Sxc151355 ath_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
12731000Sxc151355 {
12743147Sxc151355 ath_t *asc = (ath_t *)ic;
12751000Sxc151355 struct ath_hal *ah = asc->asc_ah;
12761000Sxc151355 struct ieee80211_node *in;
12771000Sxc151355 int32_t i, error;
12781000Sxc151355 uint8_t *bssid;
12791000Sxc151355 uint32_t rfilt;
12801000Sxc151355 enum ieee80211_state ostate;
12811000Sxc151355
12821000Sxc151355 static const HAL_LED_STATE leds[] = {
12831000Sxc151355 HAL_LED_INIT, /* IEEE80211_S_INIT */
12841000Sxc151355 HAL_LED_SCAN, /* IEEE80211_S_SCAN */
12851000Sxc151355 HAL_LED_AUTH, /* IEEE80211_S_AUTH */
12861000Sxc151355 HAL_LED_ASSOC, /* IEEE80211_S_ASSOC */
12871000Sxc151355 HAL_LED_RUN, /* IEEE80211_S_RUN */
12881000Sxc151355 };
12893147Sxc151355 if (!ATH_IS_RUNNING(asc))
12901000Sxc151355 return (0);
12911000Sxc151355
12923147Sxc151355 ostate = ic->ic_state;
12933147Sxc151355 if (nstate != IEEE80211_S_SCAN)
12943147Sxc151355 ath_stop_scantimer(asc);
12951000Sxc151355
12963147Sxc151355 ATH_LOCK(asc);
12971000Sxc151355 ATH_HAL_SETLEDSTATE(ah, leds[nstate]); /* set LED */
12981000Sxc151355
12991000Sxc151355 if (nstate == IEEE80211_S_INIT) {
13001000Sxc151355 asc->asc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
13018033SWang.Lin@Sun.COM /*
13028033SWang.Lin@Sun.COM * Disable interrupts.
13038033SWang.Lin@Sun.COM */
13043147Sxc151355 ATH_HAL_INTRSET(ah, asc->asc_imask &~ HAL_INT_GLOBAL);
13053147Sxc151355 ATH_UNLOCK(asc);
13063147Sxc151355 goto done;
13071000Sxc151355 }
13083147Sxc151355 in = ic->ic_bss;
13093147Sxc151355 error = ath_chan_set(asc, ic->ic_curchan);
13103147Sxc151355 if (error != 0) {
13113147Sxc151355 if (nstate != IEEE80211_S_SCAN) {
13123147Sxc151355 ATH_UNLOCK(asc);
13133147Sxc151355 ieee80211_reset_chan(ic);
13143147Sxc151355 goto bad;
13153147Sxc151355 }
13163147Sxc151355 }
13171000Sxc151355
13181000Sxc151355 rfilt = ath_calcrxfilter(asc);
13198033SWang.Lin@Sun.COM
13201000Sxc151355 if (nstate == IEEE80211_S_SCAN)
13213147Sxc151355 bssid = ic->ic_macaddr;
13221000Sxc151355 else
13231000Sxc151355 bssid = in->in_bssid;
13241000Sxc151355 ATH_HAL_SETRXFILTER(ah, rfilt);
13251000Sxc151355
13263147Sxc151355 if (nstate == IEEE80211_S_RUN && ic->ic_opmode != IEEE80211_M_IBSS)
13271000Sxc151355 ATH_HAL_SETASSOCID(ah, bssid, in->in_associd);
13281000Sxc151355 else
13291000Sxc151355 ATH_HAL_SETASSOCID(ah, bssid, 0);
13303147Sxc151355 if (ic->ic_flags & IEEE80211_F_PRIVACY) {
13311000Sxc151355 for (i = 0; i < IEEE80211_WEP_NKID; i++) {
13321000Sxc151355 if (ATH_HAL_KEYISVALID(ah, i))
13331000Sxc151355 ATH_HAL_KEYSETMAC(ah, i, bssid);
13341000Sxc151355 }
13351000Sxc151355 }
13361000Sxc151355
13371000Sxc151355 if ((nstate == IEEE80211_S_RUN) &&
13381000Sxc151355 (ostate != IEEE80211_S_RUN)) {
13391000Sxc151355 /* Configure the beacon and sleep timers. */
13401000Sxc151355 ath_beacon_config(asc);
13411000Sxc151355 } else {
13421000Sxc151355 asc->asc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
13431000Sxc151355 ATH_HAL_INTRSET(ah, asc->asc_imask);
13441000Sxc151355 }
13451000Sxc151355 /*
13461000Sxc151355 * Reset the rate control state.
13471000Sxc151355 */
13481000Sxc151355 ath_rate_ctl_reset(asc, nstate);
13491000Sxc151355
13503147Sxc151355 ATH_UNLOCK(asc);
13513147Sxc151355 done:
13523147Sxc151355 /*
13533147Sxc151355 * Invoke the parent method to complete the work.
13543147Sxc151355 */
13553147Sxc151355 error = asc->asc_newstate(ic, nstate, arg);
13563147Sxc151355 /*
13573147Sxc151355 * Finally, start any timers.
13583147Sxc151355 */
13593147Sxc151355 if (nstate == IEEE80211_S_RUN) {
13603147Sxc151355 ieee80211_start_watchdog(ic, 1);
13613147Sxc151355 } else if ((nstate == IEEE80211_S_SCAN) && (ostate != nstate)) {
13623147Sxc151355 /* start ap/neighbor scan timer */
13633147Sxc151355 ASSERT(asc->asc_scan_timer == 0);
13643147Sxc151355 asc->asc_scan_timer = timeout(ath_next_scan, (void *)asc,
13653147Sxc151355 drv_usectohz(ath_dwelltime * 1000));
13663147Sxc151355 }
13671000Sxc151355 bad:
13681000Sxc151355 return (error);
13691000Sxc151355 }
13701000Sxc151355
13711000Sxc151355 /*
13721000Sxc151355 * Periodically recalibrate the PHY to account
13731000Sxc151355 * for temperature/environment changes.
13741000Sxc151355 */
13751000Sxc151355 static void
ath_calibrate(ath_t * asc)13763147Sxc151355 ath_calibrate(ath_t *asc)
13771000Sxc151355 {
13781000Sxc151355 struct ath_hal *ah = asc->asc_ah;
13793147Sxc151355 HAL_BOOL iqcaldone;
13801000Sxc151355
13811000Sxc151355 asc->asc_stats.ast_per_cal++;
13821000Sxc151355
13831000Sxc151355 if (ATH_HAL_GETRFGAIN(ah) == HAL_RFGAIN_NEED_CHANGE) {
13841000Sxc151355 /*
13851000Sxc151355 * Rfgain is out of bounds, reset the chip
13861000Sxc151355 * to load new gain values.
13871000Sxc151355 */
13881000Sxc151355 ATH_DEBUG((ATH_DBG_HAL, "ath: ath_calibrate(): "
13891000Sxc151355 "Need change RFgain\n"));
13901000Sxc151355 asc->asc_stats.ast_per_rfgain++;
13913147Sxc151355 (void) ath_reset(&asc->asc_isc);
13921000Sxc151355 }
13933147Sxc151355 if (!ATH_HAL_CALIBRATE(ah, &asc->asc_curchan, &iqcaldone)) {
13941000Sxc151355 ATH_DEBUG((ATH_DBG_HAL, "ath: ath_calibrate(): "
13951000Sxc151355 "calibration of channel %u failed\n",
13963147Sxc151355 asc->asc_curchan.channel));
13971000Sxc151355 asc->asc_stats.ast_per_calfail++;
13981000Sxc151355 }
13991000Sxc151355 }
14001000Sxc151355
14013147Sxc151355 static void
ath_watchdog(void * arg)14023147Sxc151355 ath_watchdog(void *arg)
14033147Sxc151355 {
14043147Sxc151355 ath_t *asc = arg;
14053147Sxc151355 ieee80211com_t *ic = &asc->asc_isc;
14063147Sxc151355 int ntimer = 0;
14073147Sxc151355
14083147Sxc151355 ATH_LOCK(asc);
14093147Sxc151355 ic->ic_watchdog_timer = 0;
14103147Sxc151355 if (!ATH_IS_RUNNING(asc)) {
14113147Sxc151355 ATH_UNLOCK(asc);
14123147Sxc151355 return;
14133147Sxc151355 }
14143147Sxc151355
14153147Sxc151355 if (ic->ic_state == IEEE80211_S_RUN) {
14163147Sxc151355 /* periodic recalibration */
14173147Sxc151355 ath_calibrate(asc);
14183147Sxc151355
14193147Sxc151355 /*
14203147Sxc151355 * Start the background rate control thread if we
14213147Sxc151355 * are not configured to use a fixed xmit rate.
14223147Sxc151355 */
14233147Sxc151355 if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
14243147Sxc151355 asc->asc_stats.ast_rate_calls ++;
14253147Sxc151355 if (ic->ic_opmode == IEEE80211_M_STA)
14263147Sxc151355 ath_rate_ctl(ic, ic->ic_bss);
14273147Sxc151355 else
14283147Sxc151355 ieee80211_iterate_nodes(&ic->ic_sta,
14298033SWang.Lin@Sun.COM ath_rate_ctl, asc);
14303147Sxc151355 }
14313147Sxc151355
14323147Sxc151355 ntimer = 1;
14333147Sxc151355 }
14343147Sxc151355 ATH_UNLOCK(asc);
14353147Sxc151355
14363147Sxc151355 ieee80211_watchdog(ic);
14373147Sxc151355 if (ntimer != 0)
14383147Sxc151355 ieee80211_start_watchdog(ic, ntimer);
14393147Sxc151355 }
14403147Sxc151355
14418033SWang.Lin@Sun.COM static void
ath_tx_proc(void * arg)14428033SWang.Lin@Sun.COM ath_tx_proc(void *arg)
14438033SWang.Lin@Sun.COM {
14448033SWang.Lin@Sun.COM ath_t *asc = arg;
14458033SWang.Lin@Sun.COM ath_tx_handler(asc);
14468033SWang.Lin@Sun.COM }
14478033SWang.Lin@Sun.COM
14488033SWang.Lin@Sun.COM
14491000Sxc151355 static uint_t
ath_intr(caddr_t arg)14503147Sxc151355 ath_intr(caddr_t arg)
14511000Sxc151355 {
14527249Sff224033 /* LINTED E_BAD_PTR_CAST_ALIGN */
14533147Sxc151355 ath_t *asc = (ath_t *)arg;
14541000Sxc151355 struct ath_hal *ah = asc->asc_ah;
14551000Sxc151355 HAL_INT status;
14563147Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc;
14573147Sxc151355
14583147Sxc151355 ATH_LOCK(asc);
14591000Sxc151355
14603147Sxc151355 if (!ATH_IS_RUNNING(asc)) {
14613147Sxc151355 /*
14623147Sxc151355 * The hardware is not ready/present, don't touch anything.
14633147Sxc151355 * Note this can happen early on if the IRQ is shared.
14643147Sxc151355 */
14653147Sxc151355 ATH_UNLOCK(asc);
14663147Sxc151355 return (DDI_INTR_UNCLAIMED);
14673147Sxc151355 }
14681000Sxc151355
14691000Sxc151355 if (!ATH_HAL_INTRPEND(ah)) { /* shared irq, not for us */
14703147Sxc151355 ATH_UNLOCK(asc);
14711000Sxc151355 return (DDI_INTR_UNCLAIMED);
14721000Sxc151355 }
14731000Sxc151355
14741000Sxc151355 ATH_HAL_GETISR(ah, &status);
14751000Sxc151355 status &= asc->asc_imask;
14761000Sxc151355 if (status & HAL_INT_FATAL) {
14771000Sxc151355 asc->asc_stats.ast_hardware++;
14781000Sxc151355 goto reset;
14791000Sxc151355 } else if (status & HAL_INT_RXORN) {
14801000Sxc151355 asc->asc_stats.ast_rxorn++;
14811000Sxc151355 goto reset;
14821000Sxc151355 } else {
14831000Sxc151355 if (status & HAL_INT_RXEOL) {
14841000Sxc151355 asc->asc_stats.ast_rxeol++;
14851000Sxc151355 asc->asc_rxlink = NULL;
14861000Sxc151355 }
14871000Sxc151355 if (status & HAL_INT_TXURN) {
14881000Sxc151355 asc->asc_stats.ast_txurn++;
14891000Sxc151355 ATH_HAL_UPDATETXTRIGLEVEL(ah, AH_TRUE);
14901000Sxc151355 }
14913147Sxc151355
14921000Sxc151355 if (status & HAL_INT_RX) {
14931000Sxc151355 asc->asc_rx_pend = 1;
14941000Sxc151355 ddi_trigger_softintr(asc->asc_softint_id);
14951000Sxc151355 }
14961000Sxc151355 if (status & HAL_INT_TX) {
14978033SWang.Lin@Sun.COM if (ddi_taskq_dispatch(asc->asc_tq, ath_tx_proc,
14988033SWang.Lin@Sun.COM asc, DDI_NOSLEEP) != DDI_SUCCESS) {
14998033SWang.Lin@Sun.COM ath_problem("ath: ath_intr(): "
15008033SWang.Lin@Sun.COM "No memory available for tx taskq\n");
15018033SWang.Lin@Sun.COM }
15021000Sxc151355 }
15033147Sxc151355 ATH_UNLOCK(asc);
15041000Sxc151355
15051000Sxc151355 if (status & HAL_INT_SWBA) {
15061000Sxc151355 /* This will occur only in Host-AP or Ad-Hoc mode */
15071000Sxc151355 return (DDI_INTR_CLAIMED);
15081000Sxc151355 }
15098033SWang.Lin@Sun.COM
15101000Sxc151355 if (status & HAL_INT_BMISS) {
15113147Sxc151355 if (ic->ic_state == IEEE80211_S_RUN) {
15123147Sxc151355 (void) ieee80211_new_state(ic,
15131000Sxc151355 IEEE80211_S_ASSOC, -1);
15141000Sxc151355 }
15151000Sxc151355 }
15168033SWang.Lin@Sun.COM
15171000Sxc151355 }
15181000Sxc151355
15191000Sxc151355 return (DDI_INTR_CLAIMED);
15201000Sxc151355 reset:
15213147Sxc151355 (void) ath_reset(ic);
15223147Sxc151355 ATH_UNLOCK(asc);
15231000Sxc151355 return (DDI_INTR_CLAIMED);
15241000Sxc151355 }
15251000Sxc151355
15261000Sxc151355 static uint_t
ath_softint_handler(caddr_t data)15271000Sxc151355 ath_softint_handler(caddr_t data)
15281000Sxc151355 {
15297249Sff224033 /* LINTED E_BAD_PTR_CAST_ALIGN */
15301000Sxc151355 ath_t *asc = (ath_t *)data;
15311000Sxc151355
15321000Sxc151355 /*
15331000Sxc151355 * Check if the soft interrupt is triggered by another
15341000Sxc151355 * driver at the same level.
15351000Sxc151355 */
15363147Sxc151355 ATH_LOCK(asc);
15371000Sxc151355 if (asc->asc_rx_pend) { /* Soft interrupt for this driver */
15381000Sxc151355 asc->asc_rx_pend = 0;
15393147Sxc151355 ATH_UNLOCK(asc);
15403147Sxc151355 ath_rx_handler(asc);
15411000Sxc151355 return (DDI_INTR_CLAIMED);
15421000Sxc151355 }
15433147Sxc151355 ATH_UNLOCK(asc);
15441000Sxc151355 return (DDI_INTR_UNCLAIMED);
15451000Sxc151355 }
15461000Sxc151355
15471000Sxc151355 /*
15481000Sxc151355 * following are gld callback routine
15491000Sxc151355 * ath_gld_send, ath_gld_ioctl, ath_gld_gstat
15501000Sxc151355 * are listed in other corresponding sections.
15511000Sxc151355 * reset the hardware w/o losing operational state. this is
15521000Sxc151355 * basically a more efficient way of doing ath_gld_stop, ath_gld_start,
15531000Sxc151355 * followed by state transitions to the current 802.11
15541000Sxc151355 * operational state. used to recover from errors rx overrun
15551000Sxc151355 * and to reset the hardware when rf gain settings must be reset.
15561000Sxc151355 */
15571000Sxc151355
15583147Sxc151355 static void
ath_stop_locked(ath_t * asc)15593147Sxc151355 ath_stop_locked(ath_t *asc)
15601000Sxc151355 {
15613147Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc;
15623147Sxc151355 struct ath_hal *ah = asc->asc_ah;
15631000Sxc151355
15643147Sxc151355 ATH_LOCK_ASSERT(asc);
15656797Sxc151355 if (!asc->asc_isrunning)
15666797Sxc151355 return;
15676797Sxc151355
15683147Sxc151355 /*
15693147Sxc151355 * Shutdown the hardware and driver:
15703147Sxc151355 * reset 802.11 state machine
15713147Sxc151355 * turn off timers
15723147Sxc151355 * disable interrupts
15733147Sxc151355 * turn off the radio
15743147Sxc151355 * clear transmit machinery
15753147Sxc151355 * clear receive machinery
15763147Sxc151355 * drain and release tx queues
15773147Sxc151355 * reclaim beacon resources
15783147Sxc151355 * power down hardware
15793147Sxc151355 *
15803147Sxc151355 * Note that some of this work is not possible if the
15813147Sxc151355 * hardware is gone (invalid).
15823147Sxc151355 */
15833147Sxc151355 ATH_UNLOCK(asc);
15843147Sxc151355 ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
15853147Sxc151355 ieee80211_stop_watchdog(ic);
15863147Sxc151355 ATH_LOCK(asc);
15873147Sxc151355 ATH_HAL_INTRSET(ah, 0);
15883147Sxc151355 ath_draintxq(asc);
15896797Sxc151355 if (!asc->asc_invalid) {
15903147Sxc151355 ath_stoprecv(asc);
15913147Sxc151355 ATH_HAL_PHYDISABLE(ah);
15923147Sxc151355 } else {
15933147Sxc151355 asc->asc_rxlink = NULL;
15943147Sxc151355 }
15956797Sxc151355 asc->asc_isrunning = 0;
15961000Sxc151355 }
15971000Sxc151355
15983147Sxc151355 static void
ath_m_stop(void * arg)15993147Sxc151355 ath_m_stop(void *arg)
16001000Sxc151355 {
16013147Sxc151355 ath_t *asc = arg;
16021000Sxc151355 struct ath_hal *ah = asc->asc_ah;
16031000Sxc151355
16043147Sxc151355 ATH_LOCK(asc);
16053147Sxc151355 ath_stop_locked(asc);
16063147Sxc151355 ATH_HAL_SETPOWER(ah, HAL_PM_AWAKE);
16071000Sxc151355 asc->asc_invalid = 1;
16083147Sxc151355 ATH_UNLOCK(asc);
16091000Sxc151355 }
16101000Sxc151355
16116797Sxc151355 static int
ath_start_locked(ath_t * asc)16126797Sxc151355 ath_start_locked(ath_t *asc)
16131000Sxc151355 {
16143147Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc;
16151000Sxc151355 struct ath_hal *ah = asc->asc_ah;
16161000Sxc151355 HAL_STATUS status;
16171000Sxc151355
16186797Sxc151355 ATH_LOCK_ASSERT(asc);
16191000Sxc151355
16201000Sxc151355 /*
16211000Sxc151355 * The basic interface to setting the hardware in a good
16221000Sxc151355 * state is ``reset''. On return the hardware is known to
16231000Sxc151355 * be powered up and with interrupts disabled. This must
16241000Sxc151355 * be followed by initialization of the appropriate bits
16251000Sxc151355 * and then setup of the interrupt mask.
16261000Sxc151355 */
16273147Sxc151355 asc->asc_curchan.channel = ic->ic_curchan->ich_freq;
16283147Sxc151355 asc->asc_curchan.channelFlags = ath_chan2flags(ic, ic->ic_curchan);
16293147Sxc151355 if (!ATH_HAL_RESET(ah, (HAL_OPMODE)ic->ic_opmode,
16303147Sxc151355 &asc->asc_curchan, AH_FALSE, &status)) {
16313147Sxc151355 ATH_DEBUG((ATH_DBG_HAL, "ath: ath_m_start(): "
16326235Sxc151355 "reset hardware failed: '%s' (HAL status %u)\n",
16336235Sxc151355 ath_get_hal_status_desc(status), status));
16343147Sxc151355 return (ENOTACTIVE);
16351000Sxc151355 }
16361000Sxc151355
16373147Sxc151355 (void) ath_startrecv(asc);
16381000Sxc151355
16391000Sxc151355 /*
16401000Sxc151355 * Enable interrupts.
16411000Sxc151355 */
16421000Sxc151355 asc->asc_imask = HAL_INT_RX | HAL_INT_TX
16431000Sxc151355 | HAL_INT_RXEOL | HAL_INT_RXORN
16441000Sxc151355 | HAL_INT_FATAL | HAL_INT_GLOBAL;
16451000Sxc151355 ATH_HAL_INTRSET(ah, asc->asc_imask);
16461000Sxc151355
16471000Sxc151355 /*
16481000Sxc151355 * The hardware should be ready to go now so it's safe
16491000Sxc151355 * to kick the 802.11 state machine as it's likely to
16501000Sxc151355 * immediately call back to us to send mgmt frames.
16511000Sxc151355 */
16523147Sxc151355 ath_chan_change(asc, ic->ic_curchan);
16536797Sxc151355
16546797Sxc151355 asc->asc_isrunning = 1;
16556797Sxc151355
16566797Sxc151355 return (0);
16576797Sxc151355 }
16586797Sxc151355
16596797Sxc151355 int
ath_m_start(void * arg)16606797Sxc151355 ath_m_start(void *arg)
16616797Sxc151355 {
16626797Sxc151355 ath_t *asc = arg;
16636797Sxc151355 int err;
16646797Sxc151355
16656797Sxc151355 ATH_LOCK(asc);
16666797Sxc151355 /*
16676797Sxc151355 * Stop anything previously setup. This is safe
16686797Sxc151355 * whether this is the first time through or not.
16696797Sxc151355 */
16706797Sxc151355 ath_stop_locked(asc);
16716797Sxc151355
16726797Sxc151355 if ((err = ath_start_locked(asc)) != 0) {
16736797Sxc151355 ATH_UNLOCK(asc);
16746797Sxc151355 return (err);
16756797Sxc151355 }
16766797Sxc151355
16771000Sxc151355 asc->asc_invalid = 0;
16783147Sxc151355 ATH_UNLOCK(asc);
16796797Sxc151355
16803147Sxc151355 return (0);
16811000Sxc151355 }
16821000Sxc151355
16831000Sxc151355
16843147Sxc151355 static int
ath_m_unicst(void * arg,const uint8_t * macaddr)16853147Sxc151355 ath_m_unicst(void *arg, const uint8_t *macaddr)
16861000Sxc151355 {
16873147Sxc151355 ath_t *asc = arg;
16881000Sxc151355 struct ath_hal *ah = asc->asc_ah;
16891000Sxc151355
16901000Sxc151355 ATH_DEBUG((ATH_DBG_GLD, "ath: ath_gld_saddr(): "
16911000Sxc151355 "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
16921000Sxc151355 macaddr[0], macaddr[1], macaddr[2],
16931000Sxc151355 macaddr[3], macaddr[4], macaddr[5]));
16941000Sxc151355
16953147Sxc151355 ATH_LOCK(asc);
16963147Sxc151355 IEEE80211_ADDR_COPY(asc->asc_isc.ic_macaddr, macaddr);
16973147Sxc151355 ATH_HAL_SETMAC(ah, asc->asc_isc.ic_macaddr);
16981000Sxc151355
16993147Sxc151355 (void) ath_reset(&asc->asc_isc);
17003147Sxc151355 ATH_UNLOCK(asc);
17013147Sxc151355 return (0);
17021000Sxc151355 }
17031000Sxc151355
17041000Sxc151355 static int
ath_m_promisc(void * arg,boolean_t on)17053147Sxc151355 ath_m_promisc(void *arg, boolean_t on)
17061000Sxc151355 {
17073147Sxc151355 ath_t *asc = arg;
17081000Sxc151355 struct ath_hal *ah = asc->asc_ah;
17091000Sxc151355 uint32_t rfilt;
17101000Sxc151355
17113147Sxc151355 ATH_LOCK(asc);
17121000Sxc151355 rfilt = ATH_HAL_GETRXFILTER(ah);
17133147Sxc151355 if (on)
17143147Sxc151355 rfilt |= HAL_RX_FILTER_PROM;
17153147Sxc151355 else
17161000Sxc151355 rfilt &= ~HAL_RX_FILTER_PROM;
17176235Sxc151355 asc->asc_promisc = on;
17183147Sxc151355 ATH_HAL_SETRXFILTER(ah, rfilt);
17193147Sxc151355 ATH_UNLOCK(asc);
17201000Sxc151355
17213147Sxc151355 return (0);
17221000Sxc151355 }
17231000Sxc151355
17241000Sxc151355 static int
ath_m_multicst(void * arg,boolean_t add,const uint8_t * mca)17253147Sxc151355 ath_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
17261000Sxc151355 {
17273147Sxc151355 ath_t *asc = arg;
17283147Sxc151355 struct ath_hal *ah = asc->asc_ah;
17296235Sxc151355 uint32_t val, index, bit;
17301000Sxc151355 uint8_t pos;
17316235Sxc151355 uint32_t *mfilt = asc->asc_mcast_hash;
17321000Sxc151355
17333147Sxc151355 ATH_LOCK(asc);
17348033SWang.Lin@Sun.COM
17351000Sxc151355 /* calculate XOR of eight 6bit values */
17361000Sxc151355 val = ATH_LE_READ_4(mca + 0);
17371000Sxc151355 pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
17381000Sxc151355 val = ATH_LE_READ_4(mca + 3);
17391000Sxc151355 pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
17401000Sxc151355 pos &= 0x3f;
17416235Sxc151355 index = pos / 32;
17426235Sxc151355 bit = 1 << (pos % 32);
17436235Sxc151355
17446235Sxc151355 if (add) { /* enable multicast */
17456235Sxc151355 asc->asc_mcast_refs[pos]++;
17466235Sxc151355 mfilt[index] |= bit;
17476235Sxc151355 } else { /* disable multicast */
17486235Sxc151355 if (--asc->asc_mcast_refs[pos] == 0)
17496235Sxc151355 mfilt[index] &= ~bit;
17506235Sxc151355 }
17511000Sxc151355 ATH_HAL_SETMCASTFILTER(ah, mfilt[0], mfilt[1]);
17521000Sxc151355
17533147Sxc151355 ATH_UNLOCK(asc);
17543147Sxc151355 return (0);
17551000Sxc151355 }
17567663SSowmini.Varadhan@Sun.COM /*
17577663SSowmini.Varadhan@Sun.COM * callback functions for /get/set properties
17587663SSowmini.Varadhan@Sun.COM */
17597663SSowmini.Varadhan@Sun.COM static int
ath_m_setprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,const void * wldp_buf)17607663SSowmini.Varadhan@Sun.COM ath_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
17617663SSowmini.Varadhan@Sun.COM uint_t wldp_length, const void *wldp_buf)
17627663SSowmini.Varadhan@Sun.COM {
17637663SSowmini.Varadhan@Sun.COM ath_t *asc = arg;
17647663SSowmini.Varadhan@Sun.COM int err;
17657663SSowmini.Varadhan@Sun.COM
17667663SSowmini.Varadhan@Sun.COM err = ieee80211_setprop(&asc->asc_isc, pr_name, wldp_pr_num,
17677663SSowmini.Varadhan@Sun.COM wldp_length, wldp_buf);
17687663SSowmini.Varadhan@Sun.COM
17697663SSowmini.Varadhan@Sun.COM ATH_LOCK(asc);
17707663SSowmini.Varadhan@Sun.COM
17717663SSowmini.Varadhan@Sun.COM if (err == ENETRESET) {
17727663SSowmini.Varadhan@Sun.COM if (ATH_IS_RUNNING(asc)) {
17737663SSowmini.Varadhan@Sun.COM ATH_UNLOCK(asc);
17747663SSowmini.Varadhan@Sun.COM (void) ath_m_start(asc);
17757663SSowmini.Varadhan@Sun.COM (void) ieee80211_new_state(&asc->asc_isc,
17767663SSowmini.Varadhan@Sun.COM IEEE80211_S_SCAN, -1);
17777663SSowmini.Varadhan@Sun.COM ATH_LOCK(asc);
17787663SSowmini.Varadhan@Sun.COM }
17797663SSowmini.Varadhan@Sun.COM err = 0;
17807663SSowmini.Varadhan@Sun.COM }
17817663SSowmini.Varadhan@Sun.COM
17827663SSowmini.Varadhan@Sun.COM ATH_UNLOCK(asc);
17837663SSowmini.Varadhan@Sun.COM
17847663SSowmini.Varadhan@Sun.COM return (err);
17857663SSowmini.Varadhan@Sun.COM }
1786*11878SVenu.Iyer@Sun.COM
17877663SSowmini.Varadhan@Sun.COM static int
ath_m_getprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,void * wldp_buf)17887663SSowmini.Varadhan@Sun.COM ath_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
1789*11878SVenu.Iyer@Sun.COM uint_t wldp_length, void *wldp_buf)
17907663SSowmini.Varadhan@Sun.COM {
17917663SSowmini.Varadhan@Sun.COM ath_t *asc = arg;
17927663SSowmini.Varadhan@Sun.COM int err = 0;
17937663SSowmini.Varadhan@Sun.COM
17947663SSowmini.Varadhan@Sun.COM err = ieee80211_getprop(&asc->asc_isc, pr_name, wldp_pr_num,
1795*11878SVenu.Iyer@Sun.COM wldp_length, wldp_buf);
17967663SSowmini.Varadhan@Sun.COM
17977663SSowmini.Varadhan@Sun.COM return (err);
17987663SSowmini.Varadhan@Sun.COM }
17997663SSowmini.Varadhan@Sun.COM
18001000Sxc151355 static void
ath_m_propinfo(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,mac_prop_info_handle_t mph)1801*11878SVenu.Iyer@Sun.COM ath_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
1802*11878SVenu.Iyer@Sun.COM mac_prop_info_handle_t mph)
1803*11878SVenu.Iyer@Sun.COM {
1804*11878SVenu.Iyer@Sun.COM ath_t *asc = arg;
1805*11878SVenu.Iyer@Sun.COM
1806*11878SVenu.Iyer@Sun.COM ieee80211_propinfo(&asc->asc_isc, pr_name, wldp_pr_num, mph);
1807*11878SVenu.Iyer@Sun.COM }
1808*11878SVenu.Iyer@Sun.COM
1809*11878SVenu.Iyer@Sun.COM static void
ath_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)18103147Sxc151355 ath_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
18111000Sxc151355 {
18123147Sxc151355 ath_t *asc = arg;
18133147Sxc151355 int32_t err;
18141000Sxc151355
18153147Sxc151355 err = ieee80211_ioctl(&asc->asc_isc, wq, mp);
18163147Sxc151355 ATH_LOCK(asc);
18173147Sxc151355 if (err == ENETRESET) {
18183147Sxc151355 if (ATH_IS_RUNNING(asc)) {
18193147Sxc151355 ATH_UNLOCK(asc);
18203147Sxc151355 (void) ath_m_start(asc);
18213147Sxc151355 (void) ieee80211_new_state(&asc->asc_isc,
18223147Sxc151355 IEEE80211_S_SCAN, -1);
18233147Sxc151355 ATH_LOCK(asc);
18243147Sxc151355 }
18251000Sxc151355 }
18263147Sxc151355 ATH_UNLOCK(asc);
18271000Sxc151355 }
18281000Sxc151355
18291000Sxc151355 static int
ath_m_stat(void * arg,uint_t stat,uint64_t * val)18303147Sxc151355 ath_m_stat(void *arg, uint_t stat, uint64_t *val)
18311000Sxc151355 {
18323147Sxc151355 ath_t *asc = arg;
18333147Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc;
18343147Sxc151355 struct ieee80211_node *in = ic->ic_bss;
18351000Sxc151355 struct ieee80211_rateset *rs = &in->in_rates;
18361000Sxc151355
18373147Sxc151355 ATH_LOCK(asc);
18383147Sxc151355 switch (stat) {
18393147Sxc151355 case MAC_STAT_IFSPEED:
18403147Sxc151355 *val = (rs->ir_rates[in->in_txrate] & IEEE80211_RATE_VAL) / 2 *
18413147Sxc151355 1000000ull;
18423147Sxc151355 break;
18433147Sxc151355 case MAC_STAT_NOXMTBUF:
18443147Sxc151355 *val = asc->asc_stats.ast_tx_nobuf +
18453147Sxc151355 asc->asc_stats.ast_tx_nobufmgt;
18463147Sxc151355 break;
18473147Sxc151355 case MAC_STAT_IERRORS:
18483147Sxc151355 *val = asc->asc_stats.ast_rx_tooshort;
18493147Sxc151355 break;
18503147Sxc151355 case MAC_STAT_RBYTES:
18513147Sxc151355 *val = ic->ic_stats.is_rx_bytes;
18523147Sxc151355 break;
18533147Sxc151355 case MAC_STAT_IPACKETS:
18543147Sxc151355 *val = ic->ic_stats.is_rx_frags;
18553147Sxc151355 break;
18563147Sxc151355 case MAC_STAT_OBYTES:
18573147Sxc151355 *val = ic->ic_stats.is_tx_bytes;
18583147Sxc151355 break;
18593147Sxc151355 case MAC_STAT_OPACKETS:
18603147Sxc151355 *val = ic->ic_stats.is_tx_frags;
18613147Sxc151355 break;
18623631Sxh158540 case MAC_STAT_OERRORS:
18633147Sxc151355 case WIFI_STAT_TX_FAILED:
18643147Sxc151355 *val = asc->asc_stats.ast_tx_fifoerr +
18653631Sxh158540 asc->asc_stats.ast_tx_xretries +
18663631Sxh158540 asc->asc_stats.ast_tx_discard;
18673147Sxc151355 break;
18683147Sxc151355 case WIFI_STAT_TX_RETRANS:
18693147Sxc151355 *val = asc->asc_stats.ast_tx_xretries;
18703147Sxc151355 break;
18713147Sxc151355 case WIFI_STAT_FCS_ERRORS:
18723147Sxc151355 *val = asc->asc_stats.ast_rx_crcerr;
18733147Sxc151355 break;
18743147Sxc151355 case WIFI_STAT_WEP_ERRORS:
18753147Sxc151355 *val = asc->asc_stats.ast_rx_badcrypt;
18763147Sxc151355 break;
18773147Sxc151355 case WIFI_STAT_TX_FRAGS:
18783147Sxc151355 case WIFI_STAT_MCAST_TX:
18793147Sxc151355 case WIFI_STAT_RTS_SUCCESS:
18803147Sxc151355 case WIFI_STAT_RTS_FAILURE:
18813147Sxc151355 case WIFI_STAT_ACK_FAILURE:
18823147Sxc151355 case WIFI_STAT_RX_FRAGS:
18833147Sxc151355 case WIFI_STAT_MCAST_RX:
18843147Sxc151355 case WIFI_STAT_RX_DUPS:
18853147Sxc151355 ATH_UNLOCK(asc);
18863147Sxc151355 return (ieee80211_stat(ic, stat, val));
18873147Sxc151355 default:
18883147Sxc151355 ATH_UNLOCK(asc);
18893147Sxc151355 return (ENOTSUP);
18903147Sxc151355 }
18913147Sxc151355 ATH_UNLOCK(asc);
18921000Sxc151355
18933147Sxc151355 return (0);
18941000Sxc151355 }
18951000Sxc151355
18961000Sxc151355 static int
ath_pci_setup(ath_t * asc)18976797Sxc151355 ath_pci_setup(ath_t *asc)
18986797Sxc151355 {
18996797Sxc151355 uint16_t command;
19006797Sxc151355
19016797Sxc151355 /*
19026797Sxc151355 * Enable memory mapping and bus mastering
19036797Sxc151355 */
19046797Sxc151355 ASSERT(asc != NULL);
19056797Sxc151355 command = pci_config_get16(asc->asc_cfg_handle, PCI_CONF_COMM);
19066797Sxc151355 command |= PCI_COMM_MAE | PCI_COMM_ME;
19076797Sxc151355 pci_config_put16(asc->asc_cfg_handle, PCI_CONF_COMM, command);
19086797Sxc151355 command = pci_config_get16(asc->asc_cfg_handle, PCI_CONF_COMM);
19096797Sxc151355 if ((command & PCI_COMM_MAE) == 0) {
19106797Sxc151355 ath_problem("ath: ath_pci_setup(): "
19116797Sxc151355 "failed to enable memory mapping\n");
19126797Sxc151355 return (EIO);
19136797Sxc151355 }
19146797Sxc151355 if ((command & PCI_COMM_ME) == 0) {
19156797Sxc151355 ath_problem("ath: ath_pci_setup(): "
19166797Sxc151355 "failed to enable bus mastering\n");
19176797Sxc151355 return (EIO);
19186797Sxc151355 }
19196797Sxc151355 ATH_DEBUG((ATH_DBG_INIT, "ath: ath_pci_setup(): "
19206797Sxc151355 "set command reg to 0x%x \n", command));
19216797Sxc151355
19226797Sxc151355 return (0);
19236797Sxc151355 }
19246797Sxc151355
19256797Sxc151355 static int
ath_resume(dev_info_t * devinfo)19266797Sxc151355 ath_resume(dev_info_t *devinfo)
19276797Sxc151355 {
19286797Sxc151355 ath_t *asc;
19296797Sxc151355 int ret = DDI_SUCCESS;
19306797Sxc151355
19316797Sxc151355 asc = ddi_get_soft_state(ath_soft_state_p, ddi_get_instance(devinfo));
19326797Sxc151355 if (asc == NULL) {
19336797Sxc151355 ATH_DEBUG((ATH_DBG_SUSPEND, "ath: ath_resume(): "
19346797Sxc151355 "failed to get soft state\n"));
19356797Sxc151355 return (DDI_FAILURE);
19366797Sxc151355 }
19376797Sxc151355
19386797Sxc151355 ATH_LOCK(asc);
19396797Sxc151355 /*
19406797Sxc151355 * Set up config space command register(s). Refuse
19416797Sxc151355 * to resume on failure.
19426797Sxc151355 */
19436797Sxc151355 if (ath_pci_setup(asc) != 0) {
19446797Sxc151355 ATH_DEBUG((ATH_DBG_SUSPEND, "ath: ath_resume(): "
19456797Sxc151355 "ath_pci_setup() failed\n"));
19466797Sxc151355 ATH_UNLOCK(asc);
19476797Sxc151355 return (DDI_FAILURE);
19486797Sxc151355 }
19496797Sxc151355
19506797Sxc151355 if (!asc->asc_invalid)
19516797Sxc151355 ret = ath_start_locked(asc);
19526797Sxc151355 ATH_UNLOCK(asc);
19536797Sxc151355
19546797Sxc151355 return (ret);
19556797Sxc151355 }
19566797Sxc151355
19576797Sxc151355 static int
ath_attach(dev_info_t * devinfo,ddi_attach_cmd_t cmd)19581000Sxc151355 ath_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
19591000Sxc151355 {
19601000Sxc151355 ath_t *asc;
19613147Sxc151355 ieee80211com_t *ic;
19621000Sxc151355 struct ath_hal *ah;
19631000Sxc151355 uint8_t csz;
19641000Sxc151355 HAL_STATUS status;
19651000Sxc151355 caddr_t regs;
19661000Sxc151355 uint32_t i, val;
19676797Sxc151355 uint16_t vendor_id, device_id;
19681000Sxc151355 const char *athname;
19691000Sxc151355 int32_t ath_countrycode = CTRY_DEFAULT; /* country code */
19701000Sxc151355 int32_t err, ath_regdomain = 0; /* regulatory domain */
19711000Sxc151355 char strbuf[32];
19723147Sxc151355 int instance;
19733147Sxc151355 wifi_data_t wd = { 0 };
19743147Sxc151355 mac_register_t *macp;
19751000Sxc151355
19766797Sxc151355 switch (cmd) {
19776797Sxc151355 case DDI_ATTACH:
19786797Sxc151355 break;
19796797Sxc151355
19806797Sxc151355 case DDI_RESUME:
19816797Sxc151355 return (ath_resume(devinfo));
19826797Sxc151355
19836797Sxc151355 default:
19841000Sxc151355 return (DDI_FAILURE);
19856797Sxc151355 }
19861000Sxc151355
19873147Sxc151355 instance = ddi_get_instance(devinfo);
19883147Sxc151355 if (ddi_soft_state_zalloc(ath_soft_state_p, instance) != DDI_SUCCESS) {
19891000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
19901000Sxc151355 "Unable to alloc softstate\n"));
19911000Sxc151355 return (DDI_FAILURE);
19921000Sxc151355 }
19931000Sxc151355
19941000Sxc151355 asc = ddi_get_soft_state(ath_soft_state_p, ddi_get_instance(devinfo));
19953147Sxc151355 ic = (ieee80211com_t *)asc;
19961000Sxc151355 asc->asc_dev = devinfo;
19971000Sxc151355
19981000Sxc151355 mutex_init(&asc->asc_genlock, NULL, MUTEX_DRIVER, NULL);
19991000Sxc151355 mutex_init(&asc->asc_txbuflock, NULL, MUTEX_DRIVER, NULL);
20001000Sxc151355 mutex_init(&asc->asc_rxbuflock, NULL, MUTEX_DRIVER, NULL);
20013147Sxc151355 mutex_init(&asc->asc_resched_lock, NULL, MUTEX_DRIVER, NULL);
20021000Sxc151355
20031000Sxc151355 err = pci_config_setup(devinfo, &asc->asc_cfg_handle);
20041000Sxc151355 if (err != DDI_SUCCESS) {
20051000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
20061000Sxc151355 "pci_config_setup() failed"));
20071000Sxc151355 goto attach_fail0;
20081000Sxc151355 }
20091000Sxc151355
20106797Sxc151355 if (ath_pci_setup(asc) != 0)
20116797Sxc151355 goto attach_fail1;
20126797Sxc151355
20135420Sxc151355 /*
20145420Sxc151355 * Cache line size is used to size and align various
20155420Sxc151355 * structures used to communicate with the hardware.
20165420Sxc151355 */
20171000Sxc151355 csz = pci_config_get8(asc->asc_cfg_handle, PCI_CONF_CACHE_LINESZ);
20185420Sxc151355 if (csz == 0) {
20195420Sxc151355 /*
20205420Sxc151355 * We must have this setup properly for rx buffer
20215420Sxc151355 * DMA to work so force a reasonable value here if it
20225420Sxc151355 * comes up zero.
20235420Sxc151355 */
20245420Sxc151355 csz = ATH_DEF_CACHE_BYTES / sizeof (uint32_t);
20255420Sxc151355 pci_config_put8(asc->asc_cfg_handle, PCI_CONF_CACHE_LINESZ,
20265420Sxc151355 csz);
20275420Sxc151355 }
20281000Sxc151355 asc->asc_cachelsz = csz << 2;
20291000Sxc151355 vendor_id = pci_config_get16(asc->asc_cfg_handle, PCI_CONF_VENID);
20301000Sxc151355 device_id = pci_config_get16(asc->asc_cfg_handle, PCI_CONF_DEVID);
20311000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): vendor 0x%x, "
20321000Sxc151355 "device id 0x%x, cache size %d\n", vendor_id, device_id, csz));
20331000Sxc151355
20341000Sxc151355 athname = ath_hal_probe(vendor_id, device_id);
20351000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): athname: %s\n",
20361000Sxc151355 athname ? athname : "Atheros ???"));
20371000Sxc151355
20381000Sxc151355 pci_config_put8(asc->asc_cfg_handle, PCI_CONF_LATENCY_TIMER, 0xa8);
20391000Sxc151355 val = pci_config_get32(asc->asc_cfg_handle, 0x40);
20401000Sxc151355 if ((val & 0x0000ff00) != 0)
20411000Sxc151355 pci_config_put32(asc->asc_cfg_handle, 0x40, val & 0xffff00ff);
20421000Sxc151355
20431000Sxc151355 err = ddi_regs_map_setup(devinfo, 1,
20441000Sxc151355 ®s, 0, 0, &ath_reg_accattr, &asc->asc_io_handle);
20451000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
20461000Sxc151355 "regs map1 = %x err=%d\n", regs, err));
20471000Sxc151355 if (err != DDI_SUCCESS) {
20481000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
20491000Sxc151355 "ddi_regs_map_setup() failed"));
20501000Sxc151355 goto attach_fail1;
20511000Sxc151355 }
20521000Sxc151355
20531000Sxc151355 ah = ath_hal_attach(device_id, asc, 0, regs, &status);
20541000Sxc151355 if (ah == NULL) {
20551000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
20566235Sxc151355 "unable to attach hw: '%s' (HAL status %u)\n",
20576235Sxc151355 ath_get_hal_status_desc(status), status));
20581000Sxc151355 goto attach_fail2;
20591000Sxc151355 }
20608033SWang.Lin@Sun.COM ATH_DEBUG((ATH_DBG_ATTACH, "mac %d.%d phy %d.%d",
20618033SWang.Lin@Sun.COM ah->ah_macVersion, ah->ah_macRev,
20628033SWang.Lin@Sun.COM ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf));
20631000Sxc151355 ATH_HAL_INTRSET(ah, 0);
20641000Sxc151355 asc->asc_ah = ah;
20651000Sxc151355
20661000Sxc151355 if (ah->ah_abi != HAL_ABI_VERSION) {
20671000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
20681000Sxc151355 "HAL ABI mismatch detected (0x%x != 0x%x)\n",
20691000Sxc151355 ah->ah_abi, HAL_ABI_VERSION));
20701000Sxc151355 goto attach_fail3;
20711000Sxc151355 }
20721000Sxc151355
20731000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
20741000Sxc151355 "HAL ABI version 0x%x\n", ah->ah_abi));
20751000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
20761000Sxc151355 "HAL mac version %d.%d, phy version %d.%d\n",
20771000Sxc151355 ah->ah_macVersion, ah->ah_macRev,
20781000Sxc151355 ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf));
20791000Sxc151355 if (ah->ah_analog5GhzRev)
20801000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
20811000Sxc151355 "HAL 5ghz radio version %d.%d\n",
20821000Sxc151355 ah->ah_analog5GhzRev >> 4,
20831000Sxc151355 ah->ah_analog5GhzRev & 0xf));
20841000Sxc151355 if (ah->ah_analog2GhzRev)
20851000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
20861000Sxc151355 "HAL 2ghz radio version %d.%d\n",
20871000Sxc151355 ah->ah_analog2GhzRev >> 4,
20881000Sxc151355 ah->ah_analog2GhzRev & 0xf));
20891000Sxc151355
20901000Sxc151355 /*
20911000Sxc151355 * Check if the MAC has multi-rate retry support.
20921000Sxc151355 * We do this by trying to setup a fake extended
20931000Sxc151355 * descriptor. MAC's that don't have support will
20941000Sxc151355 * return false w/o doing anything. MAC's that do
20951000Sxc151355 * support it will return true w/o doing anything.
20961000Sxc151355 */
20971000Sxc151355 asc->asc_mrretry = ATH_HAL_SETUPXTXDESC(ah, NULL, 0, 0, 0, 0, 0, 0);
20981000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
20991000Sxc151355 "multi rate retry support=%x\n",
21001000Sxc151355 asc->asc_mrretry));
21011000Sxc151355
21024126Szf162725 /*
21034126Szf162725 * Get the hardware key cache size.
21044126Szf162725 */
21054126Szf162725 asc->asc_keymax = ATH_HAL_KEYCACHESIZE(ah);
21064126Szf162725 if (asc->asc_keymax > sizeof (asc->asc_keymap) * NBBY) {
21074126Szf162725 ATH_DEBUG((ATH_DBG_ATTACH, "ath_attach:"
21084126Szf162725 " Warning, using only %u entries in %u key cache\n",
21094126Szf162725 sizeof (asc->asc_keymap) * NBBY, asc->asc_keymax));
21104126Szf162725 asc->asc_keymax = sizeof (asc->asc_keymap) * NBBY;
21114126Szf162725 }
21124126Szf162725 /*
21134126Szf162725 * Reset the key cache since some parts do not
21144126Szf162725 * reset the contents on initial power up.
21154126Szf162725 */
21164126Szf162725 for (i = 0; i < asc->asc_keymax; i++)
21174126Szf162725 ATH_HAL_KEYRESET(ah, i);
21184126Szf162725
21191000Sxc151355 ATH_HAL_GETREGDOMAIN(ah, (uint32_t *)&ath_regdomain);
21201000Sxc151355 ATH_HAL_GETCOUNTRYCODE(ah, &ath_countrycode);
21211000Sxc151355 /*
21221000Sxc151355 * Collect the channel list using the default country
21231000Sxc151355 * code and including outdoor channels. The 802.11 layer
21241000Sxc151355 * is resposible for filtering this list to a set of
21251000Sxc151355 * channels that it considers ok to use.
21261000Sxc151355 */
21271000Sxc151355 asc->asc_have11g = 0;
21281000Sxc151355
21291000Sxc151355 /* enable outdoor use, enable extended channels */
21301000Sxc151355 err = ath_getchannels(asc, ath_countrycode, AH_FALSE, AH_TRUE);
21311000Sxc151355 if (err != 0)
21321000Sxc151355 goto attach_fail3;
21331000Sxc151355
21341000Sxc151355 /*
21351000Sxc151355 * Setup rate tables for all potential media types.
21361000Sxc151355 */
21371000Sxc151355 ath_rate_setup(asc, IEEE80211_MODE_11A);
21381000Sxc151355 ath_rate_setup(asc, IEEE80211_MODE_11B);
21391000Sxc151355 ath_rate_setup(asc, IEEE80211_MODE_11G);
21403147Sxc151355 ath_rate_setup(asc, IEEE80211_MODE_TURBO_A);
21411000Sxc151355
21421000Sxc151355 /* Setup here so ath_rate_update is happy */
21431000Sxc151355 ath_setcurmode(asc, IEEE80211_MODE_11A);
21441000Sxc151355
21451000Sxc151355 err = ath_desc_alloc(devinfo, asc);
21461000Sxc151355 if (err != DDI_SUCCESS) {
21471000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
21481000Sxc151355 "failed to allocate descriptors: %d\n", err));
21491000Sxc151355 goto attach_fail3;
21501000Sxc151355 }
21511000Sxc151355
21528033SWang.Lin@Sun.COM if ((asc->asc_tq = ddi_taskq_create(devinfo, "ath_taskq", 1,
21538033SWang.Lin@Sun.COM TASKQ_DEFAULTPRI, 0)) == NULL) {
21548033SWang.Lin@Sun.COM goto attach_fail4;
21558033SWang.Lin@Sun.COM }
21561000Sxc151355 /* Setup transmit queues in the HAL */
21571000Sxc151355 if (ath_txq_setup(asc))
21581000Sxc151355 goto attach_fail4;
21591000Sxc151355
21603147Sxc151355 ATH_HAL_GETMAC(ah, ic->ic_macaddr);
21611000Sxc151355
21623147Sxc151355 /*
21633147Sxc151355 * Initialize pointers to device specific functions which
21643147Sxc151355 * will be used by the generic layer.
21653147Sxc151355 */
21661000Sxc151355 /* 11g support is identified when we fetch the channel set */
21671000Sxc151355 if (asc->asc_have11g)
21684206Szf162725 ic->ic_caps |= IEEE80211_C_SHPREAMBLE |
21694206Szf162725 IEEE80211_C_SHSLOT; /* short slot time */
21703147Sxc151355 /*
21713147Sxc151355 * Query the hal to figure out h/w crypto support.
21723147Sxc151355 */
21733147Sxc151355 if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_WEP))
21743147Sxc151355 ic->ic_caps |= IEEE80211_C_WEP;
21753147Sxc151355 if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_AES_OCB))
21763147Sxc151355 ic->ic_caps |= IEEE80211_C_AES;
21774126Szf162725 if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_AES_CCM)) {
21784126Szf162725 ATH_DEBUG((ATH_DBG_ATTACH, "Atheros support H/W CCMP\n"));
21793147Sxc151355 ic->ic_caps |= IEEE80211_C_AES_CCM;
21804126Szf162725 }
21814126Szf162725 if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_CKIP))
21823147Sxc151355 ic->ic_caps |= IEEE80211_C_CKIP;
21834126Szf162725 if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_TKIP)) {
21844126Szf162725 ATH_DEBUG((ATH_DBG_ATTACH, "Atheros support H/W TKIP\n"));
21854126Szf162725 ic->ic_caps |= IEEE80211_C_TKIP;
21863147Sxc151355 /*
21873147Sxc151355 * Check if h/w does the MIC and/or whether the
21883147Sxc151355 * separate key cache entries are required to
21893147Sxc151355 * handle both tx+rx MIC keys.
21903147Sxc151355 */
21914126Szf162725 if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_MIC)) {
21924126Szf162725 ATH_DEBUG((ATH_DBG_ATTACH, "Support H/W TKIP MIC\n"));
21933147Sxc151355 ic->ic_caps |= IEEE80211_C_TKIPMIC;
21944126Szf162725 }
21958033SWang.Lin@Sun.COM
21968033SWang.Lin@Sun.COM /*
21978033SWang.Lin@Sun.COM * If the h/w supports storing tx+rx MIC keys
21988033SWang.Lin@Sun.COM * in one cache slot automatically enable use.
21998033SWang.Lin@Sun.COM */
22008033SWang.Lin@Sun.COM if (ATH_HAL_HASTKIPSPLIT(ah) ||
22018033SWang.Lin@Sun.COM !ATH_HAL_SETTKIPSPLIT(ah, AH_FALSE)) {
22023147Sxc151355 asc->asc_splitmic = 1;
22038033SWang.Lin@Sun.COM }
22043147Sxc151355 }
22054126Szf162725 ic->ic_caps |= IEEE80211_C_WPA; /* Support WPA/WPA2 */
22064126Szf162725
22073147Sxc151355 asc->asc_hasclrkey = ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_CLR);
22088033SWang.Lin@Sun.COM /*
22098033SWang.Lin@Sun.COM * Mark key cache slots associated with global keys
22108033SWang.Lin@Sun.COM * as in use. If we knew TKIP was not to be used we
22118033SWang.Lin@Sun.COM * could leave the +32, +64, and +32+64 slots free.
22128033SWang.Lin@Sun.COM */
22138033SWang.Lin@Sun.COM for (i = 0; i < IEEE80211_WEP_NKID; i++) {
22148033SWang.Lin@Sun.COM setbit(asc->asc_keymap, i);
22158033SWang.Lin@Sun.COM setbit(asc->asc_keymap, i+64);
22168033SWang.Lin@Sun.COM if (asc->asc_splitmic) {
22178033SWang.Lin@Sun.COM setbit(asc->asc_keymap, i+32);
22188033SWang.Lin@Sun.COM setbit(asc->asc_keymap, i+32+64);
22198033SWang.Lin@Sun.COM }
22208033SWang.Lin@Sun.COM }
22218033SWang.Lin@Sun.COM
22223147Sxc151355 ic->ic_phytype = IEEE80211_T_OFDM;
22233147Sxc151355 ic->ic_opmode = IEEE80211_M_STA;
22243147Sxc151355 ic->ic_state = IEEE80211_S_INIT;
22253147Sxc151355 ic->ic_maxrssi = ATH_MAX_RSSI;
22263147Sxc151355 ic->ic_set_shortslot = ath_set_shortslot;
22273147Sxc151355 ic->ic_xmit = ath_xmit;
22283147Sxc151355 ieee80211_attach(ic);
22291000Sxc151355
22304126Szf162725 /* different instance has different WPA door */
22314126Szf162725 (void) snprintf(ic->ic_wpadoor, MAX_IEEE80211STR, "%s_%s%d", WPA_DOOR,
22325420Sxc151355 ddi_driver_name(devinfo),
22335420Sxc151355 ddi_get_instance(devinfo));
22344126Szf162725
22353147Sxc151355 /* Override 80211 default routines */
22363147Sxc151355 ic->ic_reset = ath_reset;
22373147Sxc151355 asc->asc_newstate = ic->ic_newstate;
22383147Sxc151355 ic->ic_newstate = ath_newstate;
22393147Sxc151355 ic->ic_watchdog = ath_watchdog;
22403147Sxc151355 ic->ic_node_alloc = ath_node_alloc;
22413147Sxc151355 ic->ic_node_free = ath_node_free;
22423147Sxc151355 ic->ic_crypto.cs_key_alloc = ath_key_alloc;
22433147Sxc151355 ic->ic_crypto.cs_key_delete = ath_key_delete;
22443147Sxc151355 ic->ic_crypto.cs_key_set = ath_key_set;
22453147Sxc151355 ieee80211_media_init(ic);
22464296Szf162725 /*
22474296Szf162725 * initialize default tx key
22484296Szf162725 */
22494296Szf162725 ic->ic_def_txkey = 0;
22501000Sxc151355
22511000Sxc151355 asc->asc_rx_pend = 0;
22521000Sxc151355 ATH_HAL_INTRSET(ah, 0);
22531000Sxc151355 err = ddi_add_softintr(devinfo, DDI_SOFTINT_LOW,
22541000Sxc151355 &asc->asc_softint_id, NULL, 0, ath_softint_handler, (caddr_t)asc);
22551000Sxc151355 if (err != DDI_SUCCESS) {
22561000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
22573147Sxc151355 "ddi_add_softintr() failed\n"));
22581000Sxc151355 goto attach_fail5;
22591000Sxc151355 }
22601000Sxc151355
22611000Sxc151355 if (ddi_get_iblock_cookie(devinfo, 0, &asc->asc_iblock)
22621000Sxc151355 != DDI_SUCCESS) {
22631000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
22641000Sxc151355 "Can not get iblock cookie for INT\n"));
22651000Sxc151355 goto attach_fail6;
22661000Sxc151355 }
22671000Sxc151355
22683147Sxc151355 if (ddi_add_intr(devinfo, 0, NULL, NULL, ath_intr,
22693147Sxc151355 (caddr_t)asc) != DDI_SUCCESS) {
22701000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
22711000Sxc151355 "Can not set intr for ATH driver\n"));
22721000Sxc151355 goto attach_fail6;
22731000Sxc151355 }
22743147Sxc151355
22753147Sxc151355 /*
22763147Sxc151355 * Provide initial settings for the WiFi plugin; whenever this
22773147Sxc151355 * information changes, we need to call mac_plugindata_update()
22783147Sxc151355 */
22793147Sxc151355 wd.wd_opmode = ic->ic_opmode;
22803147Sxc151355 wd.wd_secalloc = WIFI_SEC_NONE;
22813147Sxc151355 IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
22823147Sxc151355
22833147Sxc151355 if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
22843147Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
22853147Sxc151355 "MAC version mismatch\n"));
22863147Sxc151355 goto attach_fail7;
22873147Sxc151355 }
22881000Sxc151355
22893147Sxc151355 macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI;
22903147Sxc151355 macp->m_driver = asc;
22913147Sxc151355 macp->m_dip = devinfo;
22923147Sxc151355 macp->m_src_addr = ic->ic_macaddr;
22933147Sxc151355 macp->m_callbacks = &ath_m_callbacks;
22943147Sxc151355 macp->m_min_sdu = 0;
22953147Sxc151355 macp->m_max_sdu = IEEE80211_MTU;
22963147Sxc151355 macp->m_pdata = &wd;
22973147Sxc151355 macp->m_pdata_size = sizeof (wd);
22983147Sxc151355
22993147Sxc151355 err = mac_register(macp, &ic->ic_mach);
23003147Sxc151355 mac_free(macp);
23013147Sxc151355 if (err != 0) {
23021000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
23033147Sxc151355 "mac_register err %x\n", err));
23041000Sxc151355 goto attach_fail7;
23051000Sxc151355 }
23061000Sxc151355
23071000Sxc151355 /* Create minor node of type DDI_NT_NET_WIFI */
23081000Sxc151355 (void) snprintf(strbuf, sizeof (strbuf), "%s%d",
23093147Sxc151355 ATH_NODENAME, instance);
23101000Sxc151355 err = ddi_create_minor_node(devinfo, strbuf, S_IFCHR,
23113147Sxc151355 instance + 1, DDI_NT_NET_WIFI, 0);
23121000Sxc151355 if (err != DDI_SUCCESS)
23131000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "WARN: ath: ath_attach(): "
23141000Sxc151355 "Create minor node failed - %d\n", err));
23151000Sxc151355
23163147Sxc151355 mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
23171000Sxc151355 asc->asc_invalid = 1;
23186797Sxc151355 asc->asc_isrunning = 0;
23196235Sxc151355 asc->asc_promisc = B_FALSE;
23206235Sxc151355 bzero(asc->asc_mcast_refs, sizeof (asc->asc_mcast_refs));
23216235Sxc151355 bzero(asc->asc_mcast_hash, sizeof (asc->asc_mcast_hash));
23221000Sxc151355 return (DDI_SUCCESS);
23231000Sxc151355 attach_fail7:
23241000Sxc151355 ddi_remove_intr(devinfo, 0, asc->asc_iblock);
23251000Sxc151355 attach_fail6:
23261000Sxc151355 ddi_remove_softintr(asc->asc_softint_id);
23271000Sxc151355 attach_fail5:
23283147Sxc151355 (void) ieee80211_detach(ic);
23291000Sxc151355 attach_fail4:
23301000Sxc151355 ath_desc_free(asc);
23318033SWang.Lin@Sun.COM if (asc->asc_tq)
23328033SWang.Lin@Sun.COM ddi_taskq_destroy(asc->asc_tq);
23331000Sxc151355 attach_fail3:
23341000Sxc151355 ah->ah_detach(asc->asc_ah);
23351000Sxc151355 attach_fail2:
23361000Sxc151355 ddi_regs_map_free(&asc->asc_io_handle);
23371000Sxc151355 attach_fail1:
23381000Sxc151355 pci_config_teardown(&asc->asc_cfg_handle);
23391000Sxc151355 attach_fail0:
23401000Sxc151355 asc->asc_invalid = 1;
23411000Sxc151355 mutex_destroy(&asc->asc_txbuflock);
23421000Sxc151355 for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
23431000Sxc151355 if (ATH_TXQ_SETUP(asc, i)) {
23441000Sxc151355 struct ath_txq *txq = &asc->asc_txq[i];
23451000Sxc151355 mutex_destroy(&txq->axq_lock);
23461000Sxc151355 }
23471000Sxc151355 }
23481000Sxc151355 mutex_destroy(&asc->asc_rxbuflock);
23491000Sxc151355 mutex_destroy(&asc->asc_genlock);
23503147Sxc151355 mutex_destroy(&asc->asc_resched_lock);
23513147Sxc151355 ddi_soft_state_free(ath_soft_state_p, instance);
23521000Sxc151355
23531000Sxc151355 return (DDI_FAILURE);
23541000Sxc151355 }
23551000Sxc151355
23566797Sxc151355 /*
23576797Sxc151355 * Suspend transmit/receive for powerdown
23586797Sxc151355 */
23596797Sxc151355 static int
ath_suspend(ath_t * asc)23606797Sxc151355 ath_suspend(ath_t *asc)
23616797Sxc151355 {
23626797Sxc151355 ATH_LOCK(asc);
23636797Sxc151355 ath_stop_locked(asc);
23646797Sxc151355 ATH_UNLOCK(asc);
23656797Sxc151355 ATH_DEBUG((ATH_DBG_SUSPEND, "ath: suspended.\n"));
23666797Sxc151355
23676797Sxc151355 return (DDI_SUCCESS);
23686797Sxc151355 }
23696797Sxc151355
23701000Sxc151355 static int32_t
ath_detach(dev_info_t * devinfo,ddi_detach_cmd_t cmd)23711000Sxc151355 ath_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
23721000Sxc151355 {
23731000Sxc151355 ath_t *asc;
23741000Sxc151355
23751000Sxc151355 asc = ddi_get_soft_state(ath_soft_state_p, ddi_get_instance(devinfo));
23761000Sxc151355 ASSERT(asc != NULL);
23771000Sxc151355
23786797Sxc151355 switch (cmd) {
23796797Sxc151355 case DDI_DETACH:
23806797Sxc151355 break;
23816797Sxc151355
23826797Sxc151355 case DDI_SUSPEND:
23836797Sxc151355 return (ath_suspend(asc));
23846797Sxc151355
23856797Sxc151355 default:
23861000Sxc151355 return (DDI_FAILURE);
23876797Sxc151355 }
23881000Sxc151355
23897507SXinghua.Wen@Sun.COM if (mac_disable(asc->asc_isc.ic_mach) != 0)
23907507SXinghua.Wen@Sun.COM return (DDI_FAILURE);
23917507SXinghua.Wen@Sun.COM
23923147Sxc151355 ath_stop_scantimer(asc);
23931000Sxc151355
23941000Sxc151355 /* disable interrupts */
23951000Sxc151355 ATH_HAL_INTRSET(asc->asc_ah, 0);
23961000Sxc151355
23973147Sxc151355 /*
23983147Sxc151355 * Unregister from the MAC layer subsystem
23993147Sxc151355 */
24007507SXinghua.Wen@Sun.COM (void) mac_unregister(asc->asc_isc.ic_mach);
24013147Sxc151355
24021000Sxc151355 /* free intterrupt resources */
24031000Sxc151355 ddi_remove_intr(devinfo, 0, asc->asc_iblock);
24041000Sxc151355 ddi_remove_softintr(asc->asc_softint_id);
24051000Sxc151355
24063147Sxc151355 /*
24073147Sxc151355 * NB: the order of these is important:
24083147Sxc151355 * o call the 802.11 layer before detaching the hal to
24093147Sxc151355 * insure callbacks into the driver to delete global
24103147Sxc151355 * key cache entries can be handled
24113147Sxc151355 * o reclaim the tx queue data structures after calling
24123147Sxc151355 * the 802.11 layer as we'll get called back to reclaim
24133147Sxc151355 * node state and potentially want to use them
24143147Sxc151355 * o to cleanup the tx queues the hal is called, so detach
24153147Sxc151355 * it last
24163147Sxc151355 */
24173147Sxc151355 ieee80211_detach(&asc->asc_isc);
24181000Sxc151355 ath_desc_free(asc);
24198033SWang.Lin@Sun.COM ddi_taskq_destroy(asc->asc_tq);
24203147Sxc151355 ath_txq_cleanup(asc);
24211000Sxc151355 asc->asc_ah->ah_detach(asc->asc_ah);
24221000Sxc151355
24231000Sxc151355 /* free io handle */
24241000Sxc151355 ddi_regs_map_free(&asc->asc_io_handle);
24251000Sxc151355 pci_config_teardown(&asc->asc_cfg_handle);
24261000Sxc151355
24271000Sxc151355 /* destroy locks */
24281000Sxc151355 mutex_destroy(&asc->asc_rxbuflock);
24291000Sxc151355 mutex_destroy(&asc->asc_genlock);
24303147Sxc151355 mutex_destroy(&asc->asc_resched_lock);
24311000Sxc151355
24321000Sxc151355 ddi_remove_minor_node(devinfo, NULL);
24331000Sxc151355 ddi_soft_state_free(ath_soft_state_p, ddi_get_instance(devinfo));
24341000Sxc151355
24351000Sxc151355 return (DDI_SUCCESS);
24361000Sxc151355 }
24371000Sxc151355
24387805SKonstantin.Ananyev@Sun.COM /*
24397805SKonstantin.Ananyev@Sun.COM * quiesce(9E) entry point.
24407805SKonstantin.Ananyev@Sun.COM *
24417805SKonstantin.Ananyev@Sun.COM * This function is called when the system is single-threaded at high
24427805SKonstantin.Ananyev@Sun.COM * PIL with preemption disabled. Therefore, this function must not be
24437805SKonstantin.Ananyev@Sun.COM * blocked.
24447805SKonstantin.Ananyev@Sun.COM *
24457805SKonstantin.Ananyev@Sun.COM * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
24467805SKonstantin.Ananyev@Sun.COM * DDI_FAILURE indicates an error condition and should almost never happen.
24477805SKonstantin.Ananyev@Sun.COM */
24487805SKonstantin.Ananyev@Sun.COM static int32_t
ath_quiesce(dev_info_t * devinfo)24497805SKonstantin.Ananyev@Sun.COM ath_quiesce(dev_info_t *devinfo)
24507805SKonstantin.Ananyev@Sun.COM {
24517805SKonstantin.Ananyev@Sun.COM ath_t *asc;
24527805SKonstantin.Ananyev@Sun.COM struct ath_hal *ah;
24537805SKonstantin.Ananyev@Sun.COM int i;
24547805SKonstantin.Ananyev@Sun.COM
24557805SKonstantin.Ananyev@Sun.COM asc = ddi_get_soft_state(ath_soft_state_p, ddi_get_instance(devinfo));
24567805SKonstantin.Ananyev@Sun.COM
24577805SKonstantin.Ananyev@Sun.COM if (asc == NULL || (ah = asc->asc_ah) == NULL)
24587805SKonstantin.Ananyev@Sun.COM return (DDI_FAILURE);
24597805SKonstantin.Ananyev@Sun.COM
24607805SKonstantin.Ananyev@Sun.COM /*
24617805SKonstantin.Ananyev@Sun.COM * Disable interrupts
24627805SKonstantin.Ananyev@Sun.COM */
24637805SKonstantin.Ananyev@Sun.COM ATH_HAL_INTRSET(ah, 0);
24647805SKonstantin.Ananyev@Sun.COM
24657805SKonstantin.Ananyev@Sun.COM /*
24667805SKonstantin.Ananyev@Sun.COM * Disable TX HW
24677805SKonstantin.Ananyev@Sun.COM */
24687805SKonstantin.Ananyev@Sun.COM for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
24697805SKonstantin.Ananyev@Sun.COM if (ATH_TXQ_SETUP(asc, i)) {
24707805SKonstantin.Ananyev@Sun.COM ATH_HAL_STOPTXDMA(ah, asc->asc_txq[i].axq_qnum);
24717805SKonstantin.Ananyev@Sun.COM }
24727805SKonstantin.Ananyev@Sun.COM }
24737805SKonstantin.Ananyev@Sun.COM
24747805SKonstantin.Ananyev@Sun.COM /*
24757805SKonstantin.Ananyev@Sun.COM * Disable RX HW
24767805SKonstantin.Ananyev@Sun.COM */
24777805SKonstantin.Ananyev@Sun.COM ATH_HAL_STOPPCURECV(ah);
24787805SKonstantin.Ananyev@Sun.COM ATH_HAL_SETRXFILTER(ah, 0);
24797805SKonstantin.Ananyev@Sun.COM ATH_HAL_STOPDMARECV(ah);
24807805SKonstantin.Ananyev@Sun.COM drv_usecwait(3000);
24817805SKonstantin.Ananyev@Sun.COM
24827805SKonstantin.Ananyev@Sun.COM /*
24837805SKonstantin.Ananyev@Sun.COM * Power down HW
24847805SKonstantin.Ananyev@Sun.COM */
24857805SKonstantin.Ananyev@Sun.COM ATH_HAL_PHYDISABLE(ah);
24867805SKonstantin.Ananyev@Sun.COM
24877805SKonstantin.Ananyev@Sun.COM return (DDI_SUCCESS);
24887805SKonstantin.Ananyev@Sun.COM }
24897805SKonstantin.Ananyev@Sun.COM
24903147Sxc151355 DDI_DEFINE_STREAM_OPS(ath_dev_ops, nulldev, nulldev, ath_attach, ath_detach,
24917805SKonstantin.Ananyev@Sun.COM nodev, NULL, D_MP, NULL, ath_quiesce);
24921000Sxc151355
24931000Sxc151355 static struct modldrv ath_modldrv = {
24941000Sxc151355 &mod_driverops, /* Type of module. This one is a driver */
24958033SWang.Lin@Sun.COM "ath driver 1.4/HAL 0.10.5.6", /* short description */
24961000Sxc151355 &ath_dev_ops /* driver specific ops */
24971000Sxc151355 };
24981000Sxc151355
24991000Sxc151355 static struct modlinkage modlinkage = {
25001000Sxc151355 MODREV_1, (void *)&ath_modldrv, NULL
25011000Sxc151355 };
25021000Sxc151355
25031000Sxc151355
25041000Sxc151355 int
_info(struct modinfo * modinfop)25051000Sxc151355 _info(struct modinfo *modinfop)
25061000Sxc151355 {
25071000Sxc151355 return (mod_info(&modlinkage, modinfop));
25081000Sxc151355 }
25091000Sxc151355
25101000Sxc151355 int
_init(void)25111000Sxc151355 _init(void)
25121000Sxc151355 {
25131000Sxc151355 int status;
25141000Sxc151355
25151000Sxc151355 status = ddi_soft_state_init(&ath_soft_state_p, sizeof (ath_t), 1);
25161000Sxc151355 if (status != 0)
25171000Sxc151355 return (status);
25181000Sxc151355
25191000Sxc151355 mutex_init(&ath_loglock, NULL, MUTEX_DRIVER, NULL);
25203147Sxc151355 ath_halfix_init();
25213147Sxc151355 mac_init_ops(&ath_dev_ops, "ath");
25221000Sxc151355 status = mod_install(&modlinkage);
25231000Sxc151355 if (status != 0) {
25243147Sxc151355 mac_fini_ops(&ath_dev_ops);
25253147Sxc151355 ath_halfix_finit();
25263147Sxc151355 mutex_destroy(&ath_loglock);
25271000Sxc151355 ddi_soft_state_fini(&ath_soft_state_p);
25281000Sxc151355 }
25291000Sxc151355
25301000Sxc151355 return (status);
25311000Sxc151355 }
25321000Sxc151355
25331000Sxc151355 int
_fini(void)25341000Sxc151355 _fini(void)
25351000Sxc151355 {
25361000Sxc151355 int status;
25371000Sxc151355
25381000Sxc151355 status = mod_remove(&modlinkage);
25391000Sxc151355 if (status == 0) {
25403147Sxc151355 mac_fini_ops(&ath_dev_ops);
25413147Sxc151355 ath_halfix_finit();
25423147Sxc151355 mutex_destroy(&ath_loglock);
25431000Sxc151355 ddi_soft_state_fini(&ath_soft_state_p);
25441000Sxc151355 }
25451000Sxc151355 return (status);
25461000Sxc151355 }
2547