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
ath_problem(const int8_t * fmt,...)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
ath_log(const int8_t * fmt,...)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
ath_dbg(uint32_t dbg_flags,const int8_t * fmt,...)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
ath_setup_desc(ath_t * asc,struct ath_buf * bf)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
ath_alloc_dma_mem(dev_info_t * devinfo,ddi_dma_attr_t * dma_attr,size_t memsize,ddi_device_acc_attr_t * attr_p,uint_t alloc_flags,uint_t bind_flags,dma_area_t * dma_p)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
ath_free_dma_mem(dma_area_t * dma_p)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
ath_buflist_setup(dev_info_t * devinfo,ath_t * asc,list_t * bflist,struct ath_buf ** pbf,struct ath_desc ** pds,int nbuf,uint_t dmabflags)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
ath_buflist_cleanup(list_t * buflist)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
ath_desc_free(ath_t * asc)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
ath_desc_alloc(dev_info_t * devinfo,ath_t * asc)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
ath_printrxbuf(struct ath_buf * bf,int32_t done)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
ath_rx_handler(ath_t * asc)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
ath_printtxbuf(struct ath_buf * bf,int done)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
ath_tx_start(ath_t * asc,struct ieee80211_node * in,struct ath_buf * bf,mblk_t * mp)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
ath_xmit(ieee80211com_t * ic,mblk_t * mp,uint8_t type)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 *
ath_m_tx(void * arg,mblk_t * mp)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
ath_tx_processq(ath_t * asc,struct ath_txq * txq)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
ath_tx_handler(ath_t * asc)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 *
ath_node_alloc(ieee80211com_t * ic)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
ath_node_free(struct ieee80211_node * in)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
ath_next_scan(void * arg)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
ath_stop_scantimer(ath_t * asc)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
ath_newstate(ieee80211com_t * ic,enum ieee80211_state nstate,int arg)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
ath_calibrate(ath_t * asc)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
ath_watchdog(void * arg)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
ath_tx_proc(void * arg)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
ath_intr(caddr_t arg)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
ath_softint_handler(caddr_t data)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
ath_stop_locked(ath_t * asc)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
ath_m_stop(void * arg)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
ath_start_locked(ath_t * asc)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
ath_m_start(void * arg)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
ath_m_unicst(void * arg,const uint8_t * macaddr)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
ath_m_promisc(void * arg,boolean_t on)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
ath_m_multicst(void * arg,boolean_t add,const uint8_t * mca)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
ath_m_setprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,const void * wldp_buf)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
ath_m_getprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,void * wldp_buf)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
ath_m_propinfo(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,mac_prop_info_handle_t mph)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
ath_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)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
ath_m_stat(void * arg,uint_t stat,uint64_t * val)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
ath_pci_setup(ath_t * asc)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
ath_resume(dev_info_t * devinfo)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
ath_attach(dev_info_t * devinfo,ddi_attach_cmd_t cmd)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
ath_suspend(ath_t * asc)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
ath_detach(dev_info_t * devinfo,ddi_detach_cmd_t cmd)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
ath_quiesce(dev_info_t * devinfo)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
_info(struct modinfo * modinfop)25057a1306a7Sxc151355 _info(struct modinfo *modinfop)
25067a1306a7Sxc151355 {
25077a1306a7Sxc151355 return (mod_info(&modlinkage, modinfop));
25087a1306a7Sxc151355 }
25097a1306a7Sxc151355
25107a1306a7Sxc151355 int
_init(void)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
_fini(void)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