11000Sxc151355 /* 26235Sxc151355 * Copyright 2008 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> 1353147Sxc151355 #include <sys/mac.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 /* 177*8033SWang.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, 2247663SSowmini.Varadhan@Sun.COM uint_t, uint_t, void *); 2257663SSowmini.Varadhan@Sun.COM 2263147Sxc151355 static mac_callbacks_t ath_m_callbacks = { 2277663SSowmini.Varadhan@Sun.COM MC_IOCTL | MC_SETPROP | MC_GETPROP, 2283147Sxc151355 ath_m_stat, 2293147Sxc151355 ath_m_start, 2303147Sxc151355 ath_m_stop, 2313147Sxc151355 ath_m_promisc, 2323147Sxc151355 ath_m_multicst, 2333147Sxc151355 ath_m_unicst, 2343147Sxc151355 ath_m_tx, 2353147Sxc151355 NULL, /* mc_resources; */ 2363147Sxc151355 ath_m_ioctl, 2377663SSowmini.Varadhan@Sun.COM NULL, /* mc_getcapab */ 2387663SSowmini.Varadhan@Sun.COM NULL, 2397663SSowmini.Varadhan@Sun.COM NULL, 2407663SSowmini.Varadhan@Sun.COM ath_m_setprop, 2417663SSowmini.Varadhan@Sun.COM ath_m_getprop 2423147Sxc151355 }; 2431000Sxc151355 2441000Sxc151355 /* 2451000Sxc151355 * Available debug flags: 2461000Sxc151355 * ATH_DBG_INIT, ATH_DBG_GLD, ATH_DBG_HAL, ATH_DBG_INT, ATH_DBG_ATTACH, 2471000Sxc151355 * ATH_DBG_DETACH, ATH_DBG_AUX, ATH_DBG_WIFICFG, ATH_DBG_OSDEP 2481000Sxc151355 */ 2491000Sxc151355 uint32_t ath_dbg_flags = 0; 2501000Sxc151355 2511000Sxc151355 /* 2521000Sxc151355 * Exception/warning cases not leading to panic. 2531000Sxc151355 */ 2541000Sxc151355 void 2551000Sxc151355 ath_problem(const int8_t *fmt, ...) 2561000Sxc151355 { 2571000Sxc151355 va_list args; 2581000Sxc151355 2591000Sxc151355 mutex_enter(&ath_loglock); 2601000Sxc151355 2611000Sxc151355 va_start(args, fmt); 2621000Sxc151355 vcmn_err(CE_WARN, fmt, args); 2631000Sxc151355 va_end(args); 2641000Sxc151355 2651000Sxc151355 mutex_exit(&ath_loglock); 2661000Sxc151355 } 2671000Sxc151355 2681000Sxc151355 /* 2691000Sxc151355 * Normal log information independent of debug. 2701000Sxc151355 */ 2711000Sxc151355 void 2721000Sxc151355 ath_log(const int8_t *fmt, ...) 2731000Sxc151355 { 2741000Sxc151355 va_list args; 2751000Sxc151355 2761000Sxc151355 mutex_enter(&ath_loglock); 2771000Sxc151355 2781000Sxc151355 va_start(args, fmt); 2791000Sxc151355 vcmn_err(CE_CONT, fmt, args); 2801000Sxc151355 va_end(args); 2811000Sxc151355 2821000Sxc151355 mutex_exit(&ath_loglock); 2831000Sxc151355 } 2841000Sxc151355 2851000Sxc151355 void 2861000Sxc151355 ath_dbg(uint32_t dbg_flags, const int8_t *fmt, ...) 2871000Sxc151355 { 2881000Sxc151355 va_list args; 2891000Sxc151355 2901000Sxc151355 if (dbg_flags & ath_dbg_flags) { 2911000Sxc151355 mutex_enter(&ath_loglock); 2921000Sxc151355 va_start(args, fmt); 2931000Sxc151355 vcmn_err(CE_CONT, fmt, args); 2941000Sxc151355 va_end(args); 2951000Sxc151355 mutex_exit(&ath_loglock); 2961000Sxc151355 } 2971000Sxc151355 } 2981000Sxc151355 2991000Sxc151355 void 3001000Sxc151355 ath_setup_desc(ath_t *asc, struct ath_buf *bf) 3011000Sxc151355 { 3021000Sxc151355 struct ath_desc *ds; 3031000Sxc151355 3041000Sxc151355 ds = bf->bf_desc; 3051000Sxc151355 ds->ds_link = bf->bf_daddr; 3061000Sxc151355 ds->ds_data = bf->bf_dma.cookie.dmac_address; 3071000Sxc151355 ATH_HAL_SETUPRXDESC(asc->asc_ah, ds, 3081000Sxc151355 bf->bf_dma.alength, /* buffer size */ 3091000Sxc151355 0); 3101000Sxc151355 3111000Sxc151355 if (asc->asc_rxlink != NULL) 3121000Sxc151355 *asc->asc_rxlink = bf->bf_daddr; 3131000Sxc151355 asc->asc_rxlink = &ds->ds_link; 3141000Sxc151355 } 3151000Sxc151355 3161000Sxc151355 3171000Sxc151355 /* 3181000Sxc151355 * Allocate an area of memory and a DMA handle for accessing it 3191000Sxc151355 */ 3201000Sxc151355 static int 3216235Sxc151355 ath_alloc_dma_mem(dev_info_t *devinfo, ddi_dma_attr_t *dma_attr, size_t memsize, 3226235Sxc151355 ddi_device_acc_attr_t *attr_p, uint_t alloc_flags, 3236235Sxc151355 uint_t bind_flags, dma_area_t *dma_p) 3241000Sxc151355 { 3251000Sxc151355 int err; 3261000Sxc151355 3271000Sxc151355 /* 3281000Sxc151355 * Allocate handle 3291000Sxc151355 */ 3306235Sxc151355 err = ddi_dma_alloc_handle(devinfo, dma_attr, 3315420Sxc151355 DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl); 3321000Sxc151355 if (err != DDI_SUCCESS) 3331000Sxc151355 return (DDI_FAILURE); 3341000Sxc151355 3351000Sxc151355 /* 3361000Sxc151355 * Allocate memory 3371000Sxc151355 */ 3381000Sxc151355 err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize, attr_p, 3391000Sxc151355 alloc_flags, DDI_DMA_SLEEP, NULL, &dma_p->mem_va, 3401000Sxc151355 &dma_p->alength, &dma_p->acc_hdl); 3411000Sxc151355 if (err != DDI_SUCCESS) 3421000Sxc151355 return (DDI_FAILURE); 3431000Sxc151355 3441000Sxc151355 /* 3451000Sxc151355 * Bind the two together 3461000Sxc151355 */ 3471000Sxc151355 err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL, 3485420Sxc151355 dma_p->mem_va, dma_p->alength, bind_flags, 3495420Sxc151355 DDI_DMA_SLEEP, NULL, &dma_p->cookie, &dma_p->ncookies); 3501000Sxc151355 if (err != DDI_DMA_MAPPED) 3511000Sxc151355 return (DDI_FAILURE); 3521000Sxc151355 3531000Sxc151355 dma_p->nslots = ~0U; 3541000Sxc151355 dma_p->size = ~0U; 3551000Sxc151355 dma_p->token = ~0U; 3561000Sxc151355 dma_p->offset = 0; 3571000Sxc151355 return (DDI_SUCCESS); 3581000Sxc151355 } 3591000Sxc151355 3601000Sxc151355 /* 3611000Sxc151355 * Free one allocated area of DMAable memory 3621000Sxc151355 */ 3631000Sxc151355 static void 3641000Sxc151355 ath_free_dma_mem(dma_area_t *dma_p) 3651000Sxc151355 { 3661000Sxc151355 if (dma_p->dma_hdl != NULL) { 3671000Sxc151355 (void) ddi_dma_unbind_handle(dma_p->dma_hdl); 3681000Sxc151355 if (dma_p->acc_hdl != NULL) { 3691000Sxc151355 ddi_dma_mem_free(&dma_p->acc_hdl); 3701000Sxc151355 dma_p->acc_hdl = NULL; 3711000Sxc151355 } 3721000Sxc151355 ddi_dma_free_handle(&dma_p->dma_hdl); 3731000Sxc151355 dma_p->ncookies = 0; 3741000Sxc151355 dma_p->dma_hdl = NULL; 3751000Sxc151355 } 3761000Sxc151355 } 3771000Sxc151355 3781000Sxc151355 379*8033SWang.Lin@Sun.COM /* 380*8033SWang.Lin@Sun.COM * Initialize tx/rx buffer list. Allocate DMA memory for 381*8033SWang.Lin@Sun.COM * each buffer. 382*8033SWang.Lin@Sun.COM */ 383*8033SWang.Lin@Sun.COM static int 384*8033SWang.Lin@Sun.COM ath_buflist_setup(dev_info_t *devinfo, ath_t *asc, list_t *bflist, 385*8033SWang.Lin@Sun.COM struct ath_buf **pbf, struct ath_desc **pds, int nbuf, uint_t dmabflags) 386*8033SWang.Lin@Sun.COM { 387*8033SWang.Lin@Sun.COM int i, err; 388*8033SWang.Lin@Sun.COM struct ath_buf *bf = *pbf; 389*8033SWang.Lin@Sun.COM struct ath_desc *ds = *pds; 390*8033SWang.Lin@Sun.COM 391*8033SWang.Lin@Sun.COM list_create(bflist, sizeof (struct ath_buf), 392*8033SWang.Lin@Sun.COM offsetof(struct ath_buf, bf_node)); 393*8033SWang.Lin@Sun.COM for (i = 0; i < nbuf; i++, bf++, ds++) { 394*8033SWang.Lin@Sun.COM bf->bf_desc = ds; 395*8033SWang.Lin@Sun.COM bf->bf_daddr = asc->asc_desc_dma.cookie.dmac_address + 396*8033SWang.Lin@Sun.COM ((uintptr_t)ds - (uintptr_t)asc->asc_desc); 397*8033SWang.Lin@Sun.COM list_insert_tail(bflist, bf); 398*8033SWang.Lin@Sun.COM 399*8033SWang.Lin@Sun.COM /* alloc DMA memory */ 400*8033SWang.Lin@Sun.COM err = ath_alloc_dma_mem(devinfo, &ath_dma_attr, 401*8033SWang.Lin@Sun.COM asc->asc_dmabuf_size, &ath_desc_accattr, DDI_DMA_STREAMING, 402*8033SWang.Lin@Sun.COM dmabflags, &bf->bf_dma); 403*8033SWang.Lin@Sun.COM if (err != DDI_SUCCESS) 404*8033SWang.Lin@Sun.COM return (err); 405*8033SWang.Lin@Sun.COM } 406*8033SWang.Lin@Sun.COM *pbf = bf; 407*8033SWang.Lin@Sun.COM *pds = ds; 408*8033SWang.Lin@Sun.COM 409*8033SWang.Lin@Sun.COM return (DDI_SUCCESS); 410*8033SWang.Lin@Sun.COM } 411*8033SWang.Lin@Sun.COM 412*8033SWang.Lin@Sun.COM /* 413*8033SWang.Lin@Sun.COM * Destroy tx/rx buffer list. Free DMA memory. 414*8033SWang.Lin@Sun.COM */ 415*8033SWang.Lin@Sun.COM static void 416*8033SWang.Lin@Sun.COM ath_buflist_cleanup(list_t *buflist) 417*8033SWang.Lin@Sun.COM { 418*8033SWang.Lin@Sun.COM struct ath_buf *bf; 419*8033SWang.Lin@Sun.COM 420*8033SWang.Lin@Sun.COM if (!buflist) 421*8033SWang.Lin@Sun.COM return; 422*8033SWang.Lin@Sun.COM 423*8033SWang.Lin@Sun.COM bf = list_head(buflist); 424*8033SWang.Lin@Sun.COM while (bf != NULL) { 425*8033SWang.Lin@Sun.COM if (bf->bf_m != NULL) { 426*8033SWang.Lin@Sun.COM freemsg(bf->bf_m); 427*8033SWang.Lin@Sun.COM bf->bf_m = NULL; 428*8033SWang.Lin@Sun.COM } 429*8033SWang.Lin@Sun.COM /* Free DMA buffer */ 430*8033SWang.Lin@Sun.COM ath_free_dma_mem(&bf->bf_dma); 431*8033SWang.Lin@Sun.COM if (bf->bf_in != NULL) { 432*8033SWang.Lin@Sun.COM ieee80211_free_node(bf->bf_in); 433*8033SWang.Lin@Sun.COM bf->bf_in = NULL; 434*8033SWang.Lin@Sun.COM } 435*8033SWang.Lin@Sun.COM list_remove(buflist, bf); 436*8033SWang.Lin@Sun.COM bf = list_head(buflist); 437*8033SWang.Lin@Sun.COM } 438*8033SWang.Lin@Sun.COM list_destroy(buflist); 439*8033SWang.Lin@Sun.COM } 440*8033SWang.Lin@Sun.COM 441*8033SWang.Lin@Sun.COM 442*8033SWang.Lin@Sun.COM static void 443*8033SWang.Lin@Sun.COM ath_desc_free(ath_t *asc) 444*8033SWang.Lin@Sun.COM { 445*8033SWang.Lin@Sun.COM ath_buflist_cleanup(&asc->asc_txbuf_list); 446*8033SWang.Lin@Sun.COM ath_buflist_cleanup(&asc->asc_rxbuf_list); 447*8033SWang.Lin@Sun.COM 448*8033SWang.Lin@Sun.COM /* Free descriptor DMA buffer */ 449*8033SWang.Lin@Sun.COM ath_free_dma_mem(&asc->asc_desc_dma); 450*8033SWang.Lin@Sun.COM 451*8033SWang.Lin@Sun.COM kmem_free((void *)asc->asc_vbufptr, asc->asc_vbuflen); 452*8033SWang.Lin@Sun.COM asc->asc_vbufptr = NULL; 453*8033SWang.Lin@Sun.COM } 454*8033SWang.Lin@Sun.COM 4551000Sxc151355 static int 4561000Sxc151355 ath_desc_alloc(dev_info_t *devinfo, ath_t *asc) 4571000Sxc151355 { 458*8033SWang.Lin@Sun.COM int err; 4591000Sxc151355 size_t size; 4601000Sxc151355 struct ath_desc *ds; 4611000Sxc151355 struct ath_buf *bf; 4621000Sxc151355 4631000Sxc151355 size = sizeof (struct ath_desc) * (ATH_TXBUF + ATH_RXBUF); 4641000Sxc151355 4656235Sxc151355 err = ath_alloc_dma_mem(devinfo, &ath_desc_dma_attr, size, 4666235Sxc151355 &ath_desc_accattr, DDI_DMA_CONSISTENT, 4676235Sxc151355 DDI_DMA_RDWR | DDI_DMA_CONSISTENT, &asc->asc_desc_dma); 4681000Sxc151355 4691000Sxc151355 /* virtual address of the first descriptor */ 4701000Sxc151355 asc->asc_desc = (struct ath_desc *)asc->asc_desc_dma.mem_va; 4711000Sxc151355 4721000Sxc151355 ds = asc->asc_desc; 4731000Sxc151355 ATH_DEBUG((ATH_DBG_INIT, "ath: ath_desc_alloc(): DMA map: " 4741000Sxc151355 "%p (%d) -> %p\n", 4751000Sxc151355 asc->asc_desc, asc->asc_desc_dma.alength, 4761000Sxc151355 asc->asc_desc_dma.cookie.dmac_address)); 4771000Sxc151355 4781000Sxc151355 /* allocate data structures to describe TX/RX DMA buffers */ 4791000Sxc151355 asc->asc_vbuflen = sizeof (struct ath_buf) * (ATH_TXBUF + ATH_RXBUF); 4801000Sxc151355 bf = (struct ath_buf *)kmem_zalloc(asc->asc_vbuflen, KM_SLEEP); 4811000Sxc151355 asc->asc_vbufptr = bf; 4821000Sxc151355 4831000Sxc151355 /* DMA buffer size for each TX/RX packet */ 4841000Sxc151355 asc->asc_dmabuf_size = roundup(1000 + sizeof (struct ieee80211_frame) + 4851000Sxc151355 IEEE80211_MTU + IEEE80211_CRC_LEN + 4861000Sxc151355 (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + 4871000Sxc151355 IEEE80211_WEP_CRCLEN), asc->asc_cachelsz); 4881000Sxc151355 489*8033SWang.Lin@Sun.COM /* create RX buffer list */ 490*8033SWang.Lin@Sun.COM err = ath_buflist_setup(devinfo, asc, &asc->asc_rxbuf_list, &bf, &ds, 491*8033SWang.Lin@Sun.COM ATH_RXBUF, DDI_DMA_READ | DDI_DMA_STREAMING); 492*8033SWang.Lin@Sun.COM if (err != DDI_SUCCESS) { 493*8033SWang.Lin@Sun.COM ath_desc_free(asc); 494*8033SWang.Lin@Sun.COM return (err); 4951000Sxc151355 } 4961000Sxc151355 497*8033SWang.Lin@Sun.COM /* create TX buffer list */ 498*8033SWang.Lin@Sun.COM err = ath_buflist_setup(devinfo, asc, &asc->asc_txbuf_list, &bf, &ds, 499*8033SWang.Lin@Sun.COM ATH_TXBUF, DDI_DMA_STREAMING); 500*8033SWang.Lin@Sun.COM if (err != DDI_SUCCESS) { 501*8033SWang.Lin@Sun.COM ath_desc_free(asc); 502*8033SWang.Lin@Sun.COM return (err); 503*8033SWang.Lin@Sun.COM } 5041000Sxc151355 5051000Sxc151355 5061000Sxc151355 return (DDI_SUCCESS); 5071000Sxc151355 } 5081000Sxc151355 5091000Sxc151355 static void 5101000Sxc151355 ath_printrxbuf(struct ath_buf *bf, int32_t done) 5111000Sxc151355 { 5121000Sxc151355 struct ath_desc *ds = bf->bf_desc; 513*8033SWang.Lin@Sun.COM const struct ath_rx_status *rs = &bf->bf_status.ds_rxstat; 5141000Sxc151355 5151000Sxc151355 ATH_DEBUG((ATH_DBG_RECV, "ath: R (%p %p) %08x %08x %08x " 5161000Sxc151355 "%08x %08x %08x %c\n", 5171000Sxc151355 ds, bf->bf_daddr, 5181000Sxc151355 ds->ds_link, ds->ds_data, 5191000Sxc151355 ds->ds_ctl0, ds->ds_ctl1, 5201000Sxc151355 ds->ds_hw[0], ds->ds_hw[1], 521*8033SWang.Lin@Sun.COM !done ? ' ' : (rs->rs_status == 0) ? '*' : '!')); 5221000Sxc151355 } 5231000Sxc151355 5241000Sxc151355 static void 5251000Sxc151355 ath_rx_handler(ath_t *asc) 5261000Sxc151355 { 5273147Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc; 5281000Sxc151355 struct ath_buf *bf; 5291000Sxc151355 struct ath_hal *ah = asc->asc_ah; 5301000Sxc151355 struct ath_desc *ds; 531*8033SWang.Lin@Sun.COM struct ath_rx_status *rs; 5321000Sxc151355 mblk_t *rx_mp; 5333147Sxc151355 struct ieee80211_frame *wh; 5341000Sxc151355 int32_t len, loop = 1; 5351000Sxc151355 uint8_t phyerr; 5361000Sxc151355 HAL_STATUS status; 5371000Sxc151355 HAL_NODE_STATS hal_node_stats; 5383147Sxc151355 struct ieee80211_node *in; 5391000Sxc151355 5401000Sxc151355 do { 5411000Sxc151355 mutex_enter(&asc->asc_rxbuflock); 5421000Sxc151355 bf = list_head(&asc->asc_rxbuf_list); 5431000Sxc151355 if (bf == NULL) { 5441000Sxc151355 ATH_DEBUG((ATH_DBG_RECV, "ath: ath_rx_handler(): " 5451000Sxc151355 "no buffer\n")); 5461000Sxc151355 mutex_exit(&asc->asc_rxbuflock); 5471000Sxc151355 break; 5481000Sxc151355 } 5491000Sxc151355 ASSERT(bf->bf_dma.cookie.dmac_address != NULL); 5501000Sxc151355 ds = bf->bf_desc; 5511000Sxc151355 if (ds->ds_link == bf->bf_daddr) { 5521000Sxc151355 /* 5531000Sxc151355 * Never process the self-linked entry at the end, 5541000Sxc151355 * this may be met at heavy load. 5551000Sxc151355 */ 5561000Sxc151355 mutex_exit(&asc->asc_rxbuflock); 5571000Sxc151355 break; 5581000Sxc151355 } 5591000Sxc151355 560*8033SWang.Lin@Sun.COM rs = &bf->bf_status.ds_rxstat; 5611000Sxc151355 status = ATH_HAL_RXPROCDESC(ah, ds, 5621000Sxc151355 bf->bf_daddr, 563*8033SWang.Lin@Sun.COM ATH_PA2DESC(asc, ds->ds_link), rs); 5641000Sxc151355 if (status == HAL_EINPROGRESS) { 5651000Sxc151355 mutex_exit(&asc->asc_rxbuflock); 5661000Sxc151355 break; 5671000Sxc151355 } 5681000Sxc151355 list_remove(&asc->asc_rxbuf_list, bf); 5691000Sxc151355 mutex_exit(&asc->asc_rxbuflock); 5701000Sxc151355 571*8033SWang.Lin@Sun.COM if (rs->rs_status != 0) { 572*8033SWang.Lin@Sun.COM if (rs->rs_status & HAL_RXERR_CRC) 5731000Sxc151355 asc->asc_stats.ast_rx_crcerr++; 574*8033SWang.Lin@Sun.COM if (rs->rs_status & HAL_RXERR_FIFO) 5751000Sxc151355 asc->asc_stats.ast_rx_fifoerr++; 576*8033SWang.Lin@Sun.COM if (rs->rs_status & HAL_RXERR_DECRYPT) 5771000Sxc151355 asc->asc_stats.ast_rx_badcrypt++; 578*8033SWang.Lin@Sun.COM if (rs->rs_status & HAL_RXERR_PHY) { 5791000Sxc151355 asc->asc_stats.ast_rx_phyerr++; 580*8033SWang.Lin@Sun.COM phyerr = rs->rs_phyerr & 0x1f; 5811000Sxc151355 asc->asc_stats.ast_rx_phy[phyerr]++; 5821000Sxc151355 } 5831000Sxc151355 goto rx_next; 5841000Sxc151355 } 585*8033SWang.Lin@Sun.COM len = rs->rs_datalen; 5861000Sxc151355 5871000Sxc151355 /* less than sizeof(struct ieee80211_frame) */ 5881000Sxc151355 if (len < 20) { 5891000Sxc151355 asc->asc_stats.ast_rx_tooshort++; 5901000Sxc151355 goto rx_next; 5911000Sxc151355 } 5921000Sxc151355 5931000Sxc151355 if ((rx_mp = allocb(asc->asc_dmabuf_size, BPRI_MED)) == NULL) { 5941000Sxc151355 ath_problem("ath: ath_rx_handler(): " 5951000Sxc151355 "allocing mblk buffer failed.\n"); 5961000Sxc151355 return; 5971000Sxc151355 } 5981000Sxc151355 5991000Sxc151355 ATH_DMA_SYNC(bf->bf_dma, DDI_DMA_SYNC_FORCPU); 6001000Sxc151355 bcopy(bf->bf_dma.mem_va, rx_mp->b_rptr, len); 6011000Sxc151355 6021000Sxc151355 rx_mp->b_wptr += len; 6031000Sxc151355 wh = (struct ieee80211_frame *)rx_mp->b_rptr; 6043147Sxc151355 if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == 6051000Sxc151355 IEEE80211_FC0_TYPE_CTL) { 6061000Sxc151355 /* 6071000Sxc151355 * Ignore control frame received in promisc mode. 6081000Sxc151355 */ 6091000Sxc151355 freemsg(rx_mp); 6101000Sxc151355 goto rx_next; 6111000Sxc151355 } 6121000Sxc151355 /* Remove the CRC at the end of IEEE80211 frame */ 6131000Sxc151355 rx_mp->b_wptr -= IEEE80211_CRC_LEN; 6141000Sxc151355 #ifdef DEBUG 6151000Sxc151355 ath_printrxbuf(bf, status == HAL_OK); 6161000Sxc151355 #endif /* DEBUG */ 6173147Sxc151355 /* 6183147Sxc151355 * Locate the node for sender, track state, and then 6193147Sxc151355 * pass the (referenced) node up to the 802.11 layer 6203147Sxc151355 * for its use. 6213147Sxc151355 */ 6223147Sxc151355 in = ieee80211_find_rxnode(ic, wh); 6233147Sxc151355 6243147Sxc151355 /* 6253147Sxc151355 * Send frame up for processing. 6263147Sxc151355 */ 6273147Sxc151355 (void) ieee80211_input(ic, rx_mp, in, 628*8033SWang.Lin@Sun.COM rs->rs_rssi, rs->rs_tstamp); 6293147Sxc151355 6303147Sxc151355 ieee80211_free_node(in); 6313147Sxc151355 6321000Sxc151355 rx_next: 6331000Sxc151355 mutex_enter(&asc->asc_rxbuflock); 6341000Sxc151355 list_insert_tail(&asc->asc_rxbuf_list, bf); 6351000Sxc151355 mutex_exit(&asc->asc_rxbuflock); 6361000Sxc151355 ath_setup_desc(asc, bf); 6371000Sxc151355 } while (loop); 6381000Sxc151355 6391000Sxc151355 /* rx signal state monitoring */ 6403147Sxc151355 ATH_HAL_RXMONITOR(ah, &hal_node_stats, &asc->asc_curchan); 6411000Sxc151355 } 6421000Sxc151355 6431000Sxc151355 static void 6441000Sxc151355 ath_printtxbuf(struct ath_buf *bf, int done) 6451000Sxc151355 { 6461000Sxc151355 struct ath_desc *ds = bf->bf_desc; 647*8033SWang.Lin@Sun.COM const struct ath_tx_status *ts = &bf->bf_status.ds_txstat; 6481000Sxc151355 6491000Sxc151355 ATH_DEBUG((ATH_DBG_SEND, "ath: T(%p %p) %08x %08x %08x %08x %08x" 6501000Sxc151355 " %08x %08x %08x %c\n", 6511000Sxc151355 ds, bf->bf_daddr, 6521000Sxc151355 ds->ds_link, ds->ds_data, 6531000Sxc151355 ds->ds_ctl0, ds->ds_ctl1, 6541000Sxc151355 ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3], 655*8033SWang.Lin@Sun.COM !done ? ' ' : (ts->ts_status == 0) ? '*' : '!')); 6561000Sxc151355 } 6571000Sxc151355 6581000Sxc151355 /* 6591000Sxc151355 * The input parameter mp has following assumption: 6603147Sxc151355 * For data packets, GLDv3 mac_wifi plugin allocates and fills the 6613147Sxc151355 * ieee80211 header. For management packets, net80211 allocates and 6623147Sxc151355 * fills the ieee80211 header. In both cases, enough spaces in the 6633147Sxc151355 * header are left for encryption option. 6641000Sxc151355 */ 6651000Sxc151355 static int32_t 6663147Sxc151355 ath_tx_start(ath_t *asc, struct ieee80211_node *in, struct ath_buf *bf, 6673147Sxc151355 mblk_t *mp) 6681000Sxc151355 { 6693147Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc; 6701000Sxc151355 struct ieee80211_frame *wh; 6711000Sxc151355 struct ath_hal *ah = asc->asc_ah; 6723147Sxc151355 uint32_t subtype, flags, ctsduration; 6731000Sxc151355 int32_t keyix, iswep, hdrlen, pktlen, mblen, mbslen, try0; 6743147Sxc151355 uint8_t rix, cix, txrate, ctsrate; 6751000Sxc151355 struct ath_desc *ds; 6761000Sxc151355 struct ath_txq *txq; 6771000Sxc151355 HAL_PKT_TYPE atype; 6781000Sxc151355 const HAL_RATE_TABLE *rt; 6791000Sxc151355 HAL_BOOL shortPreamble; 6801000Sxc151355 struct ath_node *an; 6813147Sxc151355 caddr_t dest; 6821000Sxc151355 6831000Sxc151355 /* 6841000Sxc151355 * CRC are added by H/W, not encaped by driver, 6851000Sxc151355 * but we must count it in pkt length. 6861000Sxc151355 */ 6871000Sxc151355 pktlen = IEEE80211_CRC_LEN; 6881000Sxc151355 6893147Sxc151355 wh = (struct ieee80211_frame *)mp->b_rptr; 6903147Sxc151355 iswep = wh->i_fc[1] & IEEE80211_FC1_WEP; 6911000Sxc151355 keyix = HAL_TXKEYIX_INVALID; 6921000Sxc151355 hdrlen = sizeof (struct ieee80211_frame); 6933147Sxc151355 if (iswep != 0) { 6943147Sxc151355 const struct ieee80211_cipher *cip; 6953147Sxc151355 struct ieee80211_key *k; 6961000Sxc151355 6973147Sxc151355 /* 6983147Sxc151355 * Construct the 802.11 header+trailer for an encrypted 6993147Sxc151355 * frame. The only reason this can fail is because of an 7003147Sxc151355 * unknown or unsupported cipher/key type. 7013147Sxc151355 */ 7023147Sxc151355 k = ieee80211_crypto_encap(ic, mp); 7033147Sxc151355 if (k == NULL) { 7043147Sxc151355 ATH_DEBUG((ATH_DBG_AUX, "crypto_encap failed\n")); 7053147Sxc151355 /* 7063147Sxc151355 * This can happen when the key is yanked after the 7073147Sxc151355 * frame was queued. Just discard the frame; the 7083147Sxc151355 * 802.11 layer counts failures and provides 7093147Sxc151355 * debugging/diagnostics. 7103147Sxc151355 */ 7113147Sxc151355 return (EIO); 7123147Sxc151355 } 7133147Sxc151355 cip = k->wk_cipher; 7141000Sxc151355 /* 7153147Sxc151355 * Adjust the packet + header lengths for the crypto 7163147Sxc151355 * additions and calculate the h/w key index. When 7173147Sxc151355 * a s/w mic is done the frame will have had any mic 7183147Sxc151355 * added to it prior to entry so m0->m_pkthdr.len above will 7193147Sxc151355 * account for it. Otherwise we need to add it to the 7203147Sxc151355 * packet length. 7211000Sxc151355 */ 7223147Sxc151355 hdrlen += cip->ic_header; 7234126Szf162725 pktlen += cip->ic_trailer; 7243147Sxc151355 if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0) 7253147Sxc151355 pktlen += cip->ic_miclen; 7263147Sxc151355 keyix = k->wk_keyix; 7271000Sxc151355 7283147Sxc151355 /* packet header may have moved, reset our local pointer */ 7293147Sxc151355 wh = (struct ieee80211_frame *)mp->b_rptr; 7301000Sxc151355 } 7311000Sxc151355 7323147Sxc151355 dest = bf->bf_dma.mem_va; 7333147Sxc151355 for (; mp != NULL; mp = mp->b_cont) { 7343147Sxc151355 mblen = MBLKL(mp); 7353147Sxc151355 bcopy(mp->b_rptr, dest, mblen); 7363147Sxc151355 dest += mblen; 7373147Sxc151355 } 7386990Sgd78059 mbslen = (uintptr_t)dest - (uintptr_t)bf->bf_dma.mem_va; 7393147Sxc151355 pktlen += mbslen; 7403147Sxc151355 7411000Sxc151355 bf->bf_in = in; 7421000Sxc151355 7431000Sxc151355 /* setup descriptors */ 7441000Sxc151355 ds = bf->bf_desc; 7451000Sxc151355 rt = asc->asc_currates; 7463147Sxc151355 ASSERT(rt != NULL); 7471000Sxc151355 7481000Sxc151355 /* 7491000Sxc151355 * The 802.11 layer marks whether or not we should 7501000Sxc151355 * use short preamble based on the current mode and 7511000Sxc151355 * negotiated parameters. 7521000Sxc151355 */ 7533147Sxc151355 if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && 7541000Sxc151355 (in->in_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) { 7551000Sxc151355 shortPreamble = AH_TRUE; 7561000Sxc151355 asc->asc_stats.ast_tx_shortpre++; 7571000Sxc151355 } else { 7581000Sxc151355 shortPreamble = AH_FALSE; 7591000Sxc151355 } 7601000Sxc151355 7611000Sxc151355 an = ATH_NODE(in); 7621000Sxc151355 7631000Sxc151355 /* 7641000Sxc151355 * Calculate Atheros packet type from IEEE80211 packet header 7651000Sxc151355 * and setup for rate calculations. 7661000Sxc151355 */ 7673147Sxc151355 switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { 7681000Sxc151355 case IEEE80211_FC0_TYPE_MGT: 7693147Sxc151355 subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 7701000Sxc151355 if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) 7711000Sxc151355 atype = HAL_PKT_TYPE_BEACON; 7721000Sxc151355 else if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) 7731000Sxc151355 atype = HAL_PKT_TYPE_PROBE_RESP; 7741000Sxc151355 else if (subtype == IEEE80211_FC0_SUBTYPE_ATIM) 7751000Sxc151355 atype = HAL_PKT_TYPE_ATIM; 7761000Sxc151355 else 7771000Sxc151355 atype = HAL_PKT_TYPE_NORMAL; 7781000Sxc151355 rix = 0; /* lowest rate */ 7791000Sxc151355 try0 = ATH_TXMAXTRY; 7801000Sxc151355 if (shortPreamble) 7811000Sxc151355 txrate = an->an_tx_mgtratesp; 7821000Sxc151355 else 7831000Sxc151355 txrate = an->an_tx_mgtrate; 7841000Sxc151355 /* force all ctl frames to highest queue */ 7851000Sxc151355 txq = asc->asc_ac2q[WME_AC_VO]; 7861000Sxc151355 break; 7871000Sxc151355 case IEEE80211_FC0_TYPE_CTL: 7881000Sxc151355 atype = HAL_PKT_TYPE_PSPOLL; 7893147Sxc151355 subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 7901000Sxc151355 rix = 0; /* lowest rate */ 7911000Sxc151355 try0 = ATH_TXMAXTRY; 7921000Sxc151355 if (shortPreamble) 7931000Sxc151355 txrate = an->an_tx_mgtratesp; 7941000Sxc151355 else 7951000Sxc151355 txrate = an->an_tx_mgtrate; 7961000Sxc151355 /* force all ctl frames to highest queue */ 7971000Sxc151355 txq = asc->asc_ac2q[WME_AC_VO]; 7981000Sxc151355 break; 7991000Sxc151355 case IEEE80211_FC0_TYPE_DATA: 8001000Sxc151355 atype = HAL_PKT_TYPE_NORMAL; 8011000Sxc151355 rix = an->an_tx_rix0; 8021000Sxc151355 try0 = an->an_tx_try0; 8031000Sxc151355 if (shortPreamble) 8041000Sxc151355 txrate = an->an_tx_rate0sp; 8051000Sxc151355 else 8061000Sxc151355 txrate = an->an_tx_rate0; 8071000Sxc151355 /* Always use background queue */ 8081000Sxc151355 txq = asc->asc_ac2q[WME_AC_BK]; 8091000Sxc151355 break; 8101000Sxc151355 default: 8111000Sxc151355 /* Unknown 802.11 frame */ 8121000Sxc151355 asc->asc_stats.ast_tx_invalid++; 8131000Sxc151355 return (1); 8141000Sxc151355 } 8151000Sxc151355 /* 8161000Sxc151355 * Calculate miscellaneous flags. 8171000Sxc151355 */ 8181000Sxc151355 flags = HAL_TXDESC_CLRDMASK; 8193147Sxc151355 if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { 8201000Sxc151355 flags |= HAL_TXDESC_NOACK; /* no ack on broad/multicast */ 8211000Sxc151355 asc->asc_stats.ast_tx_noack++; 8223147Sxc151355 } else if (pktlen > ic->ic_rtsthreshold) { 8231000Sxc151355 flags |= HAL_TXDESC_RTSENA; /* RTS based on frame length */ 8241000Sxc151355 asc->asc_stats.ast_tx_rts++; 8251000Sxc151355 } 8261000Sxc151355 8271000Sxc151355 /* 8281000Sxc151355 * Calculate duration. This logically belongs in the 802.11 8291000Sxc151355 * layer but it lacks sufficient information to calculate it. 8301000Sxc151355 */ 8311000Sxc151355 if ((flags & HAL_TXDESC_NOACK) == 0 && 8323147Sxc151355 (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != 8331000Sxc151355 IEEE80211_FC0_TYPE_CTL) { 8341000Sxc151355 uint16_t dur; 8351000Sxc151355 dur = ath_hal_computetxtime(ah, rt, IEEE80211_ACK_SIZE, 8361000Sxc151355 rix, shortPreamble); 8377249Sff224033 /* LINTED E_BAD_PTR_CAST_ALIGN */ 8383147Sxc151355 *(uint16_t *)wh->i_dur = LE_16(dur); 8391000Sxc151355 } 8401000Sxc151355 8411000Sxc151355 /* 8421000Sxc151355 * Calculate RTS/CTS rate and duration if needed. 8431000Sxc151355 */ 8441000Sxc151355 ctsduration = 0; 8451000Sxc151355 if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)) { 8461000Sxc151355 /* 8471000Sxc151355 * CTS transmit rate is derived from the transmit rate 8481000Sxc151355 * by looking in the h/w rate table. We must also factor 8491000Sxc151355 * in whether or not a short preamble is to be used. 8501000Sxc151355 */ 8511000Sxc151355 cix = rt->info[rix].controlRate; 8521000Sxc151355 ctsrate = rt->info[cix].rateCode; 8531000Sxc151355 if (shortPreamble) 8541000Sxc151355 ctsrate |= rt->info[cix].shortPreamble; 8551000Sxc151355 /* 8561000Sxc151355 * Compute the transmit duration based on the size 8571000Sxc151355 * of an ACK frame. We call into the HAL to do the 8581000Sxc151355 * computation since it depends on the characteristics 8591000Sxc151355 * of the actual PHY being used. 8601000Sxc151355 */ 8611000Sxc151355 if (flags & HAL_TXDESC_RTSENA) { /* SIFS + CTS */ 8621000Sxc151355 ctsduration += ath_hal_computetxtime(ah, 8631000Sxc151355 rt, IEEE80211_ACK_SIZE, cix, shortPreamble); 8641000Sxc151355 } 8651000Sxc151355 /* SIFS + data */ 8661000Sxc151355 ctsduration += ath_hal_computetxtime(ah, 8671000Sxc151355 rt, pktlen, rix, shortPreamble); 868*8033SWang.Lin@Sun.COM if ((flags & HAL_TXDESC_NOACK) == 0) { /* SIFS + ACK */ 8691000Sxc151355 ctsduration += ath_hal_computetxtime(ah, 8701000Sxc151355 rt, IEEE80211_ACK_SIZE, cix, shortPreamble); 8711000Sxc151355 } 8721000Sxc151355 } else 8731000Sxc151355 ctsrate = 0; 8741000Sxc151355 8751000Sxc151355 if (++txq->axq_intrcnt >= ATH_TXINTR_PERIOD) { 8761000Sxc151355 flags |= HAL_TXDESC_INTREQ; 8771000Sxc151355 txq->axq_intrcnt = 0; 8781000Sxc151355 } 8791000Sxc151355 8801000Sxc151355 /* 8811000Sxc151355 * Formulate first tx descriptor with tx controls. 8821000Sxc151355 */ 8831000Sxc151355 ATH_HAL_SETUPTXDESC(ah, ds, 8841000Sxc151355 pktlen, /* packet length */ 8851000Sxc151355 hdrlen, /* header length */ 8861000Sxc151355 atype, /* Atheros packet type */ 8871000Sxc151355 MIN(in->in_txpower, 60), /* txpower */ 8881000Sxc151355 txrate, try0, /* series 0 rate/tries */ 8893147Sxc151355 keyix, /* key cache index */ 8903147Sxc151355 an->an_tx_antenna, /* antenna mode */ 8911000Sxc151355 flags, /* flags */ 8921000Sxc151355 ctsrate, /* rts/cts rate */ 8931000Sxc151355 ctsduration); /* rts/cts duration */ 8943147Sxc151355 bf->bf_flags = flags; 8951000Sxc151355 8967249Sff224033 /* LINTED E_BAD_PTR_CAST_ALIGN */ 8971000Sxc151355 ATH_DEBUG((ATH_DBG_SEND, "ath: ath_xmit(): to %s totlen=%d " 8981000Sxc151355 "an->an_tx_rate1sp=%d tx_rate2sp=%d tx_rate3sp=%d " 8991000Sxc151355 "qnum=%d rix=%d sht=%d dur = %d\n", 9003147Sxc151355 ieee80211_macaddr_sprintf(wh->i_addr1), mbslen, an->an_tx_rate1sp, 9011000Sxc151355 an->an_tx_rate2sp, an->an_tx_rate3sp, 9023147Sxc151355 txq->axq_qnum, rix, shortPreamble, *(uint16_t *)wh->i_dur)); 9031000Sxc151355 9041000Sxc151355 /* 9051000Sxc151355 * Setup the multi-rate retry state only when we're 9061000Sxc151355 * going to use it. This assumes ath_hal_setuptxdesc 9071000Sxc151355 * initializes the descriptors (so we don't have to) 9081000Sxc151355 * when the hardware supports multi-rate retry and 9091000Sxc151355 * we don't use it. 9101000Sxc151355 */ 9111000Sxc151355 if (try0 != ATH_TXMAXTRY) 9121000Sxc151355 ATH_HAL_SETUPXTXDESC(ah, ds, 9131000Sxc151355 an->an_tx_rate1sp, 2, /* series 1 */ 9141000Sxc151355 an->an_tx_rate2sp, 2, /* series 2 */ 9151000Sxc151355 an->an_tx_rate3sp, 2); /* series 3 */ 9161000Sxc151355 9171000Sxc151355 ds->ds_link = 0; 9181000Sxc151355 ds->ds_data = bf->bf_dma.cookie.dmac_address; 9191000Sxc151355 ATH_HAL_FILLTXDESC(ah, ds, 9201000Sxc151355 mbslen, /* segment length */ 9211000Sxc151355 AH_TRUE, /* first segment */ 9221000Sxc151355 AH_TRUE, /* last segment */ 9231000Sxc151355 ds); /* first descriptor */ 9241000Sxc151355 9251000Sxc151355 ATH_DMA_SYNC(bf->bf_dma, DDI_DMA_SYNC_FORDEV); 9261000Sxc151355 9271000Sxc151355 mutex_enter(&txq->axq_lock); 9281000Sxc151355 list_insert_tail(&txq->axq_list, bf); 9291000Sxc151355 if (txq->axq_link == NULL) { 9301000Sxc151355 ATH_HAL_PUTTXBUF(ah, txq->axq_qnum, bf->bf_daddr); 9311000Sxc151355 } else { 9321000Sxc151355 *txq->axq_link = bf->bf_daddr; 9331000Sxc151355 } 9341000Sxc151355 txq->axq_link = &ds->ds_link; 9351000Sxc151355 mutex_exit(&txq->axq_lock); 9361000Sxc151355 9371000Sxc151355 ATH_HAL_TXSTART(ah, txq->axq_qnum); 9381000Sxc151355 9393147Sxc151355 ic->ic_stats.is_tx_frags++; 9403147Sxc151355 ic->ic_stats.is_tx_bytes += pktlen; 9413147Sxc151355 9421000Sxc151355 return (0); 9431000Sxc151355 } 9441000Sxc151355 9453147Sxc151355 /* 9463147Sxc151355 * Transmit a management frame. On failure we reclaim the skbuff. 9473147Sxc151355 * Note that management frames come directly from the 802.11 layer 9483147Sxc151355 * and do not honor the send queue flow control. Need to investigate 9493147Sxc151355 * using priority queueing so management frames can bypass data. 9503147Sxc151355 */ 9511000Sxc151355 static int 9523147Sxc151355 ath_xmit(ieee80211com_t *ic, mblk_t *mp, uint8_t type) 9531000Sxc151355 { 9543147Sxc151355 ath_t *asc = (ath_t *)ic; 9553147Sxc151355 struct ath_hal *ah = asc->asc_ah; 9563147Sxc151355 struct ieee80211_node *in = NULL; 9571000Sxc151355 struct ath_buf *bf = NULL; 9583147Sxc151355 struct ieee80211_frame *wh; 9593147Sxc151355 int error = 0; 9603147Sxc151355 9613147Sxc151355 ASSERT(mp->b_next == NULL); 9623147Sxc151355 9636797Sxc151355 if (!ATH_IS_RUNNING(asc)) { 9646797Sxc151355 if ((type & IEEE80211_FC0_TYPE_MASK) != 9656797Sxc151355 IEEE80211_FC0_TYPE_DATA) { 9666797Sxc151355 freemsg(mp); 9676797Sxc151355 } 9686797Sxc151355 return (ENXIO); 9696797Sxc151355 } 9706797Sxc151355 9713147Sxc151355 /* Grab a TX buffer */ 9723147Sxc151355 mutex_enter(&asc->asc_txbuflock); 9733147Sxc151355 bf = list_head(&asc->asc_txbuf_list); 9743147Sxc151355 if (bf != NULL) 9753147Sxc151355 list_remove(&asc->asc_txbuf_list, bf); 9763147Sxc151355 if (list_empty(&asc->asc_txbuf_list)) { 9773147Sxc151355 ATH_DEBUG((ATH_DBG_SEND, "ath: ath_mgmt_send(): " 9783147Sxc151355 "stop queue\n")); 9793147Sxc151355 asc->asc_stats.ast_tx_qstop++; 9803147Sxc151355 } 9813147Sxc151355 mutex_exit(&asc->asc_txbuflock); 9823147Sxc151355 if (bf == NULL) { 9833147Sxc151355 ATH_DEBUG((ATH_DBG_SEND, "ath: ath_mgmt_send(): discard, " 9843147Sxc151355 "no xmit buf\n")); 9853147Sxc151355 ic->ic_stats.is_tx_nobuf++; 9863147Sxc151355 if ((type & IEEE80211_FC0_TYPE_MASK) == 9873147Sxc151355 IEEE80211_FC0_TYPE_DATA) { 9883147Sxc151355 asc->asc_stats.ast_tx_nobuf++; 9893147Sxc151355 mutex_enter(&asc->asc_resched_lock); 9903147Sxc151355 asc->asc_resched_needed = B_TRUE; 9913147Sxc151355 mutex_exit(&asc->asc_resched_lock); 9923147Sxc151355 } else { 9933147Sxc151355 asc->asc_stats.ast_tx_nobufmgt++; 9943147Sxc151355 freemsg(mp); 9953147Sxc151355 } 9963147Sxc151355 return (ENOMEM); 9973147Sxc151355 } 9983147Sxc151355 9993147Sxc151355 wh = (struct ieee80211_frame *)mp->b_rptr; 10003147Sxc151355 10013147Sxc151355 /* Locate node */ 10023147Sxc151355 in = ieee80211_find_txnode(ic, wh->i_addr1); 10033147Sxc151355 if (in == NULL) { 10043147Sxc151355 error = EIO; 10053147Sxc151355 goto bad; 10063147Sxc151355 } 10073147Sxc151355 10083147Sxc151355 in->in_inact = 0; 10093147Sxc151355 switch (type & IEEE80211_FC0_TYPE_MASK) { 10103147Sxc151355 case IEEE80211_FC0_TYPE_DATA: 10113147Sxc151355 (void) ieee80211_encap(ic, mp, in); 10123147Sxc151355 break; 10133147Sxc151355 default: 10143147Sxc151355 if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == 10153147Sxc151355 IEEE80211_FC0_SUBTYPE_PROBE_RESP) { 10163147Sxc151355 /* fill time stamp */ 10173147Sxc151355 uint64_t tsf; 10183147Sxc151355 uint32_t *tstamp; 10193147Sxc151355 10203147Sxc151355 tsf = ATH_HAL_GETTSF64(ah); 10213147Sxc151355 /* adjust 100us delay to xmit */ 10223147Sxc151355 tsf += 100; 10237249Sff224033 /* LINTED E_BAD_PTR_CAST_ALIGN */ 10243147Sxc151355 tstamp = (uint32_t *)&wh[1]; 10253147Sxc151355 tstamp[0] = LE_32(tsf & 0xffffffff); 10263147Sxc151355 tstamp[1] = LE_32(tsf >> 32); 10273147Sxc151355 } 10283147Sxc151355 asc->asc_stats.ast_tx_mgmt++; 10293147Sxc151355 break; 10303147Sxc151355 } 10313147Sxc151355 10323147Sxc151355 error = ath_tx_start(asc, in, bf, mp); 10333147Sxc151355 if (error != 0) { 10343147Sxc151355 bad: 10353147Sxc151355 ic->ic_stats.is_tx_failed++; 10363147Sxc151355 if (bf != NULL) { 10373147Sxc151355 mutex_enter(&asc->asc_txbuflock); 10383147Sxc151355 list_insert_tail(&asc->asc_txbuf_list, bf); 10393147Sxc151355 mutex_exit(&asc->asc_txbuflock); 10403147Sxc151355 } 10413147Sxc151355 } 10423147Sxc151355 if (in != NULL) 10433147Sxc151355 ieee80211_free_node(in); 10443147Sxc151355 if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA || 10453147Sxc151355 error == 0) { 10463147Sxc151355 freemsg(mp); 10473147Sxc151355 } 10483147Sxc151355 10493147Sxc151355 return (error); 10503147Sxc151355 } 10513147Sxc151355 10523147Sxc151355 static mblk_t * 10533147Sxc151355 ath_m_tx(void *arg, mblk_t *mp) 10543147Sxc151355 { 10553147Sxc151355 ath_t *asc = arg; 10563147Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc; 10573147Sxc151355 mblk_t *next; 10584126Szf162725 int error = 0; 10591000Sxc151355 10601000Sxc151355 /* 10611000Sxc151355 * No data frames go out unless we're associated; this 10621000Sxc151355 * should not happen as the 802.11 layer does not enable 10631000Sxc151355 * the xmit queue until we enter the RUN state. 10641000Sxc151355 */ 10653147Sxc151355 if (ic->ic_state != IEEE80211_S_RUN) { 10663147Sxc151355 ATH_DEBUG((ATH_DBG_SEND, "ath: ath_m_tx(): " 10673147Sxc151355 "discard, state %u\n", ic->ic_state)); 10683631Sxh158540 asc->asc_stats.ast_tx_discard++; 10693315Sxc151355 freemsgchain(mp); 10703315Sxc151355 return (NULL); 10711000Sxc151355 } 10721000Sxc151355 10733147Sxc151355 while (mp != NULL) { 10743147Sxc151355 next = mp->b_next; 10753147Sxc151355 mp->b_next = NULL; 10764126Szf162725 error = ath_xmit(ic, mp, IEEE80211_FC0_TYPE_DATA); 10774126Szf162725 if (error != 0) { 10783147Sxc151355 mp->b_next = next; 10794126Szf162725 if (error == ENOMEM) { 10804126Szf162725 break; 10814126Szf162725 } else { 10824126Szf162725 freemsgchain(mp); /* CR6501759 issues */ 10834126Szf162725 return (NULL); 10844126Szf162725 } 10853147Sxc151355 } 10863147Sxc151355 mp = next; 10871000Sxc151355 } 10881000Sxc151355 10893147Sxc151355 return (mp); 10901000Sxc151355 } 10911000Sxc151355 10923147Sxc151355 static int 10931000Sxc151355 ath_tx_processq(ath_t *asc, struct ath_txq *txq) 10941000Sxc151355 { 10953147Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc; 10961000Sxc151355 struct ath_hal *ah = asc->asc_ah; 10971000Sxc151355 struct ath_buf *bf; 10981000Sxc151355 struct ath_desc *ds; 10991000Sxc151355 struct ieee80211_node *in; 11003147Sxc151355 int32_t sr, lr, nacked = 0; 1101*8033SWang.Lin@Sun.COM struct ath_tx_status *ts; 11021000Sxc151355 HAL_STATUS status; 11031000Sxc151355 struct ath_node *an; 11041000Sxc151355 11051000Sxc151355 for (;;) { 11061000Sxc151355 mutex_enter(&txq->axq_lock); 11071000Sxc151355 bf = list_head(&txq->axq_list); 11081000Sxc151355 if (bf == NULL) { 11091000Sxc151355 txq->axq_link = NULL; 11101000Sxc151355 mutex_exit(&txq->axq_lock); 11111000Sxc151355 break; 11121000Sxc151355 } 11131000Sxc151355 ds = bf->bf_desc; /* last decriptor */ 1114*8033SWang.Lin@Sun.COM ts = &bf->bf_status.ds_txstat; 1115*8033SWang.Lin@Sun.COM status = ATH_HAL_TXPROCDESC(ah, ds, ts); 11161000Sxc151355 #ifdef DEBUG 11171000Sxc151355 ath_printtxbuf(bf, status == HAL_OK); 11181000Sxc151355 #endif 11191000Sxc151355 if (status == HAL_EINPROGRESS) { 11201000Sxc151355 mutex_exit(&txq->axq_lock); 11211000Sxc151355 break; 11221000Sxc151355 } 11231000Sxc151355 list_remove(&txq->axq_list, bf); 11241000Sxc151355 mutex_exit(&txq->axq_lock); 11251000Sxc151355 in = bf->bf_in; 11261000Sxc151355 if (in != NULL) { 11271000Sxc151355 an = ATH_NODE(in); 11281000Sxc151355 /* Successful transmition */ 1129*8033SWang.Lin@Sun.COM if (ts->ts_status == 0) { 11301000Sxc151355 an->an_tx_ok++; 1131*8033SWang.Lin@Sun.COM an->an_tx_antenna = ts->ts_antenna; 1132*8033SWang.Lin@Sun.COM if (ts->ts_rate & HAL_TXSTAT_ALTRATE) 11331000Sxc151355 asc->asc_stats.ast_tx_altrate++; 11341000Sxc151355 asc->asc_stats.ast_tx_rssidelta = 1135*8033SWang.Lin@Sun.COM ts->ts_rssi - asc->asc_stats.ast_tx_rssi; 1136*8033SWang.Lin@Sun.COM asc->asc_stats.ast_tx_rssi = ts->ts_rssi; 11371000Sxc151355 } else { 11381000Sxc151355 an->an_tx_err++; 1139*8033SWang.Lin@Sun.COM if (ts->ts_status & HAL_TXERR_XRETRY) 1140*8033SWang.Lin@Sun.COM asc->asc_stats.ast_tx_xretries++; 1141*8033SWang.Lin@Sun.COM if (ts->ts_status & HAL_TXERR_FIFO) 11421000Sxc151355 asc->asc_stats.ast_tx_fifoerr++; 1143*8033SWang.Lin@Sun.COM if (ts->ts_status & HAL_TXERR_FILT) 1144*8033SWang.Lin@Sun.COM asc->asc_stats.ast_tx_filtered++; 11451000Sxc151355 an->an_tx_antenna = 0; /* invalidate */ 11461000Sxc151355 } 1147*8033SWang.Lin@Sun.COM sr = ts->ts_shortretry; 1148*8033SWang.Lin@Sun.COM lr = ts->ts_longretry; 11491000Sxc151355 asc->asc_stats.ast_tx_shortretry += sr; 11501000Sxc151355 asc->asc_stats.ast_tx_longretry += lr; 11513147Sxc151355 /* 11523147Sxc151355 * Hand the descriptor to the rate control algorithm. 11533147Sxc151355 */ 1154*8033SWang.Lin@Sun.COM if ((ts->ts_status & HAL_TXERR_FILT) == 0 && 11553147Sxc151355 (bf->bf_flags & HAL_TXDESC_NOACK) == 0) { 11563147Sxc151355 /* 11573147Sxc151355 * If frame was ack'd update the last rx time 11583147Sxc151355 * used to workaround phantom bmiss interrupts. 11593147Sxc151355 */ 1160*8033SWang.Lin@Sun.COM if (ts->ts_status == 0) { 11613147Sxc151355 nacked++; 11623147Sxc151355 an->an_tx_ok++; 11633147Sxc151355 } else { 11643147Sxc151355 an->an_tx_err++; 11653147Sxc151355 } 11663147Sxc151355 an->an_tx_retr += sr + lr; 11673147Sxc151355 } 11681000Sxc151355 } 11691000Sxc151355 bf->bf_in = NULL; 11701000Sxc151355 mutex_enter(&asc->asc_txbuflock); 11711000Sxc151355 list_insert_tail(&asc->asc_txbuf_list, bf); 11721000Sxc151355 mutex_exit(&asc->asc_txbuflock); 11731000Sxc151355 /* 11741000Sxc151355 * Reschedule stalled outbound packets 11751000Sxc151355 */ 11763147Sxc151355 mutex_enter(&asc->asc_resched_lock); 11773147Sxc151355 if (asc->asc_resched_needed) { 11783147Sxc151355 asc->asc_resched_needed = B_FALSE; 11793147Sxc151355 mac_tx_update(ic->ic_mach); 11801000Sxc151355 } 11813147Sxc151355 mutex_exit(&asc->asc_resched_lock); 11821000Sxc151355 } 11833147Sxc151355 return (nacked); 11841000Sxc151355 } 11851000Sxc151355 11861000Sxc151355 11871000Sxc151355 static void 11881000Sxc151355 ath_tx_handler(ath_t *asc) 11891000Sxc151355 { 11901000Sxc151355 int i; 11911000Sxc151355 11921000Sxc151355 /* 11931000Sxc151355 * Process each active queue. 11941000Sxc151355 */ 11951000Sxc151355 for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { 11961000Sxc151355 if (ATH_TXQ_SETUP(asc, i)) { 11973147Sxc151355 (void) ath_tx_processq(asc, &asc->asc_txq[i]); 11981000Sxc151355 } 11991000Sxc151355 } 12001000Sxc151355 } 12011000Sxc151355 12021000Sxc151355 static struct ieee80211_node * 12033147Sxc151355 ath_node_alloc(ieee80211com_t *ic) 12041000Sxc151355 { 12051000Sxc151355 struct ath_node *an; 12063147Sxc151355 ath_t *asc = (ath_t *)ic; 12071000Sxc151355 12081000Sxc151355 an = kmem_zalloc(sizeof (struct ath_node), KM_SLEEP); 12091000Sxc151355 ath_rate_update(asc, &an->an_node, 0); 12101000Sxc151355 return (&an->an_node); 12111000Sxc151355 } 12121000Sxc151355 12131000Sxc151355 static void 12143147Sxc151355 ath_node_free(struct ieee80211_node *in) 12151000Sxc151355 { 12163147Sxc151355 ieee80211com_t *ic = in->in_ic; 12173147Sxc151355 ath_t *asc = (ath_t *)ic; 12181000Sxc151355 struct ath_buf *bf; 12191000Sxc151355 struct ath_txq *txq; 12201000Sxc151355 int32_t i; 12211000Sxc151355 12221000Sxc151355 for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { 12231000Sxc151355 if (ATH_TXQ_SETUP(asc, i)) { 12241000Sxc151355 txq = &asc->asc_txq[i]; 12251000Sxc151355 mutex_enter(&txq->axq_lock); 12261000Sxc151355 bf = list_head(&txq->axq_list); 12271000Sxc151355 while (bf != NULL) { 12281000Sxc151355 if (bf->bf_in == in) { 12291000Sxc151355 bf->bf_in = NULL; 12301000Sxc151355 } 12311000Sxc151355 bf = list_next(&txq->axq_list, bf); 12321000Sxc151355 } 12331000Sxc151355 mutex_exit(&txq->axq_lock); 12341000Sxc151355 } 12351000Sxc151355 } 12363147Sxc151355 ic->ic_node_cleanup(in); 12374126Szf162725 if (in->in_wpa_ie != NULL) 12384126Szf162725 ieee80211_free(in->in_wpa_ie); 12391000Sxc151355 kmem_free(in, sizeof (struct ath_node)); 12401000Sxc151355 } 12411000Sxc151355 12421000Sxc151355 static void 12433147Sxc151355 ath_next_scan(void *arg) 12441000Sxc151355 { 12453147Sxc151355 ieee80211com_t *ic = arg; 12463147Sxc151355 ath_t *asc = (ath_t *)ic; 12473147Sxc151355 12483147Sxc151355 asc->asc_scan_timer = 0; 12493147Sxc151355 if (ic->ic_state == IEEE80211_S_SCAN) { 12503147Sxc151355 asc->asc_scan_timer = timeout(ath_next_scan, (void *)asc, 12513147Sxc151355 drv_usectohz(ath_dwelltime * 1000)); 12523147Sxc151355 ieee80211_next_scan(ic); 12533147Sxc151355 } 12541000Sxc151355 } 12551000Sxc151355 12563147Sxc151355 static void 12573147Sxc151355 ath_stop_scantimer(ath_t *asc) 12581000Sxc151355 { 12593147Sxc151355 timeout_id_t tmp_id = 0; 12601000Sxc151355 12613147Sxc151355 while ((asc->asc_scan_timer != 0) && (tmp_id != asc->asc_scan_timer)) { 12623147Sxc151355 tmp_id = asc->asc_scan_timer; 12633147Sxc151355 (void) untimeout(tmp_id); 12641000Sxc151355 } 12653147Sxc151355 asc->asc_scan_timer = 0; 12661000Sxc151355 } 12671000Sxc151355 12681000Sxc151355 static int32_t 12693147Sxc151355 ath_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg) 12701000Sxc151355 { 12713147Sxc151355 ath_t *asc = (ath_t *)ic; 12721000Sxc151355 struct ath_hal *ah = asc->asc_ah; 12731000Sxc151355 struct ieee80211_node *in; 12741000Sxc151355 int32_t i, error; 12751000Sxc151355 uint8_t *bssid; 12761000Sxc151355 uint32_t rfilt; 12771000Sxc151355 enum ieee80211_state ostate; 12781000Sxc151355 12791000Sxc151355 static const HAL_LED_STATE leds[] = { 12801000Sxc151355 HAL_LED_INIT, /* IEEE80211_S_INIT */ 12811000Sxc151355 HAL_LED_SCAN, /* IEEE80211_S_SCAN */ 12821000Sxc151355 HAL_LED_AUTH, /* IEEE80211_S_AUTH */ 12831000Sxc151355 HAL_LED_ASSOC, /* IEEE80211_S_ASSOC */ 12841000Sxc151355 HAL_LED_RUN, /* IEEE80211_S_RUN */ 12851000Sxc151355 }; 12863147Sxc151355 if (!ATH_IS_RUNNING(asc)) 12871000Sxc151355 return (0); 12881000Sxc151355 12893147Sxc151355 ostate = ic->ic_state; 12903147Sxc151355 if (nstate != IEEE80211_S_SCAN) 12913147Sxc151355 ath_stop_scantimer(asc); 12921000Sxc151355 12933147Sxc151355 ATH_LOCK(asc); 12941000Sxc151355 ATH_HAL_SETLEDSTATE(ah, leds[nstate]); /* set LED */ 12951000Sxc151355 12961000Sxc151355 if (nstate == IEEE80211_S_INIT) { 12971000Sxc151355 asc->asc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); 1298*8033SWang.Lin@Sun.COM /* 1299*8033SWang.Lin@Sun.COM * Disable interrupts. 1300*8033SWang.Lin@Sun.COM */ 13013147Sxc151355 ATH_HAL_INTRSET(ah, asc->asc_imask &~ HAL_INT_GLOBAL); 13023147Sxc151355 ATH_UNLOCK(asc); 13033147Sxc151355 goto done; 13041000Sxc151355 } 13053147Sxc151355 in = ic->ic_bss; 13063147Sxc151355 error = ath_chan_set(asc, ic->ic_curchan); 13073147Sxc151355 if (error != 0) { 13083147Sxc151355 if (nstate != IEEE80211_S_SCAN) { 13093147Sxc151355 ATH_UNLOCK(asc); 13103147Sxc151355 ieee80211_reset_chan(ic); 13113147Sxc151355 goto bad; 13123147Sxc151355 } 13133147Sxc151355 } 13141000Sxc151355 13151000Sxc151355 rfilt = ath_calcrxfilter(asc); 1316*8033SWang.Lin@Sun.COM 13171000Sxc151355 if (nstate == IEEE80211_S_SCAN) 13183147Sxc151355 bssid = ic->ic_macaddr; 13191000Sxc151355 else 13201000Sxc151355 bssid = in->in_bssid; 13211000Sxc151355 ATH_HAL_SETRXFILTER(ah, rfilt); 13221000Sxc151355 13233147Sxc151355 if (nstate == IEEE80211_S_RUN && ic->ic_opmode != IEEE80211_M_IBSS) 13241000Sxc151355 ATH_HAL_SETASSOCID(ah, bssid, in->in_associd); 13251000Sxc151355 else 13261000Sxc151355 ATH_HAL_SETASSOCID(ah, bssid, 0); 13273147Sxc151355 if (ic->ic_flags & IEEE80211_F_PRIVACY) { 13281000Sxc151355 for (i = 0; i < IEEE80211_WEP_NKID; i++) { 13291000Sxc151355 if (ATH_HAL_KEYISVALID(ah, i)) 13301000Sxc151355 ATH_HAL_KEYSETMAC(ah, i, bssid); 13311000Sxc151355 } 13321000Sxc151355 } 13331000Sxc151355 13341000Sxc151355 if ((nstate == IEEE80211_S_RUN) && 13351000Sxc151355 (ostate != IEEE80211_S_RUN)) { 13361000Sxc151355 /* Configure the beacon and sleep timers. */ 13371000Sxc151355 ath_beacon_config(asc); 13381000Sxc151355 } else { 13391000Sxc151355 asc->asc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); 13401000Sxc151355 ATH_HAL_INTRSET(ah, asc->asc_imask); 13411000Sxc151355 } 13421000Sxc151355 /* 13431000Sxc151355 * Reset the rate control state. 13441000Sxc151355 */ 13451000Sxc151355 ath_rate_ctl_reset(asc, nstate); 13461000Sxc151355 13473147Sxc151355 ATH_UNLOCK(asc); 13483147Sxc151355 done: 13493147Sxc151355 /* 13503147Sxc151355 * Invoke the parent method to complete the work. 13513147Sxc151355 */ 13523147Sxc151355 error = asc->asc_newstate(ic, nstate, arg); 13533147Sxc151355 /* 13543147Sxc151355 * Finally, start any timers. 13553147Sxc151355 */ 13563147Sxc151355 if (nstate == IEEE80211_S_RUN) { 13573147Sxc151355 ieee80211_start_watchdog(ic, 1); 13583147Sxc151355 } else if ((nstate == IEEE80211_S_SCAN) && (ostate != nstate)) { 13593147Sxc151355 /* start ap/neighbor scan timer */ 13603147Sxc151355 ASSERT(asc->asc_scan_timer == 0); 13613147Sxc151355 asc->asc_scan_timer = timeout(ath_next_scan, (void *)asc, 13623147Sxc151355 drv_usectohz(ath_dwelltime * 1000)); 13633147Sxc151355 } 13641000Sxc151355 bad: 13651000Sxc151355 return (error); 13661000Sxc151355 } 13671000Sxc151355 13681000Sxc151355 /* 13691000Sxc151355 * Periodically recalibrate the PHY to account 13701000Sxc151355 * for temperature/environment changes. 13711000Sxc151355 */ 13721000Sxc151355 static void 13733147Sxc151355 ath_calibrate(ath_t *asc) 13741000Sxc151355 { 13751000Sxc151355 struct ath_hal *ah = asc->asc_ah; 13763147Sxc151355 HAL_BOOL iqcaldone; 13771000Sxc151355 13781000Sxc151355 asc->asc_stats.ast_per_cal++; 13791000Sxc151355 13801000Sxc151355 if (ATH_HAL_GETRFGAIN(ah) == HAL_RFGAIN_NEED_CHANGE) { 13811000Sxc151355 /* 13821000Sxc151355 * Rfgain is out of bounds, reset the chip 13831000Sxc151355 * to load new gain values. 13841000Sxc151355 */ 13851000Sxc151355 ATH_DEBUG((ATH_DBG_HAL, "ath: ath_calibrate(): " 13861000Sxc151355 "Need change RFgain\n")); 13871000Sxc151355 asc->asc_stats.ast_per_rfgain++; 13883147Sxc151355 (void) ath_reset(&asc->asc_isc); 13891000Sxc151355 } 13903147Sxc151355 if (!ATH_HAL_CALIBRATE(ah, &asc->asc_curchan, &iqcaldone)) { 13911000Sxc151355 ATH_DEBUG((ATH_DBG_HAL, "ath: ath_calibrate(): " 13921000Sxc151355 "calibration of channel %u failed\n", 13933147Sxc151355 asc->asc_curchan.channel)); 13941000Sxc151355 asc->asc_stats.ast_per_calfail++; 13951000Sxc151355 } 13961000Sxc151355 } 13971000Sxc151355 13983147Sxc151355 static void 13993147Sxc151355 ath_watchdog(void *arg) 14003147Sxc151355 { 14013147Sxc151355 ath_t *asc = arg; 14023147Sxc151355 ieee80211com_t *ic = &asc->asc_isc; 14033147Sxc151355 int ntimer = 0; 14043147Sxc151355 14053147Sxc151355 ATH_LOCK(asc); 14063147Sxc151355 ic->ic_watchdog_timer = 0; 14073147Sxc151355 if (!ATH_IS_RUNNING(asc)) { 14083147Sxc151355 ATH_UNLOCK(asc); 14093147Sxc151355 return; 14103147Sxc151355 } 14113147Sxc151355 14123147Sxc151355 if (ic->ic_state == IEEE80211_S_RUN) { 14133147Sxc151355 /* periodic recalibration */ 14143147Sxc151355 ath_calibrate(asc); 14153147Sxc151355 14163147Sxc151355 /* 14173147Sxc151355 * Start the background rate control thread if we 14183147Sxc151355 * are not configured to use a fixed xmit rate. 14193147Sxc151355 */ 14203147Sxc151355 if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) { 14213147Sxc151355 asc->asc_stats.ast_rate_calls ++; 14223147Sxc151355 if (ic->ic_opmode == IEEE80211_M_STA) 14233147Sxc151355 ath_rate_ctl(ic, ic->ic_bss); 14243147Sxc151355 else 14253147Sxc151355 ieee80211_iterate_nodes(&ic->ic_sta, 1426*8033SWang.Lin@Sun.COM ath_rate_ctl, asc); 14273147Sxc151355 } 14283147Sxc151355 14293147Sxc151355 ntimer = 1; 14303147Sxc151355 } 14313147Sxc151355 ATH_UNLOCK(asc); 14323147Sxc151355 14333147Sxc151355 ieee80211_watchdog(ic); 14343147Sxc151355 if (ntimer != 0) 14353147Sxc151355 ieee80211_start_watchdog(ic, ntimer); 14363147Sxc151355 } 14373147Sxc151355 1438*8033SWang.Lin@Sun.COM static void 1439*8033SWang.Lin@Sun.COM ath_tx_proc(void *arg) 1440*8033SWang.Lin@Sun.COM { 1441*8033SWang.Lin@Sun.COM ath_t *asc = arg; 1442*8033SWang.Lin@Sun.COM ath_tx_handler(asc); 1443*8033SWang.Lin@Sun.COM } 1444*8033SWang.Lin@Sun.COM 1445*8033SWang.Lin@Sun.COM 14461000Sxc151355 static uint_t 14473147Sxc151355 ath_intr(caddr_t arg) 14481000Sxc151355 { 14497249Sff224033 /* LINTED E_BAD_PTR_CAST_ALIGN */ 14503147Sxc151355 ath_t *asc = (ath_t *)arg; 14511000Sxc151355 struct ath_hal *ah = asc->asc_ah; 14521000Sxc151355 HAL_INT status; 14533147Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc; 14543147Sxc151355 14553147Sxc151355 ATH_LOCK(asc); 14561000Sxc151355 14573147Sxc151355 if (!ATH_IS_RUNNING(asc)) { 14583147Sxc151355 /* 14593147Sxc151355 * The hardware is not ready/present, don't touch anything. 14603147Sxc151355 * Note this can happen early on if the IRQ is shared. 14613147Sxc151355 */ 14623147Sxc151355 ATH_UNLOCK(asc); 14633147Sxc151355 return (DDI_INTR_UNCLAIMED); 14643147Sxc151355 } 14651000Sxc151355 14661000Sxc151355 if (!ATH_HAL_INTRPEND(ah)) { /* shared irq, not for us */ 14673147Sxc151355 ATH_UNLOCK(asc); 14681000Sxc151355 return (DDI_INTR_UNCLAIMED); 14691000Sxc151355 } 14701000Sxc151355 14711000Sxc151355 ATH_HAL_GETISR(ah, &status); 14721000Sxc151355 status &= asc->asc_imask; 14731000Sxc151355 if (status & HAL_INT_FATAL) { 14741000Sxc151355 asc->asc_stats.ast_hardware++; 14751000Sxc151355 goto reset; 14761000Sxc151355 } else if (status & HAL_INT_RXORN) { 14771000Sxc151355 asc->asc_stats.ast_rxorn++; 14781000Sxc151355 goto reset; 14791000Sxc151355 } else { 14801000Sxc151355 if (status & HAL_INT_RXEOL) { 14811000Sxc151355 asc->asc_stats.ast_rxeol++; 14821000Sxc151355 asc->asc_rxlink = NULL; 14831000Sxc151355 } 14841000Sxc151355 if (status & HAL_INT_TXURN) { 14851000Sxc151355 asc->asc_stats.ast_txurn++; 14861000Sxc151355 ATH_HAL_UPDATETXTRIGLEVEL(ah, AH_TRUE); 14871000Sxc151355 } 14883147Sxc151355 14891000Sxc151355 if (status & HAL_INT_RX) { 14901000Sxc151355 asc->asc_rx_pend = 1; 14911000Sxc151355 ddi_trigger_softintr(asc->asc_softint_id); 14921000Sxc151355 } 14931000Sxc151355 if (status & HAL_INT_TX) { 1494*8033SWang.Lin@Sun.COM if (ddi_taskq_dispatch(asc->asc_tq, ath_tx_proc, 1495*8033SWang.Lin@Sun.COM asc, DDI_NOSLEEP) != DDI_SUCCESS) { 1496*8033SWang.Lin@Sun.COM ath_problem("ath: ath_intr(): " 1497*8033SWang.Lin@Sun.COM "No memory available for tx taskq\n"); 1498*8033SWang.Lin@Sun.COM } 14991000Sxc151355 } 15003147Sxc151355 ATH_UNLOCK(asc); 15011000Sxc151355 15021000Sxc151355 if (status & HAL_INT_SWBA) { 15031000Sxc151355 /* This will occur only in Host-AP or Ad-Hoc mode */ 15041000Sxc151355 return (DDI_INTR_CLAIMED); 15051000Sxc151355 } 1506*8033SWang.Lin@Sun.COM 15071000Sxc151355 if (status & HAL_INT_BMISS) { 15083147Sxc151355 if (ic->ic_state == IEEE80211_S_RUN) { 15093147Sxc151355 (void) ieee80211_new_state(ic, 15101000Sxc151355 IEEE80211_S_ASSOC, -1); 15111000Sxc151355 } 15121000Sxc151355 } 1513*8033SWang.Lin@Sun.COM 15141000Sxc151355 } 15151000Sxc151355 15161000Sxc151355 return (DDI_INTR_CLAIMED); 15171000Sxc151355 reset: 15183147Sxc151355 (void) ath_reset(ic); 15193147Sxc151355 ATH_UNLOCK(asc); 15201000Sxc151355 return (DDI_INTR_CLAIMED); 15211000Sxc151355 } 15221000Sxc151355 15231000Sxc151355 static uint_t 15241000Sxc151355 ath_softint_handler(caddr_t data) 15251000Sxc151355 { 15267249Sff224033 /* LINTED E_BAD_PTR_CAST_ALIGN */ 15271000Sxc151355 ath_t *asc = (ath_t *)data; 15281000Sxc151355 15291000Sxc151355 /* 15301000Sxc151355 * Check if the soft interrupt is triggered by another 15311000Sxc151355 * driver at the same level. 15321000Sxc151355 */ 15333147Sxc151355 ATH_LOCK(asc); 15341000Sxc151355 if (asc->asc_rx_pend) { /* Soft interrupt for this driver */ 15351000Sxc151355 asc->asc_rx_pend = 0; 15363147Sxc151355 ATH_UNLOCK(asc); 15373147Sxc151355 ath_rx_handler(asc); 15381000Sxc151355 return (DDI_INTR_CLAIMED); 15391000Sxc151355 } 15403147Sxc151355 ATH_UNLOCK(asc); 15411000Sxc151355 return (DDI_INTR_UNCLAIMED); 15421000Sxc151355 } 15431000Sxc151355 15441000Sxc151355 /* 15451000Sxc151355 * following are gld callback routine 15461000Sxc151355 * ath_gld_send, ath_gld_ioctl, ath_gld_gstat 15471000Sxc151355 * are listed in other corresponding sections. 15481000Sxc151355 * reset the hardware w/o losing operational state. this is 15491000Sxc151355 * basically a more efficient way of doing ath_gld_stop, ath_gld_start, 15501000Sxc151355 * followed by state transitions to the current 802.11 15511000Sxc151355 * operational state. used to recover from errors rx overrun 15521000Sxc151355 * and to reset the hardware when rf gain settings must be reset. 15531000Sxc151355 */ 15541000Sxc151355 15553147Sxc151355 static void 15563147Sxc151355 ath_stop_locked(ath_t *asc) 15571000Sxc151355 { 15583147Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc; 15593147Sxc151355 struct ath_hal *ah = asc->asc_ah; 15601000Sxc151355 15613147Sxc151355 ATH_LOCK_ASSERT(asc); 15626797Sxc151355 if (!asc->asc_isrunning) 15636797Sxc151355 return; 15646797Sxc151355 15653147Sxc151355 /* 15663147Sxc151355 * Shutdown the hardware and driver: 15673147Sxc151355 * reset 802.11 state machine 15683147Sxc151355 * turn off timers 15693147Sxc151355 * disable interrupts 15703147Sxc151355 * turn off the radio 15713147Sxc151355 * clear transmit machinery 15723147Sxc151355 * clear receive machinery 15733147Sxc151355 * drain and release tx queues 15743147Sxc151355 * reclaim beacon resources 15753147Sxc151355 * power down hardware 15763147Sxc151355 * 15773147Sxc151355 * Note that some of this work is not possible if the 15783147Sxc151355 * hardware is gone (invalid). 15793147Sxc151355 */ 15803147Sxc151355 ATH_UNLOCK(asc); 15813147Sxc151355 ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 15823147Sxc151355 ieee80211_stop_watchdog(ic); 15833147Sxc151355 ATH_LOCK(asc); 15843147Sxc151355 ATH_HAL_INTRSET(ah, 0); 15853147Sxc151355 ath_draintxq(asc); 15866797Sxc151355 if (!asc->asc_invalid) { 15873147Sxc151355 ath_stoprecv(asc); 15883147Sxc151355 ATH_HAL_PHYDISABLE(ah); 15893147Sxc151355 } else { 15903147Sxc151355 asc->asc_rxlink = NULL; 15913147Sxc151355 } 15926797Sxc151355 asc->asc_isrunning = 0; 15931000Sxc151355 } 15941000Sxc151355 15953147Sxc151355 static void 15963147Sxc151355 ath_m_stop(void *arg) 15971000Sxc151355 { 15983147Sxc151355 ath_t *asc = arg; 15991000Sxc151355 struct ath_hal *ah = asc->asc_ah; 16001000Sxc151355 16013147Sxc151355 ATH_LOCK(asc); 16023147Sxc151355 ath_stop_locked(asc); 16033147Sxc151355 ATH_HAL_SETPOWER(ah, HAL_PM_AWAKE); 16041000Sxc151355 asc->asc_invalid = 1; 16053147Sxc151355 ATH_UNLOCK(asc); 16061000Sxc151355 } 16071000Sxc151355 16086797Sxc151355 static int 16096797Sxc151355 ath_start_locked(ath_t *asc) 16101000Sxc151355 { 16113147Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc; 16121000Sxc151355 struct ath_hal *ah = asc->asc_ah; 16131000Sxc151355 HAL_STATUS status; 16141000Sxc151355 16156797Sxc151355 ATH_LOCK_ASSERT(asc); 16161000Sxc151355 16171000Sxc151355 /* 16181000Sxc151355 * The basic interface to setting the hardware in a good 16191000Sxc151355 * state is ``reset''. On return the hardware is known to 16201000Sxc151355 * be powered up and with interrupts disabled. This must 16211000Sxc151355 * be followed by initialization of the appropriate bits 16221000Sxc151355 * and then setup of the interrupt mask. 16231000Sxc151355 */ 16243147Sxc151355 asc->asc_curchan.channel = ic->ic_curchan->ich_freq; 16253147Sxc151355 asc->asc_curchan.channelFlags = ath_chan2flags(ic, ic->ic_curchan); 16263147Sxc151355 if (!ATH_HAL_RESET(ah, (HAL_OPMODE)ic->ic_opmode, 16273147Sxc151355 &asc->asc_curchan, AH_FALSE, &status)) { 16283147Sxc151355 ATH_DEBUG((ATH_DBG_HAL, "ath: ath_m_start(): " 16296235Sxc151355 "reset hardware failed: '%s' (HAL status %u)\n", 16306235Sxc151355 ath_get_hal_status_desc(status), status)); 16313147Sxc151355 return (ENOTACTIVE); 16321000Sxc151355 } 16331000Sxc151355 16343147Sxc151355 (void) ath_startrecv(asc); 16351000Sxc151355 16361000Sxc151355 /* 16371000Sxc151355 * Enable interrupts. 16381000Sxc151355 */ 16391000Sxc151355 asc->asc_imask = HAL_INT_RX | HAL_INT_TX 16401000Sxc151355 | HAL_INT_RXEOL | HAL_INT_RXORN 16411000Sxc151355 | HAL_INT_FATAL | HAL_INT_GLOBAL; 16421000Sxc151355 ATH_HAL_INTRSET(ah, asc->asc_imask); 16431000Sxc151355 16441000Sxc151355 /* 16451000Sxc151355 * The hardware should be ready to go now so it's safe 16461000Sxc151355 * to kick the 802.11 state machine as it's likely to 16471000Sxc151355 * immediately call back to us to send mgmt frames. 16481000Sxc151355 */ 16493147Sxc151355 ath_chan_change(asc, ic->ic_curchan); 16506797Sxc151355 16516797Sxc151355 asc->asc_isrunning = 1; 16526797Sxc151355 16536797Sxc151355 return (0); 16546797Sxc151355 } 16556797Sxc151355 16566797Sxc151355 int 16576797Sxc151355 ath_m_start(void *arg) 16586797Sxc151355 { 16596797Sxc151355 ath_t *asc = arg; 16606797Sxc151355 int err; 16616797Sxc151355 16626797Sxc151355 ATH_LOCK(asc); 16636797Sxc151355 /* 16646797Sxc151355 * Stop anything previously setup. This is safe 16656797Sxc151355 * whether this is the first time through or not. 16666797Sxc151355 */ 16676797Sxc151355 ath_stop_locked(asc); 16686797Sxc151355 16696797Sxc151355 if ((err = ath_start_locked(asc)) != 0) { 16706797Sxc151355 ATH_UNLOCK(asc); 16716797Sxc151355 return (err); 16726797Sxc151355 } 16736797Sxc151355 16741000Sxc151355 asc->asc_invalid = 0; 16753147Sxc151355 ATH_UNLOCK(asc); 16766797Sxc151355 16773147Sxc151355 return (0); 16781000Sxc151355 } 16791000Sxc151355 16801000Sxc151355 16813147Sxc151355 static int 16823147Sxc151355 ath_m_unicst(void *arg, const uint8_t *macaddr) 16831000Sxc151355 { 16843147Sxc151355 ath_t *asc = arg; 16851000Sxc151355 struct ath_hal *ah = asc->asc_ah; 16861000Sxc151355 16871000Sxc151355 ATH_DEBUG((ATH_DBG_GLD, "ath: ath_gld_saddr(): " 16881000Sxc151355 "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", 16891000Sxc151355 macaddr[0], macaddr[1], macaddr[2], 16901000Sxc151355 macaddr[3], macaddr[4], macaddr[5])); 16911000Sxc151355 16923147Sxc151355 ATH_LOCK(asc); 16933147Sxc151355 IEEE80211_ADDR_COPY(asc->asc_isc.ic_macaddr, macaddr); 16943147Sxc151355 ATH_HAL_SETMAC(ah, asc->asc_isc.ic_macaddr); 16951000Sxc151355 16963147Sxc151355 (void) ath_reset(&asc->asc_isc); 16973147Sxc151355 ATH_UNLOCK(asc); 16983147Sxc151355 return (0); 16991000Sxc151355 } 17001000Sxc151355 17011000Sxc151355 static int 17023147Sxc151355 ath_m_promisc(void *arg, boolean_t on) 17031000Sxc151355 { 17043147Sxc151355 ath_t *asc = arg; 17051000Sxc151355 struct ath_hal *ah = asc->asc_ah; 17061000Sxc151355 uint32_t rfilt; 17071000Sxc151355 17083147Sxc151355 ATH_LOCK(asc); 17091000Sxc151355 rfilt = ATH_HAL_GETRXFILTER(ah); 17103147Sxc151355 if (on) 17113147Sxc151355 rfilt |= HAL_RX_FILTER_PROM; 17123147Sxc151355 else 17131000Sxc151355 rfilt &= ~HAL_RX_FILTER_PROM; 17146235Sxc151355 asc->asc_promisc = on; 17153147Sxc151355 ATH_HAL_SETRXFILTER(ah, rfilt); 17163147Sxc151355 ATH_UNLOCK(asc); 17171000Sxc151355 17183147Sxc151355 return (0); 17191000Sxc151355 } 17201000Sxc151355 17211000Sxc151355 static int 17223147Sxc151355 ath_m_multicst(void *arg, boolean_t add, const uint8_t *mca) 17231000Sxc151355 { 17243147Sxc151355 ath_t *asc = arg; 17253147Sxc151355 struct ath_hal *ah = asc->asc_ah; 17266235Sxc151355 uint32_t val, index, bit; 17271000Sxc151355 uint8_t pos; 17286235Sxc151355 uint32_t *mfilt = asc->asc_mcast_hash; 17291000Sxc151355 17303147Sxc151355 ATH_LOCK(asc); 1731*8033SWang.Lin@Sun.COM 17321000Sxc151355 /* calculate XOR of eight 6bit values */ 17331000Sxc151355 val = ATH_LE_READ_4(mca + 0); 17341000Sxc151355 pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; 17351000Sxc151355 val = ATH_LE_READ_4(mca + 3); 17361000Sxc151355 pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; 17371000Sxc151355 pos &= 0x3f; 17386235Sxc151355 index = pos / 32; 17396235Sxc151355 bit = 1 << (pos % 32); 17406235Sxc151355 17416235Sxc151355 if (add) { /* enable multicast */ 17426235Sxc151355 asc->asc_mcast_refs[pos]++; 17436235Sxc151355 mfilt[index] |= bit; 17446235Sxc151355 } else { /* disable multicast */ 17456235Sxc151355 if (--asc->asc_mcast_refs[pos] == 0) 17466235Sxc151355 mfilt[index] &= ~bit; 17476235Sxc151355 } 17481000Sxc151355 ATH_HAL_SETMCASTFILTER(ah, mfilt[0], mfilt[1]); 17491000Sxc151355 17503147Sxc151355 ATH_UNLOCK(asc); 17513147Sxc151355 return (0); 17521000Sxc151355 } 17537663SSowmini.Varadhan@Sun.COM /* 17547663SSowmini.Varadhan@Sun.COM * callback functions for /get/set properties 17557663SSowmini.Varadhan@Sun.COM */ 17567663SSowmini.Varadhan@Sun.COM static int 17577663SSowmini.Varadhan@Sun.COM ath_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 17587663SSowmini.Varadhan@Sun.COM uint_t wldp_length, const void *wldp_buf) 17597663SSowmini.Varadhan@Sun.COM { 17607663SSowmini.Varadhan@Sun.COM ath_t *asc = arg; 17617663SSowmini.Varadhan@Sun.COM int err; 17627663SSowmini.Varadhan@Sun.COM 17637663SSowmini.Varadhan@Sun.COM err = ieee80211_setprop(&asc->asc_isc, pr_name, wldp_pr_num, 17647663SSowmini.Varadhan@Sun.COM wldp_length, wldp_buf); 17657663SSowmini.Varadhan@Sun.COM 17667663SSowmini.Varadhan@Sun.COM ATH_LOCK(asc); 17677663SSowmini.Varadhan@Sun.COM 17687663SSowmini.Varadhan@Sun.COM if (err == ENETRESET) { 17697663SSowmini.Varadhan@Sun.COM if (ATH_IS_RUNNING(asc)) { 17707663SSowmini.Varadhan@Sun.COM ATH_UNLOCK(asc); 17717663SSowmini.Varadhan@Sun.COM (void) ath_m_start(asc); 17727663SSowmini.Varadhan@Sun.COM (void) ieee80211_new_state(&asc->asc_isc, 17737663SSowmini.Varadhan@Sun.COM IEEE80211_S_SCAN, -1); 17747663SSowmini.Varadhan@Sun.COM ATH_LOCK(asc); 17757663SSowmini.Varadhan@Sun.COM } 17767663SSowmini.Varadhan@Sun.COM err = 0; 17777663SSowmini.Varadhan@Sun.COM } 17787663SSowmini.Varadhan@Sun.COM 17797663SSowmini.Varadhan@Sun.COM ATH_UNLOCK(asc); 17807663SSowmini.Varadhan@Sun.COM 17817663SSowmini.Varadhan@Sun.COM return (err); 17827663SSowmini.Varadhan@Sun.COM } 17837663SSowmini.Varadhan@Sun.COM static int 17847663SSowmini.Varadhan@Sun.COM ath_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 17857663SSowmini.Varadhan@Sun.COM uint_t pr_flags, uint_t wldp_length, void *wldp_buf) 17867663SSowmini.Varadhan@Sun.COM { 17877663SSowmini.Varadhan@Sun.COM ath_t *asc = arg; 17887663SSowmini.Varadhan@Sun.COM int err = 0; 17897663SSowmini.Varadhan@Sun.COM 17907663SSowmini.Varadhan@Sun.COM err = ieee80211_getprop(&asc->asc_isc, pr_name, wldp_pr_num, 17917663SSowmini.Varadhan@Sun.COM pr_flags, wldp_length, wldp_buf); 17927663SSowmini.Varadhan@Sun.COM 17937663SSowmini.Varadhan@Sun.COM return (err); 17947663SSowmini.Varadhan@Sun.COM } 17957663SSowmini.Varadhan@Sun.COM 17961000Sxc151355 static void 17973147Sxc151355 ath_m_ioctl(void *arg, queue_t *wq, mblk_t *mp) 17981000Sxc151355 { 17993147Sxc151355 ath_t *asc = arg; 18003147Sxc151355 int32_t err; 18011000Sxc151355 18023147Sxc151355 err = ieee80211_ioctl(&asc->asc_isc, wq, mp); 18033147Sxc151355 ATH_LOCK(asc); 18043147Sxc151355 if (err == ENETRESET) { 18053147Sxc151355 if (ATH_IS_RUNNING(asc)) { 18063147Sxc151355 ATH_UNLOCK(asc); 18073147Sxc151355 (void) ath_m_start(asc); 18083147Sxc151355 (void) ieee80211_new_state(&asc->asc_isc, 18093147Sxc151355 IEEE80211_S_SCAN, -1); 18103147Sxc151355 ATH_LOCK(asc); 18113147Sxc151355 } 18121000Sxc151355 } 18133147Sxc151355 ATH_UNLOCK(asc); 18141000Sxc151355 } 18151000Sxc151355 18161000Sxc151355 static int 18173147Sxc151355 ath_m_stat(void *arg, uint_t stat, uint64_t *val) 18181000Sxc151355 { 18193147Sxc151355 ath_t *asc = arg; 18203147Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc; 18213147Sxc151355 struct ieee80211_node *in = ic->ic_bss; 18221000Sxc151355 struct ieee80211_rateset *rs = &in->in_rates; 18231000Sxc151355 18243147Sxc151355 ATH_LOCK(asc); 18253147Sxc151355 switch (stat) { 18263147Sxc151355 case MAC_STAT_IFSPEED: 18273147Sxc151355 *val = (rs->ir_rates[in->in_txrate] & IEEE80211_RATE_VAL) / 2 * 18283147Sxc151355 1000000ull; 18293147Sxc151355 break; 18303147Sxc151355 case MAC_STAT_NOXMTBUF: 18313147Sxc151355 *val = asc->asc_stats.ast_tx_nobuf + 18323147Sxc151355 asc->asc_stats.ast_tx_nobufmgt; 18333147Sxc151355 break; 18343147Sxc151355 case MAC_STAT_IERRORS: 18353147Sxc151355 *val = asc->asc_stats.ast_rx_tooshort; 18363147Sxc151355 break; 18373147Sxc151355 case MAC_STAT_RBYTES: 18383147Sxc151355 *val = ic->ic_stats.is_rx_bytes; 18393147Sxc151355 break; 18403147Sxc151355 case MAC_STAT_IPACKETS: 18413147Sxc151355 *val = ic->ic_stats.is_rx_frags; 18423147Sxc151355 break; 18433147Sxc151355 case MAC_STAT_OBYTES: 18443147Sxc151355 *val = ic->ic_stats.is_tx_bytes; 18453147Sxc151355 break; 18463147Sxc151355 case MAC_STAT_OPACKETS: 18473147Sxc151355 *val = ic->ic_stats.is_tx_frags; 18483147Sxc151355 break; 18493631Sxh158540 case MAC_STAT_OERRORS: 18503147Sxc151355 case WIFI_STAT_TX_FAILED: 18513147Sxc151355 *val = asc->asc_stats.ast_tx_fifoerr + 18523631Sxh158540 asc->asc_stats.ast_tx_xretries + 18533631Sxh158540 asc->asc_stats.ast_tx_discard; 18543147Sxc151355 break; 18553147Sxc151355 case WIFI_STAT_TX_RETRANS: 18563147Sxc151355 *val = asc->asc_stats.ast_tx_xretries; 18573147Sxc151355 break; 18583147Sxc151355 case WIFI_STAT_FCS_ERRORS: 18593147Sxc151355 *val = asc->asc_stats.ast_rx_crcerr; 18603147Sxc151355 break; 18613147Sxc151355 case WIFI_STAT_WEP_ERRORS: 18623147Sxc151355 *val = asc->asc_stats.ast_rx_badcrypt; 18633147Sxc151355 break; 18643147Sxc151355 case WIFI_STAT_TX_FRAGS: 18653147Sxc151355 case WIFI_STAT_MCAST_TX: 18663147Sxc151355 case WIFI_STAT_RTS_SUCCESS: 18673147Sxc151355 case WIFI_STAT_RTS_FAILURE: 18683147Sxc151355 case WIFI_STAT_ACK_FAILURE: 18693147Sxc151355 case WIFI_STAT_RX_FRAGS: 18703147Sxc151355 case WIFI_STAT_MCAST_RX: 18713147Sxc151355 case WIFI_STAT_RX_DUPS: 18723147Sxc151355 ATH_UNLOCK(asc); 18733147Sxc151355 return (ieee80211_stat(ic, stat, val)); 18743147Sxc151355 default: 18753147Sxc151355 ATH_UNLOCK(asc); 18763147Sxc151355 return (ENOTSUP); 18773147Sxc151355 } 18783147Sxc151355 ATH_UNLOCK(asc); 18791000Sxc151355 18803147Sxc151355 return (0); 18811000Sxc151355 } 18821000Sxc151355 18831000Sxc151355 static int 18846797Sxc151355 ath_pci_setup(ath_t *asc) 18856797Sxc151355 { 18866797Sxc151355 uint16_t command; 18876797Sxc151355 18886797Sxc151355 /* 18896797Sxc151355 * Enable memory mapping and bus mastering 18906797Sxc151355 */ 18916797Sxc151355 ASSERT(asc != NULL); 18926797Sxc151355 command = pci_config_get16(asc->asc_cfg_handle, PCI_CONF_COMM); 18936797Sxc151355 command |= PCI_COMM_MAE | PCI_COMM_ME; 18946797Sxc151355 pci_config_put16(asc->asc_cfg_handle, PCI_CONF_COMM, command); 18956797Sxc151355 command = pci_config_get16(asc->asc_cfg_handle, PCI_CONF_COMM); 18966797Sxc151355 if ((command & PCI_COMM_MAE) == 0) { 18976797Sxc151355 ath_problem("ath: ath_pci_setup(): " 18986797Sxc151355 "failed to enable memory mapping\n"); 18996797Sxc151355 return (EIO); 19006797Sxc151355 } 19016797Sxc151355 if ((command & PCI_COMM_ME) == 0) { 19026797Sxc151355 ath_problem("ath: ath_pci_setup(): " 19036797Sxc151355 "failed to enable bus mastering\n"); 19046797Sxc151355 return (EIO); 19056797Sxc151355 } 19066797Sxc151355 ATH_DEBUG((ATH_DBG_INIT, "ath: ath_pci_setup(): " 19076797Sxc151355 "set command reg to 0x%x \n", command)); 19086797Sxc151355 19096797Sxc151355 return (0); 19106797Sxc151355 } 19116797Sxc151355 19126797Sxc151355 static int 19136797Sxc151355 ath_resume(dev_info_t *devinfo) 19146797Sxc151355 { 19156797Sxc151355 ath_t *asc; 19166797Sxc151355 int ret = DDI_SUCCESS; 19176797Sxc151355 19186797Sxc151355 asc = ddi_get_soft_state(ath_soft_state_p, ddi_get_instance(devinfo)); 19196797Sxc151355 if (asc == NULL) { 19206797Sxc151355 ATH_DEBUG((ATH_DBG_SUSPEND, "ath: ath_resume(): " 19216797Sxc151355 "failed to get soft state\n")); 19226797Sxc151355 return (DDI_FAILURE); 19236797Sxc151355 } 19246797Sxc151355 19256797Sxc151355 ATH_LOCK(asc); 19266797Sxc151355 /* 19276797Sxc151355 * Set up config space command register(s). Refuse 19286797Sxc151355 * to resume on failure. 19296797Sxc151355 */ 19306797Sxc151355 if (ath_pci_setup(asc) != 0) { 19316797Sxc151355 ATH_DEBUG((ATH_DBG_SUSPEND, "ath: ath_resume(): " 19326797Sxc151355 "ath_pci_setup() failed\n")); 19336797Sxc151355 ATH_UNLOCK(asc); 19346797Sxc151355 return (DDI_FAILURE); 19356797Sxc151355 } 19366797Sxc151355 19376797Sxc151355 if (!asc->asc_invalid) 19386797Sxc151355 ret = ath_start_locked(asc); 19396797Sxc151355 ATH_UNLOCK(asc); 19406797Sxc151355 19416797Sxc151355 return (ret); 19426797Sxc151355 } 19436797Sxc151355 19446797Sxc151355 static int 19451000Sxc151355 ath_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd) 19461000Sxc151355 { 19471000Sxc151355 ath_t *asc; 19483147Sxc151355 ieee80211com_t *ic; 19491000Sxc151355 struct ath_hal *ah; 19501000Sxc151355 uint8_t csz; 19511000Sxc151355 HAL_STATUS status; 19521000Sxc151355 caddr_t regs; 19531000Sxc151355 uint32_t i, val; 19546797Sxc151355 uint16_t vendor_id, device_id; 19551000Sxc151355 const char *athname; 19561000Sxc151355 int32_t ath_countrycode = CTRY_DEFAULT; /* country code */ 19571000Sxc151355 int32_t err, ath_regdomain = 0; /* regulatory domain */ 19581000Sxc151355 char strbuf[32]; 19593147Sxc151355 int instance; 19603147Sxc151355 wifi_data_t wd = { 0 }; 19613147Sxc151355 mac_register_t *macp; 19621000Sxc151355 19636797Sxc151355 switch (cmd) { 19646797Sxc151355 case DDI_ATTACH: 19656797Sxc151355 break; 19666797Sxc151355 19676797Sxc151355 case DDI_RESUME: 19686797Sxc151355 return (ath_resume(devinfo)); 19696797Sxc151355 19706797Sxc151355 default: 19711000Sxc151355 return (DDI_FAILURE); 19726797Sxc151355 } 19731000Sxc151355 19743147Sxc151355 instance = ddi_get_instance(devinfo); 19753147Sxc151355 if (ddi_soft_state_zalloc(ath_soft_state_p, instance) != DDI_SUCCESS) { 19761000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 19771000Sxc151355 "Unable to alloc softstate\n")); 19781000Sxc151355 return (DDI_FAILURE); 19791000Sxc151355 } 19801000Sxc151355 19811000Sxc151355 asc = ddi_get_soft_state(ath_soft_state_p, ddi_get_instance(devinfo)); 19823147Sxc151355 ic = (ieee80211com_t *)asc; 19831000Sxc151355 asc->asc_dev = devinfo; 19841000Sxc151355 19851000Sxc151355 mutex_init(&asc->asc_genlock, NULL, MUTEX_DRIVER, NULL); 19861000Sxc151355 mutex_init(&asc->asc_txbuflock, NULL, MUTEX_DRIVER, NULL); 19871000Sxc151355 mutex_init(&asc->asc_rxbuflock, NULL, MUTEX_DRIVER, NULL); 19883147Sxc151355 mutex_init(&asc->asc_resched_lock, NULL, MUTEX_DRIVER, NULL); 19891000Sxc151355 19901000Sxc151355 err = pci_config_setup(devinfo, &asc->asc_cfg_handle); 19911000Sxc151355 if (err != DDI_SUCCESS) { 19921000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 19931000Sxc151355 "pci_config_setup() failed")); 19941000Sxc151355 goto attach_fail0; 19951000Sxc151355 } 19961000Sxc151355 19976797Sxc151355 if (ath_pci_setup(asc) != 0) 19986797Sxc151355 goto attach_fail1; 19996797Sxc151355 20005420Sxc151355 /* 20015420Sxc151355 * Cache line size is used to size and align various 20025420Sxc151355 * structures used to communicate with the hardware. 20035420Sxc151355 */ 20041000Sxc151355 csz = pci_config_get8(asc->asc_cfg_handle, PCI_CONF_CACHE_LINESZ); 20055420Sxc151355 if (csz == 0) { 20065420Sxc151355 /* 20075420Sxc151355 * We must have this setup properly for rx buffer 20085420Sxc151355 * DMA to work so force a reasonable value here if it 20095420Sxc151355 * comes up zero. 20105420Sxc151355 */ 20115420Sxc151355 csz = ATH_DEF_CACHE_BYTES / sizeof (uint32_t); 20125420Sxc151355 pci_config_put8(asc->asc_cfg_handle, PCI_CONF_CACHE_LINESZ, 20135420Sxc151355 csz); 20145420Sxc151355 } 20151000Sxc151355 asc->asc_cachelsz = csz << 2; 20161000Sxc151355 vendor_id = pci_config_get16(asc->asc_cfg_handle, PCI_CONF_VENID); 20171000Sxc151355 device_id = pci_config_get16(asc->asc_cfg_handle, PCI_CONF_DEVID); 20181000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): vendor 0x%x, " 20191000Sxc151355 "device id 0x%x, cache size %d\n", vendor_id, device_id, csz)); 20201000Sxc151355 20211000Sxc151355 athname = ath_hal_probe(vendor_id, device_id); 20221000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): athname: %s\n", 20231000Sxc151355 athname ? athname : "Atheros ???")); 20241000Sxc151355 20251000Sxc151355 pci_config_put8(asc->asc_cfg_handle, PCI_CONF_LATENCY_TIMER, 0xa8); 20261000Sxc151355 val = pci_config_get32(asc->asc_cfg_handle, 0x40); 20271000Sxc151355 if ((val & 0x0000ff00) != 0) 20281000Sxc151355 pci_config_put32(asc->asc_cfg_handle, 0x40, val & 0xffff00ff); 20291000Sxc151355 20301000Sxc151355 err = ddi_regs_map_setup(devinfo, 1, 20311000Sxc151355 ®s, 0, 0, &ath_reg_accattr, &asc->asc_io_handle); 20321000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 20331000Sxc151355 "regs map1 = %x err=%d\n", regs, err)); 20341000Sxc151355 if (err != DDI_SUCCESS) { 20351000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 20361000Sxc151355 "ddi_regs_map_setup() failed")); 20371000Sxc151355 goto attach_fail1; 20381000Sxc151355 } 20391000Sxc151355 20401000Sxc151355 ah = ath_hal_attach(device_id, asc, 0, regs, &status); 20411000Sxc151355 if (ah == NULL) { 20421000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 20436235Sxc151355 "unable to attach hw: '%s' (HAL status %u)\n", 20446235Sxc151355 ath_get_hal_status_desc(status), status)); 20451000Sxc151355 goto attach_fail2; 20461000Sxc151355 } 2047*8033SWang.Lin@Sun.COM ATH_DEBUG((ATH_DBG_ATTACH, "mac %d.%d phy %d.%d", 2048*8033SWang.Lin@Sun.COM ah->ah_macVersion, ah->ah_macRev, 2049*8033SWang.Lin@Sun.COM ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf)); 20501000Sxc151355 ATH_HAL_INTRSET(ah, 0); 20511000Sxc151355 asc->asc_ah = ah; 20521000Sxc151355 20531000Sxc151355 if (ah->ah_abi != HAL_ABI_VERSION) { 20541000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 20551000Sxc151355 "HAL ABI mismatch detected (0x%x != 0x%x)\n", 20561000Sxc151355 ah->ah_abi, HAL_ABI_VERSION)); 20571000Sxc151355 goto attach_fail3; 20581000Sxc151355 } 20591000Sxc151355 20601000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 20611000Sxc151355 "HAL ABI version 0x%x\n", ah->ah_abi)); 20621000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 20631000Sxc151355 "HAL mac version %d.%d, phy version %d.%d\n", 20641000Sxc151355 ah->ah_macVersion, ah->ah_macRev, 20651000Sxc151355 ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf)); 20661000Sxc151355 if (ah->ah_analog5GhzRev) 20671000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 20681000Sxc151355 "HAL 5ghz radio version %d.%d\n", 20691000Sxc151355 ah->ah_analog5GhzRev >> 4, 20701000Sxc151355 ah->ah_analog5GhzRev & 0xf)); 20711000Sxc151355 if (ah->ah_analog2GhzRev) 20721000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 20731000Sxc151355 "HAL 2ghz radio version %d.%d\n", 20741000Sxc151355 ah->ah_analog2GhzRev >> 4, 20751000Sxc151355 ah->ah_analog2GhzRev & 0xf)); 20761000Sxc151355 20771000Sxc151355 /* 20781000Sxc151355 * Check if the MAC has multi-rate retry support. 20791000Sxc151355 * We do this by trying to setup a fake extended 20801000Sxc151355 * descriptor. MAC's that don't have support will 20811000Sxc151355 * return false w/o doing anything. MAC's that do 20821000Sxc151355 * support it will return true w/o doing anything. 20831000Sxc151355 */ 20841000Sxc151355 asc->asc_mrretry = ATH_HAL_SETUPXTXDESC(ah, NULL, 0, 0, 0, 0, 0, 0); 20851000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 20861000Sxc151355 "multi rate retry support=%x\n", 20871000Sxc151355 asc->asc_mrretry)); 20881000Sxc151355 20894126Szf162725 /* 20904126Szf162725 * Get the hardware key cache size. 20914126Szf162725 */ 20924126Szf162725 asc->asc_keymax = ATH_HAL_KEYCACHESIZE(ah); 20934126Szf162725 if (asc->asc_keymax > sizeof (asc->asc_keymap) * NBBY) { 20944126Szf162725 ATH_DEBUG((ATH_DBG_ATTACH, "ath_attach:" 20954126Szf162725 " Warning, using only %u entries in %u key cache\n", 20964126Szf162725 sizeof (asc->asc_keymap) * NBBY, asc->asc_keymax)); 20974126Szf162725 asc->asc_keymax = sizeof (asc->asc_keymap) * NBBY; 20984126Szf162725 } 20994126Szf162725 /* 21004126Szf162725 * Reset the key cache since some parts do not 21014126Szf162725 * reset the contents on initial power up. 21024126Szf162725 */ 21034126Szf162725 for (i = 0; i < asc->asc_keymax; i++) 21044126Szf162725 ATH_HAL_KEYRESET(ah, i); 21054126Szf162725 21061000Sxc151355 ATH_HAL_GETREGDOMAIN(ah, (uint32_t *)&ath_regdomain); 21071000Sxc151355 ATH_HAL_GETCOUNTRYCODE(ah, &ath_countrycode); 21081000Sxc151355 /* 21091000Sxc151355 * Collect the channel list using the default country 21101000Sxc151355 * code and including outdoor channels. The 802.11 layer 21111000Sxc151355 * is resposible for filtering this list to a set of 21121000Sxc151355 * channels that it considers ok to use. 21131000Sxc151355 */ 21141000Sxc151355 asc->asc_have11g = 0; 21151000Sxc151355 21161000Sxc151355 /* enable outdoor use, enable extended channels */ 21171000Sxc151355 err = ath_getchannels(asc, ath_countrycode, AH_FALSE, AH_TRUE); 21181000Sxc151355 if (err != 0) 21191000Sxc151355 goto attach_fail3; 21201000Sxc151355 21211000Sxc151355 /* 21221000Sxc151355 * Setup rate tables for all potential media types. 21231000Sxc151355 */ 21241000Sxc151355 ath_rate_setup(asc, IEEE80211_MODE_11A); 21251000Sxc151355 ath_rate_setup(asc, IEEE80211_MODE_11B); 21261000Sxc151355 ath_rate_setup(asc, IEEE80211_MODE_11G); 21273147Sxc151355 ath_rate_setup(asc, IEEE80211_MODE_TURBO_A); 21281000Sxc151355 21291000Sxc151355 /* Setup here so ath_rate_update is happy */ 21301000Sxc151355 ath_setcurmode(asc, IEEE80211_MODE_11A); 21311000Sxc151355 21321000Sxc151355 err = ath_desc_alloc(devinfo, asc); 21331000Sxc151355 if (err != DDI_SUCCESS) { 21341000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 21351000Sxc151355 "failed to allocate descriptors: %d\n", err)); 21361000Sxc151355 goto attach_fail3; 21371000Sxc151355 } 21381000Sxc151355 2139*8033SWang.Lin@Sun.COM if ((asc->asc_tq = ddi_taskq_create(devinfo, "ath_taskq", 1, 2140*8033SWang.Lin@Sun.COM TASKQ_DEFAULTPRI, 0)) == NULL) { 2141*8033SWang.Lin@Sun.COM goto attach_fail4; 2142*8033SWang.Lin@Sun.COM } 21431000Sxc151355 /* Setup transmit queues in the HAL */ 21441000Sxc151355 if (ath_txq_setup(asc)) 21451000Sxc151355 goto attach_fail4; 21461000Sxc151355 21473147Sxc151355 ATH_HAL_GETMAC(ah, ic->ic_macaddr); 21481000Sxc151355 21493147Sxc151355 /* 21503147Sxc151355 * Initialize pointers to device specific functions which 21513147Sxc151355 * will be used by the generic layer. 21523147Sxc151355 */ 21531000Sxc151355 /* 11g support is identified when we fetch the channel set */ 21541000Sxc151355 if (asc->asc_have11g) 21554206Szf162725 ic->ic_caps |= IEEE80211_C_SHPREAMBLE | 21564206Szf162725 IEEE80211_C_SHSLOT; /* short slot time */ 21573147Sxc151355 /* 21583147Sxc151355 * Query the hal to figure out h/w crypto support. 21593147Sxc151355 */ 21603147Sxc151355 if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_WEP)) 21613147Sxc151355 ic->ic_caps |= IEEE80211_C_WEP; 21623147Sxc151355 if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_AES_OCB)) 21633147Sxc151355 ic->ic_caps |= IEEE80211_C_AES; 21644126Szf162725 if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_AES_CCM)) { 21654126Szf162725 ATH_DEBUG((ATH_DBG_ATTACH, "Atheros support H/W CCMP\n")); 21663147Sxc151355 ic->ic_caps |= IEEE80211_C_AES_CCM; 21674126Szf162725 } 21684126Szf162725 if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_CKIP)) 21693147Sxc151355 ic->ic_caps |= IEEE80211_C_CKIP; 21704126Szf162725 if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_TKIP)) { 21714126Szf162725 ATH_DEBUG((ATH_DBG_ATTACH, "Atheros support H/W TKIP\n")); 21724126Szf162725 ic->ic_caps |= IEEE80211_C_TKIP; 21733147Sxc151355 /* 21743147Sxc151355 * Check if h/w does the MIC and/or whether the 21753147Sxc151355 * separate key cache entries are required to 21763147Sxc151355 * handle both tx+rx MIC keys. 21773147Sxc151355 */ 21784126Szf162725 if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_MIC)) { 21794126Szf162725 ATH_DEBUG((ATH_DBG_ATTACH, "Support H/W TKIP MIC\n")); 21803147Sxc151355 ic->ic_caps |= IEEE80211_C_TKIPMIC; 21814126Szf162725 } 2182*8033SWang.Lin@Sun.COM 2183*8033SWang.Lin@Sun.COM /* 2184*8033SWang.Lin@Sun.COM * If the h/w supports storing tx+rx MIC keys 2185*8033SWang.Lin@Sun.COM * in one cache slot automatically enable use. 2186*8033SWang.Lin@Sun.COM */ 2187*8033SWang.Lin@Sun.COM if (ATH_HAL_HASTKIPSPLIT(ah) || 2188*8033SWang.Lin@Sun.COM !ATH_HAL_SETTKIPSPLIT(ah, AH_FALSE)) { 21893147Sxc151355 asc->asc_splitmic = 1; 2190*8033SWang.Lin@Sun.COM } 21913147Sxc151355 } 21924126Szf162725 ic->ic_caps |= IEEE80211_C_WPA; /* Support WPA/WPA2 */ 21934126Szf162725 21943147Sxc151355 asc->asc_hasclrkey = ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_CLR); 2195*8033SWang.Lin@Sun.COM /* 2196*8033SWang.Lin@Sun.COM * Mark key cache slots associated with global keys 2197*8033SWang.Lin@Sun.COM * as in use. If we knew TKIP was not to be used we 2198*8033SWang.Lin@Sun.COM * could leave the +32, +64, and +32+64 slots free. 2199*8033SWang.Lin@Sun.COM */ 2200*8033SWang.Lin@Sun.COM for (i = 0; i < IEEE80211_WEP_NKID; i++) { 2201*8033SWang.Lin@Sun.COM setbit(asc->asc_keymap, i); 2202*8033SWang.Lin@Sun.COM setbit(asc->asc_keymap, i+64); 2203*8033SWang.Lin@Sun.COM if (asc->asc_splitmic) { 2204*8033SWang.Lin@Sun.COM setbit(asc->asc_keymap, i+32); 2205*8033SWang.Lin@Sun.COM setbit(asc->asc_keymap, i+32+64); 2206*8033SWang.Lin@Sun.COM } 2207*8033SWang.Lin@Sun.COM } 2208*8033SWang.Lin@Sun.COM 22093147Sxc151355 ic->ic_phytype = IEEE80211_T_OFDM; 22103147Sxc151355 ic->ic_opmode = IEEE80211_M_STA; 22113147Sxc151355 ic->ic_state = IEEE80211_S_INIT; 22123147Sxc151355 ic->ic_maxrssi = ATH_MAX_RSSI; 22133147Sxc151355 ic->ic_set_shortslot = ath_set_shortslot; 22143147Sxc151355 ic->ic_xmit = ath_xmit; 22153147Sxc151355 ieee80211_attach(ic); 22161000Sxc151355 22174126Szf162725 /* different instance has different WPA door */ 22184126Szf162725 (void) snprintf(ic->ic_wpadoor, MAX_IEEE80211STR, "%s_%s%d", WPA_DOOR, 22195420Sxc151355 ddi_driver_name(devinfo), 22205420Sxc151355 ddi_get_instance(devinfo)); 22214126Szf162725 22223147Sxc151355 /* Override 80211 default routines */ 22233147Sxc151355 ic->ic_reset = ath_reset; 22243147Sxc151355 asc->asc_newstate = ic->ic_newstate; 22253147Sxc151355 ic->ic_newstate = ath_newstate; 22263147Sxc151355 ic->ic_watchdog = ath_watchdog; 22273147Sxc151355 ic->ic_node_alloc = ath_node_alloc; 22283147Sxc151355 ic->ic_node_free = ath_node_free; 22293147Sxc151355 ic->ic_crypto.cs_key_alloc = ath_key_alloc; 22303147Sxc151355 ic->ic_crypto.cs_key_delete = ath_key_delete; 22313147Sxc151355 ic->ic_crypto.cs_key_set = ath_key_set; 22323147Sxc151355 ieee80211_media_init(ic); 22334296Szf162725 /* 22344296Szf162725 * initialize default tx key 22354296Szf162725 */ 22364296Szf162725 ic->ic_def_txkey = 0; 22371000Sxc151355 22381000Sxc151355 asc->asc_rx_pend = 0; 22391000Sxc151355 ATH_HAL_INTRSET(ah, 0); 22401000Sxc151355 err = ddi_add_softintr(devinfo, DDI_SOFTINT_LOW, 22411000Sxc151355 &asc->asc_softint_id, NULL, 0, ath_softint_handler, (caddr_t)asc); 22421000Sxc151355 if (err != DDI_SUCCESS) { 22431000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 22443147Sxc151355 "ddi_add_softintr() failed\n")); 22451000Sxc151355 goto attach_fail5; 22461000Sxc151355 } 22471000Sxc151355 22481000Sxc151355 if (ddi_get_iblock_cookie(devinfo, 0, &asc->asc_iblock) 22491000Sxc151355 != DDI_SUCCESS) { 22501000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 22511000Sxc151355 "Can not get iblock cookie for INT\n")); 22521000Sxc151355 goto attach_fail6; 22531000Sxc151355 } 22541000Sxc151355 22553147Sxc151355 if (ddi_add_intr(devinfo, 0, NULL, NULL, ath_intr, 22563147Sxc151355 (caddr_t)asc) != DDI_SUCCESS) { 22571000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 22581000Sxc151355 "Can not set intr for ATH driver\n")); 22591000Sxc151355 goto attach_fail6; 22601000Sxc151355 } 22613147Sxc151355 22623147Sxc151355 /* 22633147Sxc151355 * Provide initial settings for the WiFi plugin; whenever this 22643147Sxc151355 * information changes, we need to call mac_plugindata_update() 22653147Sxc151355 */ 22663147Sxc151355 wd.wd_opmode = ic->ic_opmode; 22673147Sxc151355 wd.wd_secalloc = WIFI_SEC_NONE; 22683147Sxc151355 IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid); 22693147Sxc151355 22703147Sxc151355 if ((macp = mac_alloc(MAC_VERSION)) == NULL) { 22713147Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 22723147Sxc151355 "MAC version mismatch\n")); 22733147Sxc151355 goto attach_fail7; 22743147Sxc151355 } 22751000Sxc151355 22763147Sxc151355 macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI; 22773147Sxc151355 macp->m_driver = asc; 22783147Sxc151355 macp->m_dip = devinfo; 22793147Sxc151355 macp->m_src_addr = ic->ic_macaddr; 22803147Sxc151355 macp->m_callbacks = &ath_m_callbacks; 22813147Sxc151355 macp->m_min_sdu = 0; 22823147Sxc151355 macp->m_max_sdu = IEEE80211_MTU; 22833147Sxc151355 macp->m_pdata = &wd; 22843147Sxc151355 macp->m_pdata_size = sizeof (wd); 22853147Sxc151355 22863147Sxc151355 err = mac_register(macp, &ic->ic_mach); 22873147Sxc151355 mac_free(macp); 22883147Sxc151355 if (err != 0) { 22891000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 22903147Sxc151355 "mac_register err %x\n", err)); 22911000Sxc151355 goto attach_fail7; 22921000Sxc151355 } 22931000Sxc151355 22941000Sxc151355 /* Create minor node of type DDI_NT_NET_WIFI */ 22951000Sxc151355 (void) snprintf(strbuf, sizeof (strbuf), "%s%d", 22963147Sxc151355 ATH_NODENAME, instance); 22971000Sxc151355 err = ddi_create_minor_node(devinfo, strbuf, S_IFCHR, 22983147Sxc151355 instance + 1, DDI_NT_NET_WIFI, 0); 22991000Sxc151355 if (err != DDI_SUCCESS) 23001000Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "WARN: ath: ath_attach(): " 23011000Sxc151355 "Create minor node failed - %d\n", err)); 23021000Sxc151355 23033147Sxc151355 mac_link_update(ic->ic_mach, LINK_STATE_DOWN); 23041000Sxc151355 asc->asc_invalid = 1; 23056797Sxc151355 asc->asc_isrunning = 0; 23066235Sxc151355 asc->asc_promisc = B_FALSE; 23076235Sxc151355 bzero(asc->asc_mcast_refs, sizeof (asc->asc_mcast_refs)); 23086235Sxc151355 bzero(asc->asc_mcast_hash, sizeof (asc->asc_mcast_hash)); 23091000Sxc151355 return (DDI_SUCCESS); 23101000Sxc151355 attach_fail7: 23111000Sxc151355 ddi_remove_intr(devinfo, 0, asc->asc_iblock); 23121000Sxc151355 attach_fail6: 23131000Sxc151355 ddi_remove_softintr(asc->asc_softint_id); 23141000Sxc151355 attach_fail5: 23153147Sxc151355 (void) ieee80211_detach(ic); 23161000Sxc151355 attach_fail4: 23171000Sxc151355 ath_desc_free(asc); 2318*8033SWang.Lin@Sun.COM if (asc->asc_tq) 2319*8033SWang.Lin@Sun.COM ddi_taskq_destroy(asc->asc_tq); 23201000Sxc151355 attach_fail3: 23211000Sxc151355 ah->ah_detach(asc->asc_ah); 23221000Sxc151355 attach_fail2: 23231000Sxc151355 ddi_regs_map_free(&asc->asc_io_handle); 23241000Sxc151355 attach_fail1: 23251000Sxc151355 pci_config_teardown(&asc->asc_cfg_handle); 23261000Sxc151355 attach_fail0: 23271000Sxc151355 asc->asc_invalid = 1; 23281000Sxc151355 mutex_destroy(&asc->asc_txbuflock); 23291000Sxc151355 for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { 23301000Sxc151355 if (ATH_TXQ_SETUP(asc, i)) { 23311000Sxc151355 struct ath_txq *txq = &asc->asc_txq[i]; 23321000Sxc151355 mutex_destroy(&txq->axq_lock); 23331000Sxc151355 } 23341000Sxc151355 } 23351000Sxc151355 mutex_destroy(&asc->asc_rxbuflock); 23361000Sxc151355 mutex_destroy(&asc->asc_genlock); 23373147Sxc151355 mutex_destroy(&asc->asc_resched_lock); 23383147Sxc151355 ddi_soft_state_free(ath_soft_state_p, instance); 23391000Sxc151355 23401000Sxc151355 return (DDI_FAILURE); 23411000Sxc151355 } 23421000Sxc151355 23436797Sxc151355 /* 23446797Sxc151355 * Suspend transmit/receive for powerdown 23456797Sxc151355 */ 23466797Sxc151355 static int 23476797Sxc151355 ath_suspend(ath_t *asc) 23486797Sxc151355 { 23496797Sxc151355 ATH_LOCK(asc); 23506797Sxc151355 ath_stop_locked(asc); 23516797Sxc151355 ATH_UNLOCK(asc); 23526797Sxc151355 ATH_DEBUG((ATH_DBG_SUSPEND, "ath: suspended.\n")); 23536797Sxc151355 23546797Sxc151355 return (DDI_SUCCESS); 23556797Sxc151355 } 23566797Sxc151355 23571000Sxc151355 static int32_t 23581000Sxc151355 ath_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd) 23591000Sxc151355 { 23601000Sxc151355 ath_t *asc; 23611000Sxc151355 23621000Sxc151355 asc = ddi_get_soft_state(ath_soft_state_p, ddi_get_instance(devinfo)); 23631000Sxc151355 ASSERT(asc != NULL); 23641000Sxc151355 23656797Sxc151355 switch (cmd) { 23666797Sxc151355 case DDI_DETACH: 23676797Sxc151355 break; 23686797Sxc151355 23696797Sxc151355 case DDI_SUSPEND: 23706797Sxc151355 return (ath_suspend(asc)); 23716797Sxc151355 23726797Sxc151355 default: 23731000Sxc151355 return (DDI_FAILURE); 23746797Sxc151355 } 23751000Sxc151355 23767507SXinghua.Wen@Sun.COM if (mac_disable(asc->asc_isc.ic_mach) != 0) 23777507SXinghua.Wen@Sun.COM return (DDI_FAILURE); 23787507SXinghua.Wen@Sun.COM 23793147Sxc151355 ath_stop_scantimer(asc); 23801000Sxc151355 23811000Sxc151355 /* disable interrupts */ 23821000Sxc151355 ATH_HAL_INTRSET(asc->asc_ah, 0); 23831000Sxc151355 23843147Sxc151355 /* 23853147Sxc151355 * Unregister from the MAC layer subsystem 23863147Sxc151355 */ 23877507SXinghua.Wen@Sun.COM (void) mac_unregister(asc->asc_isc.ic_mach); 23883147Sxc151355 23891000Sxc151355 /* free intterrupt resources */ 23901000Sxc151355 ddi_remove_intr(devinfo, 0, asc->asc_iblock); 23911000Sxc151355 ddi_remove_softintr(asc->asc_softint_id); 23921000Sxc151355 23933147Sxc151355 /* 23943147Sxc151355 * NB: the order of these is important: 23953147Sxc151355 * o call the 802.11 layer before detaching the hal to 23963147Sxc151355 * insure callbacks into the driver to delete global 23973147Sxc151355 * key cache entries can be handled 23983147Sxc151355 * o reclaim the tx queue data structures after calling 23993147Sxc151355 * the 802.11 layer as we'll get called back to reclaim 24003147Sxc151355 * node state and potentially want to use them 24013147Sxc151355 * o to cleanup the tx queues the hal is called, so detach 24023147Sxc151355 * it last 24033147Sxc151355 */ 24043147Sxc151355 ieee80211_detach(&asc->asc_isc); 24051000Sxc151355 ath_desc_free(asc); 2406*8033SWang.Lin@Sun.COM ddi_taskq_destroy(asc->asc_tq); 24073147Sxc151355 ath_txq_cleanup(asc); 24081000Sxc151355 asc->asc_ah->ah_detach(asc->asc_ah); 24091000Sxc151355 24101000Sxc151355 /* free io handle */ 24111000Sxc151355 ddi_regs_map_free(&asc->asc_io_handle); 24121000Sxc151355 pci_config_teardown(&asc->asc_cfg_handle); 24131000Sxc151355 24141000Sxc151355 /* destroy locks */ 24151000Sxc151355 mutex_destroy(&asc->asc_rxbuflock); 24161000Sxc151355 mutex_destroy(&asc->asc_genlock); 24173147Sxc151355 mutex_destroy(&asc->asc_resched_lock); 24181000Sxc151355 24191000Sxc151355 ddi_remove_minor_node(devinfo, NULL); 24201000Sxc151355 ddi_soft_state_free(ath_soft_state_p, ddi_get_instance(devinfo)); 24211000Sxc151355 24221000Sxc151355 return (DDI_SUCCESS); 24231000Sxc151355 } 24241000Sxc151355 24257805SKonstantin.Ananyev@Sun.COM /* 24267805SKonstantin.Ananyev@Sun.COM * quiesce(9E) entry point. 24277805SKonstantin.Ananyev@Sun.COM * 24287805SKonstantin.Ananyev@Sun.COM * This function is called when the system is single-threaded at high 24297805SKonstantin.Ananyev@Sun.COM * PIL with preemption disabled. Therefore, this function must not be 24307805SKonstantin.Ananyev@Sun.COM * blocked. 24317805SKonstantin.Ananyev@Sun.COM * 24327805SKonstantin.Ananyev@Sun.COM * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. 24337805SKonstantin.Ananyev@Sun.COM * DDI_FAILURE indicates an error condition and should almost never happen. 24347805SKonstantin.Ananyev@Sun.COM */ 24357805SKonstantin.Ananyev@Sun.COM static int32_t 24367805SKonstantin.Ananyev@Sun.COM ath_quiesce(dev_info_t *devinfo) 24377805SKonstantin.Ananyev@Sun.COM { 24387805SKonstantin.Ananyev@Sun.COM ath_t *asc; 24397805SKonstantin.Ananyev@Sun.COM struct ath_hal *ah; 24407805SKonstantin.Ananyev@Sun.COM int i; 24417805SKonstantin.Ananyev@Sun.COM 24427805SKonstantin.Ananyev@Sun.COM asc = ddi_get_soft_state(ath_soft_state_p, ddi_get_instance(devinfo)); 24437805SKonstantin.Ananyev@Sun.COM 24447805SKonstantin.Ananyev@Sun.COM if (asc == NULL || (ah = asc->asc_ah) == NULL) 24457805SKonstantin.Ananyev@Sun.COM return (DDI_FAILURE); 24467805SKonstantin.Ananyev@Sun.COM 24477805SKonstantin.Ananyev@Sun.COM /* 24487805SKonstantin.Ananyev@Sun.COM * Disable interrupts 24497805SKonstantin.Ananyev@Sun.COM */ 24507805SKonstantin.Ananyev@Sun.COM ATH_HAL_INTRSET(ah, 0); 24517805SKonstantin.Ananyev@Sun.COM 24527805SKonstantin.Ananyev@Sun.COM /* 24537805SKonstantin.Ananyev@Sun.COM * Disable TX HW 24547805SKonstantin.Ananyev@Sun.COM */ 24557805SKonstantin.Ananyev@Sun.COM for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { 24567805SKonstantin.Ananyev@Sun.COM if (ATH_TXQ_SETUP(asc, i)) { 24577805SKonstantin.Ananyev@Sun.COM ATH_HAL_STOPTXDMA(ah, asc->asc_txq[i].axq_qnum); 24587805SKonstantin.Ananyev@Sun.COM } 24597805SKonstantin.Ananyev@Sun.COM } 24607805SKonstantin.Ananyev@Sun.COM 24617805SKonstantin.Ananyev@Sun.COM /* 24627805SKonstantin.Ananyev@Sun.COM * Disable RX HW 24637805SKonstantin.Ananyev@Sun.COM */ 24647805SKonstantin.Ananyev@Sun.COM ATH_HAL_STOPPCURECV(ah); 24657805SKonstantin.Ananyev@Sun.COM ATH_HAL_SETRXFILTER(ah, 0); 24667805SKonstantin.Ananyev@Sun.COM ATH_HAL_STOPDMARECV(ah); 24677805SKonstantin.Ananyev@Sun.COM drv_usecwait(3000); 24687805SKonstantin.Ananyev@Sun.COM 24697805SKonstantin.Ananyev@Sun.COM /* 24707805SKonstantin.Ananyev@Sun.COM * Power down HW 24717805SKonstantin.Ananyev@Sun.COM */ 24727805SKonstantin.Ananyev@Sun.COM ATH_HAL_PHYDISABLE(ah); 24737805SKonstantin.Ananyev@Sun.COM 24747805SKonstantin.Ananyev@Sun.COM return (DDI_SUCCESS); 24757805SKonstantin.Ananyev@Sun.COM } 24767805SKonstantin.Ananyev@Sun.COM 24773147Sxc151355 DDI_DEFINE_STREAM_OPS(ath_dev_ops, nulldev, nulldev, ath_attach, ath_detach, 24787805SKonstantin.Ananyev@Sun.COM nodev, NULL, D_MP, NULL, ath_quiesce); 24791000Sxc151355 24801000Sxc151355 static struct modldrv ath_modldrv = { 24811000Sxc151355 &mod_driverops, /* Type of module. This one is a driver */ 2482*8033SWang.Lin@Sun.COM "ath driver 1.4/HAL 0.10.5.6", /* short description */ 24831000Sxc151355 &ath_dev_ops /* driver specific ops */ 24841000Sxc151355 }; 24851000Sxc151355 24861000Sxc151355 static struct modlinkage modlinkage = { 24871000Sxc151355 MODREV_1, (void *)&ath_modldrv, NULL 24881000Sxc151355 }; 24891000Sxc151355 24901000Sxc151355 24911000Sxc151355 int 24921000Sxc151355 _info(struct modinfo *modinfop) 24931000Sxc151355 { 24941000Sxc151355 return (mod_info(&modlinkage, modinfop)); 24951000Sxc151355 } 24961000Sxc151355 24971000Sxc151355 int 24981000Sxc151355 _init(void) 24991000Sxc151355 { 25001000Sxc151355 int status; 25011000Sxc151355 25021000Sxc151355 status = ddi_soft_state_init(&ath_soft_state_p, sizeof (ath_t), 1); 25031000Sxc151355 if (status != 0) 25041000Sxc151355 return (status); 25051000Sxc151355 25061000Sxc151355 mutex_init(&ath_loglock, NULL, MUTEX_DRIVER, NULL); 25073147Sxc151355 ath_halfix_init(); 25083147Sxc151355 mac_init_ops(&ath_dev_ops, "ath"); 25091000Sxc151355 status = mod_install(&modlinkage); 25101000Sxc151355 if (status != 0) { 25113147Sxc151355 mac_fini_ops(&ath_dev_ops); 25123147Sxc151355 ath_halfix_finit(); 25133147Sxc151355 mutex_destroy(&ath_loglock); 25141000Sxc151355 ddi_soft_state_fini(&ath_soft_state_p); 25151000Sxc151355 } 25161000Sxc151355 25171000Sxc151355 return (status); 25181000Sxc151355 } 25191000Sxc151355 25201000Sxc151355 int 25211000Sxc151355 _fini(void) 25221000Sxc151355 { 25231000Sxc151355 int status; 25241000Sxc151355 25251000Sxc151355 status = mod_remove(&modlinkage); 25261000Sxc151355 if (status == 0) { 25273147Sxc151355 mac_fini_ops(&ath_dev_ops); 25283147Sxc151355 ath_halfix_finit(); 25293147Sxc151355 mutex_destroy(&ath_loglock); 25301000Sxc151355 ddi_soft_state_fini(&ath_soft_state_p); 25311000Sxc151355 } 25321000Sxc151355 return (status); 25331000Sxc151355 } 2534