17a1306a7Sxc151355 /* 2*0dc2366fSVenugopal Iyer * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 37a1306a7Sxc151355 * Use is subject to license terms. 47a1306a7Sxc151355 */ 57a1306a7Sxc151355 67a1306a7Sxc151355 /* 77a1306a7Sxc151355 * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting 87a1306a7Sxc151355 * All rights reserved. 97a1306a7Sxc151355 * 107a1306a7Sxc151355 * Redistribution and use in source and binary forms, with or without 117a1306a7Sxc151355 * modification, are permitted provided that the following conditions 127a1306a7Sxc151355 * are met: 137a1306a7Sxc151355 * 1. Redistributions of source code must retain the above copyright 147a1306a7Sxc151355 * notice, this list of conditions and the following disclaimer, 157a1306a7Sxc151355 * without modification. 167a1306a7Sxc151355 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 177a1306a7Sxc151355 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 187a1306a7Sxc151355 * redistribution must be conditioned upon including a substantially 197a1306a7Sxc151355 * similar Disclaimer requirement for further binary redistribution. 207a1306a7Sxc151355 * 3. Neither the names of the above-listed copyright holders nor the names 217a1306a7Sxc151355 * of any contributors may be used to endorse or promote products derived 227a1306a7Sxc151355 * from this software without specific prior written permission. 237a1306a7Sxc151355 * 247a1306a7Sxc151355 * NO WARRANTY 257a1306a7Sxc151355 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 267a1306a7Sxc151355 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 277a1306a7Sxc151355 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 287a1306a7Sxc151355 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 297a1306a7Sxc151355 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 307a1306a7Sxc151355 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 317a1306a7Sxc151355 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 327a1306a7Sxc151355 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 337a1306a7Sxc151355 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 347a1306a7Sxc151355 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 357a1306a7Sxc151355 * THE POSSIBILITY OF SUCH DAMAGES. 367a1306a7Sxc151355 * 377a1306a7Sxc151355 */ 387a1306a7Sxc151355 397a1306a7Sxc151355 /* 407a1306a7Sxc151355 * Driver for the Atheros Wireless LAN controller. 417a1306a7Sxc151355 * 420ba2cbe9Sxc151355 * The Atheros driver calls into net80211 module for IEEE80211 protocol 430ba2cbe9Sxc151355 * management functionalities. The driver includes a LLD(Low Level Driver) 440ba2cbe9Sxc151355 * part to implement H/W related operations. 457a1306a7Sxc151355 * The following is the high level structure of ath driver. 467a1306a7Sxc151355 * (The arrows between modules indicate function call direction.) 477a1306a7Sxc151355 * 487a1306a7Sxc151355 * 490ba2cbe9Sxc151355 * | 500ba2cbe9Sxc151355 * | GLD thread 510ba2cbe9Sxc151355 * V 527a1306a7Sxc151355 * ================== ========================================= 530ba2cbe9Sxc151355 * | | |[1] | 540ba2cbe9Sxc151355 * | | | GLDv3 Callback functions registered | 550ba2cbe9Sxc151355 * | Net80211 | ========================= by | 560ba2cbe9Sxc151355 * | module | | | driver | 570ba2cbe9Sxc151355 * | | V | | 580ba2cbe9Sxc151355 * | |======================== | | 590ba2cbe9Sxc151355 * | Functions exported by net80211 | | | 600ba2cbe9Sxc151355 * | | | | 610ba2cbe9Sxc151355 * ========================================== ================= 620ba2cbe9Sxc151355 * | | 630ba2cbe9Sxc151355 * V | 640ba2cbe9Sxc151355 * +----------------------------------+ | 650ba2cbe9Sxc151355 * |[2] | | 660ba2cbe9Sxc151355 * | Net80211 Callback functions | | 670ba2cbe9Sxc151355 * | registered by LLD | | 680ba2cbe9Sxc151355 * +----------------------------------+ | 690ba2cbe9Sxc151355 * | | 700ba2cbe9Sxc151355 * V v 710ba2cbe9Sxc151355 * +-----------------------------------------------------------+ 720ba2cbe9Sxc151355 * |[3] | 737a1306a7Sxc151355 * | LLD Internal functions | 747a1306a7Sxc151355 * | | 750ba2cbe9Sxc151355 * +-----------------------------------------------------------+ 767a1306a7Sxc151355 * ^ 777a1306a7Sxc151355 * | Software interrupt thread 787a1306a7Sxc151355 * | 797a1306a7Sxc151355 * 807a1306a7Sxc151355 * The short description of each module is as below: 810ba2cbe9Sxc151355 * Module 1: GLD callback functions, which are intercepting the calls from 820ba2cbe9Sxc151355 * GLD to LLD. 830ba2cbe9Sxc151355 * Module 2: Net80211 callback functions registered by LLD, which 840ba2cbe9Sxc151355 * calls into LLD for H/W related functions needed by net80211. 850ba2cbe9Sxc151355 * Module 3: LLD Internal functions, which are responsible for allocing 867a1306a7Sxc151355 * descriptor/buffer, handling interrupt and other H/W 877a1306a7Sxc151355 * operations. 887a1306a7Sxc151355 * 897a1306a7Sxc151355 * All functions are running in 3 types of thread: 907a1306a7Sxc151355 * 1. GLD callbacks threads, such as ioctl, intr, etc. 910ba2cbe9Sxc151355 * 2. Clock interruptt thread which is responsible for scan, rate control and 920ba2cbe9Sxc151355 * calibration. 937a1306a7Sxc151355 * 3. Software Interrupt thread originated in LLD. 947a1306a7Sxc151355 * 957a1306a7Sxc151355 * The lock strategy is as below: 967a1306a7Sxc151355 * There have 4 queues for tx, each queue has one asc_txqlock[i] to 977a1306a7Sxc151355 * prevent conflicts access to queue resource from different thread. 987a1306a7Sxc151355 * 997a1306a7Sxc151355 * All the transmit buffers are contained in asc_txbuf which are 1007a1306a7Sxc151355 * protected by asc_txbuflock. 1017a1306a7Sxc151355 * 1027a1306a7Sxc151355 * Each receive buffers are contained in asc_rxbuf which are protected 1037a1306a7Sxc151355 * by asc_rxbuflock. 1047a1306a7Sxc151355 * 1057a1306a7Sxc151355 * In ath struct, asc_genlock is a general lock, protecting most other 1067a1306a7Sxc151355 * operational data in ath_softc struct and HAL accesses. 1077a1306a7Sxc151355 * It is acquired by the interupt handler and most "mode-ctrl" routines. 1087a1306a7Sxc151355 * 1097a1306a7Sxc151355 * Any of the locks can be acquired singly, but where multiple 1107a1306a7Sxc151355 * locks are acquired, they *must* be in the order: 1110ba2cbe9Sxc151355 * asc_genlock >> asc_txqlock[i] >> asc_txbuflock >> asc_rxbuflock 1127a1306a7Sxc151355 */ 1137a1306a7Sxc151355 1147a1306a7Sxc151355 #include <sys/param.h> 1157a1306a7Sxc151355 #include <sys/types.h> 1167a1306a7Sxc151355 #include <sys/signal.h> 1177a1306a7Sxc151355 #include <sys/stream.h> 1187a1306a7Sxc151355 #include <sys/termio.h> 1197a1306a7Sxc151355 #include <sys/errno.h> 1207a1306a7Sxc151355 #include <sys/file.h> 1217a1306a7Sxc151355 #include <sys/cmn_err.h> 1227a1306a7Sxc151355 #include <sys/stropts.h> 1237a1306a7Sxc151355 #include <sys/strsubr.h> 1247a1306a7Sxc151355 #include <sys/strtty.h> 1257a1306a7Sxc151355 #include <sys/kbio.h> 1267a1306a7Sxc151355 #include <sys/cred.h> 1277a1306a7Sxc151355 #include <sys/stat.h> 1287a1306a7Sxc151355 #include <sys/consdev.h> 1297a1306a7Sxc151355 #include <sys/kmem.h> 1307a1306a7Sxc151355 #include <sys/modctl.h> 1317a1306a7Sxc151355 #include <sys/ddi.h> 1327a1306a7Sxc151355 #include <sys/sunddi.h> 1337a1306a7Sxc151355 #include <sys/pci.h> 1347a1306a7Sxc151355 #include <sys/errno.h> 135da14cebeSEric Cheng #include <sys/mac_provider.h> 1367a1306a7Sxc151355 #include <sys/dlpi.h> 1377a1306a7Sxc151355 #include <sys/ethernet.h> 1387a1306a7Sxc151355 #include <sys/list.h> 1397a1306a7Sxc151355 #include <sys/byteorder.h> 1407a1306a7Sxc151355 #include <sys/strsun.h> 1417a1306a7Sxc151355 #include <sys/policy.h> 1427a1306a7Sxc151355 #include <inet/common.h> 1437a1306a7Sxc151355 #include <inet/nd.h> 1447a1306a7Sxc151355 #include <inet/mi.h> 1457a1306a7Sxc151355 #include <inet/wifi_ioctl.h> 1460ba2cbe9Sxc151355 #include <sys/mac_wifi.h> 1477a1306a7Sxc151355 #include "ath_hal.h" 1487a1306a7Sxc151355 #include "ath_impl.h" 1497a1306a7Sxc151355 #include "ath_aux.h" 1507a1306a7Sxc151355 #include "ath_rate.h" 1517a1306a7Sxc151355 1520ba2cbe9Sxc151355 #define ATH_MAX_RSSI 63 /* max rssi */ 1530ba2cbe9Sxc151355 1547a1306a7Sxc151355 extern void ath_halfix_init(void); 1557a1306a7Sxc151355 extern void ath_halfix_finit(void); 1567a1306a7Sxc151355 extern int32_t ath_getset(ath_t *asc, mblk_t *mp, uint32_t cmd); 1577a1306a7Sxc151355 1587a1306a7Sxc151355 /* 1597a1306a7Sxc151355 * PIO access attributes for registers 1607a1306a7Sxc151355 */ 1617a1306a7Sxc151355 static ddi_device_acc_attr_t ath_reg_accattr = { 1627a1306a7Sxc151355 DDI_DEVICE_ATTR_V0, 1637a1306a7Sxc151355 DDI_STRUCTURE_LE_ACC, 1647a1306a7Sxc151355 DDI_STRICTORDER_ACC 1657a1306a7Sxc151355 }; 1667a1306a7Sxc151355 1677a1306a7Sxc151355 /* 1687a1306a7Sxc151355 * DMA access attributes for descriptors: NOT to be byte swapped. 1697a1306a7Sxc151355 */ 1707a1306a7Sxc151355 static ddi_device_acc_attr_t ath_desc_accattr = { 1717a1306a7Sxc151355 DDI_DEVICE_ATTR_V0, 1727a1306a7Sxc151355 DDI_STRUCTURE_LE_ACC, 1737a1306a7Sxc151355 DDI_STRICTORDER_ACC 1747a1306a7Sxc151355 }; 1757a1306a7Sxc151355 1767a1306a7Sxc151355 /* 177129d67acSlin wang - Sun Microsystems - Beijing China * DMA attributes for rx/tx buffers 1787a1306a7Sxc151355 */ 1793caf1114Sxc151355 static ddi_dma_attr_t ath_dma_attr = { 1803caf1114Sxc151355 DMA_ATTR_V0, /* version number */ 1813caf1114Sxc151355 0, /* low address */ 1823caf1114Sxc151355 0xffffffffU, /* high address */ 1833caf1114Sxc151355 0x3ffffU, /* counter register max */ 1843caf1114Sxc151355 1, /* alignment */ 1853caf1114Sxc151355 0xFFF, /* burst sizes */ 1863caf1114Sxc151355 1, /* minimum transfer size */ 1873caf1114Sxc151355 0x3ffffU, /* max transfer size */ 1883caf1114Sxc151355 0xffffffffU, /* address register max */ 1893caf1114Sxc151355 1, /* no scatter-gather */ 1903caf1114Sxc151355 1, /* granularity of device */ 1913caf1114Sxc151355 0, /* DMA flags */ 1923caf1114Sxc151355 }; 1933caf1114Sxc151355 1943caf1114Sxc151355 static ddi_dma_attr_t ath_desc_dma_attr = { 1953caf1114Sxc151355 DMA_ATTR_V0, /* version number */ 1963caf1114Sxc151355 0, /* low address */ 1973caf1114Sxc151355 0xffffffffU, /* high address */ 1983caf1114Sxc151355 0xffffffffU, /* counter register max */ 1993caf1114Sxc151355 0x1000, /* alignment */ 2003caf1114Sxc151355 0xFFF, /* burst sizes */ 2013caf1114Sxc151355 1, /* minimum transfer size */ 2023caf1114Sxc151355 0xffffffffU, /* max transfer size */ 2033caf1114Sxc151355 0xffffffffU, /* address register max */ 2043caf1114Sxc151355 1, /* no scatter-gather */ 2053caf1114Sxc151355 1, /* granularity of device */ 2063caf1114Sxc151355 0, /* DMA flags */ 2077a1306a7Sxc151355 }; 2087a1306a7Sxc151355 2097a1306a7Sxc151355 static kmutex_t ath_loglock; 2107a1306a7Sxc151355 static void *ath_soft_state_p = NULL; 2110ba2cbe9Sxc151355 static int ath_dwelltime = 150; /* scan interval, ms */ 2120ba2cbe9Sxc151355 2130ba2cbe9Sxc151355 static int ath_m_stat(void *, uint_t, uint64_t *); 2140ba2cbe9Sxc151355 static int ath_m_start(void *); 2150ba2cbe9Sxc151355 static void ath_m_stop(void *); 2160ba2cbe9Sxc151355 static int ath_m_promisc(void *, boolean_t); 2170ba2cbe9Sxc151355 static int ath_m_multicst(void *, boolean_t, const uint8_t *); 2180ba2cbe9Sxc151355 static int ath_m_unicst(void *, const uint8_t *); 2190ba2cbe9Sxc151355 static mblk_t *ath_m_tx(void *, mblk_t *); 2200ba2cbe9Sxc151355 static void ath_m_ioctl(void *, queue_t *, mblk_t *); 221bcb5c89dSSowmini Varadhan static int ath_m_setprop(void *, const char *, mac_prop_id_t, 222bcb5c89dSSowmini Varadhan uint_t, const void *); 223bcb5c89dSSowmini Varadhan static int ath_m_getprop(void *, const char *, mac_prop_id_t, 224*0dc2366fSVenugopal Iyer uint_t, void *); 225*0dc2366fSVenugopal Iyer static void ath_m_propinfo(void *, const char *, mac_prop_id_t, 226*0dc2366fSVenugopal Iyer mac_prop_info_handle_t); 227bcb5c89dSSowmini Varadhan 2280ba2cbe9Sxc151355 static mac_callbacks_t ath_m_callbacks = { 229*0dc2366fSVenugopal Iyer MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO, 2300ba2cbe9Sxc151355 ath_m_stat, 2310ba2cbe9Sxc151355 ath_m_start, 2320ba2cbe9Sxc151355 ath_m_stop, 2330ba2cbe9Sxc151355 ath_m_promisc, 2340ba2cbe9Sxc151355 ath_m_multicst, 2350ba2cbe9Sxc151355 ath_m_unicst, 2360ba2cbe9Sxc151355 ath_m_tx, 237*0dc2366fSVenugopal Iyer NULL, 2380ba2cbe9Sxc151355 ath_m_ioctl, 239bcb5c89dSSowmini Varadhan NULL, /* mc_getcapab */ 240bcb5c89dSSowmini Varadhan NULL, 241bcb5c89dSSowmini Varadhan NULL, 242bcb5c89dSSowmini Varadhan ath_m_setprop, 243*0dc2366fSVenugopal Iyer ath_m_getprop, 244*0dc2366fSVenugopal Iyer ath_m_propinfo 2450ba2cbe9Sxc151355 }; 2467a1306a7Sxc151355 2477a1306a7Sxc151355 /* 2487a1306a7Sxc151355 * Available debug flags: 2497a1306a7Sxc151355 * ATH_DBG_INIT, ATH_DBG_GLD, ATH_DBG_HAL, ATH_DBG_INT, ATH_DBG_ATTACH, 2507a1306a7Sxc151355 * ATH_DBG_DETACH, ATH_DBG_AUX, ATH_DBG_WIFICFG, ATH_DBG_OSDEP 2517a1306a7Sxc151355 */ 2527a1306a7Sxc151355 uint32_t ath_dbg_flags = 0; 2537a1306a7Sxc151355 2547a1306a7Sxc151355 /* 2557a1306a7Sxc151355 * Exception/warning cases not leading to panic. 2567a1306a7Sxc151355 */ 2577a1306a7Sxc151355 void 2587a1306a7Sxc151355 ath_problem(const int8_t *fmt, ...) 2597a1306a7Sxc151355 { 2607a1306a7Sxc151355 va_list args; 2617a1306a7Sxc151355 2627a1306a7Sxc151355 mutex_enter(&ath_loglock); 2637a1306a7Sxc151355 2647a1306a7Sxc151355 va_start(args, fmt); 2657a1306a7Sxc151355 vcmn_err(CE_WARN, fmt, args); 2667a1306a7Sxc151355 va_end(args); 2677a1306a7Sxc151355 2687a1306a7Sxc151355 mutex_exit(&ath_loglock); 2697a1306a7Sxc151355 } 2707a1306a7Sxc151355 2717a1306a7Sxc151355 /* 2727a1306a7Sxc151355 * Normal log information independent of debug. 2737a1306a7Sxc151355 */ 2747a1306a7Sxc151355 void 2757a1306a7Sxc151355 ath_log(const int8_t *fmt, ...) 2767a1306a7Sxc151355 { 2777a1306a7Sxc151355 va_list args; 2787a1306a7Sxc151355 2797a1306a7Sxc151355 mutex_enter(&ath_loglock); 2807a1306a7Sxc151355 2817a1306a7Sxc151355 va_start(args, fmt); 2827a1306a7Sxc151355 vcmn_err(CE_CONT, fmt, args); 2837a1306a7Sxc151355 va_end(args); 2847a1306a7Sxc151355 2857a1306a7Sxc151355 mutex_exit(&ath_loglock); 2867a1306a7Sxc151355 } 2877a1306a7Sxc151355 2887a1306a7Sxc151355 void 2897a1306a7Sxc151355 ath_dbg(uint32_t dbg_flags, const int8_t *fmt, ...) 2907a1306a7Sxc151355 { 2917a1306a7Sxc151355 va_list args; 2927a1306a7Sxc151355 2937a1306a7Sxc151355 if (dbg_flags & ath_dbg_flags) { 2947a1306a7Sxc151355 mutex_enter(&ath_loglock); 2957a1306a7Sxc151355 va_start(args, fmt); 2967a1306a7Sxc151355 vcmn_err(CE_CONT, fmt, args); 2977a1306a7Sxc151355 va_end(args); 2987a1306a7Sxc151355 mutex_exit(&ath_loglock); 2997a1306a7Sxc151355 } 3007a1306a7Sxc151355 } 3017a1306a7Sxc151355 3027a1306a7Sxc151355 void 3037a1306a7Sxc151355 ath_setup_desc(ath_t *asc, struct ath_buf *bf) 3047a1306a7Sxc151355 { 3057a1306a7Sxc151355 struct ath_desc *ds; 3067a1306a7Sxc151355 3077a1306a7Sxc151355 ds = bf->bf_desc; 3087a1306a7Sxc151355 ds->ds_link = bf->bf_daddr; 3097a1306a7Sxc151355 ds->ds_data = bf->bf_dma.cookie.dmac_address; 3107a1306a7Sxc151355 ATH_HAL_SETUPRXDESC(asc->asc_ah, ds, 3117a1306a7Sxc151355 bf->bf_dma.alength, /* buffer size */ 3127a1306a7Sxc151355 0); 3137a1306a7Sxc151355 3147a1306a7Sxc151355 if (asc->asc_rxlink != NULL) 3157a1306a7Sxc151355 *asc->asc_rxlink = bf->bf_daddr; 3167a1306a7Sxc151355 asc->asc_rxlink = &ds->ds_link; 3177a1306a7Sxc151355 } 3187a1306a7Sxc151355 3197a1306a7Sxc151355 3207a1306a7Sxc151355 /* 3217a1306a7Sxc151355 * Allocate an area of memory and a DMA handle for accessing it 3227a1306a7Sxc151355 */ 3237a1306a7Sxc151355 static int 3243caf1114Sxc151355 ath_alloc_dma_mem(dev_info_t *devinfo, ddi_dma_attr_t *dma_attr, size_t memsize, 3257a1306a7Sxc151355 ddi_device_acc_attr_t *attr_p, uint_t alloc_flags, 3267a1306a7Sxc151355 uint_t bind_flags, dma_area_t *dma_p) 3277a1306a7Sxc151355 { 3287a1306a7Sxc151355 int err; 3297a1306a7Sxc151355 3307a1306a7Sxc151355 /* 3317a1306a7Sxc151355 * Allocate handle 3327a1306a7Sxc151355 */ 3333caf1114Sxc151355 err = ddi_dma_alloc_handle(devinfo, dma_attr, 3347a1306a7Sxc151355 DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl); 3357a1306a7Sxc151355 if (err != DDI_SUCCESS) 3367a1306a7Sxc151355 return (DDI_FAILURE); 3377a1306a7Sxc151355 3387a1306a7Sxc151355 /* 3397a1306a7Sxc151355 * Allocate memory 3407a1306a7Sxc151355 */ 3417a1306a7Sxc151355 err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize, attr_p, 3427a1306a7Sxc151355 alloc_flags, DDI_DMA_SLEEP, NULL, &dma_p->mem_va, 3437a1306a7Sxc151355 &dma_p->alength, &dma_p->acc_hdl); 3447a1306a7Sxc151355 if (err != DDI_SUCCESS) 3457a1306a7Sxc151355 return (DDI_FAILURE); 3467a1306a7Sxc151355 3477a1306a7Sxc151355 /* 3487a1306a7Sxc151355 * Bind the two together 3497a1306a7Sxc151355 */ 3507a1306a7Sxc151355 err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL, 3517a1306a7Sxc151355 dma_p->mem_va, dma_p->alength, bind_flags, 3527a1306a7Sxc151355 DDI_DMA_SLEEP, NULL, &dma_p->cookie, &dma_p->ncookies); 3537a1306a7Sxc151355 if (err != DDI_DMA_MAPPED) 3547a1306a7Sxc151355 return (DDI_FAILURE); 3557a1306a7Sxc151355 3567a1306a7Sxc151355 dma_p->nslots = ~0U; 3577a1306a7Sxc151355 dma_p->size = ~0U; 3587a1306a7Sxc151355 dma_p->token = ~0U; 3597a1306a7Sxc151355 dma_p->offset = 0; 3607a1306a7Sxc151355 return (DDI_SUCCESS); 3617a1306a7Sxc151355 } 3627a1306a7Sxc151355 3637a1306a7Sxc151355 /* 3647a1306a7Sxc151355 * Free one allocated area of DMAable memory 3657a1306a7Sxc151355 */ 3667a1306a7Sxc151355 static void 3677a1306a7Sxc151355 ath_free_dma_mem(dma_area_t *dma_p) 3687a1306a7Sxc151355 { 3697a1306a7Sxc151355 if (dma_p->dma_hdl != NULL) { 3707a1306a7Sxc151355 (void) ddi_dma_unbind_handle(dma_p->dma_hdl); 3717a1306a7Sxc151355 if (dma_p->acc_hdl != NULL) { 3727a1306a7Sxc151355 ddi_dma_mem_free(&dma_p->acc_hdl); 3737a1306a7Sxc151355 dma_p->acc_hdl = NULL; 3747a1306a7Sxc151355 } 3757a1306a7Sxc151355 ddi_dma_free_handle(&dma_p->dma_hdl); 3767a1306a7Sxc151355 dma_p->ncookies = 0; 3777a1306a7Sxc151355 dma_p->dma_hdl = NULL; 3787a1306a7Sxc151355 } 3797a1306a7Sxc151355 } 3807a1306a7Sxc151355 3817a1306a7Sxc151355 382129d67acSlin wang - Sun Microsystems - Beijing China /* 383129d67acSlin wang - Sun Microsystems - Beijing China * Initialize tx/rx buffer list. Allocate DMA memory for 384129d67acSlin wang - Sun Microsystems - Beijing China * each buffer. 385129d67acSlin wang - Sun Microsystems - Beijing China */ 386129d67acSlin wang - Sun Microsystems - Beijing China static int 387129d67acSlin wang - Sun Microsystems - Beijing China ath_buflist_setup(dev_info_t *devinfo, ath_t *asc, list_t *bflist, 388129d67acSlin wang - Sun Microsystems - Beijing China struct ath_buf **pbf, struct ath_desc **pds, int nbuf, uint_t dmabflags) 389129d67acSlin wang - Sun Microsystems - Beijing China { 390129d67acSlin wang - Sun Microsystems - Beijing China int i, err; 391129d67acSlin wang - Sun Microsystems - Beijing China struct ath_buf *bf = *pbf; 392129d67acSlin wang - Sun Microsystems - Beijing China struct ath_desc *ds = *pds; 393129d67acSlin wang - Sun Microsystems - Beijing China 394129d67acSlin wang - Sun Microsystems - Beijing China list_create(bflist, sizeof (struct ath_buf), 395129d67acSlin wang - Sun Microsystems - Beijing China offsetof(struct ath_buf, bf_node)); 396129d67acSlin wang - Sun Microsystems - Beijing China for (i = 0; i < nbuf; i++, bf++, ds++) { 397129d67acSlin wang - Sun Microsystems - Beijing China bf->bf_desc = ds; 398129d67acSlin wang - Sun Microsystems - Beijing China bf->bf_daddr = asc->asc_desc_dma.cookie.dmac_address + 399129d67acSlin wang - Sun Microsystems - Beijing China ((uintptr_t)ds - (uintptr_t)asc->asc_desc); 400129d67acSlin wang - Sun Microsystems - Beijing China list_insert_tail(bflist, bf); 401129d67acSlin wang - Sun Microsystems - Beijing China 402129d67acSlin wang - Sun Microsystems - Beijing China /* alloc DMA memory */ 403129d67acSlin wang - Sun Microsystems - Beijing China err = ath_alloc_dma_mem(devinfo, &ath_dma_attr, 404129d67acSlin wang - Sun Microsystems - Beijing China asc->asc_dmabuf_size, &ath_desc_accattr, DDI_DMA_STREAMING, 405129d67acSlin wang - Sun Microsystems - Beijing China dmabflags, &bf->bf_dma); 406129d67acSlin wang - Sun Microsystems - Beijing China if (err != DDI_SUCCESS) 407129d67acSlin wang - Sun Microsystems - Beijing China return (err); 408129d67acSlin wang - Sun Microsystems - Beijing China } 409129d67acSlin wang - Sun Microsystems - Beijing China *pbf = bf; 410129d67acSlin wang - Sun Microsystems - Beijing China *pds = ds; 411129d67acSlin wang - Sun Microsystems - Beijing China 412129d67acSlin wang - Sun Microsystems - Beijing China return (DDI_SUCCESS); 413129d67acSlin wang - Sun Microsystems - Beijing China } 414129d67acSlin wang - Sun Microsystems - Beijing China 415129d67acSlin wang - Sun Microsystems - Beijing China /* 416129d67acSlin wang - Sun Microsystems - Beijing China * Destroy tx/rx buffer list. Free DMA memory. 417129d67acSlin wang - Sun Microsystems - Beijing China */ 418129d67acSlin wang - Sun Microsystems - Beijing China static void 419129d67acSlin wang - Sun Microsystems - Beijing China ath_buflist_cleanup(list_t *buflist) 420129d67acSlin wang - Sun Microsystems - Beijing China { 421129d67acSlin wang - Sun Microsystems - Beijing China struct ath_buf *bf; 422129d67acSlin wang - Sun Microsystems - Beijing China 423129d67acSlin wang - Sun Microsystems - Beijing China if (!buflist) 424129d67acSlin wang - Sun Microsystems - Beijing China return; 425129d67acSlin wang - Sun Microsystems - Beijing China 426129d67acSlin wang - Sun Microsystems - Beijing China bf = list_head(buflist); 427129d67acSlin wang - Sun Microsystems - Beijing China while (bf != NULL) { 428129d67acSlin wang - Sun Microsystems - Beijing China if (bf->bf_m != NULL) { 429129d67acSlin wang - Sun Microsystems - Beijing China freemsg(bf->bf_m); 430129d67acSlin wang - Sun Microsystems - Beijing China bf->bf_m = NULL; 431129d67acSlin wang - Sun Microsystems - Beijing China } 432129d67acSlin wang - Sun Microsystems - Beijing China /* Free DMA buffer */ 433129d67acSlin wang - Sun Microsystems - Beijing China ath_free_dma_mem(&bf->bf_dma); 434129d67acSlin wang - Sun Microsystems - Beijing China if (bf->bf_in != NULL) { 435129d67acSlin wang - Sun Microsystems - Beijing China ieee80211_free_node(bf->bf_in); 436129d67acSlin wang - Sun Microsystems - Beijing China bf->bf_in = NULL; 437129d67acSlin wang - Sun Microsystems - Beijing China } 438129d67acSlin wang - Sun Microsystems - Beijing China list_remove(buflist, bf); 439129d67acSlin wang - Sun Microsystems - Beijing China bf = list_head(buflist); 440129d67acSlin wang - Sun Microsystems - Beijing China } 441129d67acSlin wang - Sun Microsystems - Beijing China list_destroy(buflist); 442129d67acSlin wang - Sun Microsystems - Beijing China } 443129d67acSlin wang - Sun Microsystems - Beijing China 444129d67acSlin wang - Sun Microsystems - Beijing China 445129d67acSlin wang - Sun Microsystems - Beijing China static void 446129d67acSlin wang - Sun Microsystems - Beijing China ath_desc_free(ath_t *asc) 447129d67acSlin wang - Sun Microsystems - Beijing China { 448129d67acSlin wang - Sun Microsystems - Beijing China ath_buflist_cleanup(&asc->asc_txbuf_list); 449129d67acSlin wang - Sun Microsystems - Beijing China ath_buflist_cleanup(&asc->asc_rxbuf_list); 450129d67acSlin wang - Sun Microsystems - Beijing China 451129d67acSlin wang - Sun Microsystems - Beijing China /* Free descriptor DMA buffer */ 452129d67acSlin wang - Sun Microsystems - Beijing China ath_free_dma_mem(&asc->asc_desc_dma); 453129d67acSlin wang - Sun Microsystems - Beijing China 454129d67acSlin wang - Sun Microsystems - Beijing China kmem_free((void *)asc->asc_vbufptr, asc->asc_vbuflen); 455129d67acSlin wang - Sun Microsystems - Beijing China asc->asc_vbufptr = NULL; 456129d67acSlin wang - Sun Microsystems - Beijing China } 457129d67acSlin wang - Sun Microsystems - Beijing China 4587a1306a7Sxc151355 static int 4597a1306a7Sxc151355 ath_desc_alloc(dev_info_t *devinfo, ath_t *asc) 4607a1306a7Sxc151355 { 461129d67acSlin wang - Sun Microsystems - Beijing China int err; 4627a1306a7Sxc151355 size_t size; 4637a1306a7Sxc151355 struct ath_desc *ds; 4647a1306a7Sxc151355 struct ath_buf *bf; 4657a1306a7Sxc151355 4667a1306a7Sxc151355 size = sizeof (struct ath_desc) * (ATH_TXBUF + ATH_RXBUF); 4677a1306a7Sxc151355 4683caf1114Sxc151355 err = ath_alloc_dma_mem(devinfo, &ath_desc_dma_attr, size, 4693caf1114Sxc151355 &ath_desc_accattr, DDI_DMA_CONSISTENT, 4703caf1114Sxc151355 DDI_DMA_RDWR | DDI_DMA_CONSISTENT, &asc->asc_desc_dma); 4717a1306a7Sxc151355 4727a1306a7Sxc151355 /* virtual address of the first descriptor */ 4737a1306a7Sxc151355 asc->asc_desc = (struct ath_desc *)asc->asc_desc_dma.mem_va; 4747a1306a7Sxc151355 4757a1306a7Sxc151355 ds = asc->asc_desc; 4767a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_INIT, "ath: ath_desc_alloc(): DMA map: " 4777a1306a7Sxc151355 "%p (%d) -> %p\n", 4787a1306a7Sxc151355 asc->asc_desc, asc->asc_desc_dma.alength, 4797a1306a7Sxc151355 asc->asc_desc_dma.cookie.dmac_address)); 4807a1306a7Sxc151355 4817a1306a7Sxc151355 /* allocate data structures to describe TX/RX DMA buffers */ 4827a1306a7Sxc151355 asc->asc_vbuflen = sizeof (struct ath_buf) * (ATH_TXBUF + ATH_RXBUF); 4837a1306a7Sxc151355 bf = (struct ath_buf *)kmem_zalloc(asc->asc_vbuflen, KM_SLEEP); 4847a1306a7Sxc151355 asc->asc_vbufptr = bf; 4857a1306a7Sxc151355 4867a1306a7Sxc151355 /* DMA buffer size for each TX/RX packet */ 4877a1306a7Sxc151355 asc->asc_dmabuf_size = roundup(1000 + sizeof (struct ieee80211_frame) + 4887a1306a7Sxc151355 IEEE80211_MTU + IEEE80211_CRC_LEN + 4897a1306a7Sxc151355 (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + 4907a1306a7Sxc151355 IEEE80211_WEP_CRCLEN), asc->asc_cachelsz); 4917a1306a7Sxc151355 492129d67acSlin wang - Sun Microsystems - Beijing China /* create RX buffer list */ 493129d67acSlin wang - Sun Microsystems - Beijing China err = ath_buflist_setup(devinfo, asc, &asc->asc_rxbuf_list, &bf, &ds, 494129d67acSlin wang - Sun Microsystems - Beijing China ATH_RXBUF, DDI_DMA_READ | DDI_DMA_STREAMING); 495129d67acSlin wang - Sun Microsystems - Beijing China if (err != DDI_SUCCESS) { 496129d67acSlin wang - Sun Microsystems - Beijing China ath_desc_free(asc); 4977a1306a7Sxc151355 return (err); 4987a1306a7Sxc151355 } 4997a1306a7Sxc151355 500129d67acSlin wang - Sun Microsystems - Beijing China /* create TX buffer list */ 501129d67acSlin wang - Sun Microsystems - Beijing China err = ath_buflist_setup(devinfo, asc, &asc->asc_txbuf_list, &bf, &ds, 502129d67acSlin wang - Sun Microsystems - Beijing China ATH_TXBUF, DDI_DMA_STREAMING); 503129d67acSlin wang - Sun Microsystems - Beijing China if (err != DDI_SUCCESS) { 504129d67acSlin wang - Sun Microsystems - Beijing China ath_desc_free(asc); 5057a1306a7Sxc151355 return (err); 5067a1306a7Sxc151355 } 5077a1306a7Sxc151355 508129d67acSlin wang - Sun Microsystems - Beijing China 5097a1306a7Sxc151355 return (DDI_SUCCESS); 5107a1306a7Sxc151355 } 5117a1306a7Sxc151355 5127a1306a7Sxc151355 static void 5137a1306a7Sxc151355 ath_printrxbuf(struct ath_buf *bf, int32_t done) 5147a1306a7Sxc151355 { 5157a1306a7Sxc151355 struct ath_desc *ds = bf->bf_desc; 516129d67acSlin wang - Sun Microsystems - Beijing China const struct ath_rx_status *rs = &bf->bf_status.ds_rxstat; 5177a1306a7Sxc151355 5187a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_RECV, "ath: R (%p %p) %08x %08x %08x " 5197a1306a7Sxc151355 "%08x %08x %08x %c\n", 5207a1306a7Sxc151355 ds, bf->bf_daddr, 5217a1306a7Sxc151355 ds->ds_link, ds->ds_data, 5227a1306a7Sxc151355 ds->ds_ctl0, ds->ds_ctl1, 5237a1306a7Sxc151355 ds->ds_hw[0], ds->ds_hw[1], 524129d67acSlin wang - Sun Microsystems - Beijing China !done ? ' ' : (rs->rs_status == 0) ? '*' : '!')); 5257a1306a7Sxc151355 } 5267a1306a7Sxc151355 5277a1306a7Sxc151355 static void 5287a1306a7Sxc151355 ath_rx_handler(ath_t *asc) 5297a1306a7Sxc151355 { 5300ba2cbe9Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc; 5317a1306a7Sxc151355 struct ath_buf *bf; 5327a1306a7Sxc151355 struct ath_hal *ah = asc->asc_ah; 5337a1306a7Sxc151355 struct ath_desc *ds; 534129d67acSlin wang - Sun Microsystems - Beijing China struct ath_rx_status *rs; 5357a1306a7Sxc151355 mblk_t *rx_mp; 5360ba2cbe9Sxc151355 struct ieee80211_frame *wh; 5377a1306a7Sxc151355 int32_t len, loop = 1; 5387a1306a7Sxc151355 uint8_t phyerr; 5397a1306a7Sxc151355 HAL_STATUS status; 5407a1306a7Sxc151355 HAL_NODE_STATS hal_node_stats; 5410ba2cbe9Sxc151355 struct ieee80211_node *in; 5427a1306a7Sxc151355 5437a1306a7Sxc151355 do { 5447a1306a7Sxc151355 mutex_enter(&asc->asc_rxbuflock); 5457a1306a7Sxc151355 bf = list_head(&asc->asc_rxbuf_list); 5467a1306a7Sxc151355 if (bf == NULL) { 5477a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_RECV, "ath: ath_rx_handler(): " 5487a1306a7Sxc151355 "no buffer\n")); 5497a1306a7Sxc151355 mutex_exit(&asc->asc_rxbuflock); 5507a1306a7Sxc151355 break; 5517a1306a7Sxc151355 } 5527a1306a7Sxc151355 ASSERT(bf->bf_dma.cookie.dmac_address != NULL); 5537a1306a7Sxc151355 ds = bf->bf_desc; 5547a1306a7Sxc151355 if (ds->ds_link == bf->bf_daddr) { 5557a1306a7Sxc151355 /* 5567a1306a7Sxc151355 * Never process the self-linked entry at the end, 5577a1306a7Sxc151355 * this may be met at heavy load. 5587a1306a7Sxc151355 */ 5597a1306a7Sxc151355 mutex_exit(&asc->asc_rxbuflock); 5607a1306a7Sxc151355 break; 5617a1306a7Sxc151355 } 5627a1306a7Sxc151355 563129d67acSlin wang - Sun Microsystems - Beijing China rs = &bf->bf_status.ds_rxstat; 5647a1306a7Sxc151355 status = ATH_HAL_RXPROCDESC(ah, ds, 5657a1306a7Sxc151355 bf->bf_daddr, 566129d67acSlin wang - Sun Microsystems - Beijing China ATH_PA2DESC(asc, ds->ds_link), rs); 5677a1306a7Sxc151355 if (status == HAL_EINPROGRESS) { 5687a1306a7Sxc151355 mutex_exit(&asc->asc_rxbuflock); 5697a1306a7Sxc151355 break; 5707a1306a7Sxc151355 } 5717a1306a7Sxc151355 list_remove(&asc->asc_rxbuf_list, bf); 5727a1306a7Sxc151355 mutex_exit(&asc->asc_rxbuflock); 5737a1306a7Sxc151355 574129d67acSlin wang - Sun Microsystems - Beijing China if (rs->rs_status != 0) { 575129d67acSlin wang - Sun Microsystems - Beijing China if (rs->rs_status & HAL_RXERR_CRC) 5767a1306a7Sxc151355 asc->asc_stats.ast_rx_crcerr++; 577129d67acSlin wang - Sun Microsystems - Beijing China if (rs->rs_status & HAL_RXERR_FIFO) 5787a1306a7Sxc151355 asc->asc_stats.ast_rx_fifoerr++; 579129d67acSlin wang - Sun Microsystems - Beijing China if (rs->rs_status & HAL_RXERR_DECRYPT) 5807a1306a7Sxc151355 asc->asc_stats.ast_rx_badcrypt++; 581129d67acSlin wang - Sun Microsystems - Beijing China if (rs->rs_status & HAL_RXERR_PHY) { 5827a1306a7Sxc151355 asc->asc_stats.ast_rx_phyerr++; 583129d67acSlin wang - Sun Microsystems - Beijing China phyerr = rs->rs_phyerr & 0x1f; 5847a1306a7Sxc151355 asc->asc_stats.ast_rx_phy[phyerr]++; 5857a1306a7Sxc151355 } 5867a1306a7Sxc151355 goto rx_next; 5877a1306a7Sxc151355 } 588129d67acSlin wang - Sun Microsystems - Beijing China len = rs->rs_datalen; 5897a1306a7Sxc151355 5907a1306a7Sxc151355 /* less than sizeof(struct ieee80211_frame) */ 5917a1306a7Sxc151355 if (len < 20) { 5927a1306a7Sxc151355 asc->asc_stats.ast_rx_tooshort++; 5937a1306a7Sxc151355 goto rx_next; 5947a1306a7Sxc151355 } 5957a1306a7Sxc151355 5967a1306a7Sxc151355 if ((rx_mp = allocb(asc->asc_dmabuf_size, BPRI_MED)) == NULL) { 5977a1306a7Sxc151355 ath_problem("ath: ath_rx_handler(): " 5987a1306a7Sxc151355 "allocing mblk buffer failed.\n"); 5997a1306a7Sxc151355 return; 6007a1306a7Sxc151355 } 6017a1306a7Sxc151355 6027a1306a7Sxc151355 ATH_DMA_SYNC(bf->bf_dma, DDI_DMA_SYNC_FORCPU); 6037a1306a7Sxc151355 bcopy(bf->bf_dma.mem_va, rx_mp->b_rptr, len); 6047a1306a7Sxc151355 6057a1306a7Sxc151355 rx_mp->b_wptr += len; 6067a1306a7Sxc151355 wh = (struct ieee80211_frame *)rx_mp->b_rptr; 6070ba2cbe9Sxc151355 if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == 6087a1306a7Sxc151355 IEEE80211_FC0_TYPE_CTL) { 6097a1306a7Sxc151355 /* 6107a1306a7Sxc151355 * Ignore control frame received in promisc mode. 6117a1306a7Sxc151355 */ 6127a1306a7Sxc151355 freemsg(rx_mp); 6137a1306a7Sxc151355 goto rx_next; 6147a1306a7Sxc151355 } 6157a1306a7Sxc151355 /* Remove the CRC at the end of IEEE80211 frame */ 6167a1306a7Sxc151355 rx_mp->b_wptr -= IEEE80211_CRC_LEN; 6177a1306a7Sxc151355 #ifdef DEBUG 6187a1306a7Sxc151355 ath_printrxbuf(bf, status == HAL_OK); 6197a1306a7Sxc151355 #endif /* DEBUG */ 6200ba2cbe9Sxc151355 /* 6210ba2cbe9Sxc151355 * Locate the node for sender, track state, and then 6220ba2cbe9Sxc151355 * pass the (referenced) node up to the 802.11 layer 6230ba2cbe9Sxc151355 * for its use. 6240ba2cbe9Sxc151355 */ 6250ba2cbe9Sxc151355 in = ieee80211_find_rxnode(ic, wh); 6260ba2cbe9Sxc151355 6270ba2cbe9Sxc151355 /* 6280ba2cbe9Sxc151355 * Send frame up for processing. 6290ba2cbe9Sxc151355 */ 6300ba2cbe9Sxc151355 (void) ieee80211_input(ic, rx_mp, in, 631129d67acSlin wang - Sun Microsystems - Beijing China rs->rs_rssi, rs->rs_tstamp); 6320ba2cbe9Sxc151355 6330ba2cbe9Sxc151355 ieee80211_free_node(in); 6340ba2cbe9Sxc151355 6357a1306a7Sxc151355 rx_next: 6367a1306a7Sxc151355 mutex_enter(&asc->asc_rxbuflock); 6377a1306a7Sxc151355 list_insert_tail(&asc->asc_rxbuf_list, bf); 6387a1306a7Sxc151355 mutex_exit(&asc->asc_rxbuflock); 6397a1306a7Sxc151355 ath_setup_desc(asc, bf); 6407a1306a7Sxc151355 } while (loop); 6417a1306a7Sxc151355 6427a1306a7Sxc151355 /* rx signal state monitoring */ 6430ba2cbe9Sxc151355 ATH_HAL_RXMONITOR(ah, &hal_node_stats, &asc->asc_curchan); 6447a1306a7Sxc151355 } 6457a1306a7Sxc151355 6467a1306a7Sxc151355 static void 6477a1306a7Sxc151355 ath_printtxbuf(struct ath_buf *bf, int done) 6487a1306a7Sxc151355 { 6497a1306a7Sxc151355 struct ath_desc *ds = bf->bf_desc; 650129d67acSlin wang - Sun Microsystems - Beijing China const struct ath_tx_status *ts = &bf->bf_status.ds_txstat; 6517a1306a7Sxc151355 6527a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_SEND, "ath: T(%p %p) %08x %08x %08x %08x %08x" 6537a1306a7Sxc151355 " %08x %08x %08x %c\n", 6547a1306a7Sxc151355 ds, bf->bf_daddr, 6557a1306a7Sxc151355 ds->ds_link, ds->ds_data, 6567a1306a7Sxc151355 ds->ds_ctl0, ds->ds_ctl1, 6577a1306a7Sxc151355 ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3], 658129d67acSlin wang - Sun Microsystems - Beijing China !done ? ' ' : (ts->ts_status == 0) ? '*' : '!')); 6597a1306a7Sxc151355 } 6607a1306a7Sxc151355 6617a1306a7Sxc151355 /* 6627a1306a7Sxc151355 * The input parameter mp has following assumption: 6630ba2cbe9Sxc151355 * For data packets, GLDv3 mac_wifi plugin allocates and fills the 6640ba2cbe9Sxc151355 * ieee80211 header. For management packets, net80211 allocates and 6650ba2cbe9Sxc151355 * fills the ieee80211 header. In both cases, enough spaces in the 6660ba2cbe9Sxc151355 * header are left for encryption option. 6677a1306a7Sxc151355 */ 6687a1306a7Sxc151355 static int32_t 6690ba2cbe9Sxc151355 ath_tx_start(ath_t *asc, struct ieee80211_node *in, struct ath_buf *bf, 6700ba2cbe9Sxc151355 mblk_t *mp) 6717a1306a7Sxc151355 { 6720ba2cbe9Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc; 6737a1306a7Sxc151355 struct ieee80211_frame *wh; 6747a1306a7Sxc151355 struct ath_hal *ah = asc->asc_ah; 6750ba2cbe9Sxc151355 uint32_t subtype, flags, ctsduration; 6767a1306a7Sxc151355 int32_t keyix, iswep, hdrlen, pktlen, mblen, mbslen, try0; 6770ba2cbe9Sxc151355 uint8_t rix, cix, txrate, ctsrate; 6787a1306a7Sxc151355 struct ath_desc *ds; 6797a1306a7Sxc151355 struct ath_txq *txq; 6807a1306a7Sxc151355 HAL_PKT_TYPE atype; 6817a1306a7Sxc151355 const HAL_RATE_TABLE *rt; 6827a1306a7Sxc151355 HAL_BOOL shortPreamble; 6837a1306a7Sxc151355 struct ath_node *an; 6840ba2cbe9Sxc151355 caddr_t dest; 6857a1306a7Sxc151355 6867a1306a7Sxc151355 /* 6877a1306a7Sxc151355 * CRC are added by H/W, not encaped by driver, 6887a1306a7Sxc151355 * but we must count it in pkt length. 6897a1306a7Sxc151355 */ 6907a1306a7Sxc151355 pktlen = IEEE80211_CRC_LEN; 6917a1306a7Sxc151355 6920ba2cbe9Sxc151355 wh = (struct ieee80211_frame *)mp->b_rptr; 6930ba2cbe9Sxc151355 iswep = wh->i_fc[1] & IEEE80211_FC1_WEP; 6947a1306a7Sxc151355 keyix = HAL_TXKEYIX_INVALID; 6957a1306a7Sxc151355 hdrlen = sizeof (struct ieee80211_frame); 6960ba2cbe9Sxc151355 if (iswep != 0) { 6970ba2cbe9Sxc151355 const struct ieee80211_cipher *cip; 6980ba2cbe9Sxc151355 struct ieee80211_key *k; 6997a1306a7Sxc151355 7007a1306a7Sxc151355 /* 7010ba2cbe9Sxc151355 * Construct the 802.11 header+trailer for an encrypted 7020ba2cbe9Sxc151355 * frame. The only reason this can fail is because of an 7030ba2cbe9Sxc151355 * unknown or unsupported cipher/key type. 7047a1306a7Sxc151355 */ 7050ba2cbe9Sxc151355 k = ieee80211_crypto_encap(ic, mp); 7060ba2cbe9Sxc151355 if (k == NULL) { 7070ba2cbe9Sxc151355 ATH_DEBUG((ATH_DBG_AUX, "crypto_encap failed\n")); 7087a1306a7Sxc151355 /* 7090ba2cbe9Sxc151355 * This can happen when the key is yanked after the 7100ba2cbe9Sxc151355 * frame was queued. Just discard the frame; the 7110ba2cbe9Sxc151355 * 802.11 layer counts failures and provides 7120ba2cbe9Sxc151355 * debugging/diagnostics. 7137a1306a7Sxc151355 */ 7140ba2cbe9Sxc151355 return (EIO); 7150ba2cbe9Sxc151355 } 7160ba2cbe9Sxc151355 cip = k->wk_cipher; 7170ba2cbe9Sxc151355 /* 7180ba2cbe9Sxc151355 * Adjust the packet + header lengths for the crypto 7190ba2cbe9Sxc151355 * additions and calculate the h/w key index. When 7200ba2cbe9Sxc151355 * a s/w mic is done the frame will have had any mic 7210ba2cbe9Sxc151355 * added to it prior to entry so m0->m_pkthdr.len above will 7220ba2cbe9Sxc151355 * account for it. Otherwise we need to add it to the 7230ba2cbe9Sxc151355 * packet length. 7240ba2cbe9Sxc151355 */ 7250ba2cbe9Sxc151355 hdrlen += cip->ic_header; 726a399b765Szf162725 pktlen += cip->ic_trailer; 7270ba2cbe9Sxc151355 if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0) 7280ba2cbe9Sxc151355 pktlen += cip->ic_miclen; 7290ba2cbe9Sxc151355 keyix = k->wk_keyix; 7307a1306a7Sxc151355 7310ba2cbe9Sxc151355 /* packet header may have moved, reset our local pointer */ 7320ba2cbe9Sxc151355 wh = (struct ieee80211_frame *)mp->b_rptr; 7337a1306a7Sxc151355 } 7340ba2cbe9Sxc151355 7350ba2cbe9Sxc151355 dest = bf->bf_dma.mem_va; 7360ba2cbe9Sxc151355 for (; mp != NULL; mp = mp->b_cont) { 7370ba2cbe9Sxc151355 mblen = MBLKL(mp); 7380ba2cbe9Sxc151355 bcopy(mp->b_rptr, dest, mblen); 7390ba2cbe9Sxc151355 dest += mblen; 7407a1306a7Sxc151355 } 74122eb7cb5Sgd78059 mbslen = (uintptr_t)dest - (uintptr_t)bf->bf_dma.mem_va; 7420ba2cbe9Sxc151355 pktlen += mbslen; 7437a1306a7Sxc151355 7447a1306a7Sxc151355 bf->bf_in = in; 7457a1306a7Sxc151355 7467a1306a7Sxc151355 /* setup descriptors */ 7477a1306a7Sxc151355 ds = bf->bf_desc; 7487a1306a7Sxc151355 rt = asc->asc_currates; 7490ba2cbe9Sxc151355 ASSERT(rt != NULL); 7507a1306a7Sxc151355 7517a1306a7Sxc151355 /* 7527a1306a7Sxc151355 * The 802.11 layer marks whether or not we should 7537a1306a7Sxc151355 * use short preamble based on the current mode and 7547a1306a7Sxc151355 * negotiated parameters. 7557a1306a7Sxc151355 */ 7560ba2cbe9Sxc151355 if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && 7577a1306a7Sxc151355 (in->in_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) { 7587a1306a7Sxc151355 shortPreamble = AH_TRUE; 7597a1306a7Sxc151355 asc->asc_stats.ast_tx_shortpre++; 7607a1306a7Sxc151355 } else { 7617a1306a7Sxc151355 shortPreamble = AH_FALSE; 7627a1306a7Sxc151355 } 7637a1306a7Sxc151355 7647a1306a7Sxc151355 an = ATH_NODE(in); 7657a1306a7Sxc151355 7667a1306a7Sxc151355 /* 7677a1306a7Sxc151355 * Calculate Atheros packet type from IEEE80211 packet header 7687a1306a7Sxc151355 * and setup for rate calculations. 7697a1306a7Sxc151355 */ 7700ba2cbe9Sxc151355 switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { 7717a1306a7Sxc151355 case IEEE80211_FC0_TYPE_MGT: 7720ba2cbe9Sxc151355 subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 7737a1306a7Sxc151355 if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) 7747a1306a7Sxc151355 atype = HAL_PKT_TYPE_BEACON; 7757a1306a7Sxc151355 else if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) 7767a1306a7Sxc151355 atype = HAL_PKT_TYPE_PROBE_RESP; 7777a1306a7Sxc151355 else if (subtype == IEEE80211_FC0_SUBTYPE_ATIM) 7787a1306a7Sxc151355 atype = HAL_PKT_TYPE_ATIM; 7797a1306a7Sxc151355 else 7807a1306a7Sxc151355 atype = HAL_PKT_TYPE_NORMAL; 7817a1306a7Sxc151355 rix = 0; /* lowest rate */ 7827a1306a7Sxc151355 try0 = ATH_TXMAXTRY; 7837a1306a7Sxc151355 if (shortPreamble) 7847a1306a7Sxc151355 txrate = an->an_tx_mgtratesp; 7857a1306a7Sxc151355 else 7867a1306a7Sxc151355 txrate = an->an_tx_mgtrate; 7877a1306a7Sxc151355 /* force all ctl frames to highest queue */ 7887a1306a7Sxc151355 txq = asc->asc_ac2q[WME_AC_VO]; 7897a1306a7Sxc151355 break; 7907a1306a7Sxc151355 case IEEE80211_FC0_TYPE_CTL: 7917a1306a7Sxc151355 atype = HAL_PKT_TYPE_PSPOLL; 7920ba2cbe9Sxc151355 subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 7937a1306a7Sxc151355 rix = 0; /* lowest rate */ 7947a1306a7Sxc151355 try0 = ATH_TXMAXTRY; 7957a1306a7Sxc151355 if (shortPreamble) 7967a1306a7Sxc151355 txrate = an->an_tx_mgtratesp; 7977a1306a7Sxc151355 else 7987a1306a7Sxc151355 txrate = an->an_tx_mgtrate; 7997a1306a7Sxc151355 /* force all ctl frames to highest queue */ 8007a1306a7Sxc151355 txq = asc->asc_ac2q[WME_AC_VO]; 8017a1306a7Sxc151355 break; 8027a1306a7Sxc151355 case IEEE80211_FC0_TYPE_DATA: 8037a1306a7Sxc151355 atype = HAL_PKT_TYPE_NORMAL; 8047a1306a7Sxc151355 rix = an->an_tx_rix0; 8057a1306a7Sxc151355 try0 = an->an_tx_try0; 8067a1306a7Sxc151355 if (shortPreamble) 8077a1306a7Sxc151355 txrate = an->an_tx_rate0sp; 8087a1306a7Sxc151355 else 8097a1306a7Sxc151355 txrate = an->an_tx_rate0; 8107a1306a7Sxc151355 /* Always use background queue */ 8117a1306a7Sxc151355 txq = asc->asc_ac2q[WME_AC_BK]; 8127a1306a7Sxc151355 break; 8137a1306a7Sxc151355 default: 8147a1306a7Sxc151355 /* Unknown 802.11 frame */ 8157a1306a7Sxc151355 asc->asc_stats.ast_tx_invalid++; 8167a1306a7Sxc151355 return (1); 8177a1306a7Sxc151355 } 8187a1306a7Sxc151355 /* 8197a1306a7Sxc151355 * Calculate miscellaneous flags. 8207a1306a7Sxc151355 */ 8217a1306a7Sxc151355 flags = HAL_TXDESC_CLRDMASK; 8220ba2cbe9Sxc151355 if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { 8237a1306a7Sxc151355 flags |= HAL_TXDESC_NOACK; /* no ack on broad/multicast */ 8247a1306a7Sxc151355 asc->asc_stats.ast_tx_noack++; 8250ba2cbe9Sxc151355 } else if (pktlen > ic->ic_rtsthreshold) { 8267a1306a7Sxc151355 flags |= HAL_TXDESC_RTSENA; /* RTS based on frame length */ 8277a1306a7Sxc151355 asc->asc_stats.ast_tx_rts++; 8287a1306a7Sxc151355 } 8297a1306a7Sxc151355 8307a1306a7Sxc151355 /* 8317a1306a7Sxc151355 * Calculate duration. This logically belongs in the 802.11 8327a1306a7Sxc151355 * layer but it lacks sufficient information to calculate it. 8337a1306a7Sxc151355 */ 8347a1306a7Sxc151355 if ((flags & HAL_TXDESC_NOACK) == 0 && 8350ba2cbe9Sxc151355 (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != 8367a1306a7Sxc151355 IEEE80211_FC0_TYPE_CTL) { 8377a1306a7Sxc151355 uint16_t dur; 8387a1306a7Sxc151355 dur = ath_hal_computetxtime(ah, rt, IEEE80211_ACK_SIZE, 8397a1306a7Sxc151355 rix, shortPreamble); 840ff3124efSff224033 /* LINTED E_BAD_PTR_CAST_ALIGN */ 8410ba2cbe9Sxc151355 *(uint16_t *)wh->i_dur = LE_16(dur); 8427a1306a7Sxc151355 } 8437a1306a7Sxc151355 8447a1306a7Sxc151355 /* 8457a1306a7Sxc151355 * Calculate RTS/CTS rate and duration if needed. 8467a1306a7Sxc151355 */ 8477a1306a7Sxc151355 ctsduration = 0; 8487a1306a7Sxc151355 if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)) { 8497a1306a7Sxc151355 /* 8507a1306a7Sxc151355 * CTS transmit rate is derived from the transmit rate 8517a1306a7Sxc151355 * by looking in the h/w rate table. We must also factor 8527a1306a7Sxc151355 * in whether or not a short preamble is to be used. 8537a1306a7Sxc151355 */ 8547a1306a7Sxc151355 cix = rt->info[rix].controlRate; 8557a1306a7Sxc151355 ctsrate = rt->info[cix].rateCode; 8567a1306a7Sxc151355 if (shortPreamble) 8577a1306a7Sxc151355 ctsrate |= rt->info[cix].shortPreamble; 8587a1306a7Sxc151355 /* 8597a1306a7Sxc151355 * Compute the transmit duration based on the size 8607a1306a7Sxc151355 * of an ACK frame. We call into the HAL to do the 8617a1306a7Sxc151355 * computation since it depends on the characteristics 8627a1306a7Sxc151355 * of the actual PHY being used. 8637a1306a7Sxc151355 */ 8647a1306a7Sxc151355 if (flags & HAL_TXDESC_RTSENA) { /* SIFS + CTS */ 8657a1306a7Sxc151355 ctsduration += ath_hal_computetxtime(ah, 8667a1306a7Sxc151355 rt, IEEE80211_ACK_SIZE, cix, shortPreamble); 8677a1306a7Sxc151355 } 8687a1306a7Sxc151355 /* SIFS + data */ 8697a1306a7Sxc151355 ctsduration += ath_hal_computetxtime(ah, 8707a1306a7Sxc151355 rt, pktlen, rix, shortPreamble); 8717a1306a7Sxc151355 if ((flags & HAL_TXDESC_NOACK) == 0) { /* SIFS + ACK */ 8727a1306a7Sxc151355 ctsduration += ath_hal_computetxtime(ah, 8737a1306a7Sxc151355 rt, IEEE80211_ACK_SIZE, cix, shortPreamble); 8747a1306a7Sxc151355 } 8757a1306a7Sxc151355 } else 8767a1306a7Sxc151355 ctsrate = 0; 8777a1306a7Sxc151355 8787a1306a7Sxc151355 if (++txq->axq_intrcnt >= ATH_TXINTR_PERIOD) { 8797a1306a7Sxc151355 flags |= HAL_TXDESC_INTREQ; 8807a1306a7Sxc151355 txq->axq_intrcnt = 0; 8817a1306a7Sxc151355 } 8827a1306a7Sxc151355 8837a1306a7Sxc151355 /* 8847a1306a7Sxc151355 * Formulate first tx descriptor with tx controls. 8857a1306a7Sxc151355 */ 8867a1306a7Sxc151355 ATH_HAL_SETUPTXDESC(ah, ds, 8877a1306a7Sxc151355 pktlen, /* packet length */ 8887a1306a7Sxc151355 hdrlen, /* header length */ 8897a1306a7Sxc151355 atype, /* Atheros packet type */ 8907a1306a7Sxc151355 MIN(in->in_txpower, 60), /* txpower */ 8917a1306a7Sxc151355 txrate, try0, /* series 0 rate/tries */ 8920ba2cbe9Sxc151355 keyix, /* key cache index */ 8930ba2cbe9Sxc151355 an->an_tx_antenna, /* antenna mode */ 8947a1306a7Sxc151355 flags, /* flags */ 8957a1306a7Sxc151355 ctsrate, /* rts/cts rate */ 8967a1306a7Sxc151355 ctsduration); /* rts/cts duration */ 8970ba2cbe9Sxc151355 bf->bf_flags = flags; 8987a1306a7Sxc151355 899ff3124efSff224033 /* LINTED E_BAD_PTR_CAST_ALIGN */ 9007a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_SEND, "ath: ath_xmit(): to %s totlen=%d " 9017a1306a7Sxc151355 "an->an_tx_rate1sp=%d tx_rate2sp=%d tx_rate3sp=%d " 9027a1306a7Sxc151355 "qnum=%d rix=%d sht=%d dur = %d\n", 9030ba2cbe9Sxc151355 ieee80211_macaddr_sprintf(wh->i_addr1), mbslen, an->an_tx_rate1sp, 9047a1306a7Sxc151355 an->an_tx_rate2sp, an->an_tx_rate3sp, 9050ba2cbe9Sxc151355 txq->axq_qnum, rix, shortPreamble, *(uint16_t *)wh->i_dur)); 9067a1306a7Sxc151355 9077a1306a7Sxc151355 /* 9087a1306a7Sxc151355 * Setup the multi-rate retry state only when we're 9097a1306a7Sxc151355 * going to use it. This assumes ath_hal_setuptxdesc 9107a1306a7Sxc151355 * initializes the descriptors (so we don't have to) 9117a1306a7Sxc151355 * when the hardware supports multi-rate retry and 9127a1306a7Sxc151355 * we don't use it. 9137a1306a7Sxc151355 */ 9147a1306a7Sxc151355 if (try0 != ATH_TXMAXTRY) 9157a1306a7Sxc151355 ATH_HAL_SETUPXTXDESC(ah, ds, 9167a1306a7Sxc151355 an->an_tx_rate1sp, 2, /* series 1 */ 9177a1306a7Sxc151355 an->an_tx_rate2sp, 2, /* series 2 */ 9187a1306a7Sxc151355 an->an_tx_rate3sp, 2); /* series 3 */ 9197a1306a7Sxc151355 9207a1306a7Sxc151355 ds->ds_link = 0; 9217a1306a7Sxc151355 ds->ds_data = bf->bf_dma.cookie.dmac_address; 9227a1306a7Sxc151355 ATH_HAL_FILLTXDESC(ah, ds, 9237a1306a7Sxc151355 mbslen, /* segment length */ 9247a1306a7Sxc151355 AH_TRUE, /* first segment */ 9257a1306a7Sxc151355 AH_TRUE, /* last segment */ 9267a1306a7Sxc151355 ds); /* first descriptor */ 9277a1306a7Sxc151355 9287a1306a7Sxc151355 ATH_DMA_SYNC(bf->bf_dma, DDI_DMA_SYNC_FORDEV); 9297a1306a7Sxc151355 9307a1306a7Sxc151355 mutex_enter(&txq->axq_lock); 9317a1306a7Sxc151355 list_insert_tail(&txq->axq_list, bf); 9327a1306a7Sxc151355 if (txq->axq_link == NULL) { 9337a1306a7Sxc151355 ATH_HAL_PUTTXBUF(ah, txq->axq_qnum, bf->bf_daddr); 9347a1306a7Sxc151355 } else { 9357a1306a7Sxc151355 *txq->axq_link = bf->bf_daddr; 9367a1306a7Sxc151355 } 9377a1306a7Sxc151355 txq->axq_link = &ds->ds_link; 9387a1306a7Sxc151355 mutex_exit(&txq->axq_lock); 9397a1306a7Sxc151355 9407a1306a7Sxc151355 ATH_HAL_TXSTART(ah, txq->axq_qnum); 9417a1306a7Sxc151355 9420ba2cbe9Sxc151355 ic->ic_stats.is_tx_frags++; 9430ba2cbe9Sxc151355 ic->ic_stats.is_tx_bytes += pktlen; 9440ba2cbe9Sxc151355 9457a1306a7Sxc151355 return (0); 9467a1306a7Sxc151355 } 9477a1306a7Sxc151355 9480ba2cbe9Sxc151355 /* 9490ba2cbe9Sxc151355 * Transmit a management frame. On failure we reclaim the skbuff. 9500ba2cbe9Sxc151355 * Note that management frames come directly from the 802.11 layer 9510ba2cbe9Sxc151355 * and do not honor the send queue flow control. Need to investigate 9520ba2cbe9Sxc151355 * using priority queueing so management frames can bypass data. 9530ba2cbe9Sxc151355 */ 9547a1306a7Sxc151355 static int 9550ba2cbe9Sxc151355 ath_xmit(ieee80211com_t *ic, mblk_t *mp, uint8_t type) 9567a1306a7Sxc151355 { 9570ba2cbe9Sxc151355 ath_t *asc = (ath_t *)ic; 9580ba2cbe9Sxc151355 struct ath_hal *ah = asc->asc_ah; 9590ba2cbe9Sxc151355 struct ieee80211_node *in = NULL; 9607a1306a7Sxc151355 struct ath_buf *bf = NULL; 9610ba2cbe9Sxc151355 struct ieee80211_frame *wh; 9620ba2cbe9Sxc151355 int error = 0; 9630ba2cbe9Sxc151355 9640ba2cbe9Sxc151355 ASSERT(mp->b_next == NULL); 9650ba2cbe9Sxc151355 966f11a3086Sxc151355 if (!ATH_IS_RUNNING(asc)) { 967f11a3086Sxc151355 if ((type & IEEE80211_FC0_TYPE_MASK) != 968f11a3086Sxc151355 IEEE80211_FC0_TYPE_DATA) { 969f11a3086Sxc151355 freemsg(mp); 970f11a3086Sxc151355 } 971f11a3086Sxc151355 return (ENXIO); 972f11a3086Sxc151355 } 973f11a3086Sxc151355 9740ba2cbe9Sxc151355 /* Grab a TX buffer */ 9750ba2cbe9Sxc151355 mutex_enter(&asc->asc_txbuflock); 9760ba2cbe9Sxc151355 bf = list_head(&asc->asc_txbuf_list); 9770ba2cbe9Sxc151355 if (bf != NULL) 9780ba2cbe9Sxc151355 list_remove(&asc->asc_txbuf_list, bf); 9790ba2cbe9Sxc151355 if (list_empty(&asc->asc_txbuf_list)) { 9800ba2cbe9Sxc151355 ATH_DEBUG((ATH_DBG_SEND, "ath: ath_mgmt_send(): " 9810ba2cbe9Sxc151355 "stop queue\n")); 9820ba2cbe9Sxc151355 asc->asc_stats.ast_tx_qstop++; 9830ba2cbe9Sxc151355 } 9840ba2cbe9Sxc151355 mutex_exit(&asc->asc_txbuflock); 9850ba2cbe9Sxc151355 if (bf == NULL) { 9860ba2cbe9Sxc151355 ATH_DEBUG((ATH_DBG_SEND, "ath: ath_mgmt_send(): discard, " 9870ba2cbe9Sxc151355 "no xmit buf\n")); 9880ba2cbe9Sxc151355 ic->ic_stats.is_tx_nobuf++; 9890ba2cbe9Sxc151355 if ((type & IEEE80211_FC0_TYPE_MASK) == 9900ba2cbe9Sxc151355 IEEE80211_FC0_TYPE_DATA) { 9910ba2cbe9Sxc151355 asc->asc_stats.ast_tx_nobuf++; 9920ba2cbe9Sxc151355 mutex_enter(&asc->asc_resched_lock); 9930ba2cbe9Sxc151355 asc->asc_resched_needed = B_TRUE; 9940ba2cbe9Sxc151355 mutex_exit(&asc->asc_resched_lock); 9950ba2cbe9Sxc151355 } else { 9960ba2cbe9Sxc151355 asc->asc_stats.ast_tx_nobufmgt++; 9970ba2cbe9Sxc151355 freemsg(mp); 9980ba2cbe9Sxc151355 } 9990ba2cbe9Sxc151355 return (ENOMEM); 10000ba2cbe9Sxc151355 } 10010ba2cbe9Sxc151355 10020ba2cbe9Sxc151355 wh = (struct ieee80211_frame *)mp->b_rptr; 10030ba2cbe9Sxc151355 10040ba2cbe9Sxc151355 /* Locate node */ 10050ba2cbe9Sxc151355 in = ieee80211_find_txnode(ic, wh->i_addr1); 10060ba2cbe9Sxc151355 if (in == NULL) { 10070ba2cbe9Sxc151355 error = EIO; 10080ba2cbe9Sxc151355 goto bad; 10090ba2cbe9Sxc151355 } 10100ba2cbe9Sxc151355 10110ba2cbe9Sxc151355 in->in_inact = 0; 10120ba2cbe9Sxc151355 switch (type & IEEE80211_FC0_TYPE_MASK) { 10130ba2cbe9Sxc151355 case IEEE80211_FC0_TYPE_DATA: 10140ba2cbe9Sxc151355 (void) ieee80211_encap(ic, mp, in); 10150ba2cbe9Sxc151355 break; 10160ba2cbe9Sxc151355 default: 10170ba2cbe9Sxc151355 if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == 10180ba2cbe9Sxc151355 IEEE80211_FC0_SUBTYPE_PROBE_RESP) { 10190ba2cbe9Sxc151355 /* fill time stamp */ 10200ba2cbe9Sxc151355 uint64_t tsf; 10210ba2cbe9Sxc151355 uint32_t *tstamp; 10220ba2cbe9Sxc151355 10230ba2cbe9Sxc151355 tsf = ATH_HAL_GETTSF64(ah); 10240ba2cbe9Sxc151355 /* adjust 100us delay to xmit */ 10250ba2cbe9Sxc151355 tsf += 100; 1026ff3124efSff224033 /* LINTED E_BAD_PTR_CAST_ALIGN */ 10270ba2cbe9Sxc151355 tstamp = (uint32_t *)&wh[1]; 10280ba2cbe9Sxc151355 tstamp[0] = LE_32(tsf & 0xffffffff); 10290ba2cbe9Sxc151355 tstamp[1] = LE_32(tsf >> 32); 10300ba2cbe9Sxc151355 } 10310ba2cbe9Sxc151355 asc->asc_stats.ast_tx_mgmt++; 10320ba2cbe9Sxc151355 break; 10330ba2cbe9Sxc151355 } 10340ba2cbe9Sxc151355 10350ba2cbe9Sxc151355 error = ath_tx_start(asc, in, bf, mp); 10360ba2cbe9Sxc151355 if (error != 0) { 10370ba2cbe9Sxc151355 bad: 10380ba2cbe9Sxc151355 ic->ic_stats.is_tx_failed++; 10390ba2cbe9Sxc151355 if (bf != NULL) { 10400ba2cbe9Sxc151355 mutex_enter(&asc->asc_txbuflock); 10410ba2cbe9Sxc151355 list_insert_tail(&asc->asc_txbuf_list, bf); 10420ba2cbe9Sxc151355 mutex_exit(&asc->asc_txbuflock); 10430ba2cbe9Sxc151355 } 10440ba2cbe9Sxc151355 } 10450ba2cbe9Sxc151355 if (in != NULL) 10460ba2cbe9Sxc151355 ieee80211_free_node(in); 10470ba2cbe9Sxc151355 if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA || 10480ba2cbe9Sxc151355 error == 0) { 10490ba2cbe9Sxc151355 freemsg(mp); 10500ba2cbe9Sxc151355 } 10510ba2cbe9Sxc151355 10520ba2cbe9Sxc151355 return (error); 10530ba2cbe9Sxc151355 } 10540ba2cbe9Sxc151355 10550ba2cbe9Sxc151355 static mblk_t * 10560ba2cbe9Sxc151355 ath_m_tx(void *arg, mblk_t *mp) 10570ba2cbe9Sxc151355 { 10580ba2cbe9Sxc151355 ath_t *asc = arg; 10590ba2cbe9Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc; 10600ba2cbe9Sxc151355 mblk_t *next; 1061a399b765Szf162725 int error = 0; 10627a1306a7Sxc151355 10637a1306a7Sxc151355 /* 10647a1306a7Sxc151355 * No data frames go out unless we're associated; this 10657a1306a7Sxc151355 * should not happen as the 802.11 layer does not enable 10667a1306a7Sxc151355 * the xmit queue until we enter the RUN state. 10677a1306a7Sxc151355 */ 10680ba2cbe9Sxc151355 if (ic->ic_state != IEEE80211_S_RUN) { 10690ba2cbe9Sxc151355 ATH_DEBUG((ATH_DBG_SEND, "ath: ath_m_tx(): " 10700ba2cbe9Sxc151355 "discard, state %u\n", ic->ic_state)); 10717a1306a7Sxc151355 asc->asc_stats.ast_tx_discard++; 10729ee221caSxc151355 freemsgchain(mp); 10739ee221caSxc151355 return (NULL); 10747a1306a7Sxc151355 } 10757a1306a7Sxc151355 10760ba2cbe9Sxc151355 while (mp != NULL) { 10770ba2cbe9Sxc151355 next = mp->b_next; 10780ba2cbe9Sxc151355 mp->b_next = NULL; 1079a399b765Szf162725 error = ath_xmit(ic, mp, IEEE80211_FC0_TYPE_DATA); 1080a399b765Szf162725 if (error != 0) { 10810ba2cbe9Sxc151355 mp->b_next = next; 1082a399b765Szf162725 if (error == ENOMEM) { 10830ba2cbe9Sxc151355 break; 1084a399b765Szf162725 } else { 1085a399b765Szf162725 freemsgchain(mp); /* CR6501759 issues */ 1086a399b765Szf162725 return (NULL); 1087a399b765Szf162725 } 10880ba2cbe9Sxc151355 } 10890ba2cbe9Sxc151355 mp = next; 10907a1306a7Sxc151355 } 10917a1306a7Sxc151355 10920ba2cbe9Sxc151355 return (mp); 10937a1306a7Sxc151355 } 10947a1306a7Sxc151355 10950ba2cbe9Sxc151355 static int 10967a1306a7Sxc151355 ath_tx_processq(ath_t *asc, struct ath_txq *txq) 10977a1306a7Sxc151355 { 10980ba2cbe9Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc; 10997a1306a7Sxc151355 struct ath_hal *ah = asc->asc_ah; 11007a1306a7Sxc151355 struct ath_buf *bf; 11017a1306a7Sxc151355 struct ath_desc *ds; 11027a1306a7Sxc151355 struct ieee80211_node *in; 11030ba2cbe9Sxc151355 int32_t sr, lr, nacked = 0; 1104129d67acSlin wang - Sun Microsystems - Beijing China struct ath_tx_status *ts; 11057a1306a7Sxc151355 HAL_STATUS status; 11067a1306a7Sxc151355 struct ath_node *an; 11077a1306a7Sxc151355 11087a1306a7Sxc151355 for (;;) { 11097a1306a7Sxc151355 mutex_enter(&txq->axq_lock); 11107a1306a7Sxc151355 bf = list_head(&txq->axq_list); 11117a1306a7Sxc151355 if (bf == NULL) { 11127a1306a7Sxc151355 txq->axq_link = NULL; 11137a1306a7Sxc151355 mutex_exit(&txq->axq_lock); 11147a1306a7Sxc151355 break; 11157a1306a7Sxc151355 } 11167a1306a7Sxc151355 ds = bf->bf_desc; /* last decriptor */ 1117129d67acSlin wang - Sun Microsystems - Beijing China ts = &bf->bf_status.ds_txstat; 1118129d67acSlin wang - Sun Microsystems - Beijing China status = ATH_HAL_TXPROCDESC(ah, ds, ts); 11197a1306a7Sxc151355 #ifdef DEBUG 11207a1306a7Sxc151355 ath_printtxbuf(bf, status == HAL_OK); 11217a1306a7Sxc151355 #endif 11227a1306a7Sxc151355 if (status == HAL_EINPROGRESS) { 11237a1306a7Sxc151355 mutex_exit(&txq->axq_lock); 11247a1306a7Sxc151355 break; 11257a1306a7Sxc151355 } 11267a1306a7Sxc151355 list_remove(&txq->axq_list, bf); 11277a1306a7Sxc151355 mutex_exit(&txq->axq_lock); 11287a1306a7Sxc151355 in = bf->bf_in; 11297a1306a7Sxc151355 if (in != NULL) { 11307a1306a7Sxc151355 an = ATH_NODE(in); 11317a1306a7Sxc151355 /* Successful transmition */ 1132129d67acSlin wang - Sun Microsystems - Beijing China if (ts->ts_status == 0) { 11337a1306a7Sxc151355 an->an_tx_ok++; 1134129d67acSlin wang - Sun Microsystems - Beijing China an->an_tx_antenna = ts->ts_antenna; 1135129d67acSlin wang - Sun Microsystems - Beijing China if (ts->ts_rate & HAL_TXSTAT_ALTRATE) 11367a1306a7Sxc151355 asc->asc_stats.ast_tx_altrate++; 11377a1306a7Sxc151355 asc->asc_stats.ast_tx_rssidelta = 1138129d67acSlin wang - Sun Microsystems - Beijing China ts->ts_rssi - asc->asc_stats.ast_tx_rssi; 1139129d67acSlin wang - Sun Microsystems - Beijing China asc->asc_stats.ast_tx_rssi = ts->ts_rssi; 11407a1306a7Sxc151355 } else { 11417a1306a7Sxc151355 an->an_tx_err++; 1142129d67acSlin wang - Sun Microsystems - Beijing China if (ts->ts_status & HAL_TXERR_XRETRY) 1143129d67acSlin wang - Sun Microsystems - Beijing China asc->asc_stats.ast_tx_xretries++; 1144129d67acSlin wang - Sun Microsystems - Beijing China if (ts->ts_status & HAL_TXERR_FIFO) 11457a1306a7Sxc151355 asc->asc_stats.ast_tx_fifoerr++; 1146129d67acSlin wang - Sun Microsystems - Beijing China if (ts->ts_status & HAL_TXERR_FILT) 1147129d67acSlin wang - Sun Microsystems - Beijing China asc->asc_stats.ast_tx_filtered++; 11487a1306a7Sxc151355 an->an_tx_antenna = 0; /* invalidate */ 11497a1306a7Sxc151355 } 1150129d67acSlin wang - Sun Microsystems - Beijing China sr = ts->ts_shortretry; 1151129d67acSlin wang - Sun Microsystems - Beijing China lr = ts->ts_longretry; 11527a1306a7Sxc151355 asc->asc_stats.ast_tx_shortretry += sr; 11537a1306a7Sxc151355 asc->asc_stats.ast_tx_longretry += lr; 11540ba2cbe9Sxc151355 /* 11550ba2cbe9Sxc151355 * Hand the descriptor to the rate control algorithm. 11560ba2cbe9Sxc151355 */ 1157129d67acSlin wang - Sun Microsystems - Beijing China if ((ts->ts_status & HAL_TXERR_FILT) == 0 && 11580ba2cbe9Sxc151355 (bf->bf_flags & HAL_TXDESC_NOACK) == 0) { 11590ba2cbe9Sxc151355 /* 11600ba2cbe9Sxc151355 * If frame was ack'd update the last rx time 11610ba2cbe9Sxc151355 * used to workaround phantom bmiss interrupts. 11620ba2cbe9Sxc151355 */ 1163129d67acSlin wang - Sun Microsystems - Beijing China if (ts->ts_status == 0) { 11640ba2cbe9Sxc151355 nacked++; 11650ba2cbe9Sxc151355 an->an_tx_ok++; 11660ba2cbe9Sxc151355 } else { 11670ba2cbe9Sxc151355 an->an_tx_err++; 11680ba2cbe9Sxc151355 } 11697a1306a7Sxc151355 an->an_tx_retr += sr + lr; 11707a1306a7Sxc151355 } 11710ba2cbe9Sxc151355 } 11727a1306a7Sxc151355 bf->bf_in = NULL; 11737a1306a7Sxc151355 mutex_enter(&asc->asc_txbuflock); 11747a1306a7Sxc151355 list_insert_tail(&asc->asc_txbuf_list, bf); 11757a1306a7Sxc151355 mutex_exit(&asc->asc_txbuflock); 11767a1306a7Sxc151355 /* 11777a1306a7Sxc151355 * Reschedule stalled outbound packets 11787a1306a7Sxc151355 */ 11790ba2cbe9Sxc151355 mutex_enter(&asc->asc_resched_lock); 11800ba2cbe9Sxc151355 if (asc->asc_resched_needed) { 11810ba2cbe9Sxc151355 asc->asc_resched_needed = B_FALSE; 11820ba2cbe9Sxc151355 mac_tx_update(ic->ic_mach); 11837a1306a7Sxc151355 } 11840ba2cbe9Sxc151355 mutex_exit(&asc->asc_resched_lock); 11857a1306a7Sxc151355 } 11860ba2cbe9Sxc151355 return (nacked); 11877a1306a7Sxc151355 } 11887a1306a7Sxc151355 11897a1306a7Sxc151355 11907a1306a7Sxc151355 static void 11917a1306a7Sxc151355 ath_tx_handler(ath_t *asc) 11927a1306a7Sxc151355 { 11937a1306a7Sxc151355 int i; 11947a1306a7Sxc151355 11957a1306a7Sxc151355 /* 11967a1306a7Sxc151355 * Process each active queue. 11977a1306a7Sxc151355 */ 11987a1306a7Sxc151355 for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { 11997a1306a7Sxc151355 if (ATH_TXQ_SETUP(asc, i)) { 12000ba2cbe9Sxc151355 (void) ath_tx_processq(asc, &asc->asc_txq[i]); 12017a1306a7Sxc151355 } 12027a1306a7Sxc151355 } 12037a1306a7Sxc151355 } 12047a1306a7Sxc151355 12057a1306a7Sxc151355 static struct ieee80211_node * 12060ba2cbe9Sxc151355 ath_node_alloc(ieee80211com_t *ic) 12077a1306a7Sxc151355 { 12087a1306a7Sxc151355 struct ath_node *an; 12090ba2cbe9Sxc151355 ath_t *asc = (ath_t *)ic; 12107a1306a7Sxc151355 12117a1306a7Sxc151355 an = kmem_zalloc(sizeof (struct ath_node), KM_SLEEP); 12127a1306a7Sxc151355 ath_rate_update(asc, &an->an_node, 0); 12137a1306a7Sxc151355 return (&an->an_node); 12147a1306a7Sxc151355 } 12157a1306a7Sxc151355 12167a1306a7Sxc151355 static void 12170ba2cbe9Sxc151355 ath_node_free(struct ieee80211_node *in) 12187a1306a7Sxc151355 { 12190ba2cbe9Sxc151355 ieee80211com_t *ic = in->in_ic; 12200ba2cbe9Sxc151355 ath_t *asc = (ath_t *)ic; 12217a1306a7Sxc151355 struct ath_buf *bf; 12227a1306a7Sxc151355 struct ath_txq *txq; 12237a1306a7Sxc151355 int32_t i; 12247a1306a7Sxc151355 12257a1306a7Sxc151355 for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { 12267a1306a7Sxc151355 if (ATH_TXQ_SETUP(asc, i)) { 12277a1306a7Sxc151355 txq = &asc->asc_txq[i]; 12287a1306a7Sxc151355 mutex_enter(&txq->axq_lock); 12297a1306a7Sxc151355 bf = list_head(&txq->axq_list); 12307a1306a7Sxc151355 while (bf != NULL) { 12317a1306a7Sxc151355 if (bf->bf_in == in) { 12327a1306a7Sxc151355 bf->bf_in = NULL; 12337a1306a7Sxc151355 } 12347a1306a7Sxc151355 bf = list_next(&txq->axq_list, bf); 12357a1306a7Sxc151355 } 12367a1306a7Sxc151355 mutex_exit(&txq->axq_lock); 12377a1306a7Sxc151355 } 12387a1306a7Sxc151355 } 12390ba2cbe9Sxc151355 ic->ic_node_cleanup(in); 1240a399b765Szf162725 if (in->in_wpa_ie != NULL) 1241a399b765Szf162725 ieee80211_free(in->in_wpa_ie); 12427a1306a7Sxc151355 kmem_free(in, sizeof (struct ath_node)); 12437a1306a7Sxc151355 } 12447a1306a7Sxc151355 12457a1306a7Sxc151355 static void 12460ba2cbe9Sxc151355 ath_next_scan(void *arg) 12477a1306a7Sxc151355 { 12480ba2cbe9Sxc151355 ieee80211com_t *ic = arg; 12490ba2cbe9Sxc151355 ath_t *asc = (ath_t *)ic; 12500ba2cbe9Sxc151355 12510ba2cbe9Sxc151355 asc->asc_scan_timer = 0; 12520ba2cbe9Sxc151355 if (ic->ic_state == IEEE80211_S_SCAN) { 12530ba2cbe9Sxc151355 asc->asc_scan_timer = timeout(ath_next_scan, (void *)asc, 12540ba2cbe9Sxc151355 drv_usectohz(ath_dwelltime * 1000)); 12550ba2cbe9Sxc151355 ieee80211_next_scan(ic); 12560ba2cbe9Sxc151355 } 12577a1306a7Sxc151355 } 12587a1306a7Sxc151355 12590ba2cbe9Sxc151355 static void 12600ba2cbe9Sxc151355 ath_stop_scantimer(ath_t *asc) 12617a1306a7Sxc151355 { 12620ba2cbe9Sxc151355 timeout_id_t tmp_id = 0; 12637a1306a7Sxc151355 12640ba2cbe9Sxc151355 while ((asc->asc_scan_timer != 0) && (tmp_id != asc->asc_scan_timer)) { 12650ba2cbe9Sxc151355 tmp_id = asc->asc_scan_timer; 12660ba2cbe9Sxc151355 (void) untimeout(tmp_id); 12677a1306a7Sxc151355 } 12680ba2cbe9Sxc151355 asc->asc_scan_timer = 0; 12697a1306a7Sxc151355 } 12707a1306a7Sxc151355 12717a1306a7Sxc151355 static int32_t 12720ba2cbe9Sxc151355 ath_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg) 12737a1306a7Sxc151355 { 12740ba2cbe9Sxc151355 ath_t *asc = (ath_t *)ic; 12757a1306a7Sxc151355 struct ath_hal *ah = asc->asc_ah; 12767a1306a7Sxc151355 struct ieee80211_node *in; 12777a1306a7Sxc151355 int32_t i, error; 12787a1306a7Sxc151355 uint8_t *bssid; 12797a1306a7Sxc151355 uint32_t rfilt; 12807a1306a7Sxc151355 enum ieee80211_state ostate; 12817a1306a7Sxc151355 12827a1306a7Sxc151355 static const HAL_LED_STATE leds[] = { 12837a1306a7Sxc151355 HAL_LED_INIT, /* IEEE80211_S_INIT */ 12847a1306a7Sxc151355 HAL_LED_SCAN, /* IEEE80211_S_SCAN */ 12857a1306a7Sxc151355 HAL_LED_AUTH, /* IEEE80211_S_AUTH */ 12867a1306a7Sxc151355 HAL_LED_ASSOC, /* IEEE80211_S_ASSOC */ 12877a1306a7Sxc151355 HAL_LED_RUN, /* IEEE80211_S_RUN */ 12887a1306a7Sxc151355 }; 12890ba2cbe9Sxc151355 if (!ATH_IS_RUNNING(asc)) 12907a1306a7Sxc151355 return (0); 12917a1306a7Sxc151355 12920ba2cbe9Sxc151355 ostate = ic->ic_state; 12930ba2cbe9Sxc151355 if (nstate != IEEE80211_S_SCAN) 12940ba2cbe9Sxc151355 ath_stop_scantimer(asc); 12957a1306a7Sxc151355 12960ba2cbe9Sxc151355 ATH_LOCK(asc); 12977a1306a7Sxc151355 ATH_HAL_SETLEDSTATE(ah, leds[nstate]); /* set LED */ 12987a1306a7Sxc151355 12997a1306a7Sxc151355 if (nstate == IEEE80211_S_INIT) { 13007a1306a7Sxc151355 asc->asc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); 1301129d67acSlin wang - Sun Microsystems - Beijing China /* 1302129d67acSlin wang - Sun Microsystems - Beijing China * Disable interrupts. 1303129d67acSlin wang - Sun Microsystems - Beijing China */ 13040ba2cbe9Sxc151355 ATH_HAL_INTRSET(ah, asc->asc_imask &~ HAL_INT_GLOBAL); 13050ba2cbe9Sxc151355 ATH_UNLOCK(asc); 13060ba2cbe9Sxc151355 goto done; 13070ba2cbe9Sxc151355 } 13080ba2cbe9Sxc151355 in = ic->ic_bss; 13090ba2cbe9Sxc151355 error = ath_chan_set(asc, ic->ic_curchan); 13100ba2cbe9Sxc151355 if (error != 0) { 13110ba2cbe9Sxc151355 if (nstate != IEEE80211_S_SCAN) { 13120ba2cbe9Sxc151355 ATH_UNLOCK(asc); 13130ba2cbe9Sxc151355 ieee80211_reset_chan(ic); 13147a1306a7Sxc151355 goto bad; 13157a1306a7Sxc151355 } 13160ba2cbe9Sxc151355 } 13177a1306a7Sxc151355 13187a1306a7Sxc151355 rfilt = ath_calcrxfilter(asc); 1319129d67acSlin wang - Sun Microsystems - Beijing China 13207a1306a7Sxc151355 if (nstate == IEEE80211_S_SCAN) 13210ba2cbe9Sxc151355 bssid = ic->ic_macaddr; 13227a1306a7Sxc151355 else 13237a1306a7Sxc151355 bssid = in->in_bssid; 13247a1306a7Sxc151355 ATH_HAL_SETRXFILTER(ah, rfilt); 13257a1306a7Sxc151355 13260ba2cbe9Sxc151355 if (nstate == IEEE80211_S_RUN && ic->ic_opmode != IEEE80211_M_IBSS) 13277a1306a7Sxc151355 ATH_HAL_SETASSOCID(ah, bssid, in->in_associd); 13287a1306a7Sxc151355 else 13297a1306a7Sxc151355 ATH_HAL_SETASSOCID(ah, bssid, 0); 13300ba2cbe9Sxc151355 if (ic->ic_flags & IEEE80211_F_PRIVACY) { 13317a1306a7Sxc151355 for (i = 0; i < IEEE80211_WEP_NKID; i++) { 13327a1306a7Sxc151355 if (ATH_HAL_KEYISVALID(ah, i)) 13337a1306a7Sxc151355 ATH_HAL_KEYSETMAC(ah, i, bssid); 13347a1306a7Sxc151355 } 13357a1306a7Sxc151355 } 13367a1306a7Sxc151355 13377a1306a7Sxc151355 if ((nstate == IEEE80211_S_RUN) && 13387a1306a7Sxc151355 (ostate != IEEE80211_S_RUN)) { 13397a1306a7Sxc151355 /* Configure the beacon and sleep timers. */ 13407a1306a7Sxc151355 ath_beacon_config(asc); 13417a1306a7Sxc151355 } else { 13427a1306a7Sxc151355 asc->asc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); 13437a1306a7Sxc151355 ATH_HAL_INTRSET(ah, asc->asc_imask); 13447a1306a7Sxc151355 } 13457a1306a7Sxc151355 /* 13467a1306a7Sxc151355 * Reset the rate control state. 13477a1306a7Sxc151355 */ 13487a1306a7Sxc151355 ath_rate_ctl_reset(asc, nstate); 13497a1306a7Sxc151355 13500ba2cbe9Sxc151355 ATH_UNLOCK(asc); 13510ba2cbe9Sxc151355 done: 13520ba2cbe9Sxc151355 /* 13530ba2cbe9Sxc151355 * Invoke the parent method to complete the work. 13540ba2cbe9Sxc151355 */ 13550ba2cbe9Sxc151355 error = asc->asc_newstate(ic, nstate, arg); 13560ba2cbe9Sxc151355 /* 13570ba2cbe9Sxc151355 * Finally, start any timers. 13580ba2cbe9Sxc151355 */ 13590ba2cbe9Sxc151355 if (nstate == IEEE80211_S_RUN) { 13600ba2cbe9Sxc151355 ieee80211_start_watchdog(ic, 1); 13610ba2cbe9Sxc151355 } else if ((nstate == IEEE80211_S_SCAN) && (ostate != nstate)) { 13620ba2cbe9Sxc151355 /* start ap/neighbor scan timer */ 13630ba2cbe9Sxc151355 ASSERT(asc->asc_scan_timer == 0); 13640ba2cbe9Sxc151355 asc->asc_scan_timer = timeout(ath_next_scan, (void *)asc, 13650ba2cbe9Sxc151355 drv_usectohz(ath_dwelltime * 1000)); 13660ba2cbe9Sxc151355 } 13677a1306a7Sxc151355 bad: 13687a1306a7Sxc151355 return (error); 13697a1306a7Sxc151355 } 13707a1306a7Sxc151355 13717a1306a7Sxc151355 /* 13727a1306a7Sxc151355 * Periodically recalibrate the PHY to account 13737a1306a7Sxc151355 * for temperature/environment changes. 13747a1306a7Sxc151355 */ 13757a1306a7Sxc151355 static void 13760ba2cbe9Sxc151355 ath_calibrate(ath_t *asc) 13777a1306a7Sxc151355 { 13787a1306a7Sxc151355 struct ath_hal *ah = asc->asc_ah; 13790ba2cbe9Sxc151355 HAL_BOOL iqcaldone; 13807a1306a7Sxc151355 13817a1306a7Sxc151355 asc->asc_stats.ast_per_cal++; 13827a1306a7Sxc151355 13837a1306a7Sxc151355 if (ATH_HAL_GETRFGAIN(ah) == HAL_RFGAIN_NEED_CHANGE) { 13847a1306a7Sxc151355 /* 13857a1306a7Sxc151355 * Rfgain is out of bounds, reset the chip 13867a1306a7Sxc151355 * to load new gain values. 13877a1306a7Sxc151355 */ 13887a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_HAL, "ath: ath_calibrate(): " 13897a1306a7Sxc151355 "Need change RFgain\n")); 13907a1306a7Sxc151355 asc->asc_stats.ast_per_rfgain++; 13910ba2cbe9Sxc151355 (void) ath_reset(&asc->asc_isc); 13927a1306a7Sxc151355 } 13930ba2cbe9Sxc151355 if (!ATH_HAL_CALIBRATE(ah, &asc->asc_curchan, &iqcaldone)) { 13947a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_HAL, "ath: ath_calibrate(): " 13957a1306a7Sxc151355 "calibration of channel %u failed\n", 13960ba2cbe9Sxc151355 asc->asc_curchan.channel)); 13977a1306a7Sxc151355 asc->asc_stats.ast_per_calfail++; 13987a1306a7Sxc151355 } 13997a1306a7Sxc151355 } 14007a1306a7Sxc151355 14010ba2cbe9Sxc151355 static void 14020ba2cbe9Sxc151355 ath_watchdog(void *arg) 14037a1306a7Sxc151355 { 14040ba2cbe9Sxc151355 ath_t *asc = arg; 14050ba2cbe9Sxc151355 ieee80211com_t *ic = &asc->asc_isc; 14060ba2cbe9Sxc151355 int ntimer = 0; 14070ba2cbe9Sxc151355 14080ba2cbe9Sxc151355 ATH_LOCK(asc); 14090ba2cbe9Sxc151355 ic->ic_watchdog_timer = 0; 14100ba2cbe9Sxc151355 if (!ATH_IS_RUNNING(asc)) { 14110ba2cbe9Sxc151355 ATH_UNLOCK(asc); 14120ba2cbe9Sxc151355 return; 14130ba2cbe9Sxc151355 } 14140ba2cbe9Sxc151355 14150ba2cbe9Sxc151355 if (ic->ic_state == IEEE80211_S_RUN) { 14160ba2cbe9Sxc151355 /* periodic recalibration */ 14170ba2cbe9Sxc151355 ath_calibrate(asc); 14180ba2cbe9Sxc151355 14190ba2cbe9Sxc151355 /* 14200ba2cbe9Sxc151355 * Start the background rate control thread if we 14210ba2cbe9Sxc151355 * are not configured to use a fixed xmit rate. 14220ba2cbe9Sxc151355 */ 14230ba2cbe9Sxc151355 if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) { 14240ba2cbe9Sxc151355 asc->asc_stats.ast_rate_calls ++; 14250ba2cbe9Sxc151355 if (ic->ic_opmode == IEEE80211_M_STA) 14260ba2cbe9Sxc151355 ath_rate_ctl(ic, ic->ic_bss); 14270ba2cbe9Sxc151355 else 14280ba2cbe9Sxc151355 ieee80211_iterate_nodes(&ic->ic_sta, 1429129d67acSlin wang - Sun Microsystems - Beijing China ath_rate_ctl, asc); 14300ba2cbe9Sxc151355 } 14310ba2cbe9Sxc151355 14320ba2cbe9Sxc151355 ntimer = 1; 14330ba2cbe9Sxc151355 } 14340ba2cbe9Sxc151355 ATH_UNLOCK(asc); 14350ba2cbe9Sxc151355 14360ba2cbe9Sxc151355 ieee80211_watchdog(ic); 14370ba2cbe9Sxc151355 if (ntimer != 0) 14380ba2cbe9Sxc151355 ieee80211_start_watchdog(ic, ntimer); 14390ba2cbe9Sxc151355 } 14400ba2cbe9Sxc151355 1441129d67acSlin wang - Sun Microsystems - Beijing China static void 1442129d67acSlin wang - Sun Microsystems - Beijing China ath_tx_proc(void *arg) 1443129d67acSlin wang - Sun Microsystems - Beijing China { 1444129d67acSlin wang - Sun Microsystems - Beijing China ath_t *asc = arg; 1445129d67acSlin wang - Sun Microsystems - Beijing China ath_tx_handler(asc); 1446129d67acSlin wang - Sun Microsystems - Beijing China } 1447129d67acSlin wang - Sun Microsystems - Beijing China 1448129d67acSlin wang - Sun Microsystems - Beijing China 14490ba2cbe9Sxc151355 static uint_t 14500ba2cbe9Sxc151355 ath_intr(caddr_t arg) 14510ba2cbe9Sxc151355 { 1452ff3124efSff224033 /* LINTED E_BAD_PTR_CAST_ALIGN */ 14530ba2cbe9Sxc151355 ath_t *asc = (ath_t *)arg; 14547a1306a7Sxc151355 struct ath_hal *ah = asc->asc_ah; 14557a1306a7Sxc151355 HAL_INT status; 14560ba2cbe9Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc; 14577a1306a7Sxc151355 14580ba2cbe9Sxc151355 ATH_LOCK(asc); 14590ba2cbe9Sxc151355 14600ba2cbe9Sxc151355 if (!ATH_IS_RUNNING(asc)) { 14610ba2cbe9Sxc151355 /* 14620ba2cbe9Sxc151355 * The hardware is not ready/present, don't touch anything. 14630ba2cbe9Sxc151355 * Note this can happen early on if the IRQ is shared. 14640ba2cbe9Sxc151355 */ 14650ba2cbe9Sxc151355 ATH_UNLOCK(asc); 14660ba2cbe9Sxc151355 return (DDI_INTR_UNCLAIMED); 14670ba2cbe9Sxc151355 } 14687a1306a7Sxc151355 14697a1306a7Sxc151355 if (!ATH_HAL_INTRPEND(ah)) { /* shared irq, not for us */ 14700ba2cbe9Sxc151355 ATH_UNLOCK(asc); 14717a1306a7Sxc151355 return (DDI_INTR_UNCLAIMED); 14727a1306a7Sxc151355 } 14737a1306a7Sxc151355 14747a1306a7Sxc151355 ATH_HAL_GETISR(ah, &status); 14757a1306a7Sxc151355 status &= asc->asc_imask; 14767a1306a7Sxc151355 if (status & HAL_INT_FATAL) { 14777a1306a7Sxc151355 asc->asc_stats.ast_hardware++; 14787a1306a7Sxc151355 goto reset; 14797a1306a7Sxc151355 } else if (status & HAL_INT_RXORN) { 14807a1306a7Sxc151355 asc->asc_stats.ast_rxorn++; 14817a1306a7Sxc151355 goto reset; 14827a1306a7Sxc151355 } else { 14837a1306a7Sxc151355 if (status & HAL_INT_RXEOL) { 14847a1306a7Sxc151355 asc->asc_stats.ast_rxeol++; 14857a1306a7Sxc151355 asc->asc_rxlink = NULL; 14867a1306a7Sxc151355 } 14877a1306a7Sxc151355 if (status & HAL_INT_TXURN) { 14887a1306a7Sxc151355 asc->asc_stats.ast_txurn++; 14897a1306a7Sxc151355 ATH_HAL_UPDATETXTRIGLEVEL(ah, AH_TRUE); 14907a1306a7Sxc151355 } 14910ba2cbe9Sxc151355 14927a1306a7Sxc151355 if (status & HAL_INT_RX) { 14937a1306a7Sxc151355 asc->asc_rx_pend = 1; 14947a1306a7Sxc151355 ddi_trigger_softintr(asc->asc_softint_id); 14957a1306a7Sxc151355 } 14967a1306a7Sxc151355 if (status & HAL_INT_TX) { 1497129d67acSlin wang - Sun Microsystems - Beijing China if (ddi_taskq_dispatch(asc->asc_tq, ath_tx_proc, 1498129d67acSlin wang - Sun Microsystems - Beijing China asc, DDI_NOSLEEP) != DDI_SUCCESS) { 1499129d67acSlin wang - Sun Microsystems - Beijing China ath_problem("ath: ath_intr(): " 1500129d67acSlin wang - Sun Microsystems - Beijing China "No memory available for tx taskq\n"); 1501129d67acSlin wang - Sun Microsystems - Beijing China } 15027a1306a7Sxc151355 } 15030ba2cbe9Sxc151355 ATH_UNLOCK(asc); 15047a1306a7Sxc151355 15057a1306a7Sxc151355 if (status & HAL_INT_SWBA) { 15067a1306a7Sxc151355 /* This will occur only in Host-AP or Ad-Hoc mode */ 15077a1306a7Sxc151355 return (DDI_INTR_CLAIMED); 15087a1306a7Sxc151355 } 1509129d67acSlin wang - Sun Microsystems - Beijing China 15107a1306a7Sxc151355 if (status & HAL_INT_BMISS) { 15110ba2cbe9Sxc151355 if (ic->ic_state == IEEE80211_S_RUN) { 15120ba2cbe9Sxc151355 (void) ieee80211_new_state(ic, 15137a1306a7Sxc151355 IEEE80211_S_ASSOC, -1); 15147a1306a7Sxc151355 } 15157a1306a7Sxc151355 } 1516129d67acSlin wang - Sun Microsystems - Beijing China 15177a1306a7Sxc151355 } 15187a1306a7Sxc151355 15197a1306a7Sxc151355 return (DDI_INTR_CLAIMED); 15207a1306a7Sxc151355 reset: 15210ba2cbe9Sxc151355 (void) ath_reset(ic); 15220ba2cbe9Sxc151355 ATH_UNLOCK(asc); 15237a1306a7Sxc151355 return (DDI_INTR_CLAIMED); 15247a1306a7Sxc151355 } 15257a1306a7Sxc151355 15267a1306a7Sxc151355 static uint_t 15277a1306a7Sxc151355 ath_softint_handler(caddr_t data) 15287a1306a7Sxc151355 { 1529ff3124efSff224033 /* LINTED E_BAD_PTR_CAST_ALIGN */ 15307a1306a7Sxc151355 ath_t *asc = (ath_t *)data; 15317a1306a7Sxc151355 15327a1306a7Sxc151355 /* 15337a1306a7Sxc151355 * Check if the soft interrupt is triggered by another 15347a1306a7Sxc151355 * driver at the same level. 15357a1306a7Sxc151355 */ 15360ba2cbe9Sxc151355 ATH_LOCK(asc); 15377a1306a7Sxc151355 if (asc->asc_rx_pend) { /* Soft interrupt for this driver */ 15387a1306a7Sxc151355 asc->asc_rx_pend = 0; 15390ba2cbe9Sxc151355 ATH_UNLOCK(asc); 15400ba2cbe9Sxc151355 ath_rx_handler(asc); 15417a1306a7Sxc151355 return (DDI_INTR_CLAIMED); 15427a1306a7Sxc151355 } 15430ba2cbe9Sxc151355 ATH_UNLOCK(asc); 15447a1306a7Sxc151355 return (DDI_INTR_UNCLAIMED); 15457a1306a7Sxc151355 } 15467a1306a7Sxc151355 15477a1306a7Sxc151355 /* 15487a1306a7Sxc151355 * following are gld callback routine 15497a1306a7Sxc151355 * ath_gld_send, ath_gld_ioctl, ath_gld_gstat 15507a1306a7Sxc151355 * are listed in other corresponding sections. 15517a1306a7Sxc151355 * reset the hardware w/o losing operational state. this is 15527a1306a7Sxc151355 * basically a more efficient way of doing ath_gld_stop, ath_gld_start, 15537a1306a7Sxc151355 * followed by state transitions to the current 802.11 15547a1306a7Sxc151355 * operational state. used to recover from errors rx overrun 15557a1306a7Sxc151355 * and to reset the hardware when rf gain settings must be reset. 15567a1306a7Sxc151355 */ 15577a1306a7Sxc151355 15580ba2cbe9Sxc151355 static void 15590ba2cbe9Sxc151355 ath_stop_locked(ath_t *asc) 15607a1306a7Sxc151355 { 15610ba2cbe9Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc; 15627a1306a7Sxc151355 struct ath_hal *ah = asc->asc_ah; 15637a1306a7Sxc151355 15640ba2cbe9Sxc151355 ATH_LOCK_ASSERT(asc); 1565f11a3086Sxc151355 if (!asc->asc_isrunning) 1566f11a3086Sxc151355 return; 1567f11a3086Sxc151355 15680ba2cbe9Sxc151355 /* 15690ba2cbe9Sxc151355 * Shutdown the hardware and driver: 15700ba2cbe9Sxc151355 * reset 802.11 state machine 15710ba2cbe9Sxc151355 * turn off timers 15720ba2cbe9Sxc151355 * disable interrupts 15730ba2cbe9Sxc151355 * turn off the radio 15740ba2cbe9Sxc151355 * clear transmit machinery 15750ba2cbe9Sxc151355 * clear receive machinery 15760ba2cbe9Sxc151355 * drain and release tx queues 15770ba2cbe9Sxc151355 * reclaim beacon resources 15780ba2cbe9Sxc151355 * power down hardware 15790ba2cbe9Sxc151355 * 15800ba2cbe9Sxc151355 * Note that some of this work is not possible if the 15810ba2cbe9Sxc151355 * hardware is gone (invalid). 15820ba2cbe9Sxc151355 */ 15830ba2cbe9Sxc151355 ATH_UNLOCK(asc); 15840ba2cbe9Sxc151355 ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 15850ba2cbe9Sxc151355 ieee80211_stop_watchdog(ic); 15860ba2cbe9Sxc151355 ATH_LOCK(asc); 15877a1306a7Sxc151355 ATH_HAL_INTRSET(ah, 0); 15887a1306a7Sxc151355 ath_draintxq(asc); 1589f11a3086Sxc151355 if (!asc->asc_invalid) { 15907a1306a7Sxc151355 ath_stoprecv(asc); 15910ba2cbe9Sxc151355 ATH_HAL_PHYDISABLE(ah); 15920ba2cbe9Sxc151355 } else { 15937a1306a7Sxc151355 asc->asc_rxlink = NULL; 15940ba2cbe9Sxc151355 } 1595f11a3086Sxc151355 asc->asc_isrunning = 0; 15960ba2cbe9Sxc151355 } 15977a1306a7Sxc151355 15980ba2cbe9Sxc151355 static void 15990ba2cbe9Sxc151355 ath_m_stop(void *arg) 16000ba2cbe9Sxc151355 { 16010ba2cbe9Sxc151355 ath_t *asc = arg; 16020ba2cbe9Sxc151355 struct ath_hal *ah = asc->asc_ah; 16030ba2cbe9Sxc151355 16040ba2cbe9Sxc151355 ATH_LOCK(asc); 16050ba2cbe9Sxc151355 ath_stop_locked(asc); 16060ba2cbe9Sxc151355 ATH_HAL_SETPOWER(ah, HAL_PM_AWAKE); 16077a1306a7Sxc151355 asc->asc_invalid = 1; 16080ba2cbe9Sxc151355 ATH_UNLOCK(asc); 16097a1306a7Sxc151355 } 16107a1306a7Sxc151355 1611f11a3086Sxc151355 static int 1612f11a3086Sxc151355 ath_start_locked(ath_t *asc) 16137a1306a7Sxc151355 { 16140ba2cbe9Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc; 16157a1306a7Sxc151355 struct ath_hal *ah = asc->asc_ah; 16167a1306a7Sxc151355 HAL_STATUS status; 16177a1306a7Sxc151355 1618f11a3086Sxc151355 ATH_LOCK_ASSERT(asc); 16197a1306a7Sxc151355 16207a1306a7Sxc151355 /* 16217a1306a7Sxc151355 * The basic interface to setting the hardware in a good 16227a1306a7Sxc151355 * state is ``reset''. On return the hardware is known to 16237a1306a7Sxc151355 * be powered up and with interrupts disabled. This must 16247a1306a7Sxc151355 * be followed by initialization of the appropriate bits 16257a1306a7Sxc151355 * and then setup of the interrupt mask. 16267a1306a7Sxc151355 */ 16270ba2cbe9Sxc151355 asc->asc_curchan.channel = ic->ic_curchan->ich_freq; 16280ba2cbe9Sxc151355 asc->asc_curchan.channelFlags = ath_chan2flags(ic, ic->ic_curchan); 16290ba2cbe9Sxc151355 if (!ATH_HAL_RESET(ah, (HAL_OPMODE)ic->ic_opmode, 16300ba2cbe9Sxc151355 &asc->asc_curchan, AH_FALSE, &status)) { 16310ba2cbe9Sxc151355 ATH_DEBUG((ATH_DBG_HAL, "ath: ath_m_start(): " 16323caf1114Sxc151355 "reset hardware failed: '%s' (HAL status %u)\n", 16333caf1114Sxc151355 ath_get_hal_status_desc(status), status)); 16340ba2cbe9Sxc151355 return (ENOTACTIVE); 16357a1306a7Sxc151355 } 16367a1306a7Sxc151355 16370ba2cbe9Sxc151355 (void) ath_startrecv(asc); 16387a1306a7Sxc151355 16397a1306a7Sxc151355 /* 16407a1306a7Sxc151355 * Enable interrupts. 16417a1306a7Sxc151355 */ 16427a1306a7Sxc151355 asc->asc_imask = HAL_INT_RX | HAL_INT_TX 16437a1306a7Sxc151355 | HAL_INT_RXEOL | HAL_INT_RXORN 16447a1306a7Sxc151355 | HAL_INT_FATAL | HAL_INT_GLOBAL; 16457a1306a7Sxc151355 ATH_HAL_INTRSET(ah, asc->asc_imask); 16467a1306a7Sxc151355 16477a1306a7Sxc151355 /* 16487a1306a7Sxc151355 * The hardware should be ready to go now so it's safe 16497a1306a7Sxc151355 * to kick the 802.11 state machine as it's likely to 16507a1306a7Sxc151355 * immediately call back to us to send mgmt frames. 16517a1306a7Sxc151355 */ 16520ba2cbe9Sxc151355 ath_chan_change(asc, ic->ic_curchan); 1653f11a3086Sxc151355 1654f11a3086Sxc151355 asc->asc_isrunning = 1; 1655f11a3086Sxc151355 1656f11a3086Sxc151355 return (0); 1657f11a3086Sxc151355 } 1658f11a3086Sxc151355 1659f11a3086Sxc151355 int 1660f11a3086Sxc151355 ath_m_start(void *arg) 1661f11a3086Sxc151355 { 1662f11a3086Sxc151355 ath_t *asc = arg; 1663f11a3086Sxc151355 int err; 1664f11a3086Sxc151355 1665f11a3086Sxc151355 ATH_LOCK(asc); 1666f11a3086Sxc151355 /* 1667f11a3086Sxc151355 * Stop anything previously setup. This is safe 1668f11a3086Sxc151355 * whether this is the first time through or not. 1669f11a3086Sxc151355 */ 1670f11a3086Sxc151355 ath_stop_locked(asc); 1671f11a3086Sxc151355 1672f11a3086Sxc151355 if ((err = ath_start_locked(asc)) != 0) { 1673f11a3086Sxc151355 ATH_UNLOCK(asc); 1674f11a3086Sxc151355 return (err); 1675f11a3086Sxc151355 } 1676f11a3086Sxc151355 16777a1306a7Sxc151355 asc->asc_invalid = 0; 16780ba2cbe9Sxc151355 ATH_UNLOCK(asc); 1679f11a3086Sxc151355 16800ba2cbe9Sxc151355 return (0); 16817a1306a7Sxc151355 } 16827a1306a7Sxc151355 16837a1306a7Sxc151355 16840ba2cbe9Sxc151355 static int 16850ba2cbe9Sxc151355 ath_m_unicst(void *arg, const uint8_t *macaddr) 16867a1306a7Sxc151355 { 16870ba2cbe9Sxc151355 ath_t *asc = arg; 16887a1306a7Sxc151355 struct ath_hal *ah = asc->asc_ah; 16897a1306a7Sxc151355 16907a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_GLD, "ath: ath_gld_saddr(): " 16917a1306a7Sxc151355 "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", 16927a1306a7Sxc151355 macaddr[0], macaddr[1], macaddr[2], 16937a1306a7Sxc151355 macaddr[3], macaddr[4], macaddr[5])); 16947a1306a7Sxc151355 16950ba2cbe9Sxc151355 ATH_LOCK(asc); 16960ba2cbe9Sxc151355 IEEE80211_ADDR_COPY(asc->asc_isc.ic_macaddr, macaddr); 16970ba2cbe9Sxc151355 ATH_HAL_SETMAC(ah, asc->asc_isc.ic_macaddr); 16987a1306a7Sxc151355 16990ba2cbe9Sxc151355 (void) ath_reset(&asc->asc_isc); 17000ba2cbe9Sxc151355 ATH_UNLOCK(asc); 17010ba2cbe9Sxc151355 return (0); 17027a1306a7Sxc151355 } 17037a1306a7Sxc151355 17047a1306a7Sxc151355 static int 17050ba2cbe9Sxc151355 ath_m_promisc(void *arg, boolean_t on) 17067a1306a7Sxc151355 { 17070ba2cbe9Sxc151355 ath_t *asc = arg; 17087a1306a7Sxc151355 struct ath_hal *ah = asc->asc_ah; 17097a1306a7Sxc151355 uint32_t rfilt; 17107a1306a7Sxc151355 17110ba2cbe9Sxc151355 ATH_LOCK(asc); 17127a1306a7Sxc151355 rfilt = ATH_HAL_GETRXFILTER(ah); 17130ba2cbe9Sxc151355 if (on) 17140ba2cbe9Sxc151355 rfilt |= HAL_RX_FILTER_PROM; 17150ba2cbe9Sxc151355 else 17167a1306a7Sxc151355 rfilt &= ~HAL_RX_FILTER_PROM; 17173caf1114Sxc151355 asc->asc_promisc = on; 17187a1306a7Sxc151355 ATH_HAL_SETRXFILTER(ah, rfilt); 17190ba2cbe9Sxc151355 ATH_UNLOCK(asc); 17207a1306a7Sxc151355 17210ba2cbe9Sxc151355 return (0); 17227a1306a7Sxc151355 } 17237a1306a7Sxc151355 17247a1306a7Sxc151355 static int 17250ba2cbe9Sxc151355 ath_m_multicst(void *arg, boolean_t add, const uint8_t *mca) 17267a1306a7Sxc151355 { 17270ba2cbe9Sxc151355 ath_t *asc = arg; 17280ba2cbe9Sxc151355 struct ath_hal *ah = asc->asc_ah; 17293caf1114Sxc151355 uint32_t val, index, bit; 17307a1306a7Sxc151355 uint8_t pos; 17313caf1114Sxc151355 uint32_t *mfilt = asc->asc_mcast_hash; 17327a1306a7Sxc151355 17330ba2cbe9Sxc151355 ATH_LOCK(asc); 1734129d67acSlin wang - Sun Microsystems - Beijing China 17357a1306a7Sxc151355 /* calculate XOR of eight 6bit values */ 17367a1306a7Sxc151355 val = ATH_LE_READ_4(mca + 0); 17377a1306a7Sxc151355 pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; 17387a1306a7Sxc151355 val = ATH_LE_READ_4(mca + 3); 17397a1306a7Sxc151355 pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; 17407a1306a7Sxc151355 pos &= 0x3f; 17413caf1114Sxc151355 index = pos / 32; 17423caf1114Sxc151355 bit = 1 << (pos % 32); 17433caf1114Sxc151355 17443caf1114Sxc151355 if (add) { /* enable multicast */ 17453caf1114Sxc151355 asc->asc_mcast_refs[pos]++; 17463caf1114Sxc151355 mfilt[index] |= bit; 17473caf1114Sxc151355 } else { /* disable multicast */ 17483caf1114Sxc151355 if (--asc->asc_mcast_refs[pos] == 0) 17493caf1114Sxc151355 mfilt[index] &= ~bit; 17503caf1114Sxc151355 } 17517a1306a7Sxc151355 ATH_HAL_SETMCASTFILTER(ah, mfilt[0], mfilt[1]); 17527a1306a7Sxc151355 17530ba2cbe9Sxc151355 ATH_UNLOCK(asc); 17540ba2cbe9Sxc151355 return (0); 17557a1306a7Sxc151355 } 1756bcb5c89dSSowmini Varadhan /* 1757bcb5c89dSSowmini Varadhan * callback functions for /get/set properties 1758bcb5c89dSSowmini Varadhan */ 1759bcb5c89dSSowmini Varadhan static int 1760bcb5c89dSSowmini Varadhan ath_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 1761bcb5c89dSSowmini Varadhan uint_t wldp_length, const void *wldp_buf) 1762bcb5c89dSSowmini Varadhan { 1763bcb5c89dSSowmini Varadhan ath_t *asc = arg; 1764bcb5c89dSSowmini Varadhan int err; 1765bcb5c89dSSowmini Varadhan 1766bcb5c89dSSowmini Varadhan err = ieee80211_setprop(&asc->asc_isc, pr_name, wldp_pr_num, 1767bcb5c89dSSowmini Varadhan wldp_length, wldp_buf); 1768bcb5c89dSSowmini Varadhan 1769bcb5c89dSSowmini Varadhan ATH_LOCK(asc); 1770bcb5c89dSSowmini Varadhan 1771bcb5c89dSSowmini Varadhan if (err == ENETRESET) { 1772bcb5c89dSSowmini Varadhan if (ATH_IS_RUNNING(asc)) { 1773bcb5c89dSSowmini Varadhan ATH_UNLOCK(asc); 1774bcb5c89dSSowmini Varadhan (void) ath_m_start(asc); 1775bcb5c89dSSowmini Varadhan (void) ieee80211_new_state(&asc->asc_isc, 1776bcb5c89dSSowmini Varadhan IEEE80211_S_SCAN, -1); 1777bcb5c89dSSowmini Varadhan ATH_LOCK(asc); 1778bcb5c89dSSowmini Varadhan } 1779bcb5c89dSSowmini Varadhan err = 0; 1780bcb5c89dSSowmini Varadhan } 1781bcb5c89dSSowmini Varadhan 1782bcb5c89dSSowmini Varadhan ATH_UNLOCK(asc); 1783bcb5c89dSSowmini Varadhan 1784bcb5c89dSSowmini Varadhan return (err); 1785bcb5c89dSSowmini Varadhan } 1786*0dc2366fSVenugopal Iyer 1787bcb5c89dSSowmini Varadhan static int 1788bcb5c89dSSowmini Varadhan ath_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 1789*0dc2366fSVenugopal Iyer uint_t wldp_length, void *wldp_buf) 1790bcb5c89dSSowmini Varadhan { 1791bcb5c89dSSowmini Varadhan ath_t *asc = arg; 1792bcb5c89dSSowmini Varadhan int err = 0; 1793bcb5c89dSSowmini Varadhan 1794bcb5c89dSSowmini Varadhan err = ieee80211_getprop(&asc->asc_isc, pr_name, wldp_pr_num, 1795*0dc2366fSVenugopal Iyer wldp_length, wldp_buf); 1796bcb5c89dSSowmini Varadhan 1797bcb5c89dSSowmini Varadhan return (err); 1798bcb5c89dSSowmini Varadhan } 1799bcb5c89dSSowmini Varadhan 18007a1306a7Sxc151355 static void 1801*0dc2366fSVenugopal Iyer ath_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 1802*0dc2366fSVenugopal Iyer mac_prop_info_handle_t mph) 1803*0dc2366fSVenugopal Iyer { 1804*0dc2366fSVenugopal Iyer ath_t *asc = arg; 1805*0dc2366fSVenugopal Iyer 1806*0dc2366fSVenugopal Iyer ieee80211_propinfo(&asc->asc_isc, pr_name, wldp_pr_num, mph); 1807*0dc2366fSVenugopal Iyer } 1808*0dc2366fSVenugopal Iyer 1809*0dc2366fSVenugopal Iyer static void 18100ba2cbe9Sxc151355 ath_m_ioctl(void *arg, queue_t *wq, mblk_t *mp) 18117a1306a7Sxc151355 { 18120ba2cbe9Sxc151355 ath_t *asc = arg; 18130ba2cbe9Sxc151355 int32_t err; 18147a1306a7Sxc151355 18150ba2cbe9Sxc151355 err = ieee80211_ioctl(&asc->asc_isc, wq, mp); 18160ba2cbe9Sxc151355 ATH_LOCK(asc); 18170ba2cbe9Sxc151355 if (err == ENETRESET) { 18180ba2cbe9Sxc151355 if (ATH_IS_RUNNING(asc)) { 18190ba2cbe9Sxc151355 ATH_UNLOCK(asc); 18200ba2cbe9Sxc151355 (void) ath_m_start(asc); 18210ba2cbe9Sxc151355 (void) ieee80211_new_state(&asc->asc_isc, 18220ba2cbe9Sxc151355 IEEE80211_S_SCAN, -1); 18230ba2cbe9Sxc151355 ATH_LOCK(asc); 18247a1306a7Sxc151355 } 18257a1306a7Sxc151355 } 18260ba2cbe9Sxc151355 ATH_UNLOCK(asc); 18277a1306a7Sxc151355 } 18287a1306a7Sxc151355 18297a1306a7Sxc151355 static int 18300ba2cbe9Sxc151355 ath_m_stat(void *arg, uint_t stat, uint64_t *val) 18317a1306a7Sxc151355 { 18320ba2cbe9Sxc151355 ath_t *asc = arg; 18330ba2cbe9Sxc151355 ieee80211com_t *ic = (ieee80211com_t *)asc; 18340ba2cbe9Sxc151355 struct ieee80211_node *in = ic->ic_bss; 18357a1306a7Sxc151355 struct ieee80211_rateset *rs = &in->in_rates; 18367a1306a7Sxc151355 18370ba2cbe9Sxc151355 ATH_LOCK(asc); 18380ba2cbe9Sxc151355 switch (stat) { 18390ba2cbe9Sxc151355 case MAC_STAT_IFSPEED: 18400ba2cbe9Sxc151355 *val = (rs->ir_rates[in->in_txrate] & IEEE80211_RATE_VAL) / 2 * 18410ba2cbe9Sxc151355 1000000ull; 18420ba2cbe9Sxc151355 break; 18430ba2cbe9Sxc151355 case MAC_STAT_NOXMTBUF: 18440ba2cbe9Sxc151355 *val = asc->asc_stats.ast_tx_nobuf + 18450ba2cbe9Sxc151355 asc->asc_stats.ast_tx_nobufmgt; 18460ba2cbe9Sxc151355 break; 18470ba2cbe9Sxc151355 case MAC_STAT_IERRORS: 18480ba2cbe9Sxc151355 *val = asc->asc_stats.ast_rx_tooshort; 18490ba2cbe9Sxc151355 break; 18500ba2cbe9Sxc151355 case MAC_STAT_RBYTES: 18510ba2cbe9Sxc151355 *val = ic->ic_stats.is_rx_bytes; 18520ba2cbe9Sxc151355 break; 18530ba2cbe9Sxc151355 case MAC_STAT_IPACKETS: 18540ba2cbe9Sxc151355 *val = ic->ic_stats.is_rx_frags; 18550ba2cbe9Sxc151355 break; 18560ba2cbe9Sxc151355 case MAC_STAT_OBYTES: 18570ba2cbe9Sxc151355 *val = ic->ic_stats.is_tx_bytes; 18580ba2cbe9Sxc151355 break; 18590ba2cbe9Sxc151355 case MAC_STAT_OPACKETS: 18600ba2cbe9Sxc151355 *val = ic->ic_stats.is_tx_frags; 18610ba2cbe9Sxc151355 break; 1862cd5560efSxh158540 case MAC_STAT_OERRORS: 18630ba2cbe9Sxc151355 case WIFI_STAT_TX_FAILED: 18640ba2cbe9Sxc151355 *val = asc->asc_stats.ast_tx_fifoerr + 1865cd5560efSxh158540 asc->asc_stats.ast_tx_xretries + 1866cd5560efSxh158540 asc->asc_stats.ast_tx_discard; 18670ba2cbe9Sxc151355 break; 18680ba2cbe9Sxc151355 case WIFI_STAT_TX_RETRANS: 18690ba2cbe9Sxc151355 *val = asc->asc_stats.ast_tx_xretries; 18700ba2cbe9Sxc151355 break; 18710ba2cbe9Sxc151355 case WIFI_STAT_FCS_ERRORS: 18720ba2cbe9Sxc151355 *val = asc->asc_stats.ast_rx_crcerr; 18730ba2cbe9Sxc151355 break; 18740ba2cbe9Sxc151355 case WIFI_STAT_WEP_ERRORS: 18750ba2cbe9Sxc151355 *val = asc->asc_stats.ast_rx_badcrypt; 18760ba2cbe9Sxc151355 break; 18770ba2cbe9Sxc151355 case WIFI_STAT_TX_FRAGS: 18780ba2cbe9Sxc151355 case WIFI_STAT_MCAST_TX: 18790ba2cbe9Sxc151355 case WIFI_STAT_RTS_SUCCESS: 18800ba2cbe9Sxc151355 case WIFI_STAT_RTS_FAILURE: 18810ba2cbe9Sxc151355 case WIFI_STAT_ACK_FAILURE: 18820ba2cbe9Sxc151355 case WIFI_STAT_RX_FRAGS: 18830ba2cbe9Sxc151355 case WIFI_STAT_MCAST_RX: 18840ba2cbe9Sxc151355 case WIFI_STAT_RX_DUPS: 18850ba2cbe9Sxc151355 ATH_UNLOCK(asc); 18860ba2cbe9Sxc151355 return (ieee80211_stat(ic, stat, val)); 18870ba2cbe9Sxc151355 default: 18880ba2cbe9Sxc151355 ATH_UNLOCK(asc); 18890ba2cbe9Sxc151355 return (ENOTSUP); 18900ba2cbe9Sxc151355 } 18910ba2cbe9Sxc151355 ATH_UNLOCK(asc); 18927a1306a7Sxc151355 18930ba2cbe9Sxc151355 return (0); 18947a1306a7Sxc151355 } 18957a1306a7Sxc151355 18967a1306a7Sxc151355 static int 1897f11a3086Sxc151355 ath_pci_setup(ath_t *asc) 1898f11a3086Sxc151355 { 1899f11a3086Sxc151355 uint16_t command; 1900f11a3086Sxc151355 1901f11a3086Sxc151355 /* 1902f11a3086Sxc151355 * Enable memory mapping and bus mastering 1903f11a3086Sxc151355 */ 1904f11a3086Sxc151355 ASSERT(asc != NULL); 1905f11a3086Sxc151355 command = pci_config_get16(asc->asc_cfg_handle, PCI_CONF_COMM); 1906f11a3086Sxc151355 command |= PCI_COMM_MAE | PCI_COMM_ME; 1907f11a3086Sxc151355 pci_config_put16(asc->asc_cfg_handle, PCI_CONF_COMM, command); 1908f11a3086Sxc151355 command = pci_config_get16(asc->asc_cfg_handle, PCI_CONF_COMM); 1909f11a3086Sxc151355 if ((command & PCI_COMM_MAE) == 0) { 1910f11a3086Sxc151355 ath_problem("ath: ath_pci_setup(): " 1911f11a3086Sxc151355 "failed to enable memory mapping\n"); 1912f11a3086Sxc151355 return (EIO); 1913f11a3086Sxc151355 } 1914f11a3086Sxc151355 if ((command & PCI_COMM_ME) == 0) { 1915f11a3086Sxc151355 ath_problem("ath: ath_pci_setup(): " 1916f11a3086Sxc151355 "failed to enable bus mastering\n"); 1917f11a3086Sxc151355 return (EIO); 1918f11a3086Sxc151355 } 1919f11a3086Sxc151355 ATH_DEBUG((ATH_DBG_INIT, "ath: ath_pci_setup(): " 1920f11a3086Sxc151355 "set command reg to 0x%x \n", command)); 1921f11a3086Sxc151355 1922f11a3086Sxc151355 return (0); 1923f11a3086Sxc151355 } 1924f11a3086Sxc151355 1925f11a3086Sxc151355 static int 1926f11a3086Sxc151355 ath_resume(dev_info_t *devinfo) 1927f11a3086Sxc151355 { 1928f11a3086Sxc151355 ath_t *asc; 1929f11a3086Sxc151355 int ret = DDI_SUCCESS; 1930f11a3086Sxc151355 1931f11a3086Sxc151355 asc = ddi_get_soft_state(ath_soft_state_p, ddi_get_instance(devinfo)); 1932f11a3086Sxc151355 if (asc == NULL) { 1933f11a3086Sxc151355 ATH_DEBUG((ATH_DBG_SUSPEND, "ath: ath_resume(): " 1934f11a3086Sxc151355 "failed to get soft state\n")); 1935f11a3086Sxc151355 return (DDI_FAILURE); 1936f11a3086Sxc151355 } 1937f11a3086Sxc151355 1938f11a3086Sxc151355 ATH_LOCK(asc); 1939f11a3086Sxc151355 /* 1940f11a3086Sxc151355 * Set up config space command register(s). Refuse 1941f11a3086Sxc151355 * to resume on failure. 1942f11a3086Sxc151355 */ 1943f11a3086Sxc151355 if (ath_pci_setup(asc) != 0) { 1944f11a3086Sxc151355 ATH_DEBUG((ATH_DBG_SUSPEND, "ath: ath_resume(): " 1945f11a3086Sxc151355 "ath_pci_setup() failed\n")); 1946f11a3086Sxc151355 ATH_UNLOCK(asc); 1947f11a3086Sxc151355 return (DDI_FAILURE); 1948f11a3086Sxc151355 } 1949f11a3086Sxc151355 1950f11a3086Sxc151355 if (!asc->asc_invalid) 1951f11a3086Sxc151355 ret = ath_start_locked(asc); 1952f11a3086Sxc151355 ATH_UNLOCK(asc); 1953f11a3086Sxc151355 1954f11a3086Sxc151355 return (ret); 1955f11a3086Sxc151355 } 1956f11a3086Sxc151355 1957f11a3086Sxc151355 static int 19587a1306a7Sxc151355 ath_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd) 19597a1306a7Sxc151355 { 19607a1306a7Sxc151355 ath_t *asc; 19610ba2cbe9Sxc151355 ieee80211com_t *ic; 19627a1306a7Sxc151355 struct ath_hal *ah; 19637a1306a7Sxc151355 uint8_t csz; 19647a1306a7Sxc151355 HAL_STATUS status; 19657a1306a7Sxc151355 caddr_t regs; 19667a1306a7Sxc151355 uint32_t i, val; 1967f11a3086Sxc151355 uint16_t vendor_id, device_id; 19687a1306a7Sxc151355 const char *athname; 19697a1306a7Sxc151355 int32_t ath_countrycode = CTRY_DEFAULT; /* country code */ 19707a1306a7Sxc151355 int32_t err, ath_regdomain = 0; /* regulatory domain */ 19717a1306a7Sxc151355 char strbuf[32]; 19720ba2cbe9Sxc151355 int instance; 19730ba2cbe9Sxc151355 wifi_data_t wd = { 0 }; 19740ba2cbe9Sxc151355 mac_register_t *macp; 19757a1306a7Sxc151355 1976f11a3086Sxc151355 switch (cmd) { 1977f11a3086Sxc151355 case DDI_ATTACH: 1978f11a3086Sxc151355 break; 1979f11a3086Sxc151355 1980f11a3086Sxc151355 case DDI_RESUME: 1981f11a3086Sxc151355 return (ath_resume(devinfo)); 1982f11a3086Sxc151355 1983f11a3086Sxc151355 default: 19847a1306a7Sxc151355 return (DDI_FAILURE); 1985f11a3086Sxc151355 } 19867a1306a7Sxc151355 19870ba2cbe9Sxc151355 instance = ddi_get_instance(devinfo); 19880ba2cbe9Sxc151355 if (ddi_soft_state_zalloc(ath_soft_state_p, instance) != DDI_SUCCESS) { 19897a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 19907a1306a7Sxc151355 "Unable to alloc softstate\n")); 19917a1306a7Sxc151355 return (DDI_FAILURE); 19927a1306a7Sxc151355 } 19937a1306a7Sxc151355 19947a1306a7Sxc151355 asc = ddi_get_soft_state(ath_soft_state_p, ddi_get_instance(devinfo)); 19950ba2cbe9Sxc151355 ic = (ieee80211com_t *)asc; 19967a1306a7Sxc151355 asc->asc_dev = devinfo; 19977a1306a7Sxc151355 19987a1306a7Sxc151355 mutex_init(&asc->asc_genlock, NULL, MUTEX_DRIVER, NULL); 19997a1306a7Sxc151355 mutex_init(&asc->asc_txbuflock, NULL, MUTEX_DRIVER, NULL); 20007a1306a7Sxc151355 mutex_init(&asc->asc_rxbuflock, NULL, MUTEX_DRIVER, NULL); 20010ba2cbe9Sxc151355 mutex_init(&asc->asc_resched_lock, NULL, MUTEX_DRIVER, NULL); 20027a1306a7Sxc151355 20037a1306a7Sxc151355 err = pci_config_setup(devinfo, &asc->asc_cfg_handle); 20047a1306a7Sxc151355 if (err != DDI_SUCCESS) { 20057a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 20067a1306a7Sxc151355 "pci_config_setup() failed")); 20077a1306a7Sxc151355 goto attach_fail0; 20087a1306a7Sxc151355 } 20097a1306a7Sxc151355 2010f11a3086Sxc151355 if (ath_pci_setup(asc) != 0) 2011f11a3086Sxc151355 goto attach_fail1; 2012f11a3086Sxc151355 20131f156c6aSxc151355 /* 20141f156c6aSxc151355 * Cache line size is used to size and align various 20151f156c6aSxc151355 * structures used to communicate with the hardware. 20161f156c6aSxc151355 */ 20177a1306a7Sxc151355 csz = pci_config_get8(asc->asc_cfg_handle, PCI_CONF_CACHE_LINESZ); 20181f156c6aSxc151355 if (csz == 0) { 20191f156c6aSxc151355 /* 20201f156c6aSxc151355 * We must have this setup properly for rx buffer 20211f156c6aSxc151355 * DMA to work so force a reasonable value here if it 20221f156c6aSxc151355 * comes up zero. 20231f156c6aSxc151355 */ 20241f156c6aSxc151355 csz = ATH_DEF_CACHE_BYTES / sizeof (uint32_t); 20251f156c6aSxc151355 pci_config_put8(asc->asc_cfg_handle, PCI_CONF_CACHE_LINESZ, 20261f156c6aSxc151355 csz); 20271f156c6aSxc151355 } 20287a1306a7Sxc151355 asc->asc_cachelsz = csz << 2; 20297a1306a7Sxc151355 vendor_id = pci_config_get16(asc->asc_cfg_handle, PCI_CONF_VENID); 20307a1306a7Sxc151355 device_id = pci_config_get16(asc->asc_cfg_handle, PCI_CONF_DEVID); 20317a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): vendor 0x%x, " 20327a1306a7Sxc151355 "device id 0x%x, cache size %d\n", vendor_id, device_id, csz)); 20337a1306a7Sxc151355 20347a1306a7Sxc151355 athname = ath_hal_probe(vendor_id, device_id); 20357a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): athname: %s\n", 20367a1306a7Sxc151355 athname ? athname : "Atheros ???")); 20377a1306a7Sxc151355 20387a1306a7Sxc151355 pci_config_put8(asc->asc_cfg_handle, PCI_CONF_LATENCY_TIMER, 0xa8); 20397a1306a7Sxc151355 val = pci_config_get32(asc->asc_cfg_handle, 0x40); 20407a1306a7Sxc151355 if ((val & 0x0000ff00) != 0) 20417a1306a7Sxc151355 pci_config_put32(asc->asc_cfg_handle, 0x40, val & 0xffff00ff); 20427a1306a7Sxc151355 20437a1306a7Sxc151355 err = ddi_regs_map_setup(devinfo, 1, 20447a1306a7Sxc151355 ®s, 0, 0, &ath_reg_accattr, &asc->asc_io_handle); 20457a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 20467a1306a7Sxc151355 "regs map1 = %x err=%d\n", regs, err)); 20477a1306a7Sxc151355 if (err != DDI_SUCCESS) { 20487a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 20497a1306a7Sxc151355 "ddi_regs_map_setup() failed")); 20507a1306a7Sxc151355 goto attach_fail1; 20517a1306a7Sxc151355 } 20527a1306a7Sxc151355 20537a1306a7Sxc151355 ah = ath_hal_attach(device_id, asc, 0, regs, &status); 20547a1306a7Sxc151355 if (ah == NULL) { 20557a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 20563caf1114Sxc151355 "unable to attach hw: '%s' (HAL status %u)\n", 20573caf1114Sxc151355 ath_get_hal_status_desc(status), status)); 20587a1306a7Sxc151355 goto attach_fail2; 20597a1306a7Sxc151355 } 2060129d67acSlin wang - Sun Microsystems - Beijing China ATH_DEBUG((ATH_DBG_ATTACH, "mac %d.%d phy %d.%d", 2061129d67acSlin wang - Sun Microsystems - Beijing China ah->ah_macVersion, ah->ah_macRev, 2062129d67acSlin wang - Sun Microsystems - Beijing China ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf)); 20637a1306a7Sxc151355 ATH_HAL_INTRSET(ah, 0); 20647a1306a7Sxc151355 asc->asc_ah = ah; 20657a1306a7Sxc151355 20667a1306a7Sxc151355 if (ah->ah_abi != HAL_ABI_VERSION) { 20677a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 20687a1306a7Sxc151355 "HAL ABI mismatch detected (0x%x != 0x%x)\n", 20697a1306a7Sxc151355 ah->ah_abi, HAL_ABI_VERSION)); 20707a1306a7Sxc151355 goto attach_fail3; 20717a1306a7Sxc151355 } 20727a1306a7Sxc151355 20737a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 20747a1306a7Sxc151355 "HAL ABI version 0x%x\n", ah->ah_abi)); 20757a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 20767a1306a7Sxc151355 "HAL mac version %d.%d, phy version %d.%d\n", 20777a1306a7Sxc151355 ah->ah_macVersion, ah->ah_macRev, 20787a1306a7Sxc151355 ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf)); 20797a1306a7Sxc151355 if (ah->ah_analog5GhzRev) 20807a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 20817a1306a7Sxc151355 "HAL 5ghz radio version %d.%d\n", 20827a1306a7Sxc151355 ah->ah_analog5GhzRev >> 4, 20837a1306a7Sxc151355 ah->ah_analog5GhzRev & 0xf)); 20847a1306a7Sxc151355 if (ah->ah_analog2GhzRev) 20857a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 20867a1306a7Sxc151355 "HAL 2ghz radio version %d.%d\n", 20877a1306a7Sxc151355 ah->ah_analog2GhzRev >> 4, 20887a1306a7Sxc151355 ah->ah_analog2GhzRev & 0xf)); 20897a1306a7Sxc151355 20907a1306a7Sxc151355 /* 20917a1306a7Sxc151355 * Check if the MAC has multi-rate retry support. 20927a1306a7Sxc151355 * We do this by trying to setup a fake extended 20937a1306a7Sxc151355 * descriptor. MAC's that don't have support will 20947a1306a7Sxc151355 * return false w/o doing anything. MAC's that do 20957a1306a7Sxc151355 * support it will return true w/o doing anything. 20967a1306a7Sxc151355 */ 20977a1306a7Sxc151355 asc->asc_mrretry = ATH_HAL_SETUPXTXDESC(ah, NULL, 0, 0, 0, 0, 0, 0); 20987a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 20997a1306a7Sxc151355 "multi rate retry support=%x\n", 21007a1306a7Sxc151355 asc->asc_mrretry)); 21017a1306a7Sxc151355 2102a399b765Szf162725 /* 2103a399b765Szf162725 * Get the hardware key cache size. 2104a399b765Szf162725 */ 2105a399b765Szf162725 asc->asc_keymax = ATH_HAL_KEYCACHESIZE(ah); 2106a399b765Szf162725 if (asc->asc_keymax > sizeof (asc->asc_keymap) * NBBY) { 2107a399b765Szf162725 ATH_DEBUG((ATH_DBG_ATTACH, "ath_attach:" 2108a399b765Szf162725 " Warning, using only %u entries in %u key cache\n", 2109a399b765Szf162725 sizeof (asc->asc_keymap) * NBBY, asc->asc_keymax)); 2110a399b765Szf162725 asc->asc_keymax = sizeof (asc->asc_keymap) * NBBY; 2111a399b765Szf162725 } 2112a399b765Szf162725 /* 2113a399b765Szf162725 * Reset the key cache since some parts do not 2114a399b765Szf162725 * reset the contents on initial power up. 2115a399b765Szf162725 */ 2116a399b765Szf162725 for (i = 0; i < asc->asc_keymax; i++) 2117a399b765Szf162725 ATH_HAL_KEYRESET(ah, i); 2118a399b765Szf162725 21197a1306a7Sxc151355 ATH_HAL_GETREGDOMAIN(ah, (uint32_t *)&ath_regdomain); 21207a1306a7Sxc151355 ATH_HAL_GETCOUNTRYCODE(ah, &ath_countrycode); 21217a1306a7Sxc151355 /* 21227a1306a7Sxc151355 * Collect the channel list using the default country 21237a1306a7Sxc151355 * code and including outdoor channels. The 802.11 layer 21247a1306a7Sxc151355 * is resposible for filtering this list to a set of 21257a1306a7Sxc151355 * channels that it considers ok to use. 21267a1306a7Sxc151355 */ 21277a1306a7Sxc151355 asc->asc_have11g = 0; 21287a1306a7Sxc151355 21297a1306a7Sxc151355 /* enable outdoor use, enable extended channels */ 21307a1306a7Sxc151355 err = ath_getchannels(asc, ath_countrycode, AH_FALSE, AH_TRUE); 21317a1306a7Sxc151355 if (err != 0) 21327a1306a7Sxc151355 goto attach_fail3; 21337a1306a7Sxc151355 21347a1306a7Sxc151355 /* 21357a1306a7Sxc151355 * Setup rate tables for all potential media types. 21367a1306a7Sxc151355 */ 21377a1306a7Sxc151355 ath_rate_setup(asc, IEEE80211_MODE_11A); 21387a1306a7Sxc151355 ath_rate_setup(asc, IEEE80211_MODE_11B); 21397a1306a7Sxc151355 ath_rate_setup(asc, IEEE80211_MODE_11G); 21400ba2cbe9Sxc151355 ath_rate_setup(asc, IEEE80211_MODE_TURBO_A); 21417a1306a7Sxc151355 21427a1306a7Sxc151355 /* Setup here so ath_rate_update is happy */ 21437a1306a7Sxc151355 ath_setcurmode(asc, IEEE80211_MODE_11A); 21447a1306a7Sxc151355 21457a1306a7Sxc151355 err = ath_desc_alloc(devinfo, asc); 21467a1306a7Sxc151355 if (err != DDI_SUCCESS) { 21477a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 21487a1306a7Sxc151355 "failed to allocate descriptors: %d\n", err)); 21497a1306a7Sxc151355 goto attach_fail3; 21507a1306a7Sxc151355 } 21517a1306a7Sxc151355 2152129d67acSlin wang - Sun Microsystems - Beijing China if ((asc->asc_tq = ddi_taskq_create(devinfo, "ath_taskq", 1, 2153129d67acSlin wang - Sun Microsystems - Beijing China TASKQ_DEFAULTPRI, 0)) == NULL) { 2154129d67acSlin wang - Sun Microsystems - Beijing China goto attach_fail4; 2155129d67acSlin wang - Sun Microsystems - Beijing China } 21567a1306a7Sxc151355 /* Setup transmit queues in the HAL */ 21577a1306a7Sxc151355 if (ath_txq_setup(asc)) 21587a1306a7Sxc151355 goto attach_fail4; 21597a1306a7Sxc151355 21600ba2cbe9Sxc151355 ATH_HAL_GETMAC(ah, ic->ic_macaddr); 21617a1306a7Sxc151355 21620ba2cbe9Sxc151355 /* 21630ba2cbe9Sxc151355 * Initialize pointers to device specific functions which 21640ba2cbe9Sxc151355 * will be used by the generic layer. 21650ba2cbe9Sxc151355 */ 21667a1306a7Sxc151355 /* 11g support is identified when we fetch the channel set */ 21677a1306a7Sxc151355 if (asc->asc_have11g) 2168c1500db9Szf162725 ic->ic_caps |= IEEE80211_C_SHPREAMBLE | 2169c1500db9Szf162725 IEEE80211_C_SHSLOT; /* short slot time */ 21700ba2cbe9Sxc151355 /* 21710ba2cbe9Sxc151355 * Query the hal to figure out h/w crypto support. 21720ba2cbe9Sxc151355 */ 21730ba2cbe9Sxc151355 if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_WEP)) 21740ba2cbe9Sxc151355 ic->ic_caps |= IEEE80211_C_WEP; 21750ba2cbe9Sxc151355 if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_AES_OCB)) 21760ba2cbe9Sxc151355 ic->ic_caps |= IEEE80211_C_AES; 2177a399b765Szf162725 if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_AES_CCM)) { 2178a399b765Szf162725 ATH_DEBUG((ATH_DBG_ATTACH, "Atheros support H/W CCMP\n")); 21790ba2cbe9Sxc151355 ic->ic_caps |= IEEE80211_C_AES_CCM; 2180a399b765Szf162725 } 2181a399b765Szf162725 if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_CKIP)) 21820ba2cbe9Sxc151355 ic->ic_caps |= IEEE80211_C_CKIP; 2183a399b765Szf162725 if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_TKIP)) { 2184a399b765Szf162725 ATH_DEBUG((ATH_DBG_ATTACH, "Atheros support H/W TKIP\n")); 2185a399b765Szf162725 ic->ic_caps |= IEEE80211_C_TKIP; 21860ba2cbe9Sxc151355 /* 21870ba2cbe9Sxc151355 * Check if h/w does the MIC and/or whether the 21880ba2cbe9Sxc151355 * separate key cache entries are required to 21890ba2cbe9Sxc151355 * handle both tx+rx MIC keys. 21900ba2cbe9Sxc151355 */ 2191a399b765Szf162725 if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_MIC)) { 2192a399b765Szf162725 ATH_DEBUG((ATH_DBG_ATTACH, "Support H/W TKIP MIC\n")); 21930ba2cbe9Sxc151355 ic->ic_caps |= IEEE80211_C_TKIPMIC; 2194a399b765Szf162725 } 2195129d67acSlin wang - Sun Microsystems - Beijing China 2196129d67acSlin wang - Sun Microsystems - Beijing China /* 2197129d67acSlin wang - Sun Microsystems - Beijing China * If the h/w supports storing tx+rx MIC keys 2198129d67acSlin wang - Sun Microsystems - Beijing China * in one cache slot automatically enable use. 2199129d67acSlin wang - Sun Microsystems - Beijing China */ 2200129d67acSlin wang - Sun Microsystems - Beijing China if (ATH_HAL_HASTKIPSPLIT(ah) || 2201129d67acSlin wang - Sun Microsystems - Beijing China !ATH_HAL_SETTKIPSPLIT(ah, AH_FALSE)) { 22020ba2cbe9Sxc151355 asc->asc_splitmic = 1; 22030ba2cbe9Sxc151355 } 2204129d67acSlin wang - Sun Microsystems - Beijing China } 2205a399b765Szf162725 ic->ic_caps |= IEEE80211_C_WPA; /* Support WPA/WPA2 */ 2206a399b765Szf162725 22070ba2cbe9Sxc151355 asc->asc_hasclrkey = ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_CLR); 2208129d67acSlin wang - Sun Microsystems - Beijing China /* 2209129d67acSlin wang - Sun Microsystems - Beijing China * Mark key cache slots associated with global keys 2210129d67acSlin wang - Sun Microsystems - Beijing China * as in use. If we knew TKIP was not to be used we 2211129d67acSlin wang - Sun Microsystems - Beijing China * could leave the +32, +64, and +32+64 slots free. 2212129d67acSlin wang - Sun Microsystems - Beijing China */ 2213129d67acSlin wang - Sun Microsystems - Beijing China for (i = 0; i < IEEE80211_WEP_NKID; i++) { 2214129d67acSlin wang - Sun Microsystems - Beijing China setbit(asc->asc_keymap, i); 2215129d67acSlin wang - Sun Microsystems - Beijing China setbit(asc->asc_keymap, i+64); 2216129d67acSlin wang - Sun Microsystems - Beijing China if (asc->asc_splitmic) { 2217129d67acSlin wang - Sun Microsystems - Beijing China setbit(asc->asc_keymap, i+32); 2218129d67acSlin wang - Sun Microsystems - Beijing China setbit(asc->asc_keymap, i+32+64); 2219129d67acSlin wang - Sun Microsystems - Beijing China } 2220129d67acSlin wang - Sun Microsystems - Beijing China } 2221129d67acSlin wang - Sun Microsystems - Beijing China 22220ba2cbe9Sxc151355 ic->ic_phytype = IEEE80211_T_OFDM; 22230ba2cbe9Sxc151355 ic->ic_opmode = IEEE80211_M_STA; 22240ba2cbe9Sxc151355 ic->ic_state = IEEE80211_S_INIT; 22250ba2cbe9Sxc151355 ic->ic_maxrssi = ATH_MAX_RSSI; 22260ba2cbe9Sxc151355 ic->ic_set_shortslot = ath_set_shortslot; 22270ba2cbe9Sxc151355 ic->ic_xmit = ath_xmit; 22280ba2cbe9Sxc151355 ieee80211_attach(ic); 22297a1306a7Sxc151355 2230a399b765Szf162725 /* different instance has different WPA door */ 2231a399b765Szf162725 (void) snprintf(ic->ic_wpadoor, MAX_IEEE80211STR, "%s_%s%d", WPA_DOOR, 2232a399b765Szf162725 ddi_driver_name(devinfo), 2233a399b765Szf162725 ddi_get_instance(devinfo)); 2234a399b765Szf162725 22350ba2cbe9Sxc151355 /* Override 80211 default routines */ 22360ba2cbe9Sxc151355 ic->ic_reset = ath_reset; 22370ba2cbe9Sxc151355 asc->asc_newstate = ic->ic_newstate; 22380ba2cbe9Sxc151355 ic->ic_newstate = ath_newstate; 22390ba2cbe9Sxc151355 ic->ic_watchdog = ath_watchdog; 22400ba2cbe9Sxc151355 ic->ic_node_alloc = ath_node_alloc; 22410ba2cbe9Sxc151355 ic->ic_node_free = ath_node_free; 22420ba2cbe9Sxc151355 ic->ic_crypto.cs_key_alloc = ath_key_alloc; 22430ba2cbe9Sxc151355 ic->ic_crypto.cs_key_delete = ath_key_delete; 22440ba2cbe9Sxc151355 ic->ic_crypto.cs_key_set = ath_key_set; 22450ba2cbe9Sxc151355 ieee80211_media_init(ic); 2246d2894050Szf162725 /* 2247d2894050Szf162725 * initialize default tx key 2248d2894050Szf162725 */ 2249d2894050Szf162725 ic->ic_def_txkey = 0; 22507a1306a7Sxc151355 22517a1306a7Sxc151355 asc->asc_rx_pend = 0; 22527a1306a7Sxc151355 ATH_HAL_INTRSET(ah, 0); 22537a1306a7Sxc151355 err = ddi_add_softintr(devinfo, DDI_SOFTINT_LOW, 22547a1306a7Sxc151355 &asc->asc_softint_id, NULL, 0, ath_softint_handler, (caddr_t)asc); 22557a1306a7Sxc151355 if (err != DDI_SUCCESS) { 22567a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 22570ba2cbe9Sxc151355 "ddi_add_softintr() failed\n")); 22587a1306a7Sxc151355 goto attach_fail5; 22597a1306a7Sxc151355 } 22607a1306a7Sxc151355 22617a1306a7Sxc151355 if (ddi_get_iblock_cookie(devinfo, 0, &asc->asc_iblock) 22627a1306a7Sxc151355 != DDI_SUCCESS) { 22637a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 22647a1306a7Sxc151355 "Can not get iblock cookie for INT\n")); 22657a1306a7Sxc151355 goto attach_fail6; 22667a1306a7Sxc151355 } 22677a1306a7Sxc151355 22680ba2cbe9Sxc151355 if (ddi_add_intr(devinfo, 0, NULL, NULL, ath_intr, 22690ba2cbe9Sxc151355 (caddr_t)asc) != DDI_SUCCESS) { 22707a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 22717a1306a7Sxc151355 "Can not set intr for ATH driver\n")); 22727a1306a7Sxc151355 goto attach_fail6; 22737a1306a7Sxc151355 } 22747a1306a7Sxc151355 22750ba2cbe9Sxc151355 /* 22760ba2cbe9Sxc151355 * Provide initial settings for the WiFi plugin; whenever this 22770ba2cbe9Sxc151355 * information changes, we need to call mac_plugindata_update() 22780ba2cbe9Sxc151355 */ 22790ba2cbe9Sxc151355 wd.wd_opmode = ic->ic_opmode; 22800ba2cbe9Sxc151355 wd.wd_secalloc = WIFI_SEC_NONE; 22810ba2cbe9Sxc151355 IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid); 22820ba2cbe9Sxc151355 22830ba2cbe9Sxc151355 if ((macp = mac_alloc(MAC_VERSION)) == NULL) { 22847a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 22850ba2cbe9Sxc151355 "MAC version mismatch\n")); 22860ba2cbe9Sxc151355 goto attach_fail7; 22870ba2cbe9Sxc151355 } 22880ba2cbe9Sxc151355 22890ba2cbe9Sxc151355 macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI; 22900ba2cbe9Sxc151355 macp->m_driver = asc; 22910ba2cbe9Sxc151355 macp->m_dip = devinfo; 22920ba2cbe9Sxc151355 macp->m_src_addr = ic->ic_macaddr; 22930ba2cbe9Sxc151355 macp->m_callbacks = &ath_m_callbacks; 22940ba2cbe9Sxc151355 macp->m_min_sdu = 0; 22950ba2cbe9Sxc151355 macp->m_max_sdu = IEEE80211_MTU; 22960ba2cbe9Sxc151355 macp->m_pdata = &wd; 22970ba2cbe9Sxc151355 macp->m_pdata_size = sizeof (wd); 22980ba2cbe9Sxc151355 22990ba2cbe9Sxc151355 err = mac_register(macp, &ic->ic_mach); 23000ba2cbe9Sxc151355 mac_free(macp); 23010ba2cbe9Sxc151355 if (err != 0) { 23020ba2cbe9Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): " 23030ba2cbe9Sxc151355 "mac_register err %x\n", err)); 23047a1306a7Sxc151355 goto attach_fail7; 23057a1306a7Sxc151355 } 23067a1306a7Sxc151355 23077a1306a7Sxc151355 /* Create minor node of type DDI_NT_NET_WIFI */ 23087a1306a7Sxc151355 (void) snprintf(strbuf, sizeof (strbuf), "%s%d", 23090ba2cbe9Sxc151355 ATH_NODENAME, instance); 23107a1306a7Sxc151355 err = ddi_create_minor_node(devinfo, strbuf, S_IFCHR, 23110ba2cbe9Sxc151355 instance + 1, DDI_NT_NET_WIFI, 0); 23127a1306a7Sxc151355 if (err != DDI_SUCCESS) 23137a1306a7Sxc151355 ATH_DEBUG((ATH_DBG_ATTACH, "WARN: ath: ath_attach(): " 23147a1306a7Sxc151355 "Create minor node failed - %d\n", err)); 23157a1306a7Sxc151355 23160ba2cbe9Sxc151355 mac_link_update(ic->ic_mach, LINK_STATE_DOWN); 23177a1306a7Sxc151355 asc->asc_invalid = 1; 2318f11a3086Sxc151355 asc->asc_isrunning = 0; 23193caf1114Sxc151355 asc->asc_promisc = B_FALSE; 23203caf1114Sxc151355 bzero(asc->asc_mcast_refs, sizeof (asc->asc_mcast_refs)); 23213caf1114Sxc151355 bzero(asc->asc_mcast_hash, sizeof (asc->asc_mcast_hash)); 23227a1306a7Sxc151355 return (DDI_SUCCESS); 23237a1306a7Sxc151355 attach_fail7: 23247a1306a7Sxc151355 ddi_remove_intr(devinfo, 0, asc->asc_iblock); 23257a1306a7Sxc151355 attach_fail6: 23267a1306a7Sxc151355 ddi_remove_softintr(asc->asc_softint_id); 23277a1306a7Sxc151355 attach_fail5: 23280ba2cbe9Sxc151355 (void) ieee80211_detach(ic); 23297a1306a7Sxc151355 attach_fail4: 23307a1306a7Sxc151355 ath_desc_free(asc); 2331129d67acSlin wang - Sun Microsystems - Beijing China if (asc->asc_tq) 2332129d67acSlin wang - Sun Microsystems - Beijing China ddi_taskq_destroy(asc->asc_tq); 23337a1306a7Sxc151355 attach_fail3: 23347a1306a7Sxc151355 ah->ah_detach(asc->asc_ah); 23357a1306a7Sxc151355 attach_fail2: 23367a1306a7Sxc151355 ddi_regs_map_free(&asc->asc_io_handle); 23377a1306a7Sxc151355 attach_fail1: 23387a1306a7Sxc151355 pci_config_teardown(&asc->asc_cfg_handle); 23397a1306a7Sxc151355 attach_fail0: 23407a1306a7Sxc151355 asc->asc_invalid = 1; 23417a1306a7Sxc151355 mutex_destroy(&asc->asc_txbuflock); 23427a1306a7Sxc151355 for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { 23437a1306a7Sxc151355 if (ATH_TXQ_SETUP(asc, i)) { 23447a1306a7Sxc151355 struct ath_txq *txq = &asc->asc_txq[i]; 23457a1306a7Sxc151355 mutex_destroy(&txq->axq_lock); 23467a1306a7Sxc151355 } 23477a1306a7Sxc151355 } 23487a1306a7Sxc151355 mutex_destroy(&asc->asc_rxbuflock); 23497a1306a7Sxc151355 mutex_destroy(&asc->asc_genlock); 23500ba2cbe9Sxc151355 mutex_destroy(&asc->asc_resched_lock); 23510ba2cbe9Sxc151355 ddi_soft_state_free(ath_soft_state_p, instance); 23527a1306a7Sxc151355 23537a1306a7Sxc151355 return (DDI_FAILURE); 23547a1306a7Sxc151355 } 23557a1306a7Sxc151355 2356f11a3086Sxc151355 /* 2357f11a3086Sxc151355 * Suspend transmit/receive for powerdown 2358f11a3086Sxc151355 */ 2359f11a3086Sxc151355 static int 2360f11a3086Sxc151355 ath_suspend(ath_t *asc) 2361f11a3086Sxc151355 { 2362f11a3086Sxc151355 ATH_LOCK(asc); 2363f11a3086Sxc151355 ath_stop_locked(asc); 2364f11a3086Sxc151355 ATH_UNLOCK(asc); 2365f11a3086Sxc151355 ATH_DEBUG((ATH_DBG_SUSPEND, "ath: suspended.\n")); 2366f11a3086Sxc151355 2367f11a3086Sxc151355 return (DDI_SUCCESS); 2368f11a3086Sxc151355 } 2369f11a3086Sxc151355 23707a1306a7Sxc151355 static int32_t 23717a1306a7Sxc151355 ath_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd) 23727a1306a7Sxc151355 { 23737a1306a7Sxc151355 ath_t *asc; 23747a1306a7Sxc151355 23757a1306a7Sxc151355 asc = ddi_get_soft_state(ath_soft_state_p, ddi_get_instance(devinfo)); 23767a1306a7Sxc151355 ASSERT(asc != NULL); 23777a1306a7Sxc151355 2378f11a3086Sxc151355 switch (cmd) { 2379f11a3086Sxc151355 case DDI_DETACH: 2380f11a3086Sxc151355 break; 2381f11a3086Sxc151355 2382f11a3086Sxc151355 case DDI_SUSPEND: 2383f11a3086Sxc151355 return (ath_suspend(asc)); 2384f11a3086Sxc151355 2385f11a3086Sxc151355 default: 23867a1306a7Sxc151355 return (DDI_FAILURE); 2387f11a3086Sxc151355 } 23887a1306a7Sxc151355 238942516a0cSxinghua wen - Sun Microsystems - Beijing China if (mac_disable(asc->asc_isc.ic_mach) != 0) 239042516a0cSxinghua wen - Sun Microsystems - Beijing China return (DDI_FAILURE); 239142516a0cSxinghua wen - Sun Microsystems - Beijing China 23920ba2cbe9Sxc151355 ath_stop_scantimer(asc); 23937a1306a7Sxc151355 23947a1306a7Sxc151355 /* disable interrupts */ 23957a1306a7Sxc151355 ATH_HAL_INTRSET(asc->asc_ah, 0); 23967a1306a7Sxc151355 23970ba2cbe9Sxc151355 /* 23980ba2cbe9Sxc151355 * Unregister from the MAC layer subsystem 23990ba2cbe9Sxc151355 */ 240042516a0cSxinghua wen - Sun Microsystems - Beijing China (void) mac_unregister(asc->asc_isc.ic_mach); 24010ba2cbe9Sxc151355 24027a1306a7Sxc151355 /* free intterrupt resources */ 24037a1306a7Sxc151355 ddi_remove_intr(devinfo, 0, asc->asc_iblock); 24047a1306a7Sxc151355 ddi_remove_softintr(asc->asc_softint_id); 24057a1306a7Sxc151355 24060ba2cbe9Sxc151355 /* 24070ba2cbe9Sxc151355 * NB: the order of these is important: 24080ba2cbe9Sxc151355 * o call the 802.11 layer before detaching the hal to 24090ba2cbe9Sxc151355 * insure callbacks into the driver to delete global 24100ba2cbe9Sxc151355 * key cache entries can be handled 24110ba2cbe9Sxc151355 * o reclaim the tx queue data structures after calling 24120ba2cbe9Sxc151355 * the 802.11 layer as we'll get called back to reclaim 24130ba2cbe9Sxc151355 * node state and potentially want to use them 24140ba2cbe9Sxc151355 * o to cleanup the tx queues the hal is called, so detach 24150ba2cbe9Sxc151355 * it last 24160ba2cbe9Sxc151355 */ 24170ba2cbe9Sxc151355 ieee80211_detach(&asc->asc_isc); 24187a1306a7Sxc151355 ath_desc_free(asc); 2419129d67acSlin wang - Sun Microsystems - Beijing China ddi_taskq_destroy(asc->asc_tq); 24200ba2cbe9Sxc151355 ath_txq_cleanup(asc); 24217a1306a7Sxc151355 asc->asc_ah->ah_detach(asc->asc_ah); 24227a1306a7Sxc151355 24237a1306a7Sxc151355 /* free io handle */ 24247a1306a7Sxc151355 ddi_regs_map_free(&asc->asc_io_handle); 24257a1306a7Sxc151355 pci_config_teardown(&asc->asc_cfg_handle); 24267a1306a7Sxc151355 24277a1306a7Sxc151355 /* destroy locks */ 24287a1306a7Sxc151355 mutex_destroy(&asc->asc_rxbuflock); 24297a1306a7Sxc151355 mutex_destroy(&asc->asc_genlock); 24300ba2cbe9Sxc151355 mutex_destroy(&asc->asc_resched_lock); 24317a1306a7Sxc151355 24327a1306a7Sxc151355 ddi_remove_minor_node(devinfo, NULL); 24337a1306a7Sxc151355 ddi_soft_state_free(ath_soft_state_p, ddi_get_instance(devinfo)); 24347a1306a7Sxc151355 24357a1306a7Sxc151355 return (DDI_SUCCESS); 24367a1306a7Sxc151355 } 24377a1306a7Sxc151355 2438b9ee76dbSKonstantin Ananyev /* 2439b9ee76dbSKonstantin Ananyev * quiesce(9E) entry point. 2440b9ee76dbSKonstantin Ananyev * 2441b9ee76dbSKonstantin Ananyev * This function is called when the system is single-threaded at high 2442b9ee76dbSKonstantin Ananyev * PIL with preemption disabled. Therefore, this function must not be 2443b9ee76dbSKonstantin Ananyev * blocked. 2444b9ee76dbSKonstantin Ananyev * 2445b9ee76dbSKonstantin Ananyev * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. 2446b9ee76dbSKonstantin Ananyev * DDI_FAILURE indicates an error condition and should almost never happen. 2447b9ee76dbSKonstantin Ananyev */ 2448b9ee76dbSKonstantin Ananyev static int32_t 2449b9ee76dbSKonstantin Ananyev ath_quiesce(dev_info_t *devinfo) 2450b9ee76dbSKonstantin Ananyev { 2451b9ee76dbSKonstantin Ananyev ath_t *asc; 2452b9ee76dbSKonstantin Ananyev struct ath_hal *ah; 2453b9ee76dbSKonstantin Ananyev int i; 2454b9ee76dbSKonstantin Ananyev 2455b9ee76dbSKonstantin Ananyev asc = ddi_get_soft_state(ath_soft_state_p, ddi_get_instance(devinfo)); 2456b9ee76dbSKonstantin Ananyev 2457b9ee76dbSKonstantin Ananyev if (asc == NULL || (ah = asc->asc_ah) == NULL) 2458b9ee76dbSKonstantin Ananyev return (DDI_FAILURE); 2459b9ee76dbSKonstantin Ananyev 2460b9ee76dbSKonstantin Ananyev /* 2461b9ee76dbSKonstantin Ananyev * Disable interrupts 2462b9ee76dbSKonstantin Ananyev */ 2463b9ee76dbSKonstantin Ananyev ATH_HAL_INTRSET(ah, 0); 2464b9ee76dbSKonstantin Ananyev 2465b9ee76dbSKonstantin Ananyev /* 2466b9ee76dbSKonstantin Ananyev * Disable TX HW 2467b9ee76dbSKonstantin Ananyev */ 2468b9ee76dbSKonstantin Ananyev for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { 2469b9ee76dbSKonstantin Ananyev if (ATH_TXQ_SETUP(asc, i)) { 2470b9ee76dbSKonstantin Ananyev ATH_HAL_STOPTXDMA(ah, asc->asc_txq[i].axq_qnum); 2471b9ee76dbSKonstantin Ananyev } 2472b9ee76dbSKonstantin Ananyev } 2473b9ee76dbSKonstantin Ananyev 2474b9ee76dbSKonstantin Ananyev /* 2475b9ee76dbSKonstantin Ananyev * Disable RX HW 2476b9ee76dbSKonstantin Ananyev */ 2477b9ee76dbSKonstantin Ananyev ATH_HAL_STOPPCURECV(ah); 2478b9ee76dbSKonstantin Ananyev ATH_HAL_SETRXFILTER(ah, 0); 2479b9ee76dbSKonstantin Ananyev ATH_HAL_STOPDMARECV(ah); 2480b9ee76dbSKonstantin Ananyev drv_usecwait(3000); 2481b9ee76dbSKonstantin Ananyev 2482b9ee76dbSKonstantin Ananyev /* 2483b9ee76dbSKonstantin Ananyev * Power down HW 2484b9ee76dbSKonstantin Ananyev */ 2485b9ee76dbSKonstantin Ananyev ATH_HAL_PHYDISABLE(ah); 2486b9ee76dbSKonstantin Ananyev 2487b9ee76dbSKonstantin Ananyev return (DDI_SUCCESS); 2488b9ee76dbSKonstantin Ananyev } 2489b9ee76dbSKonstantin Ananyev 24900ba2cbe9Sxc151355 DDI_DEFINE_STREAM_OPS(ath_dev_ops, nulldev, nulldev, ath_attach, ath_detach, 2491b9ee76dbSKonstantin Ananyev nodev, NULL, D_MP, NULL, ath_quiesce); 24927a1306a7Sxc151355 24937a1306a7Sxc151355 static struct modldrv ath_modldrv = { 24947a1306a7Sxc151355 &mod_driverops, /* Type of module. This one is a driver */ 2495129d67acSlin wang - Sun Microsystems - Beijing China "ath driver 1.4/HAL 0.10.5.6", /* short description */ 24967a1306a7Sxc151355 &ath_dev_ops /* driver specific ops */ 24977a1306a7Sxc151355 }; 24987a1306a7Sxc151355 24997a1306a7Sxc151355 static struct modlinkage modlinkage = { 25007a1306a7Sxc151355 MODREV_1, (void *)&ath_modldrv, NULL 25017a1306a7Sxc151355 }; 25027a1306a7Sxc151355 25037a1306a7Sxc151355 25047a1306a7Sxc151355 int 25057a1306a7Sxc151355 _info(struct modinfo *modinfop) 25067a1306a7Sxc151355 { 25077a1306a7Sxc151355 return (mod_info(&modlinkage, modinfop)); 25087a1306a7Sxc151355 } 25097a1306a7Sxc151355 25107a1306a7Sxc151355 int 25117a1306a7Sxc151355 _init(void) 25127a1306a7Sxc151355 { 25137a1306a7Sxc151355 int status; 25147a1306a7Sxc151355 25157a1306a7Sxc151355 status = ddi_soft_state_init(&ath_soft_state_p, sizeof (ath_t), 1); 25167a1306a7Sxc151355 if (status != 0) 25177a1306a7Sxc151355 return (status); 25187a1306a7Sxc151355 25197a1306a7Sxc151355 mutex_init(&ath_loglock, NULL, MUTEX_DRIVER, NULL); 25200ba2cbe9Sxc151355 ath_halfix_init(); 25210ba2cbe9Sxc151355 mac_init_ops(&ath_dev_ops, "ath"); 25227a1306a7Sxc151355 status = mod_install(&modlinkage); 25237a1306a7Sxc151355 if (status != 0) { 25240ba2cbe9Sxc151355 mac_fini_ops(&ath_dev_ops); 25250ba2cbe9Sxc151355 ath_halfix_finit(); 25267a1306a7Sxc151355 mutex_destroy(&ath_loglock); 25270ba2cbe9Sxc151355 ddi_soft_state_fini(&ath_soft_state_p); 25287a1306a7Sxc151355 } 25297a1306a7Sxc151355 25307a1306a7Sxc151355 return (status); 25317a1306a7Sxc151355 } 25327a1306a7Sxc151355 25337a1306a7Sxc151355 int 25347a1306a7Sxc151355 _fini(void) 25357a1306a7Sxc151355 { 25367a1306a7Sxc151355 int status; 25377a1306a7Sxc151355 25387a1306a7Sxc151355 status = mod_remove(&modlinkage); 25397a1306a7Sxc151355 if (status == 0) { 25400ba2cbe9Sxc151355 mac_fini_ops(&ath_dev_ops); 25410ba2cbe9Sxc151355 ath_halfix_finit(); 25427a1306a7Sxc151355 mutex_destroy(&ath_loglock); 25430ba2cbe9Sxc151355 ddi_soft_state_fini(&ath_soft_state_p); 25447a1306a7Sxc151355 } 25457a1306a7Sxc151355 return (status); 25467a1306a7Sxc151355 } 2547