19999SWang.Lin@Sun.COM /*
211729SWang.Lin@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
39999SWang.Lin@Sun.COM * Use is subject to license terms.
49999SWang.Lin@Sun.COM */
59999SWang.Lin@Sun.COM
69999SWang.Lin@Sun.COM /*
79999SWang.Lin@Sun.COM * Copyright (c) 2008 Atheros Communications Inc.
89999SWang.Lin@Sun.COM *
99999SWang.Lin@Sun.COM * Permission to use, copy, modify, and/or distribute this software for any
109999SWang.Lin@Sun.COM * purpose with or without fee is hereby granted, provided that the above
119999SWang.Lin@Sun.COM * copyright notice and this permission notice appear in all copies.
129999SWang.Lin@Sun.COM *
139999SWang.Lin@Sun.COM * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
149999SWang.Lin@Sun.COM * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
159999SWang.Lin@Sun.COM * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
169999SWang.Lin@Sun.COM * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
179999SWang.Lin@Sun.COM * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
189999SWang.Lin@Sun.COM * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
199999SWang.Lin@Sun.COM * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
209999SWang.Lin@Sun.COM */
219999SWang.Lin@Sun.COM
229999SWang.Lin@Sun.COM #include <sys/param.h>
239999SWang.Lin@Sun.COM #include <sys/types.h>
249999SWang.Lin@Sun.COM #include <sys/signal.h>
259999SWang.Lin@Sun.COM #include <sys/stream.h>
269999SWang.Lin@Sun.COM #include <sys/termio.h>
279999SWang.Lin@Sun.COM #include <sys/errno.h>
289999SWang.Lin@Sun.COM #include <sys/file.h>
299999SWang.Lin@Sun.COM #include <sys/cmn_err.h>
309999SWang.Lin@Sun.COM #include <sys/stropts.h>
319999SWang.Lin@Sun.COM #include <sys/strsubr.h>
329999SWang.Lin@Sun.COM #include <sys/strtty.h>
339999SWang.Lin@Sun.COM #include <sys/kbio.h>
349999SWang.Lin@Sun.COM #include <sys/cred.h>
359999SWang.Lin@Sun.COM #include <sys/stat.h>
369999SWang.Lin@Sun.COM #include <sys/consdev.h>
379999SWang.Lin@Sun.COM #include <sys/kmem.h>
389999SWang.Lin@Sun.COM #include <sys/modctl.h>
399999SWang.Lin@Sun.COM #include <sys/ddi.h>
409999SWang.Lin@Sun.COM #include <sys/sunddi.h>
419999SWang.Lin@Sun.COM #include <sys/pci.h>
429999SWang.Lin@Sun.COM #include <sys/errno.h>
439999SWang.Lin@Sun.COM #include <sys/mac_provider.h>
449999SWang.Lin@Sun.COM #include <sys/dlpi.h>
459999SWang.Lin@Sun.COM #include <sys/ethernet.h>
469999SWang.Lin@Sun.COM #include <sys/list.h>
479999SWang.Lin@Sun.COM #include <sys/byteorder.h>
489999SWang.Lin@Sun.COM #include <sys/strsun.h>
499999SWang.Lin@Sun.COM #include <sys/policy.h>
509999SWang.Lin@Sun.COM #include <inet/common.h>
519999SWang.Lin@Sun.COM #include <inet/nd.h>
529999SWang.Lin@Sun.COM #include <inet/mi.h>
539999SWang.Lin@Sun.COM #include <inet/wifi_ioctl.h>
549999SWang.Lin@Sun.COM #include <sys/mac_wifi.h>
5511729SWang.Lin@Sun.COM #include <sys/net80211.h>
5611729SWang.Lin@Sun.COM #include <sys/net80211_proto.h>
5711729SWang.Lin@Sun.COM #include <sys/net80211_ht.h>
5811729SWang.Lin@Sun.COM
599999SWang.Lin@Sun.COM
609999SWang.Lin@Sun.COM #include "arn_ath9k.h"
619999SWang.Lin@Sun.COM #include "arn_core.h"
629999SWang.Lin@Sun.COM #include "arn_reg.h"
639999SWang.Lin@Sun.COM #include "arn_hw.h"
649999SWang.Lin@Sun.COM
6511377SWang.Lin@Sun.COM #define ARN_MAX_RSSI 45 /* max rssi */
669999SWang.Lin@Sun.COM
679999SWang.Lin@Sun.COM /*
6811729SWang.Lin@Sun.COM * Default 11n reates supported by this station.
6911729SWang.Lin@Sun.COM */
7011729SWang.Lin@Sun.COM extern struct ieee80211_htrateset ieee80211_rateset_11n;
7111729SWang.Lin@Sun.COM
7211729SWang.Lin@Sun.COM /*
739999SWang.Lin@Sun.COM * PIO access attributes for registers
749999SWang.Lin@Sun.COM */
759999SWang.Lin@Sun.COM static ddi_device_acc_attr_t arn_reg_accattr = {
769999SWang.Lin@Sun.COM DDI_DEVICE_ATTR_V0,
779999SWang.Lin@Sun.COM DDI_STRUCTURE_LE_ACC,
7811729SWang.Lin@Sun.COM DDI_STRICTORDER_ACC,
7911729SWang.Lin@Sun.COM DDI_DEFAULT_ACC
809999SWang.Lin@Sun.COM };
819999SWang.Lin@Sun.COM
829999SWang.Lin@Sun.COM /*
839999SWang.Lin@Sun.COM * DMA access attributes for descriptors: NOT to be byte swapped.
849999SWang.Lin@Sun.COM */
859999SWang.Lin@Sun.COM static ddi_device_acc_attr_t arn_desc_accattr = {
869999SWang.Lin@Sun.COM DDI_DEVICE_ATTR_V0,
879999SWang.Lin@Sun.COM DDI_STRUCTURE_LE_ACC,
8811729SWang.Lin@Sun.COM DDI_STRICTORDER_ACC,
8911729SWang.Lin@Sun.COM DDI_DEFAULT_ACC
909999SWang.Lin@Sun.COM };
919999SWang.Lin@Sun.COM
929999SWang.Lin@Sun.COM /*
939999SWang.Lin@Sun.COM * Describes the chip's DMA engine
949999SWang.Lin@Sun.COM */
959999SWang.Lin@Sun.COM static ddi_dma_attr_t arn_dma_attr = {
969999SWang.Lin@Sun.COM DMA_ATTR_V0, /* version number */
979999SWang.Lin@Sun.COM 0, /* low address */
989999SWang.Lin@Sun.COM 0xffffffffU, /* high address */
999999SWang.Lin@Sun.COM 0x3ffffU, /* counter register max */
1009999SWang.Lin@Sun.COM 1, /* alignment */
1019999SWang.Lin@Sun.COM 0xFFF, /* burst sizes */
1029999SWang.Lin@Sun.COM 1, /* minimum transfer size */
1039999SWang.Lin@Sun.COM 0x3ffffU, /* max transfer size */
1049999SWang.Lin@Sun.COM 0xffffffffU, /* address register max */
1059999SWang.Lin@Sun.COM 1, /* no scatter-gather */
1069999SWang.Lin@Sun.COM 1, /* granularity of device */
1079999SWang.Lin@Sun.COM 0, /* DMA flags */
1089999SWang.Lin@Sun.COM };
1099999SWang.Lin@Sun.COM
1109999SWang.Lin@Sun.COM static ddi_dma_attr_t arn_desc_dma_attr = {
1119999SWang.Lin@Sun.COM DMA_ATTR_V0, /* version number */
1129999SWang.Lin@Sun.COM 0, /* low address */
1139999SWang.Lin@Sun.COM 0xffffffffU, /* high address */
1149999SWang.Lin@Sun.COM 0xffffffffU, /* counter register max */
1159999SWang.Lin@Sun.COM 0x1000, /* alignment */
1169999SWang.Lin@Sun.COM 0xFFF, /* burst sizes */
1179999SWang.Lin@Sun.COM 1, /* minimum transfer size */
1189999SWang.Lin@Sun.COM 0xffffffffU, /* max transfer size */
1199999SWang.Lin@Sun.COM 0xffffffffU, /* address register max */
1209999SWang.Lin@Sun.COM 1, /* no scatter-gather */
1219999SWang.Lin@Sun.COM 1, /* granularity of device */
1229999SWang.Lin@Sun.COM 0, /* DMA flags */
1239999SWang.Lin@Sun.COM };
1249999SWang.Lin@Sun.COM
1259999SWang.Lin@Sun.COM #define ATH_DEF_CACHE_BYTES 32 /* default cache line size */
1269999SWang.Lin@Sun.COM
1279999SWang.Lin@Sun.COM static kmutex_t arn_loglock;
1289999SWang.Lin@Sun.COM static void *arn_soft_state_p = NULL;
12911729SWang.Lin@Sun.COM static int arn_dwelltime = 200; /* scan interval */
1309999SWang.Lin@Sun.COM
1319999SWang.Lin@Sun.COM static int arn_m_stat(void *, uint_t, uint64_t *);
1329999SWang.Lin@Sun.COM static int arn_m_start(void *);
1339999SWang.Lin@Sun.COM static void arn_m_stop(void *);
1349999SWang.Lin@Sun.COM static int arn_m_promisc(void *, boolean_t);
1359999SWang.Lin@Sun.COM static int arn_m_multicst(void *, boolean_t, const uint8_t *);
1369999SWang.Lin@Sun.COM static int arn_m_unicst(void *, const uint8_t *);
1379999SWang.Lin@Sun.COM static mblk_t *arn_m_tx(void *, mblk_t *);
1389999SWang.Lin@Sun.COM static void arn_m_ioctl(void *, queue_t *, mblk_t *);
1399999SWang.Lin@Sun.COM static int arn_m_setprop(void *, const char *, mac_prop_id_t,
1409999SWang.Lin@Sun.COM uint_t, const void *);
1419999SWang.Lin@Sun.COM static int arn_m_getprop(void *, const char *, mac_prop_id_t,
142*11878SVenu.Iyer@Sun.COM uint_t, void *);
143*11878SVenu.Iyer@Sun.COM static void arn_m_propinfo(void *, const char *, mac_prop_id_t,
144*11878SVenu.Iyer@Sun.COM mac_prop_info_handle_t);
1459999SWang.Lin@Sun.COM
1469999SWang.Lin@Sun.COM /* MAC Callcack Functions */
1479999SWang.Lin@Sun.COM static mac_callbacks_t arn_m_callbacks = {
148*11878SVenu.Iyer@Sun.COM MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
1499999SWang.Lin@Sun.COM arn_m_stat,
1509999SWang.Lin@Sun.COM arn_m_start,
1519999SWang.Lin@Sun.COM arn_m_stop,
1529999SWang.Lin@Sun.COM arn_m_promisc,
1539999SWang.Lin@Sun.COM arn_m_multicst,
1549999SWang.Lin@Sun.COM arn_m_unicst,
1559999SWang.Lin@Sun.COM arn_m_tx,
156*11878SVenu.Iyer@Sun.COM NULL,
1579999SWang.Lin@Sun.COM arn_m_ioctl,
1589999SWang.Lin@Sun.COM NULL,
1599999SWang.Lin@Sun.COM NULL,
1609999SWang.Lin@Sun.COM NULL,
1619999SWang.Lin@Sun.COM arn_m_setprop,
162*11878SVenu.Iyer@Sun.COM arn_m_getprop,
163*11878SVenu.Iyer@Sun.COM arn_m_propinfo
1649999SWang.Lin@Sun.COM };
1659999SWang.Lin@Sun.COM
1669999SWang.Lin@Sun.COM /*
1679999SWang.Lin@Sun.COM * ARN_DBG_HW
1689999SWang.Lin@Sun.COM * ARN_DBG_REG_IO
1699999SWang.Lin@Sun.COM * ARN_DBG_QUEUE
1709999SWang.Lin@Sun.COM * ARN_DBG_EEPROM
1719999SWang.Lin@Sun.COM * ARN_DBG_XMIT
1729999SWang.Lin@Sun.COM * ARN_DBG_RECV
1739999SWang.Lin@Sun.COM * ARN_DBG_CALIBRATE
1749999SWang.Lin@Sun.COM * ARN_DBG_CHANNEL
1759999SWang.Lin@Sun.COM * ARN_DBG_INTERRUPT
1769999SWang.Lin@Sun.COM * ARN_DBG_REGULATORY
1779999SWang.Lin@Sun.COM * ARN_DBG_ANI
1789999SWang.Lin@Sun.COM * ARN_DBG_POWER_MGMT
1799999SWang.Lin@Sun.COM * ARN_DBG_KEYCACHE
1809999SWang.Lin@Sun.COM * ARN_DBG_BEACON
1819999SWang.Lin@Sun.COM * ARN_DBG_RATE
1829999SWang.Lin@Sun.COM * ARN_DBG_INIT
1839999SWang.Lin@Sun.COM * ARN_DBG_ATTACH
1849999SWang.Lin@Sun.COM * ARN_DBG_DEATCH
1859999SWang.Lin@Sun.COM * ARN_DBG_AGGR
1869999SWang.Lin@Sun.COM * ARN_DBG_RESET
1879999SWang.Lin@Sun.COM * ARN_DBG_FATAL
1889999SWang.Lin@Sun.COM * ARN_DBG_ANY
1899999SWang.Lin@Sun.COM * ARN_DBG_ALL
1909999SWang.Lin@Sun.COM */
1919999SWang.Lin@Sun.COM uint32_t arn_dbg_mask = 0;
1929999SWang.Lin@Sun.COM
1939999SWang.Lin@Sun.COM /*
1949999SWang.Lin@Sun.COM * Exception/warning cases not leading to panic.
1959999SWang.Lin@Sun.COM */
1969999SWang.Lin@Sun.COM void
arn_problem(const int8_t * fmt,...)1979999SWang.Lin@Sun.COM arn_problem(const int8_t *fmt, ...)
1989999SWang.Lin@Sun.COM {
1999999SWang.Lin@Sun.COM va_list args;
2009999SWang.Lin@Sun.COM
2019999SWang.Lin@Sun.COM mutex_enter(&arn_loglock);
2029999SWang.Lin@Sun.COM
2039999SWang.Lin@Sun.COM va_start(args, fmt);
2049999SWang.Lin@Sun.COM vcmn_err(CE_WARN, fmt, args);
2059999SWang.Lin@Sun.COM va_end(args);
2069999SWang.Lin@Sun.COM
2079999SWang.Lin@Sun.COM mutex_exit(&arn_loglock);
2089999SWang.Lin@Sun.COM }
2099999SWang.Lin@Sun.COM
2109999SWang.Lin@Sun.COM /*
2119999SWang.Lin@Sun.COM * Normal log information independent of debug.
2129999SWang.Lin@Sun.COM */
2139999SWang.Lin@Sun.COM void
arn_log(const int8_t * fmt,...)2149999SWang.Lin@Sun.COM arn_log(const int8_t *fmt, ...)
2159999SWang.Lin@Sun.COM {
2169999SWang.Lin@Sun.COM va_list args;
2179999SWang.Lin@Sun.COM
2189999SWang.Lin@Sun.COM mutex_enter(&arn_loglock);
2199999SWang.Lin@Sun.COM
2209999SWang.Lin@Sun.COM va_start(args, fmt);
2219999SWang.Lin@Sun.COM vcmn_err(CE_CONT, fmt, args);
2229999SWang.Lin@Sun.COM va_end(args);
2239999SWang.Lin@Sun.COM
2249999SWang.Lin@Sun.COM mutex_exit(&arn_loglock);
2259999SWang.Lin@Sun.COM }
2269999SWang.Lin@Sun.COM
2279999SWang.Lin@Sun.COM void
arn_dbg(uint32_t dbg_flags,const int8_t * fmt,...)2289999SWang.Lin@Sun.COM arn_dbg(uint32_t dbg_flags, const int8_t *fmt, ...)
2299999SWang.Lin@Sun.COM {
2309999SWang.Lin@Sun.COM va_list args;
2319999SWang.Lin@Sun.COM
2329999SWang.Lin@Sun.COM if (dbg_flags & arn_dbg_mask) {
2339999SWang.Lin@Sun.COM mutex_enter(&arn_loglock);
2349999SWang.Lin@Sun.COM va_start(args, fmt);
2359999SWang.Lin@Sun.COM vcmn_err(CE_CONT, fmt, args);
2369999SWang.Lin@Sun.COM va_end(args);
2379999SWang.Lin@Sun.COM mutex_exit(&arn_loglock);
2389999SWang.Lin@Sun.COM }
2399999SWang.Lin@Sun.COM }
2409999SWang.Lin@Sun.COM
2419999SWang.Lin@Sun.COM /*
2429999SWang.Lin@Sun.COM * Read and write, they both share the same lock. We do this to serialize
2439999SWang.Lin@Sun.COM * reads and writes on Atheros 802.11n PCI devices only. This is required
2449999SWang.Lin@Sun.COM * as the FIFO on these devices can only accept sanely 2 requests. After
2459999SWang.Lin@Sun.COM * that the device goes bananas. Serializing the reads/writes prevents this
2469999SWang.Lin@Sun.COM * from happening.
2479999SWang.Lin@Sun.COM */
2489999SWang.Lin@Sun.COM void
arn_iowrite32(struct ath_hal * ah,uint32_t reg_offset,uint32_t val)2499999SWang.Lin@Sun.COM arn_iowrite32(struct ath_hal *ah, uint32_t reg_offset, uint32_t val)
2509999SWang.Lin@Sun.COM {
2519999SWang.Lin@Sun.COM struct arn_softc *sc = ah->ah_sc;
2529999SWang.Lin@Sun.COM if (ah->ah_config.serialize_regmode == SER_REG_MODE_ON) {
2539999SWang.Lin@Sun.COM mutex_enter(&sc->sc_serial_rw);
2549999SWang.Lin@Sun.COM ddi_put32(sc->sc_io_handle,
2559999SWang.Lin@Sun.COM (uint32_t *)((uintptr_t)(sc->mem) + (reg_offset)), val);
2569999SWang.Lin@Sun.COM mutex_exit(&sc->sc_serial_rw);
2579999SWang.Lin@Sun.COM } else {
2589999SWang.Lin@Sun.COM ddi_put32(sc->sc_io_handle,
2599999SWang.Lin@Sun.COM (uint32_t *)((uintptr_t)(sc->mem) + (reg_offset)), val);
2609999SWang.Lin@Sun.COM }
2619999SWang.Lin@Sun.COM }
2629999SWang.Lin@Sun.COM
2639999SWang.Lin@Sun.COM unsigned int
arn_ioread32(struct ath_hal * ah,uint32_t reg_offset)2649999SWang.Lin@Sun.COM arn_ioread32(struct ath_hal *ah, uint32_t reg_offset)
2659999SWang.Lin@Sun.COM {
2669999SWang.Lin@Sun.COM uint32_t val;
2679999SWang.Lin@Sun.COM struct arn_softc *sc = ah->ah_sc;
2689999SWang.Lin@Sun.COM if (ah->ah_config.serialize_regmode == SER_REG_MODE_ON) {
2699999SWang.Lin@Sun.COM mutex_enter(&sc->sc_serial_rw);
2709999SWang.Lin@Sun.COM val = ddi_get32(sc->sc_io_handle,
2719999SWang.Lin@Sun.COM (uint32_t *)((uintptr_t)(sc->mem) + (reg_offset)));
2729999SWang.Lin@Sun.COM mutex_exit(&sc->sc_serial_rw);
2739999SWang.Lin@Sun.COM } else {
2749999SWang.Lin@Sun.COM val = ddi_get32(sc->sc_io_handle,
2759999SWang.Lin@Sun.COM (uint32_t *)((uintptr_t)(sc->mem) + (reg_offset)));
2769999SWang.Lin@Sun.COM }
2779999SWang.Lin@Sun.COM
2789999SWang.Lin@Sun.COM return (val);
2799999SWang.Lin@Sun.COM }
2809999SWang.Lin@Sun.COM
2819999SWang.Lin@Sun.COM /*
2829999SWang.Lin@Sun.COM * Allocate an area of memory and a DMA handle for accessing it
2839999SWang.Lin@Sun.COM */
2849999SWang.Lin@Sun.COM static int
arn_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)2859999SWang.Lin@Sun.COM arn_alloc_dma_mem(dev_info_t *devinfo, ddi_dma_attr_t *dma_attr, size_t memsize,
2869999SWang.Lin@Sun.COM ddi_device_acc_attr_t *attr_p, uint_t alloc_flags,
2879999SWang.Lin@Sun.COM uint_t bind_flags, dma_area_t *dma_p)
2889999SWang.Lin@Sun.COM {
2899999SWang.Lin@Sun.COM int err;
2909999SWang.Lin@Sun.COM
2919999SWang.Lin@Sun.COM /*
2929999SWang.Lin@Sun.COM * Allocate handle
2939999SWang.Lin@Sun.COM */
2949999SWang.Lin@Sun.COM err = ddi_dma_alloc_handle(devinfo, dma_attr,
2959999SWang.Lin@Sun.COM DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl);
2969999SWang.Lin@Sun.COM if (err != DDI_SUCCESS)
2979999SWang.Lin@Sun.COM return (DDI_FAILURE);
2989999SWang.Lin@Sun.COM
2999999SWang.Lin@Sun.COM /*
3009999SWang.Lin@Sun.COM * Allocate memory
3019999SWang.Lin@Sun.COM */
3029999SWang.Lin@Sun.COM err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize, attr_p,
3039999SWang.Lin@Sun.COM alloc_flags, DDI_DMA_SLEEP, NULL, &dma_p->mem_va,
3049999SWang.Lin@Sun.COM &dma_p->alength, &dma_p->acc_hdl);
3059999SWang.Lin@Sun.COM if (err != DDI_SUCCESS)
3069999SWang.Lin@Sun.COM return (DDI_FAILURE);
3079999SWang.Lin@Sun.COM
3089999SWang.Lin@Sun.COM /*
3099999SWang.Lin@Sun.COM * Bind the two together
3109999SWang.Lin@Sun.COM */
3119999SWang.Lin@Sun.COM err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL,
3129999SWang.Lin@Sun.COM dma_p->mem_va, dma_p->alength, bind_flags,
3139999SWang.Lin@Sun.COM DDI_DMA_SLEEP, NULL, &dma_p->cookie, &dma_p->ncookies);
3149999SWang.Lin@Sun.COM if (err != DDI_DMA_MAPPED)
3159999SWang.Lin@Sun.COM return (DDI_FAILURE);
3169999SWang.Lin@Sun.COM
3179999SWang.Lin@Sun.COM dma_p->nslots = ~0U;
3189999SWang.Lin@Sun.COM dma_p->size = ~0U;
3199999SWang.Lin@Sun.COM dma_p->token = ~0U;
3209999SWang.Lin@Sun.COM dma_p->offset = 0;
3219999SWang.Lin@Sun.COM return (DDI_SUCCESS);
3229999SWang.Lin@Sun.COM }
3239999SWang.Lin@Sun.COM
3249999SWang.Lin@Sun.COM /*
3259999SWang.Lin@Sun.COM * Free one allocated area of DMAable memory
3269999SWang.Lin@Sun.COM */
3279999SWang.Lin@Sun.COM static void
arn_free_dma_mem(dma_area_t * dma_p)3289999SWang.Lin@Sun.COM arn_free_dma_mem(dma_area_t *dma_p)
3299999SWang.Lin@Sun.COM {
3309999SWang.Lin@Sun.COM if (dma_p->dma_hdl != NULL) {
3319999SWang.Lin@Sun.COM (void) ddi_dma_unbind_handle(dma_p->dma_hdl);
3329999SWang.Lin@Sun.COM if (dma_p->acc_hdl != NULL) {
3339999SWang.Lin@Sun.COM ddi_dma_mem_free(&dma_p->acc_hdl);
3349999SWang.Lin@Sun.COM dma_p->acc_hdl = NULL;
3359999SWang.Lin@Sun.COM }
3369999SWang.Lin@Sun.COM ddi_dma_free_handle(&dma_p->dma_hdl);
3379999SWang.Lin@Sun.COM dma_p->ncookies = 0;
3389999SWang.Lin@Sun.COM dma_p->dma_hdl = NULL;
3399999SWang.Lin@Sun.COM }
3409999SWang.Lin@Sun.COM }
3419999SWang.Lin@Sun.COM
3429999SWang.Lin@Sun.COM /*
3439999SWang.Lin@Sun.COM * Initialize tx, rx. or beacon buffer list. Allocate DMA memory for
3449999SWang.Lin@Sun.COM * each buffer.
3459999SWang.Lin@Sun.COM */
3469999SWang.Lin@Sun.COM static int
arn_buflist_setup(dev_info_t * devinfo,struct arn_softc * sc,list_t * bflist,struct ath_buf ** pbf,struct ath_desc ** pds,int nbuf,uint_t dmabflags,uint32_t buflen)34711729SWang.Lin@Sun.COM arn_buflist_setup(dev_info_t *devinfo,
34811729SWang.Lin@Sun.COM struct arn_softc *sc,
34911729SWang.Lin@Sun.COM list_t *bflist,
35011729SWang.Lin@Sun.COM struct ath_buf **pbf,
35111729SWang.Lin@Sun.COM struct ath_desc **pds,
35211729SWang.Lin@Sun.COM int nbuf,
35311729SWang.Lin@Sun.COM uint_t dmabflags,
35411729SWang.Lin@Sun.COM uint32_t buflen)
3559999SWang.Lin@Sun.COM {
3569999SWang.Lin@Sun.COM int i, err;
3579999SWang.Lin@Sun.COM struct ath_buf *bf = *pbf;
3589999SWang.Lin@Sun.COM struct ath_desc *ds = *pds;
3599999SWang.Lin@Sun.COM
3609999SWang.Lin@Sun.COM list_create(bflist, sizeof (struct ath_buf),
3619999SWang.Lin@Sun.COM offsetof(struct ath_buf, bf_node));
3629999SWang.Lin@Sun.COM for (i = 0; i < nbuf; i++, bf++, ds++) {
3639999SWang.Lin@Sun.COM bf->bf_desc = ds;
3649999SWang.Lin@Sun.COM bf->bf_daddr = sc->sc_desc_dma.cookie.dmac_address +
3659999SWang.Lin@Sun.COM ((uintptr_t)ds - (uintptr_t)sc->sc_desc);
3669999SWang.Lin@Sun.COM list_insert_tail(bflist, bf);
3679999SWang.Lin@Sun.COM
3689999SWang.Lin@Sun.COM /* alloc DMA memory */
3699999SWang.Lin@Sun.COM err = arn_alloc_dma_mem(devinfo, &arn_dma_attr,
37011729SWang.Lin@Sun.COM buflen, &arn_desc_accattr, DDI_DMA_STREAMING,
3719999SWang.Lin@Sun.COM dmabflags, &bf->bf_dma);
3729999SWang.Lin@Sun.COM if (err != DDI_SUCCESS)
3739999SWang.Lin@Sun.COM return (err);
3749999SWang.Lin@Sun.COM }
3759999SWang.Lin@Sun.COM *pbf = bf;
3769999SWang.Lin@Sun.COM *pds = ds;
3779999SWang.Lin@Sun.COM
3789999SWang.Lin@Sun.COM return (DDI_SUCCESS);
3799999SWang.Lin@Sun.COM }
3809999SWang.Lin@Sun.COM
3819999SWang.Lin@Sun.COM /*
3829999SWang.Lin@Sun.COM * Destroy tx, rx or beacon buffer list. Free DMA memory.
3839999SWang.Lin@Sun.COM */
3849999SWang.Lin@Sun.COM static void
arn_buflist_cleanup(list_t * buflist)3859999SWang.Lin@Sun.COM arn_buflist_cleanup(list_t *buflist)
3869999SWang.Lin@Sun.COM {
3879999SWang.Lin@Sun.COM struct ath_buf *bf;
3889999SWang.Lin@Sun.COM
3899999SWang.Lin@Sun.COM if (!buflist)
3909999SWang.Lin@Sun.COM return;
3919999SWang.Lin@Sun.COM
3929999SWang.Lin@Sun.COM bf = list_head(buflist);
3939999SWang.Lin@Sun.COM while (bf != NULL) {
3949999SWang.Lin@Sun.COM if (bf->bf_m != NULL) {
3959999SWang.Lin@Sun.COM freemsg(bf->bf_m);
3969999SWang.Lin@Sun.COM bf->bf_m = NULL;
3979999SWang.Lin@Sun.COM }
3989999SWang.Lin@Sun.COM /* Free DMA buffer */
3999999SWang.Lin@Sun.COM arn_free_dma_mem(&bf->bf_dma);
4009999SWang.Lin@Sun.COM if (bf->bf_in != NULL) {
4019999SWang.Lin@Sun.COM ieee80211_free_node(bf->bf_in);
4029999SWang.Lin@Sun.COM bf->bf_in = NULL;
4039999SWang.Lin@Sun.COM }
4049999SWang.Lin@Sun.COM list_remove(buflist, bf);
4059999SWang.Lin@Sun.COM bf = list_head(buflist);
4069999SWang.Lin@Sun.COM }
4079999SWang.Lin@Sun.COM list_destroy(buflist);
4089999SWang.Lin@Sun.COM }
4099999SWang.Lin@Sun.COM
4109999SWang.Lin@Sun.COM static void
arn_desc_free(struct arn_softc * sc)4119999SWang.Lin@Sun.COM arn_desc_free(struct arn_softc *sc)
4129999SWang.Lin@Sun.COM {
4139999SWang.Lin@Sun.COM arn_buflist_cleanup(&sc->sc_txbuf_list);
4149999SWang.Lin@Sun.COM arn_buflist_cleanup(&sc->sc_rxbuf_list);
4159999SWang.Lin@Sun.COM #ifdef ARN_IBSS
4169999SWang.Lin@Sun.COM arn_buflist_cleanup(&sc->sc_bcbuf_list);
4179999SWang.Lin@Sun.COM #endif
4189999SWang.Lin@Sun.COM
4199999SWang.Lin@Sun.COM /* Free descriptor DMA buffer */
4209999SWang.Lin@Sun.COM arn_free_dma_mem(&sc->sc_desc_dma);
4219999SWang.Lin@Sun.COM
4229999SWang.Lin@Sun.COM kmem_free((void *)sc->sc_vbufptr, sc->sc_vbuflen);
4239999SWang.Lin@Sun.COM sc->sc_vbufptr = NULL;
4249999SWang.Lin@Sun.COM }
4259999SWang.Lin@Sun.COM
4269999SWang.Lin@Sun.COM static int
arn_desc_alloc(dev_info_t * devinfo,struct arn_softc * sc)4279999SWang.Lin@Sun.COM arn_desc_alloc(dev_info_t *devinfo, struct arn_softc *sc)
4289999SWang.Lin@Sun.COM {
4299999SWang.Lin@Sun.COM int err;
4309999SWang.Lin@Sun.COM size_t size;
4319999SWang.Lin@Sun.COM struct ath_desc *ds;
4329999SWang.Lin@Sun.COM struct ath_buf *bf;
4339999SWang.Lin@Sun.COM
4349999SWang.Lin@Sun.COM #ifdef ARN_IBSS
4359999SWang.Lin@Sun.COM size = sizeof (struct ath_desc) * (ATH_TXBUF + ATH_RXBUF + ATH_BCBUF);
4369999SWang.Lin@Sun.COM #else
4379999SWang.Lin@Sun.COM size = sizeof (struct ath_desc) * (ATH_TXBUF + ATH_RXBUF);
4389999SWang.Lin@Sun.COM #endif
4399999SWang.Lin@Sun.COM
4409999SWang.Lin@Sun.COM err = arn_alloc_dma_mem(devinfo, &arn_desc_dma_attr, size,
4419999SWang.Lin@Sun.COM &arn_desc_accattr, DDI_DMA_CONSISTENT,
4429999SWang.Lin@Sun.COM DDI_DMA_RDWR | DDI_DMA_CONSISTENT, &sc->sc_desc_dma);
4439999SWang.Lin@Sun.COM
4449999SWang.Lin@Sun.COM /* virtual address of the first descriptor */
4459999SWang.Lin@Sun.COM sc->sc_desc = (struct ath_desc *)sc->sc_desc_dma.mem_va;
4469999SWang.Lin@Sun.COM
4479999SWang.Lin@Sun.COM ds = sc->sc_desc;
4489999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INIT, "arn: arn_desc_alloc(): DMA map: "
4499999SWang.Lin@Sun.COM "%p (%d) -> %p\n",
4509999SWang.Lin@Sun.COM sc->sc_desc, sc->sc_desc_dma.alength,
4519999SWang.Lin@Sun.COM sc->sc_desc_dma.cookie.dmac_address));
4529999SWang.Lin@Sun.COM
4539999SWang.Lin@Sun.COM /* allocate data structures to describe TX/RX DMA buffers */
4549999SWang.Lin@Sun.COM #ifdef ARN_IBSS
4559999SWang.Lin@Sun.COM sc->sc_vbuflen = sizeof (struct ath_buf) * (ATH_TXBUF + ATH_RXBUF +
4569999SWang.Lin@Sun.COM ATH_BCBUF);
4579999SWang.Lin@Sun.COM #else
4589999SWang.Lin@Sun.COM sc->sc_vbuflen = sizeof (struct ath_buf) * (ATH_TXBUF + ATH_RXBUF);
4599999SWang.Lin@Sun.COM #endif
4609999SWang.Lin@Sun.COM bf = (struct ath_buf *)kmem_zalloc(sc->sc_vbuflen, KM_SLEEP);
4619999SWang.Lin@Sun.COM sc->sc_vbufptr = bf;
4629999SWang.Lin@Sun.COM
4639999SWang.Lin@Sun.COM /* DMA buffer size for each TX/RX packet */
46411729SWang.Lin@Sun.COM #ifdef ARN_TX_AGGREGRATION
46511729SWang.Lin@Sun.COM sc->tx_dmabuf_size =
46611729SWang.Lin@Sun.COM roundup((IEEE80211_MAX_MPDU_LEN + 3840 * 2),
46711729SWang.Lin@Sun.COM min(sc->sc_cachelsz, (uint16_t)64));
46811729SWang.Lin@Sun.COM #else
46911729SWang.Lin@Sun.COM sc->tx_dmabuf_size =
47011729SWang.Lin@Sun.COM roundup(IEEE80211_MAX_MPDU_LEN, min(sc->sc_cachelsz, (uint16_t)64));
47111729SWang.Lin@Sun.COM #endif
47211729SWang.Lin@Sun.COM sc->rx_dmabuf_size =
47311729SWang.Lin@Sun.COM roundup(IEEE80211_MAX_MPDU_LEN, min(sc->sc_cachelsz, (uint16_t)64));
4749999SWang.Lin@Sun.COM
4759999SWang.Lin@Sun.COM /* create RX buffer list */
4769999SWang.Lin@Sun.COM err = arn_buflist_setup(devinfo, sc, &sc->sc_rxbuf_list, &bf, &ds,
47711729SWang.Lin@Sun.COM ATH_RXBUF, DDI_DMA_READ | DDI_DMA_STREAMING, sc->rx_dmabuf_size);
4789999SWang.Lin@Sun.COM if (err != DDI_SUCCESS) {
4799999SWang.Lin@Sun.COM arn_desc_free(sc);
4809999SWang.Lin@Sun.COM return (err);
4819999SWang.Lin@Sun.COM }
4829999SWang.Lin@Sun.COM
4839999SWang.Lin@Sun.COM /* create TX buffer list */
4849999SWang.Lin@Sun.COM err = arn_buflist_setup(devinfo, sc, &sc->sc_txbuf_list, &bf, &ds,
48511729SWang.Lin@Sun.COM ATH_TXBUF, DDI_DMA_STREAMING, sc->tx_dmabuf_size);
4869999SWang.Lin@Sun.COM if (err != DDI_SUCCESS) {
4879999SWang.Lin@Sun.COM arn_desc_free(sc);
4889999SWang.Lin@Sun.COM return (err);
4899999SWang.Lin@Sun.COM }
4909999SWang.Lin@Sun.COM
4919999SWang.Lin@Sun.COM /* create beacon buffer list */
4929999SWang.Lin@Sun.COM #ifdef ARN_IBSS
4939999SWang.Lin@Sun.COM err = arn_buflist_setup(devinfo, sc, &sc->sc_bcbuf_list, &bf, &ds,
4949999SWang.Lin@Sun.COM ATH_BCBUF, DDI_DMA_STREAMING);
4959999SWang.Lin@Sun.COM if (err != DDI_SUCCESS) {
4969999SWang.Lin@Sun.COM arn_desc_free(sc);
4979999SWang.Lin@Sun.COM return (err);
4989999SWang.Lin@Sun.COM }
4999999SWang.Lin@Sun.COM #endif
5009999SWang.Lin@Sun.COM
5019999SWang.Lin@Sun.COM return (DDI_SUCCESS);
5029999SWang.Lin@Sun.COM }
5039999SWang.Lin@Sun.COM
5049999SWang.Lin@Sun.COM static struct ath_rate_table *
5059999SWang.Lin@Sun.COM /* LINTED E_STATIC_UNUSED */
arn_get_ratetable(struct arn_softc * sc,uint32_t mode)5069999SWang.Lin@Sun.COM arn_get_ratetable(struct arn_softc *sc, uint32_t mode)
5079999SWang.Lin@Sun.COM {
5089999SWang.Lin@Sun.COM struct ath_rate_table *rate_table = NULL;
5099999SWang.Lin@Sun.COM
5109999SWang.Lin@Sun.COM switch (mode) {
5119999SWang.Lin@Sun.COM case IEEE80211_MODE_11A:
5129999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11A];
5139999SWang.Lin@Sun.COM break;
5149999SWang.Lin@Sun.COM case IEEE80211_MODE_11B:
5159999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11B];
5169999SWang.Lin@Sun.COM break;
5179999SWang.Lin@Sun.COM case IEEE80211_MODE_11G:
5189999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11G];
5199999SWang.Lin@Sun.COM break;
5209999SWang.Lin@Sun.COM #ifdef ARB_11N
5219999SWang.Lin@Sun.COM case IEEE80211_MODE_11NA_HT20:
5229999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT20];
5239999SWang.Lin@Sun.COM break;
5249999SWang.Lin@Sun.COM case IEEE80211_MODE_11NG_HT20:
5259999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT20];
5269999SWang.Lin@Sun.COM break;
5279999SWang.Lin@Sun.COM case IEEE80211_MODE_11NA_HT40PLUS:
5289999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS];
5299999SWang.Lin@Sun.COM break;
5309999SWang.Lin@Sun.COM case IEEE80211_MODE_11NA_HT40MINUS:
5319999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS];
5329999SWang.Lin@Sun.COM break;
5339999SWang.Lin@Sun.COM case IEEE80211_MODE_11NG_HT40PLUS:
5349999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS];
5359999SWang.Lin@Sun.COM break;
5369999SWang.Lin@Sun.COM case IEEE80211_MODE_11NG_HT40MINUS:
5379999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS];
5389999SWang.Lin@Sun.COM break;
5399999SWang.Lin@Sun.COM #endif
5409999SWang.Lin@Sun.COM default:
5419999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_FATAL, "arn: arn_get_ratetable(): "
5429999SWang.Lin@Sun.COM "invalid mode %u\n", mode));
5439999SWang.Lin@Sun.COM return (NULL);
5449999SWang.Lin@Sun.COM }
5459999SWang.Lin@Sun.COM
5469999SWang.Lin@Sun.COM return (rate_table);
5479999SWang.Lin@Sun.COM
5489999SWang.Lin@Sun.COM }
5499999SWang.Lin@Sun.COM
5509999SWang.Lin@Sun.COM static void
arn_setcurmode(struct arn_softc * sc,enum wireless_mode mode)5519999SWang.Lin@Sun.COM arn_setcurmode(struct arn_softc *sc, enum wireless_mode mode)
5529999SWang.Lin@Sun.COM {
5539999SWang.Lin@Sun.COM struct ath_rate_table *rt;
5549999SWang.Lin@Sun.COM int i;
5559999SWang.Lin@Sun.COM
5569999SWang.Lin@Sun.COM for (i = 0; i < sizeof (sc->asc_rixmap); i++)
5579999SWang.Lin@Sun.COM sc->asc_rixmap[i] = 0xff;
5589999SWang.Lin@Sun.COM
5599999SWang.Lin@Sun.COM rt = sc->hw_rate_table[mode];
5609999SWang.Lin@Sun.COM ASSERT(rt != NULL);
5619999SWang.Lin@Sun.COM
5629999SWang.Lin@Sun.COM for (i = 0; i < rt->rate_cnt; i++)
5639999SWang.Lin@Sun.COM sc->asc_rixmap[rt->info[i].dot11rate &
5649999SWang.Lin@Sun.COM IEEE80211_RATE_VAL] = (uint8_t)i; /* LINT */
5659999SWang.Lin@Sun.COM
5669999SWang.Lin@Sun.COM sc->sc_currates = rt;
5679999SWang.Lin@Sun.COM sc->sc_curmode = mode;
5689999SWang.Lin@Sun.COM
5699999SWang.Lin@Sun.COM /*
5709999SWang.Lin@Sun.COM * All protection frames are transmited at 2Mb/s for
5719999SWang.Lin@Sun.COM * 11g, otherwise at 1Mb/s.
5729999SWang.Lin@Sun.COM * XXX select protection rate index from rate table.
5739999SWang.Lin@Sun.COM */
5749999SWang.Lin@Sun.COM sc->sc_protrix = (mode == ATH9K_MODE_11G ? 1 : 0);
5759999SWang.Lin@Sun.COM }
5769999SWang.Lin@Sun.COM
5779999SWang.Lin@Sun.COM static enum wireless_mode
arn_chan2mode(struct ath9k_channel * chan)5789999SWang.Lin@Sun.COM arn_chan2mode(struct ath9k_channel *chan)
5799999SWang.Lin@Sun.COM {
5809999SWang.Lin@Sun.COM if (chan->chanmode == CHANNEL_A)
5819999SWang.Lin@Sun.COM return (ATH9K_MODE_11A);
5829999SWang.Lin@Sun.COM else if (chan->chanmode == CHANNEL_G)
5839999SWang.Lin@Sun.COM return (ATH9K_MODE_11G);
5849999SWang.Lin@Sun.COM else if (chan->chanmode == CHANNEL_B)
5859999SWang.Lin@Sun.COM return (ATH9K_MODE_11B);
5869999SWang.Lin@Sun.COM else if (chan->chanmode == CHANNEL_A_HT20)
5879999SWang.Lin@Sun.COM return (ATH9K_MODE_11NA_HT20);
5889999SWang.Lin@Sun.COM else if (chan->chanmode == CHANNEL_G_HT20)
5899999SWang.Lin@Sun.COM return (ATH9K_MODE_11NG_HT20);
5909999SWang.Lin@Sun.COM else if (chan->chanmode == CHANNEL_A_HT40PLUS)
5919999SWang.Lin@Sun.COM return (ATH9K_MODE_11NA_HT40PLUS);
5929999SWang.Lin@Sun.COM else if (chan->chanmode == CHANNEL_A_HT40MINUS)
5939999SWang.Lin@Sun.COM return (ATH9K_MODE_11NA_HT40MINUS);
5949999SWang.Lin@Sun.COM else if (chan->chanmode == CHANNEL_G_HT40PLUS)
5959999SWang.Lin@Sun.COM return (ATH9K_MODE_11NG_HT40PLUS);
5969999SWang.Lin@Sun.COM else if (chan->chanmode == CHANNEL_G_HT40MINUS)
5979999SWang.Lin@Sun.COM return (ATH9K_MODE_11NG_HT40MINUS);
5989999SWang.Lin@Sun.COM
5999999SWang.Lin@Sun.COM return (ATH9K_MODE_11B);
6009999SWang.Lin@Sun.COM }
6019999SWang.Lin@Sun.COM
6029999SWang.Lin@Sun.COM static void
arn_update_txpow(struct arn_softc * sc)6039999SWang.Lin@Sun.COM arn_update_txpow(struct arn_softc *sc)
6049999SWang.Lin@Sun.COM {
6059999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
6069999SWang.Lin@Sun.COM uint32_t txpow;
6079999SWang.Lin@Sun.COM
6089999SWang.Lin@Sun.COM if (sc->sc_curtxpow != sc->sc_config.txpowlimit) {
6099999SWang.Lin@Sun.COM (void) ath9k_hw_set_txpowerlimit(ah, sc->sc_config.txpowlimit);
6109999SWang.Lin@Sun.COM /* read back in case value is clamped */
6119999SWang.Lin@Sun.COM (void) ath9k_hw_getcapability(ah, ATH9K_CAP_TXPOW, 1, &txpow);
6129999SWang.Lin@Sun.COM sc->sc_curtxpow = (uint32_t)txpow;
6139999SWang.Lin@Sun.COM }
6149999SWang.Lin@Sun.COM }
6159999SWang.Lin@Sun.COM
61611729SWang.Lin@Sun.COM uint8_t
parse_mpdudensity(uint8_t mpdudensity)61711729SWang.Lin@Sun.COM parse_mpdudensity(uint8_t mpdudensity)
61811729SWang.Lin@Sun.COM {
61911729SWang.Lin@Sun.COM /*
62011729SWang.Lin@Sun.COM * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing":
62111729SWang.Lin@Sun.COM * 0 for no restriction
62211729SWang.Lin@Sun.COM * 1 for 1/4 us
62311729SWang.Lin@Sun.COM * 2 for 1/2 us
62411729SWang.Lin@Sun.COM * 3 for 1 us
62511729SWang.Lin@Sun.COM * 4 for 2 us
62611729SWang.Lin@Sun.COM * 5 for 4 us
62711729SWang.Lin@Sun.COM * 6 for 8 us
62811729SWang.Lin@Sun.COM * 7 for 16 us
62911729SWang.Lin@Sun.COM */
63011729SWang.Lin@Sun.COM switch (mpdudensity) {
63111729SWang.Lin@Sun.COM case 0:
63211729SWang.Lin@Sun.COM return (0);
63311729SWang.Lin@Sun.COM case 1:
63411729SWang.Lin@Sun.COM case 2:
63511729SWang.Lin@Sun.COM case 3:
63611729SWang.Lin@Sun.COM /*
63711729SWang.Lin@Sun.COM * Our lower layer calculations limit our
63811729SWang.Lin@Sun.COM * precision to 1 microsecond
63911729SWang.Lin@Sun.COM */
64011729SWang.Lin@Sun.COM return (1);
64111729SWang.Lin@Sun.COM case 4:
64211729SWang.Lin@Sun.COM return (2);
64311729SWang.Lin@Sun.COM case 5:
64411729SWang.Lin@Sun.COM return (4);
64511729SWang.Lin@Sun.COM case 6:
64611729SWang.Lin@Sun.COM return (8);
64711729SWang.Lin@Sun.COM case 7:
64811729SWang.Lin@Sun.COM return (16);
64911729SWang.Lin@Sun.COM default:
65011729SWang.Lin@Sun.COM return (0);
65111729SWang.Lin@Sun.COM }
65211729SWang.Lin@Sun.COM }
65311729SWang.Lin@Sun.COM
6549999SWang.Lin@Sun.COM static void
arn_setup_rates(struct arn_softc * sc,uint32_t mode)6559999SWang.Lin@Sun.COM arn_setup_rates(struct arn_softc *sc, uint32_t mode)
6569999SWang.Lin@Sun.COM {
6579999SWang.Lin@Sun.COM int i, maxrates;
6589999SWang.Lin@Sun.COM struct ath_rate_table *rate_table = NULL;
6599999SWang.Lin@Sun.COM struct ieee80211_rateset *rateset;
6609999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc;
6619999SWang.Lin@Sun.COM
6629999SWang.Lin@Sun.COM /* rate_table = arn_get_ratetable(sc, mode); */
6639999SWang.Lin@Sun.COM switch (mode) {
6649999SWang.Lin@Sun.COM case IEEE80211_MODE_11A:
6659999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11A];
6669999SWang.Lin@Sun.COM break;
6679999SWang.Lin@Sun.COM case IEEE80211_MODE_11B:
6689999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11B];
6699999SWang.Lin@Sun.COM break;
6709999SWang.Lin@Sun.COM case IEEE80211_MODE_11G:
6719999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11G];
6729999SWang.Lin@Sun.COM break;
6739999SWang.Lin@Sun.COM #ifdef ARN_11N
6749999SWang.Lin@Sun.COM case IEEE80211_MODE_11NA_HT20:
6759999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT20];
6769999SWang.Lin@Sun.COM break;
6779999SWang.Lin@Sun.COM case IEEE80211_MODE_11NG_HT20:
6789999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT20];
6799999SWang.Lin@Sun.COM break;
6809999SWang.Lin@Sun.COM case IEEE80211_MODE_11NA_HT40PLUS:
6819999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS];
6829999SWang.Lin@Sun.COM break;
6839999SWang.Lin@Sun.COM case IEEE80211_MODE_11NA_HT40MINUS:
6849999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS];
6859999SWang.Lin@Sun.COM break;
6869999SWang.Lin@Sun.COM case IEEE80211_MODE_11NG_HT40PLUS:
6879999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS];
6889999SWang.Lin@Sun.COM break;
6899999SWang.Lin@Sun.COM case IEEE80211_MODE_11NG_HT40MINUS:
6909999SWang.Lin@Sun.COM rate_table = sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS];
6919999SWang.Lin@Sun.COM break;
6929999SWang.Lin@Sun.COM #endif
6939999SWang.Lin@Sun.COM default:
6949999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_RATE, "arn: arn_get_ratetable(): "
6959999SWang.Lin@Sun.COM "invalid mode %u\n", mode));
6969999SWang.Lin@Sun.COM break;
6979999SWang.Lin@Sun.COM }
6989999SWang.Lin@Sun.COM if (rate_table == NULL)
6999999SWang.Lin@Sun.COM return;
7009999SWang.Lin@Sun.COM if (rate_table->rate_cnt > ATH_RATE_MAX) {
7019999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_RATE, "arn: arn_rate_setup(): "
7029999SWang.Lin@Sun.COM "rate table too small (%u > %u)\n",
7039999SWang.Lin@Sun.COM rate_table->rate_cnt, IEEE80211_RATE_MAXSIZE));
7049999SWang.Lin@Sun.COM maxrates = ATH_RATE_MAX;
7059999SWang.Lin@Sun.COM } else
7069999SWang.Lin@Sun.COM maxrates = rate_table->rate_cnt;
7079999SWang.Lin@Sun.COM
7089999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_RATE, "arn: arn_rate_setup(): "
7099999SWang.Lin@Sun.COM "maxrates is %d\n", maxrates));
7109999SWang.Lin@Sun.COM
7119999SWang.Lin@Sun.COM rateset = &ic->ic_sup_rates[mode];
7129999SWang.Lin@Sun.COM for (i = 0; i < maxrates; i++) {
7139999SWang.Lin@Sun.COM rateset->ir_rates[i] = rate_table->info[i].dot11rate;
7149999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_RATE, "arn: arn_rate_setup(): "
7159999SWang.Lin@Sun.COM "%d\n", rate_table->info[i].dot11rate));
7169999SWang.Lin@Sun.COM }
7179999SWang.Lin@Sun.COM rateset->ir_nrates = (uint8_t)maxrates; /* ??? */
7189999SWang.Lin@Sun.COM }
7199999SWang.Lin@Sun.COM
7209999SWang.Lin@Sun.COM static int
arn_setup_channels(struct arn_softc * sc)7219999SWang.Lin@Sun.COM arn_setup_channels(struct arn_softc *sc)
7229999SWang.Lin@Sun.COM {
7239999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
7249999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc;
7259999SWang.Lin@Sun.COM int nchan, i, index;
7269999SWang.Lin@Sun.COM uint8_t regclassids[ATH_REGCLASSIDS_MAX];
7279999SWang.Lin@Sun.COM uint32_t nregclass = 0;
7289999SWang.Lin@Sun.COM struct ath9k_channel *c;
7299999SWang.Lin@Sun.COM
7309999SWang.Lin@Sun.COM /* Fill in ah->ah_channels */
7319999SWang.Lin@Sun.COM if (!ath9k_regd_init_channels(ah, ATH_CHAN_MAX, (uint32_t *)&nchan,
7329999SWang.Lin@Sun.COM regclassids, ATH_REGCLASSIDS_MAX, &nregclass, CTRY_DEFAULT,
7339999SWang.Lin@Sun.COM B_FALSE, 1)) {
7349999SWang.Lin@Sun.COM uint32_t rd = ah->ah_currentRD;
7359999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_CHANNEL, "arn: arn_setup_channels(): "
7369999SWang.Lin@Sun.COM "unable to collect channel list; "
7379999SWang.Lin@Sun.COM "regdomain likely %u country code %u\n",
7389999SWang.Lin@Sun.COM rd, CTRY_DEFAULT));
7399999SWang.Lin@Sun.COM return (EINVAL);
7409999SWang.Lin@Sun.COM }
7419999SWang.Lin@Sun.COM
7429999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_CHANNEL, "arn: arn_setup_channels(): "
7439999SWang.Lin@Sun.COM "number of channel is %d\n", nchan));
7449999SWang.Lin@Sun.COM
7459999SWang.Lin@Sun.COM for (i = 0; i < nchan; i++) {
7469999SWang.Lin@Sun.COM c = &ah->ah_channels[i];
74711729SWang.Lin@Sun.COM uint32_t flags;
7489999SWang.Lin@Sun.COM index = ath9k_hw_mhz2ieee(ah, c->channel, c->channelFlags);
7499999SWang.Lin@Sun.COM
7509999SWang.Lin@Sun.COM if (index > IEEE80211_CHAN_MAX) {
7519999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_CHANNEL,
7529999SWang.Lin@Sun.COM "arn: arn_setup_channels(): "
7539999SWang.Lin@Sun.COM "bad hal channel %d (%u/%x) ignored\n",
7549999SWang.Lin@Sun.COM index, c->channel, c->channelFlags));
7559999SWang.Lin@Sun.COM continue;
7569999SWang.Lin@Sun.COM }
7579999SWang.Lin@Sun.COM /* NB: flags are known to be compatible */
7589999SWang.Lin@Sun.COM if (index < 0) {
7599999SWang.Lin@Sun.COM /*
7609999SWang.Lin@Sun.COM * can't handle frequency <2400MHz (negative
7619999SWang.Lin@Sun.COM * channels) right now
7629999SWang.Lin@Sun.COM */
7639999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_CHANNEL,
7649999SWang.Lin@Sun.COM "arn: arn_setup_channels(): "
7659999SWang.Lin@Sun.COM "hal channel %d (%u/%x) "
7669999SWang.Lin@Sun.COM "cannot be handled, ignored\n",
7679999SWang.Lin@Sun.COM index, c->channel, c->channelFlags));
7689999SWang.Lin@Sun.COM continue;
7699999SWang.Lin@Sun.COM }
7709999SWang.Lin@Sun.COM
7719999SWang.Lin@Sun.COM /*
7729999SWang.Lin@Sun.COM * Calculate net80211 flags; most are compatible
7739999SWang.Lin@Sun.COM * but some need massaging. Note the static turbo
7749999SWang.Lin@Sun.COM * conversion can be removed once net80211 is updated
7759999SWang.Lin@Sun.COM * to understand static vs. dynamic turbo.
7769999SWang.Lin@Sun.COM */
7779999SWang.Lin@Sun.COM
7789999SWang.Lin@Sun.COM flags = c->channelFlags & (CHANNEL_ALL | CHANNEL_PASSIVE);
7799999SWang.Lin@Sun.COM
7809999SWang.Lin@Sun.COM if (ic->ic_sup_channels[index].ich_freq == 0) {
7819999SWang.Lin@Sun.COM ic->ic_sup_channels[index].ich_freq = c->channel;
7829999SWang.Lin@Sun.COM ic->ic_sup_channels[index].ich_flags = flags;
7839999SWang.Lin@Sun.COM } else {
7849999SWang.Lin@Sun.COM /* channels overlap; e.g. 11g and 11b */
7859999SWang.Lin@Sun.COM ic->ic_sup_channels[index].ich_flags |= flags;
7869999SWang.Lin@Sun.COM }
7879999SWang.Lin@Sun.COM if ((c->channelFlags & CHANNEL_G) == CHANNEL_G) {
7889999SWang.Lin@Sun.COM sc->sc_have11g = 1;
7899999SWang.Lin@Sun.COM ic->ic_caps |= IEEE80211_C_SHPREAMBLE |
7909999SWang.Lin@Sun.COM IEEE80211_C_SHSLOT; /* short slot time */
7919999SWang.Lin@Sun.COM }
7929999SWang.Lin@Sun.COM }
7939999SWang.Lin@Sun.COM
7949999SWang.Lin@Sun.COM return (0);
7959999SWang.Lin@Sun.COM }
7969999SWang.Lin@Sun.COM
7979999SWang.Lin@Sun.COM uint32_t
arn_chan2flags(ieee80211com_t * isc,struct ieee80211_channel * chan)7989999SWang.Lin@Sun.COM arn_chan2flags(ieee80211com_t *isc, struct ieee80211_channel *chan)
7999999SWang.Lin@Sun.COM {
80011729SWang.Lin@Sun.COM uint32_t channel_mode;
80111729SWang.Lin@Sun.COM switch (ieee80211_chan2mode(isc, chan)) {
80211729SWang.Lin@Sun.COM case IEEE80211_MODE_11NA:
80311729SWang.Lin@Sun.COM if (chan->ich_flags & IEEE80211_CHAN_HT40U)
80411729SWang.Lin@Sun.COM channel_mode = CHANNEL_A_HT40PLUS;
80511729SWang.Lin@Sun.COM else if (chan->ich_flags & IEEE80211_CHAN_HT40D)
80611729SWang.Lin@Sun.COM channel_mode = CHANNEL_A_HT40MINUS;
80711729SWang.Lin@Sun.COM else
80811729SWang.Lin@Sun.COM channel_mode = CHANNEL_A_HT20;
80911729SWang.Lin@Sun.COM break;
81011729SWang.Lin@Sun.COM case IEEE80211_MODE_11NG:
81111729SWang.Lin@Sun.COM if (chan->ich_flags & IEEE80211_CHAN_HT40U)
81211729SWang.Lin@Sun.COM channel_mode = CHANNEL_G_HT40PLUS;
81311729SWang.Lin@Sun.COM else if (chan->ich_flags & IEEE80211_CHAN_HT40D)
81411729SWang.Lin@Sun.COM channel_mode = CHANNEL_G_HT40MINUS;
81511729SWang.Lin@Sun.COM else
81611729SWang.Lin@Sun.COM channel_mode = CHANNEL_G_HT20;
81711729SWang.Lin@Sun.COM break;
81811729SWang.Lin@Sun.COM case IEEE80211_MODE_TURBO_G:
81911729SWang.Lin@Sun.COM case IEEE80211_MODE_STURBO_A:
82011729SWang.Lin@Sun.COM case IEEE80211_MODE_TURBO_A:
82111729SWang.Lin@Sun.COM channel_mode = 0;
82211729SWang.Lin@Sun.COM break;
82311729SWang.Lin@Sun.COM case IEEE80211_MODE_11A:
82411729SWang.Lin@Sun.COM channel_mode = CHANNEL_A;
82511729SWang.Lin@Sun.COM break;
82611729SWang.Lin@Sun.COM case IEEE80211_MODE_11G:
82711729SWang.Lin@Sun.COM channel_mode = CHANNEL_B;
82811729SWang.Lin@Sun.COM break;
82911729SWang.Lin@Sun.COM case IEEE80211_MODE_11B:
83011729SWang.Lin@Sun.COM channel_mode = CHANNEL_G;
83111729SWang.Lin@Sun.COM break;
83211729SWang.Lin@Sun.COM case IEEE80211_MODE_FH:
83311729SWang.Lin@Sun.COM channel_mode = 0;
83411729SWang.Lin@Sun.COM break;
83511729SWang.Lin@Sun.COM default:
83611729SWang.Lin@Sun.COM break;
83711729SWang.Lin@Sun.COM }
83811729SWang.Lin@Sun.COM
83911729SWang.Lin@Sun.COM return (channel_mode);
8409999SWang.Lin@Sun.COM }
8419999SWang.Lin@Sun.COM
8429999SWang.Lin@Sun.COM /*
8439999SWang.Lin@Sun.COM * Update internal state after a channel change.
8449999SWang.Lin@Sun.COM */
8459999SWang.Lin@Sun.COM void
arn_chan_change(struct arn_softc * sc,struct ieee80211_channel * chan)8469999SWang.Lin@Sun.COM arn_chan_change(struct arn_softc *sc, struct ieee80211_channel *chan)
8479999SWang.Lin@Sun.COM {
8489999SWang.Lin@Sun.COM struct ieee80211com *ic = &sc->sc_isc;
8499999SWang.Lin@Sun.COM enum ieee80211_phymode mode;
8509999SWang.Lin@Sun.COM enum wireless_mode wlmode;
8519999SWang.Lin@Sun.COM
8529999SWang.Lin@Sun.COM /*
8539999SWang.Lin@Sun.COM * Change channels and update the h/w rate map
8549999SWang.Lin@Sun.COM * if we're switching; e.g. 11a to 11b/g.
8559999SWang.Lin@Sun.COM */
8569999SWang.Lin@Sun.COM mode = ieee80211_chan2mode(ic, chan);
8579999SWang.Lin@Sun.COM switch (mode) {
8589999SWang.Lin@Sun.COM case IEEE80211_MODE_11A:
8599999SWang.Lin@Sun.COM wlmode = ATH9K_MODE_11A;
8609999SWang.Lin@Sun.COM break;
8619999SWang.Lin@Sun.COM case IEEE80211_MODE_11B:
8629999SWang.Lin@Sun.COM wlmode = ATH9K_MODE_11B;
8639999SWang.Lin@Sun.COM break;
8649999SWang.Lin@Sun.COM case IEEE80211_MODE_11G:
8659999SWang.Lin@Sun.COM wlmode = ATH9K_MODE_11B;
8669999SWang.Lin@Sun.COM break;
8679999SWang.Lin@Sun.COM default:
8689999SWang.Lin@Sun.COM break;
8699999SWang.Lin@Sun.COM }
8709999SWang.Lin@Sun.COM if (wlmode != sc->sc_curmode)
8719999SWang.Lin@Sun.COM arn_setcurmode(sc, wlmode);
8729999SWang.Lin@Sun.COM
8739999SWang.Lin@Sun.COM }
8749999SWang.Lin@Sun.COM
8759999SWang.Lin@Sun.COM /*
8769999SWang.Lin@Sun.COM * Set/change channels. If the channel is really being changed, it's done
8779999SWang.Lin@Sun.COM * by reseting the chip. To accomplish this we must first cleanup any pending
8789999SWang.Lin@Sun.COM * DMA, then restart stuff.
8799999SWang.Lin@Sun.COM */
8809999SWang.Lin@Sun.COM static int
arn_set_channel(struct arn_softc * sc,struct ath9k_channel * hchan)8819999SWang.Lin@Sun.COM arn_set_channel(struct arn_softc *sc, struct ath9k_channel *hchan)
8829999SWang.Lin@Sun.COM {
8839999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
8849999SWang.Lin@Sun.COM ieee80211com_t *ic = &sc->sc_isc;
8859999SWang.Lin@Sun.COM boolean_t fastcc = B_TRUE;
8869999SWang.Lin@Sun.COM boolean_t stopped;
8879999SWang.Lin@Sun.COM struct ieee80211_channel chan;
8889999SWang.Lin@Sun.COM enum wireless_mode curmode;
8899999SWang.Lin@Sun.COM
8909999SWang.Lin@Sun.COM if (sc->sc_flags & SC_OP_INVALID)
8919999SWang.Lin@Sun.COM return (EIO);
8929999SWang.Lin@Sun.COM
8939999SWang.Lin@Sun.COM if (hchan->channel != sc->sc_ah->ah_curchan->channel ||
8949999SWang.Lin@Sun.COM hchan->channelFlags != sc->sc_ah->ah_curchan->channelFlags ||
8959999SWang.Lin@Sun.COM (sc->sc_flags & SC_OP_CHAINMASK_UPDATE) ||
8969999SWang.Lin@Sun.COM (sc->sc_flags & SC_OP_FULL_RESET)) {
8979999SWang.Lin@Sun.COM int status;
8989999SWang.Lin@Sun.COM
8999999SWang.Lin@Sun.COM /*
9009999SWang.Lin@Sun.COM * This is only performed if the channel settings have
9019999SWang.Lin@Sun.COM * actually changed.
9029999SWang.Lin@Sun.COM *
9039999SWang.Lin@Sun.COM * To switch channels clear any pending DMA operations;
9049999SWang.Lin@Sun.COM * wait long enough for the RX fifo to drain, reset the
9059999SWang.Lin@Sun.COM * hardware at the new frequency, and then re-enable
9069999SWang.Lin@Sun.COM * the relevant bits of the h/w.
9079999SWang.Lin@Sun.COM */
9089999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(ah, 0); /* disable interrupts */
9099999SWang.Lin@Sun.COM arn_draintxq(sc, B_FALSE); /* clear pending tx frames */
9109999SWang.Lin@Sun.COM stopped = arn_stoprecv(sc); /* turn off frame recv */
9119999SWang.Lin@Sun.COM
9129999SWang.Lin@Sun.COM /*
9139999SWang.Lin@Sun.COM * XXX: do not flush receive queue here. We don't want
9149999SWang.Lin@Sun.COM * to flush data frames already in queue because of
9159999SWang.Lin@Sun.COM * changing channel.
9169999SWang.Lin@Sun.COM */
9179999SWang.Lin@Sun.COM
9189999SWang.Lin@Sun.COM if (!stopped || (sc->sc_flags & SC_OP_FULL_RESET))
9199999SWang.Lin@Sun.COM fastcc = B_FALSE;
9209999SWang.Lin@Sun.COM
9219999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_CHANNEL, "arn: arn_set_channel(): "
9229999SWang.Lin@Sun.COM "(%u MHz) -> (%u MHz), cflags:%x, chanwidth: %d\n",
9239999SWang.Lin@Sun.COM sc->sc_ah->ah_curchan->channel,
9249999SWang.Lin@Sun.COM hchan->channel, hchan->channelFlags, sc->tx_chan_width));
9259999SWang.Lin@Sun.COM
9269999SWang.Lin@Sun.COM if (!ath9k_hw_reset(ah, hchan, sc->tx_chan_width,
9279999SWang.Lin@Sun.COM sc->sc_tx_chainmask, sc->sc_rx_chainmask,
9289999SWang.Lin@Sun.COM sc->sc_ht_extprotspacing, fastcc, &status)) {
9299999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_FATAL, "arn: arn_set_channel(): "
9309999SWang.Lin@Sun.COM "unable to reset channel %u (%uMhz) "
9319999SWang.Lin@Sun.COM "flags 0x%x hal status %u\n",
9329999SWang.Lin@Sun.COM ath9k_hw_mhz2ieee(ah, hchan->channel,
9339999SWang.Lin@Sun.COM hchan->channelFlags),
9349999SWang.Lin@Sun.COM hchan->channel, hchan->channelFlags, status));
9359999SWang.Lin@Sun.COM return (EIO);
9369999SWang.Lin@Sun.COM }
9379999SWang.Lin@Sun.COM
9389999SWang.Lin@Sun.COM sc->sc_curchan = *hchan;
9399999SWang.Lin@Sun.COM
9409999SWang.Lin@Sun.COM sc->sc_flags &= ~SC_OP_CHAINMASK_UPDATE;
9419999SWang.Lin@Sun.COM sc->sc_flags &= ~SC_OP_FULL_RESET;
9429999SWang.Lin@Sun.COM
9439999SWang.Lin@Sun.COM if (arn_startrecv(sc) != 0) {
9449999SWang.Lin@Sun.COM arn_problem("arn: arn_set_channel(): "
9459999SWang.Lin@Sun.COM "unable to restart recv logic\n");
9469999SWang.Lin@Sun.COM return (EIO);
9479999SWang.Lin@Sun.COM }
9489999SWang.Lin@Sun.COM
9499999SWang.Lin@Sun.COM chan.ich_freq = hchan->channel;
9509999SWang.Lin@Sun.COM chan.ich_flags = hchan->channelFlags;
9519999SWang.Lin@Sun.COM ic->ic_ibss_chan = &chan;
9529999SWang.Lin@Sun.COM
9539999SWang.Lin@Sun.COM /*
9549999SWang.Lin@Sun.COM * Change channels and update the h/w rate map
9559999SWang.Lin@Sun.COM * if we're switching; e.g. 11a to 11b/g.
9569999SWang.Lin@Sun.COM */
9579999SWang.Lin@Sun.COM curmode = arn_chan2mode(hchan);
9589999SWang.Lin@Sun.COM if (curmode != sc->sc_curmode)
9599999SWang.Lin@Sun.COM arn_setcurmode(sc, arn_chan2mode(hchan));
9609999SWang.Lin@Sun.COM
9619999SWang.Lin@Sun.COM arn_update_txpow(sc);
9629999SWang.Lin@Sun.COM
9639999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(ah, sc->sc_imask);
9649999SWang.Lin@Sun.COM }
9659999SWang.Lin@Sun.COM
9669999SWang.Lin@Sun.COM return (0);
9679999SWang.Lin@Sun.COM }
9689999SWang.Lin@Sun.COM
9699999SWang.Lin@Sun.COM /*
9709999SWang.Lin@Sun.COM * This routine performs the periodic noise floor calibration function
9719999SWang.Lin@Sun.COM * that is used to adjust and optimize the chip performance. This
9729999SWang.Lin@Sun.COM * takes environmental changes (location, temperature) into account.
9739999SWang.Lin@Sun.COM * When the task is complete, it reschedules itself depending on the
9749999SWang.Lin@Sun.COM * appropriate interval that was calculated.
9759999SWang.Lin@Sun.COM */
9769999SWang.Lin@Sun.COM static void
arn_ani_calibrate(void * arg)9779999SWang.Lin@Sun.COM arn_ani_calibrate(void *arg)
9789999SWang.Lin@Sun.COM
9799999SWang.Lin@Sun.COM {
9809999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)arg;
9819999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)ic;
9829999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
9839999SWang.Lin@Sun.COM boolean_t longcal = B_FALSE;
9849999SWang.Lin@Sun.COM boolean_t shortcal = B_FALSE;
9859999SWang.Lin@Sun.COM boolean_t aniflag = B_FALSE;
9869999SWang.Lin@Sun.COM unsigned int timestamp = drv_hztousec(ddi_get_lbolt())/1000;
9879999SWang.Lin@Sun.COM uint32_t cal_interval;
9889999SWang.Lin@Sun.COM
9899999SWang.Lin@Sun.COM /*
9909999SWang.Lin@Sun.COM * don't calibrate when we're scanning.
9919999SWang.Lin@Sun.COM * we are most likely not on our home channel.
9929999SWang.Lin@Sun.COM */
9939999SWang.Lin@Sun.COM if (ic->ic_state != IEEE80211_S_RUN)
9949999SWang.Lin@Sun.COM goto settimer;
9959999SWang.Lin@Sun.COM
9969999SWang.Lin@Sun.COM /* Long calibration runs independently of short calibration. */
9979999SWang.Lin@Sun.COM if ((timestamp - sc->sc_ani.sc_longcal_timer) >= ATH_LONG_CALINTERVAL) {
9989999SWang.Lin@Sun.COM longcal = B_TRUE;
9999999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_CALIBRATE, "arn: "
10009999SWang.Lin@Sun.COM "%s: longcal @%lu\n", __func__, drv_hztousec));
10019999SWang.Lin@Sun.COM sc->sc_ani.sc_longcal_timer = timestamp;
10029999SWang.Lin@Sun.COM }
10039999SWang.Lin@Sun.COM
10049999SWang.Lin@Sun.COM /* Short calibration applies only while sc_caldone is FALSE */
10059999SWang.Lin@Sun.COM if (!sc->sc_ani.sc_caldone) {
10069999SWang.Lin@Sun.COM if ((timestamp - sc->sc_ani.sc_shortcal_timer) >=
10079999SWang.Lin@Sun.COM ATH_SHORT_CALINTERVAL) {
10089999SWang.Lin@Sun.COM shortcal = B_TRUE;
10099999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_CALIBRATE, "arn: "
10109999SWang.Lin@Sun.COM "%s: shortcal @%lu\n",
10119999SWang.Lin@Sun.COM __func__, drv_hztousec));
10129999SWang.Lin@Sun.COM sc->sc_ani.sc_shortcal_timer = timestamp;
10139999SWang.Lin@Sun.COM sc->sc_ani.sc_resetcal_timer = timestamp;
10149999SWang.Lin@Sun.COM }
10159999SWang.Lin@Sun.COM } else {
10169999SWang.Lin@Sun.COM if ((timestamp - sc->sc_ani.sc_resetcal_timer) >=
10179999SWang.Lin@Sun.COM ATH_RESTART_CALINTERVAL) {
10189999SWang.Lin@Sun.COM ath9k_hw_reset_calvalid(ah, ah->ah_curchan,
10199999SWang.Lin@Sun.COM &sc->sc_ani.sc_caldone);
10209999SWang.Lin@Sun.COM if (sc->sc_ani.sc_caldone)
10219999SWang.Lin@Sun.COM sc->sc_ani.sc_resetcal_timer = timestamp;
10229999SWang.Lin@Sun.COM }
10239999SWang.Lin@Sun.COM }
10249999SWang.Lin@Sun.COM
10259999SWang.Lin@Sun.COM /* Verify whether we must check ANI */
10269999SWang.Lin@Sun.COM if ((timestamp - sc->sc_ani.sc_checkani_timer) >=
10279999SWang.Lin@Sun.COM ATH_ANI_POLLINTERVAL) {
10289999SWang.Lin@Sun.COM aniflag = B_TRUE;
10299999SWang.Lin@Sun.COM sc->sc_ani.sc_checkani_timer = timestamp;
10309999SWang.Lin@Sun.COM }
10319999SWang.Lin@Sun.COM
10329999SWang.Lin@Sun.COM /* Skip all processing if there's nothing to do. */
10339999SWang.Lin@Sun.COM if (longcal || shortcal || aniflag) {
10349999SWang.Lin@Sun.COM /* Call ANI routine if necessary */
10359999SWang.Lin@Sun.COM if (aniflag)
10369999SWang.Lin@Sun.COM ath9k_hw_ani_monitor(ah, &sc->sc_halstats,
10379999SWang.Lin@Sun.COM ah->ah_curchan);
10389999SWang.Lin@Sun.COM
10399999SWang.Lin@Sun.COM /* Perform calibration if necessary */
10409999SWang.Lin@Sun.COM if (longcal || shortcal) {
10419999SWang.Lin@Sun.COM boolean_t iscaldone = B_FALSE;
10429999SWang.Lin@Sun.COM
10439999SWang.Lin@Sun.COM if (ath9k_hw_calibrate(ah, ah->ah_curchan,
10449999SWang.Lin@Sun.COM sc->sc_rx_chainmask, longcal, &iscaldone)) {
10459999SWang.Lin@Sun.COM if (longcal)
10469999SWang.Lin@Sun.COM sc->sc_ani.sc_noise_floor =
10479999SWang.Lin@Sun.COM ath9k_hw_getchan_noise(ah,
10489999SWang.Lin@Sun.COM ah->ah_curchan);
10499999SWang.Lin@Sun.COM
10509999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_CALIBRATE, "arn: "
10519999SWang.Lin@Sun.COM "%s: calibrate chan %u/%x nf: %d\n",
10529999SWang.Lin@Sun.COM __func__,
10539999SWang.Lin@Sun.COM ah->ah_curchan->channel,
10549999SWang.Lin@Sun.COM ah->ah_curchan->channelFlags,
10559999SWang.Lin@Sun.COM sc->sc_ani.sc_noise_floor));
10569999SWang.Lin@Sun.COM } else {
10579999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_CALIBRATE, "arn: "
10589999SWang.Lin@Sun.COM "%s: calibrate chan %u/%x failed\n",
10599999SWang.Lin@Sun.COM __func__,
10609999SWang.Lin@Sun.COM ah->ah_curchan->channel,
10619999SWang.Lin@Sun.COM ah->ah_curchan->channelFlags));
10629999SWang.Lin@Sun.COM }
10639999SWang.Lin@Sun.COM sc->sc_ani.sc_caldone = iscaldone;
10649999SWang.Lin@Sun.COM }
10659999SWang.Lin@Sun.COM }
10669999SWang.Lin@Sun.COM
10679999SWang.Lin@Sun.COM settimer:
10689999SWang.Lin@Sun.COM /*
10699999SWang.Lin@Sun.COM * Set timer interval based on previous results.
10709999SWang.Lin@Sun.COM * The interval must be the shortest necessary to satisfy ANI,
10719999SWang.Lin@Sun.COM * short calibration and long calibration.
10729999SWang.Lin@Sun.COM */
10739999SWang.Lin@Sun.COM cal_interval = ATH_LONG_CALINTERVAL;
10749999SWang.Lin@Sun.COM if (sc->sc_ah->ah_config.enable_ani)
10759999SWang.Lin@Sun.COM cal_interval =
10769999SWang.Lin@Sun.COM min(cal_interval, (uint32_t)ATH_ANI_POLLINTERVAL);
10779999SWang.Lin@Sun.COM
10789999SWang.Lin@Sun.COM if (!sc->sc_ani.sc_caldone)
10799999SWang.Lin@Sun.COM cal_interval = min(cal_interval,
10809999SWang.Lin@Sun.COM (uint32_t)ATH_SHORT_CALINTERVAL);
10819999SWang.Lin@Sun.COM
10829999SWang.Lin@Sun.COM sc->sc_scan_timer = 0;
10839999SWang.Lin@Sun.COM sc->sc_scan_timer = timeout(arn_ani_calibrate, (void *)sc,
10849999SWang.Lin@Sun.COM drv_usectohz(cal_interval * 1000));
10859999SWang.Lin@Sun.COM }
10869999SWang.Lin@Sun.COM
10879999SWang.Lin@Sun.COM static void
arn_stop_caltimer(struct arn_softc * sc)10889999SWang.Lin@Sun.COM arn_stop_caltimer(struct arn_softc *sc)
10899999SWang.Lin@Sun.COM {
10909999SWang.Lin@Sun.COM timeout_id_t tmp_id = 0;
10919999SWang.Lin@Sun.COM
10929999SWang.Lin@Sun.COM while ((sc->sc_cal_timer != 0) && (tmp_id != sc->sc_cal_timer)) {
10939999SWang.Lin@Sun.COM tmp_id = sc->sc_cal_timer;
10949999SWang.Lin@Sun.COM (void) untimeout(tmp_id);
10959999SWang.Lin@Sun.COM }
10969999SWang.Lin@Sun.COM sc->sc_cal_timer = 0;
10979999SWang.Lin@Sun.COM }
10989999SWang.Lin@Sun.COM
10999999SWang.Lin@Sun.COM static uint_t
arn_isr(caddr_t arg)11009999SWang.Lin@Sun.COM arn_isr(caddr_t arg)
11019999SWang.Lin@Sun.COM {
11029999SWang.Lin@Sun.COM /* LINTED E_BAD_PTR_CAST_ALIGN */
11039999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)arg;
11049999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
11059999SWang.Lin@Sun.COM enum ath9k_int status;
11069999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc;
11079999SWang.Lin@Sun.COM
11089999SWang.Lin@Sun.COM ARN_LOCK(sc);
11099999SWang.Lin@Sun.COM
11109999SWang.Lin@Sun.COM if (sc->sc_flags & SC_OP_INVALID) {
11119999SWang.Lin@Sun.COM /*
11129999SWang.Lin@Sun.COM * The hardware is not ready/present, don't
11139999SWang.Lin@Sun.COM * touch anything. Note this can happen early
11149999SWang.Lin@Sun.COM * on if the IRQ is shared.
11159999SWang.Lin@Sun.COM */
11169999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
11179999SWang.Lin@Sun.COM return (DDI_INTR_UNCLAIMED);
11189999SWang.Lin@Sun.COM }
11199999SWang.Lin@Sun.COM if (!ath9k_hw_intrpend(ah)) { /* shared irq, not for us */
11209999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
11219999SWang.Lin@Sun.COM return (DDI_INTR_UNCLAIMED);
11229999SWang.Lin@Sun.COM }
11239999SWang.Lin@Sun.COM
11249999SWang.Lin@Sun.COM /*
11259999SWang.Lin@Sun.COM * Figure out the reason(s) for the interrupt. Note
11269999SWang.Lin@Sun.COM * that the hal returns a pseudo-ISR that may include
11279999SWang.Lin@Sun.COM * bits we haven't explicitly enabled so we mask the
11289999SWang.Lin@Sun.COM * value to insure we only process bits we requested.
11299999SWang.Lin@Sun.COM */
11309999SWang.Lin@Sun.COM (void) ath9k_hw_getisr(ah, &status); /* NB: clears ISR too */
11319999SWang.Lin@Sun.COM
11329999SWang.Lin@Sun.COM status &= sc->sc_imask; /* discard unasked-for bits */
11339999SWang.Lin@Sun.COM
11349999SWang.Lin@Sun.COM /*
11359999SWang.Lin@Sun.COM * If there are no status bits set, then this interrupt was not
11369999SWang.Lin@Sun.COM * for me (should have been caught above).
11379999SWang.Lin@Sun.COM */
11389999SWang.Lin@Sun.COM if (!status) {
11399999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
11409999SWang.Lin@Sun.COM return (DDI_INTR_UNCLAIMED);
11419999SWang.Lin@Sun.COM }
11429999SWang.Lin@Sun.COM
11439999SWang.Lin@Sun.COM sc->sc_intrstatus = status;
11449999SWang.Lin@Sun.COM
11459999SWang.Lin@Sun.COM if (status & ATH9K_INT_FATAL) {
11469999SWang.Lin@Sun.COM /* need a chip reset */
11479999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
11489999SWang.Lin@Sun.COM "ATH9K_INT_FATAL\n"));
11499999SWang.Lin@Sun.COM goto reset;
11509999SWang.Lin@Sun.COM } else if (status & ATH9K_INT_RXORN) {
11519999SWang.Lin@Sun.COM /* need a chip reset */
11529999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
11539999SWang.Lin@Sun.COM "ATH9K_INT_RXORN\n"));
11549999SWang.Lin@Sun.COM goto reset;
11559999SWang.Lin@Sun.COM } else {
11569999SWang.Lin@Sun.COM if (status & ATH9K_INT_RXEOL) {
11579999SWang.Lin@Sun.COM /*
11589999SWang.Lin@Sun.COM * NB: the hardware should re-read the link when
11599999SWang.Lin@Sun.COM * RXE bit is written, but it doesn't work
11609999SWang.Lin@Sun.COM * at least on older hardware revs.
11619999SWang.Lin@Sun.COM */
11629999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
11639999SWang.Lin@Sun.COM "ATH9K_INT_RXEOL\n"));
11649999SWang.Lin@Sun.COM sc->sc_rxlink = NULL;
11659999SWang.Lin@Sun.COM }
11669999SWang.Lin@Sun.COM if (status & ATH9K_INT_TXURN) {
11679999SWang.Lin@Sun.COM /* bump tx trigger level */
11689999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
11699999SWang.Lin@Sun.COM "ATH9K_INT_TXURN\n"));
11709999SWang.Lin@Sun.COM (void) ath9k_hw_updatetxtriglevel(ah, B_TRUE);
11719999SWang.Lin@Sun.COM }
11729999SWang.Lin@Sun.COM /* XXX: optimize this */
11739999SWang.Lin@Sun.COM if (status & ATH9K_INT_RX) {
11749999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
11759999SWang.Lin@Sun.COM "ATH9K_INT_RX\n"));
11769999SWang.Lin@Sun.COM sc->sc_rx_pend = 1;
11779999SWang.Lin@Sun.COM ddi_trigger_softintr(sc->sc_softint_id);
11789999SWang.Lin@Sun.COM }
11799999SWang.Lin@Sun.COM if (status & ATH9K_INT_TX) {
11809999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
11819999SWang.Lin@Sun.COM "ATH9K_INT_TX\n"));
11829999SWang.Lin@Sun.COM if (ddi_taskq_dispatch(sc->sc_tq,
11839999SWang.Lin@Sun.COM arn_tx_int_proc, sc, DDI_NOSLEEP) !=
11849999SWang.Lin@Sun.COM DDI_SUCCESS) {
11859999SWang.Lin@Sun.COM arn_problem("arn: arn_isr(): "
11869999SWang.Lin@Sun.COM "No memory for tx taskq\n");
11879999SWang.Lin@Sun.COM }
11889999SWang.Lin@Sun.COM }
11899999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_INT_MIB
11909999SWang.Lin@Sun.COM if (status & ATH9K_INT_MIB) {
11919999SWang.Lin@Sun.COM /*
11929999SWang.Lin@Sun.COM * Disable interrupts until we service the MIB
11939999SWang.Lin@Sun.COM * interrupt; otherwise it will continue to
11949999SWang.Lin@Sun.COM * fire.
11959999SWang.Lin@Sun.COM */
11969999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(ah, 0);
11979999SWang.Lin@Sun.COM /*
11989999SWang.Lin@Sun.COM * Let the hal handle the event. We assume
11999999SWang.Lin@Sun.COM * it will clear whatever condition caused
12009999SWang.Lin@Sun.COM * the interrupt.
12019999SWang.Lin@Sun.COM */
12029999SWang.Lin@Sun.COM ath9k_hw_procmibevent(ah, &sc->sc_halstats);
12039999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(ah, sc->sc_imask);
12049999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
12059999SWang.Lin@Sun.COM "ATH9K_INT_MIB\n"));
12069999SWang.Lin@Sun.COM }
12079999SWang.Lin@Sun.COM #endif
12089999SWang.Lin@Sun.COM
12099999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_INT_TIM_TIMER
12109999SWang.Lin@Sun.COM if (status & ATH9K_INT_TIM_TIMER) {
12119999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
12129999SWang.Lin@Sun.COM "ATH9K_INT_TIM_TIMER\n"));
12139999SWang.Lin@Sun.COM if (!(ah->ah_caps.hw_caps &
12149999SWang.Lin@Sun.COM ATH9K_HW_CAP_AUTOSLEEP)) {
12159999SWang.Lin@Sun.COM /*
12169999SWang.Lin@Sun.COM * Clear RxAbort bit so that we can
12179999SWang.Lin@Sun.COM * receive frames
12189999SWang.Lin@Sun.COM */
12199999SWang.Lin@Sun.COM ath9k_hw_setrxabort(ah, 0);
12209999SWang.Lin@Sun.COM goto reset;
12219999SWang.Lin@Sun.COM }
12229999SWang.Lin@Sun.COM }
12239999SWang.Lin@Sun.COM #endif
12249999SWang.Lin@Sun.COM
12259999SWang.Lin@Sun.COM if (status & ATH9K_INT_BMISS) {
12269999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
12279999SWang.Lin@Sun.COM "ATH9K_INT_BMISS\n"));
122811729SWang.Lin@Sun.COM #ifdef ARN_HW_BEACON_MISS_HANDLE
122911377SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
123011377SWang.Lin@Sun.COM "handle beacon mmiss by H/W mechanism\n"));
12319999SWang.Lin@Sun.COM if (ddi_taskq_dispatch(sc->sc_tq, arn_bmiss_proc,
12329999SWang.Lin@Sun.COM sc, DDI_NOSLEEP) != DDI_SUCCESS) {
12339999SWang.Lin@Sun.COM arn_problem("arn: arn_isr(): "
12349999SWang.Lin@Sun.COM "No memory available for bmiss taskq\n");
12359999SWang.Lin@Sun.COM }
123611377SWang.Lin@Sun.COM #else
123711377SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
123811377SWang.Lin@Sun.COM "handle beacon mmiss by S/W mechanism\n"));
123911729SWang.Lin@Sun.COM #endif /* ARN_HW_BEACON_MISS_HANDLE */
12409999SWang.Lin@Sun.COM }
12419999SWang.Lin@Sun.COM
12429999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
12439999SWang.Lin@Sun.COM
12449999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_INT_CST
12459999SWang.Lin@Sun.COM /* carrier sense timeout */
12469999SWang.Lin@Sun.COM if (status & ATH9K_INT_CST) {
12479999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
12489999SWang.Lin@Sun.COM "ATH9K_INT_CST\n"));
12499999SWang.Lin@Sun.COM return (DDI_INTR_CLAIMED);
12509999SWang.Lin@Sun.COM }
12519999SWang.Lin@Sun.COM #endif
12529999SWang.Lin@Sun.COM
12539999SWang.Lin@Sun.COM if (status & ATH9K_INT_SWBA) {
12549999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "arn: arn_isr(): "
12559999SWang.Lin@Sun.COM "ATH9K_INT_SWBA\n"));
12569999SWang.Lin@Sun.COM /* This will occur only in Host-AP or Ad-Hoc mode */
12579999SWang.Lin@Sun.COM return (DDI_INTR_CLAIMED);
12589999SWang.Lin@Sun.COM }
12599999SWang.Lin@Sun.COM }
12609999SWang.Lin@Sun.COM
12619999SWang.Lin@Sun.COM return (DDI_INTR_CLAIMED);
12629999SWang.Lin@Sun.COM reset:
12639999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INTERRUPT, "Rset for fatal err\n"));
12649999SWang.Lin@Sun.COM (void) arn_reset(ic);
12659999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
12669999SWang.Lin@Sun.COM return (DDI_INTR_CLAIMED);
12679999SWang.Lin@Sun.COM }
12689999SWang.Lin@Sun.COM
12699999SWang.Lin@Sun.COM static int
arn_get_channel(struct arn_softc * sc,struct ieee80211_channel * chan)12709999SWang.Lin@Sun.COM arn_get_channel(struct arn_softc *sc, struct ieee80211_channel *chan)
12719999SWang.Lin@Sun.COM {
12729999SWang.Lin@Sun.COM int i;
12739999SWang.Lin@Sun.COM
12749999SWang.Lin@Sun.COM for (i = 0; i < sc->sc_ah->ah_nchan; i++) {
12759999SWang.Lin@Sun.COM if (sc->sc_ah->ah_channels[i].channel == chan->ich_freq)
12769999SWang.Lin@Sun.COM return (i);
12779999SWang.Lin@Sun.COM }
12789999SWang.Lin@Sun.COM
12799999SWang.Lin@Sun.COM return (-1);
12809999SWang.Lin@Sun.COM }
12819999SWang.Lin@Sun.COM
12829999SWang.Lin@Sun.COM int
arn_reset(ieee80211com_t * ic)12839999SWang.Lin@Sun.COM arn_reset(ieee80211com_t *ic)
12849999SWang.Lin@Sun.COM {
12859999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)ic;
12869999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
12879999SWang.Lin@Sun.COM int status;
12889999SWang.Lin@Sun.COM int error = 0;
12899999SWang.Lin@Sun.COM
12909999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(ah, 0);
12919999SWang.Lin@Sun.COM arn_draintxq(sc, 0);
12929999SWang.Lin@Sun.COM (void) arn_stoprecv(sc);
12939999SWang.Lin@Sun.COM
12949999SWang.Lin@Sun.COM if (!ath9k_hw_reset(ah, sc->sc_ah->ah_curchan, sc->tx_chan_width,
12959999SWang.Lin@Sun.COM sc->sc_tx_chainmask, sc->sc_rx_chainmask,
12969999SWang.Lin@Sun.COM sc->sc_ht_extprotspacing, B_FALSE, &status)) {
12979999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_RESET, "arn: arn_reset(): "
12989999SWang.Lin@Sun.COM "unable to reset hardware; hal status %u\n", status));
12999999SWang.Lin@Sun.COM error = EIO;
13009999SWang.Lin@Sun.COM }
13019999SWang.Lin@Sun.COM
13029999SWang.Lin@Sun.COM if (arn_startrecv(sc) != 0)
13039999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_RESET, "arn: arn_reset(): "
13049999SWang.Lin@Sun.COM "unable to start recv logic\n"));
13059999SWang.Lin@Sun.COM
13069999SWang.Lin@Sun.COM /*
13079999SWang.Lin@Sun.COM * We may be doing a reset in response to a request
13089999SWang.Lin@Sun.COM * that changes the channel so update any state that
13099999SWang.Lin@Sun.COM * might change as a result.
13109999SWang.Lin@Sun.COM */
13119999SWang.Lin@Sun.COM arn_setcurmode(sc, arn_chan2mode(sc->sc_ah->ah_curchan));
13129999SWang.Lin@Sun.COM
13139999SWang.Lin@Sun.COM arn_update_txpow(sc);
13149999SWang.Lin@Sun.COM
13159999SWang.Lin@Sun.COM if (sc->sc_flags & SC_OP_BEACONS)
13169999SWang.Lin@Sun.COM arn_beacon_config(sc); /* restart beacons */
13179999SWang.Lin@Sun.COM
13189999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(ah, sc->sc_imask);
13199999SWang.Lin@Sun.COM
13209999SWang.Lin@Sun.COM return (error);
13219999SWang.Lin@Sun.COM }
13229999SWang.Lin@Sun.COM
13239999SWang.Lin@Sun.COM int
arn_get_hal_qnum(uint16_t queue,struct arn_softc * sc)13249999SWang.Lin@Sun.COM arn_get_hal_qnum(uint16_t queue, struct arn_softc *sc)
13259999SWang.Lin@Sun.COM {
13269999SWang.Lin@Sun.COM int qnum;
13279999SWang.Lin@Sun.COM
13289999SWang.Lin@Sun.COM switch (queue) {
13299999SWang.Lin@Sun.COM case WME_AC_VO:
13309999SWang.Lin@Sun.COM qnum = sc->sc_haltype2q[ATH9K_WME_AC_VO];
13319999SWang.Lin@Sun.COM break;
13329999SWang.Lin@Sun.COM case WME_AC_VI:
13339999SWang.Lin@Sun.COM qnum = sc->sc_haltype2q[ATH9K_WME_AC_VI];
13349999SWang.Lin@Sun.COM break;
13359999SWang.Lin@Sun.COM case WME_AC_BE:
13369999SWang.Lin@Sun.COM qnum = sc->sc_haltype2q[ATH9K_WME_AC_BE];
13379999SWang.Lin@Sun.COM break;
13389999SWang.Lin@Sun.COM case WME_AC_BK:
13399999SWang.Lin@Sun.COM qnum = sc->sc_haltype2q[ATH9K_WME_AC_BK];
13409999SWang.Lin@Sun.COM break;
13419999SWang.Lin@Sun.COM default:
13429999SWang.Lin@Sun.COM qnum = sc->sc_haltype2q[ATH9K_WME_AC_BE];
13439999SWang.Lin@Sun.COM break;
13449999SWang.Lin@Sun.COM }
13459999SWang.Lin@Sun.COM
13469999SWang.Lin@Sun.COM return (qnum);
13479999SWang.Lin@Sun.COM }
13489999SWang.Lin@Sun.COM
13499999SWang.Lin@Sun.COM static struct {
13509999SWang.Lin@Sun.COM uint32_t version;
13519999SWang.Lin@Sun.COM const char *name;
13529999SWang.Lin@Sun.COM } ath_mac_bb_names[] = {
13539999SWang.Lin@Sun.COM { AR_SREV_VERSION_5416_PCI, "5416" },
13549999SWang.Lin@Sun.COM { AR_SREV_VERSION_5416_PCIE, "5418" },
13559999SWang.Lin@Sun.COM { AR_SREV_VERSION_9100, "9100" },
13569999SWang.Lin@Sun.COM { AR_SREV_VERSION_9160, "9160" },
13579999SWang.Lin@Sun.COM { AR_SREV_VERSION_9280, "9280" },
13589999SWang.Lin@Sun.COM { AR_SREV_VERSION_9285, "9285" }
13599999SWang.Lin@Sun.COM };
13609999SWang.Lin@Sun.COM
13619999SWang.Lin@Sun.COM static struct {
13629999SWang.Lin@Sun.COM uint16_t version;
13639999SWang.Lin@Sun.COM const char *name;
13649999SWang.Lin@Sun.COM } ath_rf_names[] = {
13659999SWang.Lin@Sun.COM { 0, "5133" },
13669999SWang.Lin@Sun.COM { AR_RAD5133_SREV_MAJOR, "5133" },
13679999SWang.Lin@Sun.COM { AR_RAD5122_SREV_MAJOR, "5122" },
13689999SWang.Lin@Sun.COM { AR_RAD2133_SREV_MAJOR, "2133" },
13699999SWang.Lin@Sun.COM { AR_RAD2122_SREV_MAJOR, "2122" }
13709999SWang.Lin@Sun.COM };
13719999SWang.Lin@Sun.COM
13729999SWang.Lin@Sun.COM /*
13739999SWang.Lin@Sun.COM * Return the MAC/BB name. "????" is returned if the MAC/BB is unknown.
13749999SWang.Lin@Sun.COM */
13759999SWang.Lin@Sun.COM
13769999SWang.Lin@Sun.COM static const char *
arn_mac_bb_name(uint32_t mac_bb_version)13779999SWang.Lin@Sun.COM arn_mac_bb_name(uint32_t mac_bb_version)
13789999SWang.Lin@Sun.COM {
13799999SWang.Lin@Sun.COM int i;
13809999SWang.Lin@Sun.COM
13819999SWang.Lin@Sun.COM for (i = 0; i < ARRAY_SIZE(ath_mac_bb_names); i++) {
13829999SWang.Lin@Sun.COM if (ath_mac_bb_names[i].version == mac_bb_version) {
13839999SWang.Lin@Sun.COM return (ath_mac_bb_names[i].name);
13849999SWang.Lin@Sun.COM }
13859999SWang.Lin@Sun.COM }
13869999SWang.Lin@Sun.COM
13879999SWang.Lin@Sun.COM return ("????");
13889999SWang.Lin@Sun.COM }
13899999SWang.Lin@Sun.COM
13909999SWang.Lin@Sun.COM /*
13919999SWang.Lin@Sun.COM * Return the RF name. "????" is returned if the RF is unknown.
13929999SWang.Lin@Sun.COM */
13939999SWang.Lin@Sun.COM
13949999SWang.Lin@Sun.COM static const char *
arn_rf_name(uint16_t rf_version)13959999SWang.Lin@Sun.COM arn_rf_name(uint16_t rf_version)
13969999SWang.Lin@Sun.COM {
13979999SWang.Lin@Sun.COM int i;
13989999SWang.Lin@Sun.COM
13999999SWang.Lin@Sun.COM for (i = 0; i < ARRAY_SIZE(ath_rf_names); i++) {
14009999SWang.Lin@Sun.COM if (ath_rf_names[i].version == rf_version) {
14019999SWang.Lin@Sun.COM return (ath_rf_names[i].name);
14029999SWang.Lin@Sun.COM }
14039999SWang.Lin@Sun.COM }
14049999SWang.Lin@Sun.COM
14059999SWang.Lin@Sun.COM return ("????");
14069999SWang.Lin@Sun.COM }
14079999SWang.Lin@Sun.COM
14089999SWang.Lin@Sun.COM static void
arn_next_scan(void * arg)14099999SWang.Lin@Sun.COM arn_next_scan(void *arg)
14109999SWang.Lin@Sun.COM {
14119999SWang.Lin@Sun.COM ieee80211com_t *ic = arg;
14129999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)ic;
14139999SWang.Lin@Sun.COM
14149999SWang.Lin@Sun.COM sc->sc_scan_timer = 0;
14159999SWang.Lin@Sun.COM if (ic->ic_state == IEEE80211_S_SCAN) {
14169999SWang.Lin@Sun.COM sc->sc_scan_timer = timeout(arn_next_scan, (void *)sc,
14179999SWang.Lin@Sun.COM drv_usectohz(arn_dwelltime * 1000));
14189999SWang.Lin@Sun.COM ieee80211_next_scan(ic);
14199999SWang.Lin@Sun.COM }
14209999SWang.Lin@Sun.COM }
14219999SWang.Lin@Sun.COM
14229999SWang.Lin@Sun.COM static void
arn_stop_scantimer(struct arn_softc * sc)14239999SWang.Lin@Sun.COM arn_stop_scantimer(struct arn_softc *sc)
14249999SWang.Lin@Sun.COM {
14259999SWang.Lin@Sun.COM timeout_id_t tmp_id = 0;
14269999SWang.Lin@Sun.COM
14279999SWang.Lin@Sun.COM while ((sc->sc_scan_timer != 0) && (tmp_id != sc->sc_scan_timer)) {
14289999SWang.Lin@Sun.COM tmp_id = sc->sc_scan_timer;
14299999SWang.Lin@Sun.COM (void) untimeout(tmp_id);
14309999SWang.Lin@Sun.COM }
14319999SWang.Lin@Sun.COM sc->sc_scan_timer = 0;
14329999SWang.Lin@Sun.COM }
14339999SWang.Lin@Sun.COM
14349999SWang.Lin@Sun.COM static int32_t
arn_newstate(ieee80211com_t * ic,enum ieee80211_state nstate,int arg)14359999SWang.Lin@Sun.COM arn_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
14369999SWang.Lin@Sun.COM {
14379999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)ic;
14389999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
14399999SWang.Lin@Sun.COM struct ieee80211_node *in;
14409999SWang.Lin@Sun.COM int32_t i, error;
14419999SWang.Lin@Sun.COM uint8_t *bssid;
14429999SWang.Lin@Sun.COM uint32_t rfilt;
14439999SWang.Lin@Sun.COM enum ieee80211_state ostate;
14449999SWang.Lin@Sun.COM struct ath9k_channel *channel;
14459999SWang.Lin@Sun.COM int pos;
14469999SWang.Lin@Sun.COM
14479999SWang.Lin@Sun.COM /* Should set up & init LED here */
14489999SWang.Lin@Sun.COM
14499999SWang.Lin@Sun.COM if (sc->sc_flags & SC_OP_INVALID)
14509999SWang.Lin@Sun.COM return (0);
14519999SWang.Lin@Sun.COM
14529999SWang.Lin@Sun.COM ostate = ic->ic_state;
14539999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INIT, "arn: arn_newstate(): "
14549999SWang.Lin@Sun.COM "%x -> %x!\n", ostate, nstate));
14559999SWang.Lin@Sun.COM
14569999SWang.Lin@Sun.COM ARN_LOCK(sc);
14579999SWang.Lin@Sun.COM
14589999SWang.Lin@Sun.COM if (nstate != IEEE80211_S_SCAN)
14599999SWang.Lin@Sun.COM arn_stop_scantimer(sc);
14609999SWang.Lin@Sun.COM if (nstate != IEEE80211_S_RUN)
14619999SWang.Lin@Sun.COM arn_stop_caltimer(sc);
14629999SWang.Lin@Sun.COM
14639999SWang.Lin@Sun.COM /* Should set LED here */
14649999SWang.Lin@Sun.COM
14659999SWang.Lin@Sun.COM if (nstate == IEEE80211_S_INIT) {
14669999SWang.Lin@Sun.COM sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
14679999SWang.Lin@Sun.COM /*
14689999SWang.Lin@Sun.COM * Disable interrupts.
14699999SWang.Lin@Sun.COM */
14709999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts
14719999SWang.Lin@Sun.COM (ah, sc->sc_imask &~ ATH9K_INT_GLOBAL);
14729999SWang.Lin@Sun.COM
14739999SWang.Lin@Sun.COM #ifdef ARN_IBSS
14749999SWang.Lin@Sun.COM if (ic->ic_opmode == IEEE80211_M_IBSS) {
14759999SWang.Lin@Sun.COM (void) ath9k_hw_stoptxdma(ah, sc->sc_beaconq);
14769999SWang.Lin@Sun.COM arn_beacon_return(sc);
14779999SWang.Lin@Sun.COM }
14789999SWang.Lin@Sun.COM #endif
14799999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
14809999SWang.Lin@Sun.COM ieee80211_stop_watchdog(ic);
14819999SWang.Lin@Sun.COM goto done;
14829999SWang.Lin@Sun.COM }
14839999SWang.Lin@Sun.COM in = ic->ic_bss;
14849999SWang.Lin@Sun.COM
14859999SWang.Lin@Sun.COM pos = arn_get_channel(sc, ic->ic_curchan);
14869999SWang.Lin@Sun.COM
14879999SWang.Lin@Sun.COM if (pos == -1) {
14889999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_FATAL, "arn: "
14899999SWang.Lin@Sun.COM "%s: Invalid channel\n", __func__));
14909999SWang.Lin@Sun.COM error = EINVAL;
14919999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
14929999SWang.Lin@Sun.COM goto bad;
14939999SWang.Lin@Sun.COM }
149411729SWang.Lin@Sun.COM
149511729SWang.Lin@Sun.COM if (in->in_htcap & IEEE80211_HTCAP_CHWIDTH40) {
149611729SWang.Lin@Sun.COM arn_update_chainmask(sc);
149711729SWang.Lin@Sun.COM sc->tx_chan_width = ATH9K_HT_MACMODE_2040;
149811729SWang.Lin@Sun.COM } else
149911729SWang.Lin@Sun.COM sc->tx_chan_width = ATH9K_HT_MACMODE_20;
150011729SWang.Lin@Sun.COM
15019999SWang.Lin@Sun.COM sc->sc_ah->ah_channels[pos].chanmode =
15029999SWang.Lin@Sun.COM arn_chan2flags(ic, ic->ic_curchan);
15039999SWang.Lin@Sun.COM channel = &sc->sc_ah->ah_channels[pos];
15049999SWang.Lin@Sun.COM if (channel == NULL) {
15059999SWang.Lin@Sun.COM arn_problem("arn_newstate(): channel == NULL");
15069999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
15079999SWang.Lin@Sun.COM goto bad;
15089999SWang.Lin@Sun.COM }
15099999SWang.Lin@Sun.COM error = arn_set_channel(sc, channel);
15109999SWang.Lin@Sun.COM if (error != 0) {
15119999SWang.Lin@Sun.COM if (nstate != IEEE80211_S_SCAN) {
15129999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
15139999SWang.Lin@Sun.COM ieee80211_reset_chan(ic);
15149999SWang.Lin@Sun.COM goto bad;
15159999SWang.Lin@Sun.COM }
15169999SWang.Lin@Sun.COM }
15179999SWang.Lin@Sun.COM
15189999SWang.Lin@Sun.COM /*
15199999SWang.Lin@Sun.COM * Get the receive filter according to the
15209999SWang.Lin@Sun.COM * operating mode and state
15219999SWang.Lin@Sun.COM */
15229999SWang.Lin@Sun.COM rfilt = arn_calcrxfilter(sc);
15239999SWang.Lin@Sun.COM
15249999SWang.Lin@Sun.COM if (nstate == IEEE80211_S_SCAN)
15259999SWang.Lin@Sun.COM bssid = ic->ic_macaddr;
15269999SWang.Lin@Sun.COM else
15279999SWang.Lin@Sun.COM bssid = in->in_bssid;
15289999SWang.Lin@Sun.COM
15299999SWang.Lin@Sun.COM ath9k_hw_setrxfilter(ah, rfilt);
15309999SWang.Lin@Sun.COM
15319999SWang.Lin@Sun.COM if (nstate == IEEE80211_S_RUN && ic->ic_opmode != IEEE80211_M_IBSS)
15329999SWang.Lin@Sun.COM ath9k_hw_write_associd(ah, bssid, in->in_associd);
15339999SWang.Lin@Sun.COM else
15349999SWang.Lin@Sun.COM ath9k_hw_write_associd(ah, bssid, 0);
15359999SWang.Lin@Sun.COM
15369999SWang.Lin@Sun.COM /* Check for WLAN_CAPABILITY_PRIVACY ? */
15379999SWang.Lin@Sun.COM if (ic->ic_flags & IEEE80211_F_PRIVACY) {
15389999SWang.Lin@Sun.COM for (i = 0; i < IEEE80211_WEP_NKID; i++) {
15399999SWang.Lin@Sun.COM if (ath9k_hw_keyisvalid(ah, (uint16_t)i))
15409999SWang.Lin@Sun.COM (void) ath9k_hw_keysetmac(ah, (uint16_t)i,
15419999SWang.Lin@Sun.COM bssid);
15429999SWang.Lin@Sun.COM }
15439999SWang.Lin@Sun.COM }
15449999SWang.Lin@Sun.COM
15459999SWang.Lin@Sun.COM if (nstate == IEEE80211_S_RUN) {
15469999SWang.Lin@Sun.COM switch (ic->ic_opmode) {
15479999SWang.Lin@Sun.COM #ifdef ARN_IBSS
15489999SWang.Lin@Sun.COM case IEEE80211_M_IBSS:
15499999SWang.Lin@Sun.COM /*
15509999SWang.Lin@Sun.COM * Allocate and setup the beacon frame.
15519999SWang.Lin@Sun.COM * Stop any previous beacon DMA.
15529999SWang.Lin@Sun.COM */
15539999SWang.Lin@Sun.COM (void) ath9k_hw_stoptxdma(ah, sc->sc_beaconq);
15549999SWang.Lin@Sun.COM arn_beacon_return(sc);
15559999SWang.Lin@Sun.COM error = arn_beacon_alloc(sc, in);
15569999SWang.Lin@Sun.COM if (error != 0) {
15579999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
15589999SWang.Lin@Sun.COM goto bad;
15599999SWang.Lin@Sun.COM }
15609999SWang.Lin@Sun.COM /*
15619999SWang.Lin@Sun.COM * If joining an adhoc network defer beacon timer
15629999SWang.Lin@Sun.COM * configuration to the next beacon frame so we
15639999SWang.Lin@Sun.COM * have a current TSF to use. Otherwise we're
15649999SWang.Lin@Sun.COM * starting an ibss/bss so there's no need to delay.
15659999SWang.Lin@Sun.COM */
15669999SWang.Lin@Sun.COM if (ic->ic_opmode == IEEE80211_M_IBSS &&
15679999SWang.Lin@Sun.COM ic->ic_bss->in_tstamp.tsf != 0) {
15689999SWang.Lin@Sun.COM sc->sc_bsync = 1;
15699999SWang.Lin@Sun.COM } else {
15709999SWang.Lin@Sun.COM arn_beacon_config(sc);
15719999SWang.Lin@Sun.COM }
15729999SWang.Lin@Sun.COM break;
15739999SWang.Lin@Sun.COM #endif /* ARN_IBSS */
15749999SWang.Lin@Sun.COM case IEEE80211_M_STA:
15759999SWang.Lin@Sun.COM if (ostate != IEEE80211_S_RUN) {
15769999SWang.Lin@Sun.COM /*
15779999SWang.Lin@Sun.COM * Defer beacon timer configuration to the next
15789999SWang.Lin@Sun.COM * beacon frame so we have a current TSF to use.
15799999SWang.Lin@Sun.COM * Any TSF collected when scanning is likely old
15809999SWang.Lin@Sun.COM */
15819999SWang.Lin@Sun.COM #ifdef ARN_IBSS
15829999SWang.Lin@Sun.COM sc->sc_bsync = 1;
15839999SWang.Lin@Sun.COM #else
15849999SWang.Lin@Sun.COM /* Configure the beacon and sleep timers. */
15859999SWang.Lin@Sun.COM arn_beacon_config(sc);
158611729SWang.Lin@Sun.COM /* Reset rssi stats */
158711377SWang.Lin@Sun.COM sc->sc_halstats.ns_avgbrssi =
158811377SWang.Lin@Sun.COM ATH_RSSI_DUMMY_MARKER;
158911377SWang.Lin@Sun.COM sc->sc_halstats.ns_avgrssi =
159011377SWang.Lin@Sun.COM ATH_RSSI_DUMMY_MARKER;
159111377SWang.Lin@Sun.COM sc->sc_halstats.ns_avgtxrssi =
159211377SWang.Lin@Sun.COM ATH_RSSI_DUMMY_MARKER;
159311377SWang.Lin@Sun.COM sc->sc_halstats.ns_avgtxrate =
159411377SWang.Lin@Sun.COM ATH_RATE_DUMMY_MARKER;
159511729SWang.Lin@Sun.COM /* end */
159611729SWang.Lin@Sun.COM
15979999SWang.Lin@Sun.COM #endif /* ARN_IBSS */
15989999SWang.Lin@Sun.COM }
15999999SWang.Lin@Sun.COM break;
16009999SWang.Lin@Sun.COM default:
16019999SWang.Lin@Sun.COM break;
16029999SWang.Lin@Sun.COM }
16039999SWang.Lin@Sun.COM } else {
16049999SWang.Lin@Sun.COM sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
16059999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(ah, sc->sc_imask);
16069999SWang.Lin@Sun.COM }
16079999SWang.Lin@Sun.COM
16089999SWang.Lin@Sun.COM /*
16099999SWang.Lin@Sun.COM * Reset the rate control state.
16109999SWang.Lin@Sun.COM */
16119999SWang.Lin@Sun.COM arn_rate_ctl_reset(sc, nstate);
16129999SWang.Lin@Sun.COM
16139999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
16149999SWang.Lin@Sun.COM done:
16159999SWang.Lin@Sun.COM /*
16169999SWang.Lin@Sun.COM * Invoke the parent method to complete the work.
16179999SWang.Lin@Sun.COM */
16189999SWang.Lin@Sun.COM error = sc->sc_newstate(ic, nstate, arg);
16199999SWang.Lin@Sun.COM
16209999SWang.Lin@Sun.COM /*
16219999SWang.Lin@Sun.COM * Finally, start any timers.
16229999SWang.Lin@Sun.COM */
16239999SWang.Lin@Sun.COM if (nstate == IEEE80211_S_RUN) {
16249999SWang.Lin@Sun.COM ieee80211_start_watchdog(ic, 1);
16259999SWang.Lin@Sun.COM ASSERT(sc->sc_cal_timer == 0);
16269999SWang.Lin@Sun.COM sc->sc_cal_timer = timeout(arn_ani_calibrate, (void *)sc,
16279999SWang.Lin@Sun.COM drv_usectohz(100 * 1000));
16289999SWang.Lin@Sun.COM } else if ((nstate == IEEE80211_S_SCAN) && (ostate != nstate)) {
16299999SWang.Lin@Sun.COM /* start ap/neighbor scan timer */
16309999SWang.Lin@Sun.COM /* ASSERT(sc->sc_scan_timer == 0); */
16319999SWang.Lin@Sun.COM if (sc->sc_scan_timer != 0) {
16329999SWang.Lin@Sun.COM (void) untimeout(sc->sc_scan_timer);
16339999SWang.Lin@Sun.COM sc->sc_scan_timer = 0;
16349999SWang.Lin@Sun.COM }
16359999SWang.Lin@Sun.COM sc->sc_scan_timer = timeout(arn_next_scan, (void *)sc,
16369999SWang.Lin@Sun.COM drv_usectohz(arn_dwelltime * 1000));
16379999SWang.Lin@Sun.COM }
16389999SWang.Lin@Sun.COM
16399999SWang.Lin@Sun.COM bad:
16409999SWang.Lin@Sun.COM return (error);
16419999SWang.Lin@Sun.COM }
16429999SWang.Lin@Sun.COM
16439999SWang.Lin@Sun.COM static void
arn_watchdog(void * arg)16449999SWang.Lin@Sun.COM arn_watchdog(void *arg)
16459999SWang.Lin@Sun.COM {
16469999SWang.Lin@Sun.COM struct arn_softc *sc = arg;
16479999SWang.Lin@Sun.COM ieee80211com_t *ic = &sc->sc_isc;
16489999SWang.Lin@Sun.COM int ntimer = 0;
16499999SWang.Lin@Sun.COM
16509999SWang.Lin@Sun.COM ARN_LOCK(sc);
16519999SWang.Lin@Sun.COM ic->ic_watchdog_timer = 0;
16529999SWang.Lin@Sun.COM if (sc->sc_flags & SC_OP_INVALID) {
16539999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
16549999SWang.Lin@Sun.COM return;
16559999SWang.Lin@Sun.COM }
16569999SWang.Lin@Sun.COM
16579999SWang.Lin@Sun.COM if (ic->ic_state == IEEE80211_S_RUN) {
16589999SWang.Lin@Sun.COM /*
16599999SWang.Lin@Sun.COM * Start the background rate control thread if we
16609999SWang.Lin@Sun.COM * are not configured to use a fixed xmit rate.
16619999SWang.Lin@Sun.COM */
166211729SWang.Lin@Sun.COM #ifdef ARN_LEGACY_RC
16639999SWang.Lin@Sun.COM if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
16649999SWang.Lin@Sun.COM sc->sc_stats.ast_rate_calls ++;
16659999SWang.Lin@Sun.COM if (ic->ic_opmode == IEEE80211_M_STA)
16669999SWang.Lin@Sun.COM arn_rate_ctl(ic, ic->ic_bss);
16679999SWang.Lin@Sun.COM else
16689999SWang.Lin@Sun.COM ieee80211_iterate_nodes(&ic->ic_sta,
16699999SWang.Lin@Sun.COM arn_rate_ctl, sc);
16709999SWang.Lin@Sun.COM }
167111729SWang.Lin@Sun.COM #endif /* ARN_LEGACY_RC */
167211729SWang.Lin@Sun.COM
167311729SWang.Lin@Sun.COM #ifdef ARN_HW_BEACON_MISS_HANDLE
167411729SWang.Lin@Sun.COM /* nothing to do here */
167511377SWang.Lin@Sun.COM #else
167611729SWang.Lin@Sun.COM /* currently set 10 seconds as beacon miss threshold */
167711729SWang.Lin@Sun.COM if (ic->ic_beaconmiss++ > 100) {
167811729SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_BEACON, "arn_watchdog():"
167911729SWang.Lin@Sun.COM "Beacon missed for 10 seconds, run"
168011729SWang.Lin@Sun.COM "ieee80211_new_state(ic, IEEE80211_S_INIT, -1)\n"));
168111729SWang.Lin@Sun.COM ARN_UNLOCK(sc);
168211729SWang.Lin@Sun.COM (void) ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
168311729SWang.Lin@Sun.COM return;
168411729SWang.Lin@Sun.COM }
168511729SWang.Lin@Sun.COM #endif /* ARN_HW_BEACON_MISS_HANDLE */
168611377SWang.Lin@Sun.COM
16879999SWang.Lin@Sun.COM ntimer = 1;
16889999SWang.Lin@Sun.COM }
16899999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
16909999SWang.Lin@Sun.COM
16919999SWang.Lin@Sun.COM ieee80211_watchdog(ic);
16929999SWang.Lin@Sun.COM if (ntimer != 0)
16939999SWang.Lin@Sun.COM ieee80211_start_watchdog(ic, ntimer);
16949999SWang.Lin@Sun.COM }
16959999SWang.Lin@Sun.COM
169611729SWang.Lin@Sun.COM /* ARGSUSED */
16979999SWang.Lin@Sun.COM static struct ieee80211_node *
arn_node_alloc(ieee80211com_t * ic)16989999SWang.Lin@Sun.COM arn_node_alloc(ieee80211com_t *ic)
16999999SWang.Lin@Sun.COM {
17009999SWang.Lin@Sun.COM struct ath_node *an;
170111729SWang.Lin@Sun.COM #ifdef ARN_TX_AGGREGATION
17029999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)ic;
170311729SWang.Lin@Sun.COM #endif
17049999SWang.Lin@Sun.COM
17059999SWang.Lin@Sun.COM an = kmem_zalloc(sizeof (struct ath_node), KM_SLEEP);
170611729SWang.Lin@Sun.COM
170711729SWang.Lin@Sun.COM /* legacy rate control */
170811729SWang.Lin@Sun.COM #ifdef ARN_LEGACY_RC
17099999SWang.Lin@Sun.COM arn_rate_update(sc, &an->an_node, 0);
171011729SWang.Lin@Sun.COM #endif
171111729SWang.Lin@Sun.COM
171211729SWang.Lin@Sun.COM #ifdef ARN_TX_AGGREGATION
171311729SWang.Lin@Sun.COM if (sc->sc_flags & SC_OP_TXAGGR) {
171411729SWang.Lin@Sun.COM arn_tx_node_init(sc, an);
171511729SWang.Lin@Sun.COM }
171611729SWang.Lin@Sun.COM #endif /* ARN_TX_AGGREGATION */
171711729SWang.Lin@Sun.COM
171811729SWang.Lin@Sun.COM an->last_rssi = ATH_RSSI_DUMMY_MARKER;
17199999SWang.Lin@Sun.COM
17209999SWang.Lin@Sun.COM return ((an != NULL) ? &an->an_node : NULL);
17219999SWang.Lin@Sun.COM }
17229999SWang.Lin@Sun.COM
17239999SWang.Lin@Sun.COM static void
arn_node_free(struct ieee80211_node * in)17249999SWang.Lin@Sun.COM arn_node_free(struct ieee80211_node *in)
17259999SWang.Lin@Sun.COM {
17269999SWang.Lin@Sun.COM ieee80211com_t *ic = in->in_ic;
17279999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)ic;
17289999SWang.Lin@Sun.COM struct ath_buf *bf;
17299999SWang.Lin@Sun.COM struct ath_txq *txq;
17309999SWang.Lin@Sun.COM int32_t i;
17319999SWang.Lin@Sun.COM
173211729SWang.Lin@Sun.COM #ifdef ARN_TX_AGGREGATION
173311729SWang.Lin@Sun.COM if (sc->sc_flags & SC_OP_TXAGGR)
173411729SWang.Lin@Sun.COM arn_tx_node_cleanup(sc, in);
173511729SWang.Lin@Sun.COM #endif /* TX_AGGREGATION */
173611729SWang.Lin@Sun.COM
17379999SWang.Lin@Sun.COM for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
17389999SWang.Lin@Sun.COM if (ARN_TXQ_SETUP(sc, i)) {
17399999SWang.Lin@Sun.COM txq = &sc->sc_txq[i];
17409999SWang.Lin@Sun.COM mutex_enter(&txq->axq_lock);
17419999SWang.Lin@Sun.COM bf = list_head(&txq->axq_list);
17429999SWang.Lin@Sun.COM while (bf != NULL) {
17439999SWang.Lin@Sun.COM if (bf->bf_in == in) {
17449999SWang.Lin@Sun.COM bf->bf_in = NULL;
17459999SWang.Lin@Sun.COM }
17469999SWang.Lin@Sun.COM bf = list_next(&txq->axq_list, bf);
17479999SWang.Lin@Sun.COM }
17489999SWang.Lin@Sun.COM mutex_exit(&txq->axq_lock);
17499999SWang.Lin@Sun.COM }
17509999SWang.Lin@Sun.COM }
17519999SWang.Lin@Sun.COM
17529999SWang.Lin@Sun.COM ic->ic_node_cleanup(in);
175311377SWang.Lin@Sun.COM
17549999SWang.Lin@Sun.COM if (in->in_wpa_ie != NULL)
17559999SWang.Lin@Sun.COM ieee80211_free(in->in_wpa_ie);
175611377SWang.Lin@Sun.COM
175711377SWang.Lin@Sun.COM if (in->in_wme_ie != NULL)
175811377SWang.Lin@Sun.COM ieee80211_free(in->in_wme_ie);
175911377SWang.Lin@Sun.COM
176011377SWang.Lin@Sun.COM if (in->in_htcap_ie != NULL)
176111377SWang.Lin@Sun.COM ieee80211_free(in->in_htcap_ie);
176211377SWang.Lin@Sun.COM
17639999SWang.Lin@Sun.COM kmem_free(in, sizeof (struct ath_node));
17649999SWang.Lin@Sun.COM }
17659999SWang.Lin@Sun.COM
17669999SWang.Lin@Sun.COM /*
17679999SWang.Lin@Sun.COM * Allocate tx/rx key slots for TKIP. We allocate one slot for
17689999SWang.Lin@Sun.COM * each key. MIC is right after the decrypt/encrypt key.
17699999SWang.Lin@Sun.COM */
17709999SWang.Lin@Sun.COM static uint16_t
arn_key_alloc_pair(struct arn_softc * sc,ieee80211_keyix * txkeyix,ieee80211_keyix * rxkeyix)17719999SWang.Lin@Sun.COM arn_key_alloc_pair(struct arn_softc *sc, ieee80211_keyix *txkeyix,
17729999SWang.Lin@Sun.COM ieee80211_keyix *rxkeyix)
17739999SWang.Lin@Sun.COM {
17749999SWang.Lin@Sun.COM uint16_t i, keyix;
17759999SWang.Lin@Sun.COM
17769999SWang.Lin@Sun.COM ASSERT(!sc->sc_splitmic);
17779999SWang.Lin@Sun.COM for (i = 0; i < ARRAY_SIZE(sc->sc_keymap)/4; i++) {
17789999SWang.Lin@Sun.COM uint8_t b = sc->sc_keymap[i];
17799999SWang.Lin@Sun.COM if (b == 0xff)
17809999SWang.Lin@Sun.COM continue;
17819999SWang.Lin@Sun.COM for (keyix = i * NBBY; keyix < (i + 1) * NBBY;
17829999SWang.Lin@Sun.COM keyix++, b >>= 1) {
17839999SWang.Lin@Sun.COM if ((b & 1) || is_set(keyix+64, sc->sc_keymap)) {
17849999SWang.Lin@Sun.COM /* full pair unavailable */
17859999SWang.Lin@Sun.COM continue;
17869999SWang.Lin@Sun.COM }
17879999SWang.Lin@Sun.COM set_bit(keyix, sc->sc_keymap);
17889999SWang.Lin@Sun.COM set_bit(keyix+64, sc->sc_keymap);
17899999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_KEYCACHE,
17909999SWang.Lin@Sun.COM "arn_key_alloc_pair(): key pair %u,%u\n",
17919999SWang.Lin@Sun.COM keyix, keyix+64));
17929999SWang.Lin@Sun.COM *txkeyix = *rxkeyix = keyix;
17939999SWang.Lin@Sun.COM return (1);
17949999SWang.Lin@Sun.COM }
17959999SWang.Lin@Sun.COM }
17969999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_KEYCACHE, "arn_key_alloc_pair():"
17979999SWang.Lin@Sun.COM " out of pair space\n"));
17989999SWang.Lin@Sun.COM
17999999SWang.Lin@Sun.COM return (0);
18009999SWang.Lin@Sun.COM }
18019999SWang.Lin@Sun.COM
18029999SWang.Lin@Sun.COM /*
18039999SWang.Lin@Sun.COM * Allocate tx/rx key slots for TKIP. We allocate two slots for
18049999SWang.Lin@Sun.COM * each key, one for decrypt/encrypt and the other for the MIC.
18059999SWang.Lin@Sun.COM */
18069999SWang.Lin@Sun.COM static int
arn_key_alloc_2pair(struct arn_softc * sc,ieee80211_keyix * txkeyix,ieee80211_keyix * rxkeyix)18079999SWang.Lin@Sun.COM arn_key_alloc_2pair(struct arn_softc *sc, ieee80211_keyix *txkeyix,
18089999SWang.Lin@Sun.COM ieee80211_keyix *rxkeyix)
18099999SWang.Lin@Sun.COM {
18109999SWang.Lin@Sun.COM uint16_t i, keyix;
18119999SWang.Lin@Sun.COM
18129999SWang.Lin@Sun.COM ASSERT(sc->sc_splitmic);
18139999SWang.Lin@Sun.COM for (i = 0; i < ARRAY_SIZE(sc->sc_keymap)/4; i++) {
18149999SWang.Lin@Sun.COM uint8_t b = sc->sc_keymap[i];
18159999SWang.Lin@Sun.COM if (b != 0xff) {
18169999SWang.Lin@Sun.COM /*
18179999SWang.Lin@Sun.COM * One or more slots in this byte are free.
18189999SWang.Lin@Sun.COM */
18199999SWang.Lin@Sun.COM keyix = i*NBBY;
18209999SWang.Lin@Sun.COM while (b & 1) {
18219999SWang.Lin@Sun.COM again:
18229999SWang.Lin@Sun.COM keyix++;
18239999SWang.Lin@Sun.COM b >>= 1;
18249999SWang.Lin@Sun.COM }
18259999SWang.Lin@Sun.COM /* XXX IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV */
18269999SWang.Lin@Sun.COM if (is_set(keyix+32, sc->sc_keymap) ||
18279999SWang.Lin@Sun.COM is_set(keyix+64, sc->sc_keymap) ||
18289999SWang.Lin@Sun.COM is_set(keyix+32+64, sc->sc_keymap)) {
18299999SWang.Lin@Sun.COM /* full pair unavailable */
18309999SWang.Lin@Sun.COM if (keyix == (i+1)*NBBY) {
18319999SWang.Lin@Sun.COM /* no slots were appropriate, advance */
18329999SWang.Lin@Sun.COM continue;
18339999SWang.Lin@Sun.COM }
18349999SWang.Lin@Sun.COM goto again;
18359999SWang.Lin@Sun.COM }
18369999SWang.Lin@Sun.COM set_bit(keyix, sc->sc_keymap);
18379999SWang.Lin@Sun.COM set_bit(keyix+64, sc->sc_keymap);
18389999SWang.Lin@Sun.COM set_bit(keyix+32, sc->sc_keymap);
18399999SWang.Lin@Sun.COM set_bit(keyix+32+64, sc->sc_keymap);
18409999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_KEYCACHE,
18419999SWang.Lin@Sun.COM "arn_key_alloc_2pair(): key pair %u,%u %u,%u\n",
18429999SWang.Lin@Sun.COM keyix, keyix+64,
18439999SWang.Lin@Sun.COM keyix+32, keyix+32+64));
18449999SWang.Lin@Sun.COM *txkeyix = *rxkeyix = keyix;
18459999SWang.Lin@Sun.COM return (1);
18469999SWang.Lin@Sun.COM }
18479999SWang.Lin@Sun.COM }
18489999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_KEYCACHE, "arn_key_alloc_2pair(): "
18499999SWang.Lin@Sun.COM " out of pair space\n"));
18509999SWang.Lin@Sun.COM
18519999SWang.Lin@Sun.COM return (0);
18529999SWang.Lin@Sun.COM }
18539999SWang.Lin@Sun.COM /*
18549999SWang.Lin@Sun.COM * Allocate a single key cache slot.
18559999SWang.Lin@Sun.COM */
18569999SWang.Lin@Sun.COM static int
arn_key_alloc_single(struct arn_softc * sc,ieee80211_keyix * txkeyix,ieee80211_keyix * rxkeyix)18579999SWang.Lin@Sun.COM arn_key_alloc_single(struct arn_softc *sc, ieee80211_keyix *txkeyix,
18589999SWang.Lin@Sun.COM ieee80211_keyix *rxkeyix)
18599999SWang.Lin@Sun.COM {
18609999SWang.Lin@Sun.COM uint16_t i, keyix;
18619999SWang.Lin@Sun.COM
18629999SWang.Lin@Sun.COM /* try i,i+32,i+64,i+32+64 to minimize key pair conflicts */
18639999SWang.Lin@Sun.COM for (i = 0; i < ARRAY_SIZE(sc->sc_keymap); i++) {
18649999SWang.Lin@Sun.COM uint8_t b = sc->sc_keymap[i];
18659999SWang.Lin@Sun.COM
18669999SWang.Lin@Sun.COM if (b != 0xff) {
18679999SWang.Lin@Sun.COM /*
18689999SWang.Lin@Sun.COM * One or more slots are free.
18699999SWang.Lin@Sun.COM */
18709999SWang.Lin@Sun.COM keyix = i*NBBY;
18719999SWang.Lin@Sun.COM while (b & 1)
18729999SWang.Lin@Sun.COM keyix++, b >>= 1;
18739999SWang.Lin@Sun.COM set_bit(keyix, sc->sc_keymap);
18749999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_KEYCACHE, "arn_key_alloc_single(): "
18759999SWang.Lin@Sun.COM "key %u\n", keyix));
18769999SWang.Lin@Sun.COM *txkeyix = *rxkeyix = keyix;
18779999SWang.Lin@Sun.COM return (1);
18789999SWang.Lin@Sun.COM }
18799999SWang.Lin@Sun.COM }
18809999SWang.Lin@Sun.COM return (0);
18819999SWang.Lin@Sun.COM }
18829999SWang.Lin@Sun.COM
18839999SWang.Lin@Sun.COM /*
18849999SWang.Lin@Sun.COM * Allocate one or more key cache slots for a unicast key. The
18859999SWang.Lin@Sun.COM * key itself is needed only to identify the cipher. For hardware
18869999SWang.Lin@Sun.COM * TKIP with split cipher+MIC keys we allocate two key cache slot
18879999SWang.Lin@Sun.COM * pairs so that we can setup separate TX and RX MIC keys. Note
18889999SWang.Lin@Sun.COM * that the MIC key for a TKIP key at slot i is assumed by the
18899999SWang.Lin@Sun.COM * hardware to be at slot i+64. This limits TKIP keys to the first
18909999SWang.Lin@Sun.COM * 64 entries.
18919999SWang.Lin@Sun.COM */
18929999SWang.Lin@Sun.COM /* ARGSUSED */
18939999SWang.Lin@Sun.COM int
arn_key_alloc(ieee80211com_t * ic,const struct ieee80211_key * k,ieee80211_keyix * keyix,ieee80211_keyix * rxkeyix)18949999SWang.Lin@Sun.COM arn_key_alloc(ieee80211com_t *ic, const struct ieee80211_key *k,
18959999SWang.Lin@Sun.COM ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
18969999SWang.Lin@Sun.COM {
18979999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)ic;
18989999SWang.Lin@Sun.COM
18999999SWang.Lin@Sun.COM /*
19009999SWang.Lin@Sun.COM * We allocate two pair for TKIP when using the h/w to do
19019999SWang.Lin@Sun.COM * the MIC. For everything else, including software crypto,
19029999SWang.Lin@Sun.COM * we allocate a single entry. Note that s/w crypto requires
19039999SWang.Lin@Sun.COM * a pass-through slot on the 5211 and 5212. The 5210 does
19049999SWang.Lin@Sun.COM * not support pass-through cache entries and we map all
19059999SWang.Lin@Sun.COM * those requests to slot 0.
19069999SWang.Lin@Sun.COM */
19079999SWang.Lin@Sun.COM if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
19089999SWang.Lin@Sun.COM return (arn_key_alloc_single(sc, keyix, rxkeyix));
19099999SWang.Lin@Sun.COM } else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP &&
19109999SWang.Lin@Sun.COM (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
19119999SWang.Lin@Sun.COM if (sc->sc_splitmic)
19129999SWang.Lin@Sun.COM return (arn_key_alloc_2pair(sc, keyix, rxkeyix));
19139999SWang.Lin@Sun.COM else
19149999SWang.Lin@Sun.COM return (arn_key_alloc_pair(sc, keyix, rxkeyix));
19159999SWang.Lin@Sun.COM } else {
19169999SWang.Lin@Sun.COM return (arn_key_alloc_single(sc, keyix, rxkeyix));
19179999SWang.Lin@Sun.COM }
19189999SWang.Lin@Sun.COM }
19199999SWang.Lin@Sun.COM
19209999SWang.Lin@Sun.COM /*
19219999SWang.Lin@Sun.COM * Delete an entry in the key cache allocated by ath_key_alloc.
19229999SWang.Lin@Sun.COM */
19239999SWang.Lin@Sun.COM int
arn_key_delete(ieee80211com_t * ic,const struct ieee80211_key * k)19249999SWang.Lin@Sun.COM arn_key_delete(ieee80211com_t *ic, const struct ieee80211_key *k)
19259999SWang.Lin@Sun.COM {
19269999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)ic;
19279999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
19289999SWang.Lin@Sun.COM const struct ieee80211_cipher *cip = k->wk_cipher;
19299999SWang.Lin@Sun.COM ieee80211_keyix keyix = k->wk_keyix;
19309999SWang.Lin@Sun.COM
19319999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_KEYCACHE, "arn_key_delete():"
19329999SWang.Lin@Sun.COM " delete key %u ic_cipher=0x%x\n", keyix, cip->ic_cipher));
19339999SWang.Lin@Sun.COM
19349999SWang.Lin@Sun.COM (void) ath9k_hw_keyreset(ah, keyix);
19359999SWang.Lin@Sun.COM /*
19369999SWang.Lin@Sun.COM * Handle split tx/rx keying required for TKIP with h/w MIC.
19379999SWang.Lin@Sun.COM */
19389999SWang.Lin@Sun.COM if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
19399999SWang.Lin@Sun.COM (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic)
19409999SWang.Lin@Sun.COM (void) ath9k_hw_keyreset(ah, keyix+32); /* RX key */
19419999SWang.Lin@Sun.COM
19429999SWang.Lin@Sun.COM if (keyix >= IEEE80211_WEP_NKID) {
19439999SWang.Lin@Sun.COM /*
19449999SWang.Lin@Sun.COM * Don't touch keymap entries for global keys so
19459999SWang.Lin@Sun.COM * they are never considered for dynamic allocation.
19469999SWang.Lin@Sun.COM */
19479999SWang.Lin@Sun.COM clr_bit(keyix, sc->sc_keymap);
19489999SWang.Lin@Sun.COM if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
19499999SWang.Lin@Sun.COM (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
19509999SWang.Lin@Sun.COM /*
19519999SWang.Lin@Sun.COM * If splitmic is true +64 is TX key MIC,
19529999SWang.Lin@Sun.COM * else +64 is RX key + RX key MIC.
19539999SWang.Lin@Sun.COM */
19549999SWang.Lin@Sun.COM clr_bit(keyix+64, sc->sc_keymap);
19559999SWang.Lin@Sun.COM if (sc->sc_splitmic) {
19569999SWang.Lin@Sun.COM /* Rx key */
19579999SWang.Lin@Sun.COM clr_bit(keyix+32, sc->sc_keymap);
19589999SWang.Lin@Sun.COM /* RX key MIC */
19599999SWang.Lin@Sun.COM clr_bit(keyix+32+64, sc->sc_keymap);
19609999SWang.Lin@Sun.COM }
19619999SWang.Lin@Sun.COM }
19629999SWang.Lin@Sun.COM }
19639999SWang.Lin@Sun.COM return (1);
19649999SWang.Lin@Sun.COM }
19659999SWang.Lin@Sun.COM
19669999SWang.Lin@Sun.COM /*
19679999SWang.Lin@Sun.COM * Set a TKIP key into the hardware. This handles the
19689999SWang.Lin@Sun.COM * potential distribution of key state to multiple key
19699999SWang.Lin@Sun.COM * cache slots for TKIP.
19709999SWang.Lin@Sun.COM */
19719999SWang.Lin@Sun.COM static int
arn_keyset_tkip(struct arn_softc * sc,const struct ieee80211_key * k,struct ath9k_keyval * hk,const uint8_t mac[IEEE80211_ADDR_LEN])19729999SWang.Lin@Sun.COM arn_keyset_tkip(struct arn_softc *sc, const struct ieee80211_key *k,
19739999SWang.Lin@Sun.COM struct ath9k_keyval *hk, const uint8_t mac[IEEE80211_ADDR_LEN])
19749999SWang.Lin@Sun.COM {
19759999SWang.Lin@Sun.COM uint8_t *key_rxmic = NULL;
19769999SWang.Lin@Sun.COM uint8_t *key_txmic = NULL;
19779999SWang.Lin@Sun.COM uint8_t *key = (uint8_t *)&(k->wk_key[0]);
19789999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
19799999SWang.Lin@Sun.COM
19809999SWang.Lin@Sun.COM key_txmic = key + 16;
19819999SWang.Lin@Sun.COM key_rxmic = key + 24;
19829999SWang.Lin@Sun.COM
19839999SWang.Lin@Sun.COM if (mac == NULL) {
19849999SWang.Lin@Sun.COM /* Group key installation */
19859999SWang.Lin@Sun.COM (void) memcpy(hk->kv_mic, key_rxmic, sizeof (hk->kv_mic));
19869999SWang.Lin@Sun.COM return (ath9k_hw_set_keycache_entry(ah, k->wk_keyix, hk,
19879999SWang.Lin@Sun.COM mac, B_FALSE));
19889999SWang.Lin@Sun.COM }
19899999SWang.Lin@Sun.COM if (!sc->sc_splitmic) {
19909999SWang.Lin@Sun.COM /*
19919999SWang.Lin@Sun.COM * data key goes at first index,
19929999SWang.Lin@Sun.COM * the hal handles the MIC keys at index+64.
19939999SWang.Lin@Sun.COM */
19949999SWang.Lin@Sun.COM (void) memcpy(hk->kv_mic, key_rxmic, sizeof (hk->kv_mic));
19959999SWang.Lin@Sun.COM (void) memcpy(hk->kv_txmic, key_txmic, sizeof (hk->kv_txmic));
19969999SWang.Lin@Sun.COM return (ath9k_hw_set_keycache_entry(ah, k->wk_keyix, hk,
19979999SWang.Lin@Sun.COM mac, B_FALSE));
19989999SWang.Lin@Sun.COM }
19999999SWang.Lin@Sun.COM /*
20009999SWang.Lin@Sun.COM * TX key goes at first index, RX key at +32.
20019999SWang.Lin@Sun.COM * The hal handles the MIC keys at index+64.
20029999SWang.Lin@Sun.COM */
20039999SWang.Lin@Sun.COM (void) memcpy(hk->kv_mic, key_txmic, sizeof (hk->kv_mic));
20049999SWang.Lin@Sun.COM if (!(ath9k_hw_set_keycache_entry(ah, k->wk_keyix, hk, NULL,
20059999SWang.Lin@Sun.COM B_FALSE))) {
20069999SWang.Lin@Sun.COM /* Txmic entry failed. No need to proceed further */
20079999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_KEYCACHE,
20089999SWang.Lin@Sun.COM "%s Setting TX MIC Key Failed\n", __func__));
20099999SWang.Lin@Sun.COM return (0);
20109999SWang.Lin@Sun.COM }
20119999SWang.Lin@Sun.COM
20129999SWang.Lin@Sun.COM (void) memcpy(hk->kv_mic, key_rxmic, sizeof (hk->kv_mic));
20139999SWang.Lin@Sun.COM
20149999SWang.Lin@Sun.COM /* XXX delete tx key on failure? */
20159999SWang.Lin@Sun.COM return (ath9k_hw_set_keycache_entry(ah, k->wk_keyix, hk, mac, B_FALSE));
20169999SWang.Lin@Sun.COM
20179999SWang.Lin@Sun.COM }
20189999SWang.Lin@Sun.COM
20199999SWang.Lin@Sun.COM int
arn_key_set(ieee80211com_t * ic,const struct ieee80211_key * k,const uint8_t mac[IEEE80211_ADDR_LEN])20209999SWang.Lin@Sun.COM arn_key_set(ieee80211com_t *ic, const struct ieee80211_key *k,
20219999SWang.Lin@Sun.COM const uint8_t mac[IEEE80211_ADDR_LEN])
20229999SWang.Lin@Sun.COM {
20239999SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)ic;
20249999SWang.Lin@Sun.COM const struct ieee80211_cipher *cip = k->wk_cipher;
20259999SWang.Lin@Sun.COM struct ath9k_keyval hk;
20269999SWang.Lin@Sun.COM
20279999SWang.Lin@Sun.COM /* cipher table */
20289999SWang.Lin@Sun.COM static const uint8_t ciphermap[] = {
20299999SWang.Lin@Sun.COM ATH9K_CIPHER_WEP, /* IEEE80211_CIPHER_WEP */
20309999SWang.Lin@Sun.COM ATH9K_CIPHER_TKIP, /* IEEE80211_CIPHER_TKIP */
20319999SWang.Lin@Sun.COM ATH9K_CIPHER_AES_OCB, /* IEEE80211_CIPHER_AES_OCB */
20329999SWang.Lin@Sun.COM ATH9K_CIPHER_AES_CCM, /* IEEE80211_CIPHER_AES_CCM */
20339999SWang.Lin@Sun.COM ATH9K_CIPHER_CKIP, /* IEEE80211_CIPHER_CKIP */
20349999SWang.Lin@Sun.COM ATH9K_CIPHER_CLR, /* IEEE80211_CIPHER_NONE */
20359999SWang.Lin@Sun.COM };
20369999SWang.Lin@Sun.COM
20379999SWang.Lin@Sun.COM bzero(&hk, sizeof (hk));
20389999SWang.Lin@Sun.COM
20399999SWang.Lin@Sun.COM /*
20409999SWang.Lin@Sun.COM * Software crypto uses a "clear key" so non-crypto
20419999SWang.Lin@Sun.COM * state kept in the key cache are maintainedd so that
20429999SWang.Lin@Sun.COM * rx frames have an entry to match.
20439999SWang.Lin@Sun.COM */
20449999SWang.Lin@Sun.COM if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) {
20459999SWang.Lin@Sun.COM ASSERT(cip->ic_cipher < 6);
20469999SWang.Lin@Sun.COM hk.kv_type = ciphermap[cip->ic_cipher];
20479999SWang.Lin@Sun.COM hk.kv_len = k->wk_keylen;
20489999SWang.Lin@Sun.COM bcopy(k->wk_key, hk.kv_val, k->wk_keylen);
20499999SWang.Lin@Sun.COM } else {
20509999SWang.Lin@Sun.COM hk.kv_type = ATH9K_CIPHER_CLR;
20519999SWang.Lin@Sun.COM }
20529999SWang.Lin@Sun.COM
20539999SWang.Lin@Sun.COM if (hk.kv_type == ATH9K_CIPHER_TKIP &&
20549999SWang.Lin@Sun.COM (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
20559999SWang.Lin@Sun.COM return (arn_keyset_tkip(sc, k, &hk, mac));
20569999SWang.Lin@Sun.COM } else {
20579999SWang.Lin@Sun.COM return (ath9k_hw_set_keycache_entry(sc->sc_ah,
20589999SWang.Lin@Sun.COM k->wk_keyix, &hk, mac, B_FALSE));
20599999SWang.Lin@Sun.COM }
20609999SWang.Lin@Sun.COM }
20619999SWang.Lin@Sun.COM
20629999SWang.Lin@Sun.COM /*
20639999SWang.Lin@Sun.COM * Enable/Disable short slot timing
20649999SWang.Lin@Sun.COM */
20659999SWang.Lin@Sun.COM void
arn_set_shortslot(ieee80211com_t * ic,int onoff)20669999SWang.Lin@Sun.COM arn_set_shortslot(ieee80211com_t *ic, int onoff)
20679999SWang.Lin@Sun.COM {
20689999SWang.Lin@Sun.COM struct ath_hal *ah = ((struct arn_softc *)ic)->sc_ah;
20699999SWang.Lin@Sun.COM
20709999SWang.Lin@Sun.COM if (onoff)
20719999SWang.Lin@Sun.COM (void) ath9k_hw_setslottime(ah, ATH9K_SLOT_TIME_9);
20729999SWang.Lin@Sun.COM else
20739999SWang.Lin@Sun.COM (void) ath9k_hw_setslottime(ah, ATH9K_SLOT_TIME_20);
20749999SWang.Lin@Sun.COM }
20759999SWang.Lin@Sun.COM
20769999SWang.Lin@Sun.COM static int
arn_open(struct arn_softc * sc)20779999SWang.Lin@Sun.COM arn_open(struct arn_softc *sc)
20789999SWang.Lin@Sun.COM {
20799999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc;
20809999SWang.Lin@Sun.COM struct ieee80211_channel *curchan = ic->ic_curchan;
20819999SWang.Lin@Sun.COM struct ath9k_channel *init_channel;
20829999SWang.Lin@Sun.COM int error = 0, pos, status;
20839999SWang.Lin@Sun.COM
20849999SWang.Lin@Sun.COM ARN_LOCK_ASSERT(sc);
20859999SWang.Lin@Sun.COM
20869999SWang.Lin@Sun.COM pos = arn_get_channel(sc, curchan);
20879999SWang.Lin@Sun.COM if (pos == -1) {
20889999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_FATAL, "arn: "
20899999SWang.Lin@Sun.COM "%s: Invalid channel\n", __func__));
20909999SWang.Lin@Sun.COM error = EINVAL;
20919999SWang.Lin@Sun.COM goto error;
20929999SWang.Lin@Sun.COM }
20939999SWang.Lin@Sun.COM
20949999SWang.Lin@Sun.COM sc->tx_chan_width = ATH9K_HT_MACMODE_20;
20959999SWang.Lin@Sun.COM
20969999SWang.Lin@Sun.COM if (sc->sc_curmode == ATH9K_MODE_11A) {
20979999SWang.Lin@Sun.COM sc->sc_ah->ah_channels[pos].chanmode = CHANNEL_A;
20989999SWang.Lin@Sun.COM } else {
20999999SWang.Lin@Sun.COM sc->sc_ah->ah_channels[pos].chanmode = CHANNEL_G;
21009999SWang.Lin@Sun.COM }
21019999SWang.Lin@Sun.COM
21029999SWang.Lin@Sun.COM init_channel = &sc->sc_ah->ah_channels[pos];
21039999SWang.Lin@Sun.COM
21049999SWang.Lin@Sun.COM /* Reset SERDES registers */
21059999SWang.Lin@Sun.COM ath9k_hw_configpcipowersave(sc->sc_ah, 0);
21069999SWang.Lin@Sun.COM
21079999SWang.Lin@Sun.COM /*
21089999SWang.Lin@Sun.COM * The basic interface to setting the hardware in a good
21099999SWang.Lin@Sun.COM * state is ``reset''. On return the hardware is known to
21109999SWang.Lin@Sun.COM * be powered up and with interrupts disabled. This must
21119999SWang.Lin@Sun.COM * be followed by initialization of the appropriate bits
21129999SWang.Lin@Sun.COM * and then setup of the interrupt mask.
21139999SWang.Lin@Sun.COM */
21149999SWang.Lin@Sun.COM if (!ath9k_hw_reset(sc->sc_ah, init_channel,
21159999SWang.Lin@Sun.COM sc->tx_chan_width, sc->sc_tx_chainmask,
21169999SWang.Lin@Sun.COM sc->sc_rx_chainmask, sc->sc_ht_extprotspacing,
21179999SWang.Lin@Sun.COM B_FALSE, &status)) {
21189999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_FATAL, "arn: "
21199999SWang.Lin@Sun.COM "%s: unable to reset hardware; hal status %u "
21209999SWang.Lin@Sun.COM "(freq %u flags 0x%x)\n", __func__, status,
21219999SWang.Lin@Sun.COM init_channel->channel, init_channel->channelFlags));
21229999SWang.Lin@Sun.COM
21239999SWang.Lin@Sun.COM error = EIO;
21249999SWang.Lin@Sun.COM goto error;
21259999SWang.Lin@Sun.COM }
21269999SWang.Lin@Sun.COM
21279999SWang.Lin@Sun.COM /*
21289999SWang.Lin@Sun.COM * This is needed only to setup initial state
21299999SWang.Lin@Sun.COM * but it's best done after a reset.
21309999SWang.Lin@Sun.COM */
21319999SWang.Lin@Sun.COM arn_update_txpow(sc);
21329999SWang.Lin@Sun.COM
21339999SWang.Lin@Sun.COM /*
21349999SWang.Lin@Sun.COM * Setup the hardware after reset:
21359999SWang.Lin@Sun.COM * The receive engine is set going.
21369999SWang.Lin@Sun.COM * Frame transmit is handled entirely
21379999SWang.Lin@Sun.COM * in the frame output path; there's nothing to do
21389999SWang.Lin@Sun.COM * here except setup the interrupt mask.
21399999SWang.Lin@Sun.COM */
21409999SWang.Lin@Sun.COM if (arn_startrecv(sc) != 0) {
21419999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INIT, "arn: "
21429999SWang.Lin@Sun.COM "%s: unable to start recv logic\n", __func__));
21439999SWang.Lin@Sun.COM error = EIO;
21449999SWang.Lin@Sun.COM goto error;
21459999SWang.Lin@Sun.COM }
21469999SWang.Lin@Sun.COM
21479999SWang.Lin@Sun.COM /* Setup our intr mask. */
21489999SWang.Lin@Sun.COM sc->sc_imask = ATH9K_INT_RX | ATH9K_INT_TX |
21499999SWang.Lin@Sun.COM ATH9K_INT_RXEOL | ATH9K_INT_RXORN |
21509999SWang.Lin@Sun.COM ATH9K_INT_FATAL | ATH9K_INT_GLOBAL;
21519999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_HW_CAP_GTT
21529999SWang.Lin@Sun.COM if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_GTT)
21539999SWang.Lin@Sun.COM sc->sc_imask |= ATH9K_INT_GTT;
21549999SWang.Lin@Sun.COM #endif
21559999SWang.Lin@Sun.COM
21569999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_HW_CAP_GTT
21579999SWang.Lin@Sun.COM if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT)
21589999SWang.Lin@Sun.COM sc->sc_imask |= ATH9K_INT_CST;
21599999SWang.Lin@Sun.COM #endif
21609999SWang.Lin@Sun.COM
21619999SWang.Lin@Sun.COM /*
21629999SWang.Lin@Sun.COM * Enable MIB interrupts when there are hardware phy counters.
21639999SWang.Lin@Sun.COM * Note we only do this (at the moment) for station mode.
21649999SWang.Lin@Sun.COM */
21659999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_INT_MIB
21669999SWang.Lin@Sun.COM if (ath9k_hw_phycounters(sc->sc_ah) &&
21679999SWang.Lin@Sun.COM ((sc->sc_ah->ah_opmode == ATH9K_M_STA) ||
21689999SWang.Lin@Sun.COM (sc->sc_ah->ah_opmode == ATH9K_M_IBSS)))
21699999SWang.Lin@Sun.COM sc->sc_imask |= ATH9K_INT_MIB;
21709999SWang.Lin@Sun.COM #endif
21719999SWang.Lin@Sun.COM /*
21729999SWang.Lin@Sun.COM * Some hardware processes the TIM IE and fires an
21739999SWang.Lin@Sun.COM * interrupt when the TIM bit is set. For hardware
21749999SWang.Lin@Sun.COM * that does, if not overridden by configuration,
21759999SWang.Lin@Sun.COM * enable the TIM interrupt when operating as station.
21769999SWang.Lin@Sun.COM */
21779999SWang.Lin@Sun.COM #ifdef ARN_ATH9K_INT_TIM
21789999SWang.Lin@Sun.COM if ((sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_ENHANCEDPM) &&
21799999SWang.Lin@Sun.COM (sc->sc_ah->ah_opmode == ATH9K_M_STA) &&
21809999SWang.Lin@Sun.COM !sc->sc_config.swBeaconProcess)
21819999SWang.Lin@Sun.COM sc->sc_imask |= ATH9K_INT_TIM;
21829999SWang.Lin@Sun.COM #endif
21839999SWang.Lin@Sun.COM if (arn_chan2mode(init_channel) != sc->sc_curmode)
21849999SWang.Lin@Sun.COM arn_setcurmode(sc, arn_chan2mode(init_channel));
21859999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INIT, "arn: "
21869999SWang.Lin@Sun.COM "%s: current mode after arn_setcurmode is %d\n",
21879999SWang.Lin@Sun.COM __func__, sc->sc_curmode));
21889999SWang.Lin@Sun.COM
21899999SWang.Lin@Sun.COM sc->sc_isrunning = 1;
21909999SWang.Lin@Sun.COM
21919999SWang.Lin@Sun.COM /* Disable BMISS interrupt when we're not associated */
21929999SWang.Lin@Sun.COM sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
21939999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask);
21949999SWang.Lin@Sun.COM
21959999SWang.Lin@Sun.COM return (0);
21969999SWang.Lin@Sun.COM
21979999SWang.Lin@Sun.COM error:
21989999SWang.Lin@Sun.COM return (error);
21999999SWang.Lin@Sun.COM }
22009999SWang.Lin@Sun.COM
22019999SWang.Lin@Sun.COM static void
arn_close(struct arn_softc * sc)22029999SWang.Lin@Sun.COM arn_close(struct arn_softc *sc)
22039999SWang.Lin@Sun.COM {
22049999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc;
22059999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
22069999SWang.Lin@Sun.COM
22079999SWang.Lin@Sun.COM ARN_LOCK_ASSERT(sc);
22089999SWang.Lin@Sun.COM
22099999SWang.Lin@Sun.COM if (!sc->sc_isrunning)
22109999SWang.Lin@Sun.COM return;
22119999SWang.Lin@Sun.COM
22129999SWang.Lin@Sun.COM /*
22139999SWang.Lin@Sun.COM * Shutdown the hardware and driver
22149999SWang.Lin@Sun.COM * Note that some of this work is not possible if the
22159999SWang.Lin@Sun.COM * hardware is gone (invalid).
22169999SWang.Lin@Sun.COM */
22179999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
22189999SWang.Lin@Sun.COM ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
22199999SWang.Lin@Sun.COM ieee80211_stop_watchdog(ic);
22209999SWang.Lin@Sun.COM ARN_LOCK(sc);
22219999SWang.Lin@Sun.COM
22229999SWang.Lin@Sun.COM /*
22239999SWang.Lin@Sun.COM * make sure h/w will not generate any interrupt
22249999SWang.Lin@Sun.COM * before setting the invalid flag.
22259999SWang.Lin@Sun.COM */
22269999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(ah, 0);
22279999SWang.Lin@Sun.COM
22289999SWang.Lin@Sun.COM if (!(sc->sc_flags & SC_OP_INVALID)) {
22299999SWang.Lin@Sun.COM arn_draintxq(sc, 0);
22309999SWang.Lin@Sun.COM (void) arn_stoprecv(sc);
22319999SWang.Lin@Sun.COM (void) ath9k_hw_phy_disable(ah);
22329999SWang.Lin@Sun.COM } else {
22339999SWang.Lin@Sun.COM sc->sc_rxlink = NULL;
22349999SWang.Lin@Sun.COM }
22359999SWang.Lin@Sun.COM
22369999SWang.Lin@Sun.COM sc->sc_isrunning = 0;
22379999SWang.Lin@Sun.COM }
22389999SWang.Lin@Sun.COM
22399999SWang.Lin@Sun.COM /*
22409999SWang.Lin@Sun.COM * MAC callback functions
22419999SWang.Lin@Sun.COM */
22429999SWang.Lin@Sun.COM static int
arn_m_stat(void * arg,uint_t stat,uint64_t * val)22439999SWang.Lin@Sun.COM arn_m_stat(void *arg, uint_t stat, uint64_t *val)
22449999SWang.Lin@Sun.COM {
22459999SWang.Lin@Sun.COM struct arn_softc *sc = arg;
22469999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc;
22479999SWang.Lin@Sun.COM struct ieee80211_node *in;
22489999SWang.Lin@Sun.COM struct ieee80211_rateset *rs;
22499999SWang.Lin@Sun.COM
22509999SWang.Lin@Sun.COM ARN_LOCK(sc);
22519999SWang.Lin@Sun.COM switch (stat) {
22529999SWang.Lin@Sun.COM case MAC_STAT_IFSPEED:
22539999SWang.Lin@Sun.COM in = ic->ic_bss;
22549999SWang.Lin@Sun.COM rs = &in->in_rates;
22559999SWang.Lin@Sun.COM *val = (rs->ir_rates[in->in_txrate] & IEEE80211_RATE_VAL) / 2 *
22569999SWang.Lin@Sun.COM 1000000ull;
22579999SWang.Lin@Sun.COM break;
22589999SWang.Lin@Sun.COM case MAC_STAT_NOXMTBUF:
22599999SWang.Lin@Sun.COM *val = sc->sc_stats.ast_tx_nobuf +
22609999SWang.Lin@Sun.COM sc->sc_stats.ast_tx_nobufmgt;
22619999SWang.Lin@Sun.COM break;
22629999SWang.Lin@Sun.COM case MAC_STAT_IERRORS:
22639999SWang.Lin@Sun.COM *val = sc->sc_stats.ast_rx_tooshort;
22649999SWang.Lin@Sun.COM break;
22659999SWang.Lin@Sun.COM case MAC_STAT_RBYTES:
22669999SWang.Lin@Sun.COM *val = ic->ic_stats.is_rx_bytes;
22679999SWang.Lin@Sun.COM break;
22689999SWang.Lin@Sun.COM case MAC_STAT_IPACKETS:
22699999SWang.Lin@Sun.COM *val = ic->ic_stats.is_rx_frags;
22709999SWang.Lin@Sun.COM break;
22719999SWang.Lin@Sun.COM case MAC_STAT_OBYTES:
22729999SWang.Lin@Sun.COM *val = ic->ic_stats.is_tx_bytes;
22739999SWang.Lin@Sun.COM break;
22749999SWang.Lin@Sun.COM case MAC_STAT_OPACKETS:
22759999SWang.Lin@Sun.COM *val = ic->ic_stats.is_tx_frags;
22769999SWang.Lin@Sun.COM break;
22779999SWang.Lin@Sun.COM case MAC_STAT_OERRORS:
22789999SWang.Lin@Sun.COM case WIFI_STAT_TX_FAILED:
22799999SWang.Lin@Sun.COM *val = sc->sc_stats.ast_tx_fifoerr +
22809999SWang.Lin@Sun.COM sc->sc_stats.ast_tx_xretries +
22819999SWang.Lin@Sun.COM sc->sc_stats.ast_tx_discard;
22829999SWang.Lin@Sun.COM break;
22839999SWang.Lin@Sun.COM case WIFI_STAT_TX_RETRANS:
22849999SWang.Lin@Sun.COM *val = sc->sc_stats.ast_tx_xretries;
22859999SWang.Lin@Sun.COM break;
22869999SWang.Lin@Sun.COM case WIFI_STAT_FCS_ERRORS:
22879999SWang.Lin@Sun.COM *val = sc->sc_stats.ast_rx_crcerr;
22889999SWang.Lin@Sun.COM break;
22899999SWang.Lin@Sun.COM case WIFI_STAT_WEP_ERRORS:
22909999SWang.Lin@Sun.COM *val = sc->sc_stats.ast_rx_badcrypt;
22919999SWang.Lin@Sun.COM break;
22929999SWang.Lin@Sun.COM case WIFI_STAT_TX_FRAGS:
22939999SWang.Lin@Sun.COM case WIFI_STAT_MCAST_TX:
22949999SWang.Lin@Sun.COM case WIFI_STAT_RTS_SUCCESS:
22959999SWang.Lin@Sun.COM case WIFI_STAT_RTS_FAILURE:
22969999SWang.Lin@Sun.COM case WIFI_STAT_ACK_FAILURE:
22979999SWang.Lin@Sun.COM case WIFI_STAT_RX_FRAGS:
22989999SWang.Lin@Sun.COM case WIFI_STAT_MCAST_RX:
22999999SWang.Lin@Sun.COM case WIFI_STAT_RX_DUPS:
23009999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
23019999SWang.Lin@Sun.COM return (ieee80211_stat(ic, stat, val));
23029999SWang.Lin@Sun.COM default:
23039999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
23049999SWang.Lin@Sun.COM return (ENOTSUP);
23059999SWang.Lin@Sun.COM }
23069999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
23079999SWang.Lin@Sun.COM
23089999SWang.Lin@Sun.COM return (0);
23099999SWang.Lin@Sun.COM }
23109999SWang.Lin@Sun.COM
23119999SWang.Lin@Sun.COM int
arn_m_start(void * arg)23129999SWang.Lin@Sun.COM arn_m_start(void *arg)
23139999SWang.Lin@Sun.COM {
23149999SWang.Lin@Sun.COM struct arn_softc *sc = arg;
23159999SWang.Lin@Sun.COM int err = 0;
23169999SWang.Lin@Sun.COM
23179999SWang.Lin@Sun.COM ARN_LOCK(sc);
23189999SWang.Lin@Sun.COM
23199999SWang.Lin@Sun.COM /*
23209999SWang.Lin@Sun.COM * Stop anything previously setup. This is safe
23219999SWang.Lin@Sun.COM * whether this is the first time through or not.
23229999SWang.Lin@Sun.COM */
23239999SWang.Lin@Sun.COM
23249999SWang.Lin@Sun.COM arn_close(sc);
23259999SWang.Lin@Sun.COM
23269999SWang.Lin@Sun.COM if ((err = arn_open(sc)) != 0) {
23279999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
23289999SWang.Lin@Sun.COM return (err);
23299999SWang.Lin@Sun.COM }
23309999SWang.Lin@Sun.COM
23319999SWang.Lin@Sun.COM /* H/W is reday now */
23329999SWang.Lin@Sun.COM sc->sc_flags &= ~SC_OP_INVALID;
23339999SWang.Lin@Sun.COM
23349999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
23359999SWang.Lin@Sun.COM
23369999SWang.Lin@Sun.COM return (0);
23379999SWang.Lin@Sun.COM }
23389999SWang.Lin@Sun.COM
23399999SWang.Lin@Sun.COM static void
arn_m_stop(void * arg)23409999SWang.Lin@Sun.COM arn_m_stop(void *arg)
23419999SWang.Lin@Sun.COM {
23429999SWang.Lin@Sun.COM struct arn_softc *sc = arg;
23439999SWang.Lin@Sun.COM
23449999SWang.Lin@Sun.COM ARN_LOCK(sc);
23459999SWang.Lin@Sun.COM arn_close(sc);
23469999SWang.Lin@Sun.COM
23479999SWang.Lin@Sun.COM /* disable HAL and put h/w to sleep */
23489999SWang.Lin@Sun.COM (void) ath9k_hw_disable(sc->sc_ah);
23499999SWang.Lin@Sun.COM ath9k_hw_configpcipowersave(sc->sc_ah, 1);
23509999SWang.Lin@Sun.COM
23519999SWang.Lin@Sun.COM /* XXX: hardware will not be ready in suspend state */
23529999SWang.Lin@Sun.COM sc->sc_flags |= SC_OP_INVALID;
23539999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
23549999SWang.Lin@Sun.COM }
23559999SWang.Lin@Sun.COM
23569999SWang.Lin@Sun.COM static int
arn_m_promisc(void * arg,boolean_t on)23579999SWang.Lin@Sun.COM arn_m_promisc(void *arg, boolean_t on)
23589999SWang.Lin@Sun.COM {
23599999SWang.Lin@Sun.COM struct arn_softc *sc = arg;
23609999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
23619999SWang.Lin@Sun.COM uint32_t rfilt;
23629999SWang.Lin@Sun.COM
23639999SWang.Lin@Sun.COM ARN_LOCK(sc);
23649999SWang.Lin@Sun.COM
23659999SWang.Lin@Sun.COM rfilt = ath9k_hw_getrxfilter(ah);
23669999SWang.Lin@Sun.COM if (on)
23679999SWang.Lin@Sun.COM rfilt |= ATH9K_RX_FILTER_PROM;
23689999SWang.Lin@Sun.COM else
23699999SWang.Lin@Sun.COM rfilt &= ~ATH9K_RX_FILTER_PROM;
23709999SWang.Lin@Sun.COM sc->sc_promisc = on;
23719999SWang.Lin@Sun.COM ath9k_hw_setrxfilter(ah, rfilt);
23729999SWang.Lin@Sun.COM
23739999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
23749999SWang.Lin@Sun.COM
23759999SWang.Lin@Sun.COM return (0);
23769999SWang.Lin@Sun.COM }
23779999SWang.Lin@Sun.COM
23789999SWang.Lin@Sun.COM static int
arn_m_multicst(void * arg,boolean_t add,const uint8_t * mca)23799999SWang.Lin@Sun.COM arn_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
23809999SWang.Lin@Sun.COM {
23819999SWang.Lin@Sun.COM struct arn_softc *sc = arg;
23829999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
23839999SWang.Lin@Sun.COM uint32_t val, index, bit;
23849999SWang.Lin@Sun.COM uint8_t pos;
23859999SWang.Lin@Sun.COM uint32_t *mfilt = sc->sc_mcast_hash;
23869999SWang.Lin@Sun.COM
23879999SWang.Lin@Sun.COM ARN_LOCK(sc);
23889999SWang.Lin@Sun.COM
23899999SWang.Lin@Sun.COM /* calculate XOR of eight 6bit values */
23909999SWang.Lin@Sun.COM val = ARN_LE_READ_32(mca + 0);
23919999SWang.Lin@Sun.COM pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
23929999SWang.Lin@Sun.COM val = ARN_LE_READ_32(mca + 3);
23939999SWang.Lin@Sun.COM pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
23949999SWang.Lin@Sun.COM pos &= 0x3f;
23959999SWang.Lin@Sun.COM index = pos / 32;
23969999SWang.Lin@Sun.COM bit = 1 << (pos % 32);
23979999SWang.Lin@Sun.COM
23989999SWang.Lin@Sun.COM if (add) { /* enable multicast */
23999999SWang.Lin@Sun.COM sc->sc_mcast_refs[pos]++;
24009999SWang.Lin@Sun.COM mfilt[index] |= bit;
24019999SWang.Lin@Sun.COM } else { /* disable multicast */
24029999SWang.Lin@Sun.COM if (--sc->sc_mcast_refs[pos] == 0)
24039999SWang.Lin@Sun.COM mfilt[index] &= ~bit;
24049999SWang.Lin@Sun.COM }
24059999SWang.Lin@Sun.COM ath9k_hw_setmcastfilter(ah, mfilt[0], mfilt[1]);
24069999SWang.Lin@Sun.COM
24079999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
24089999SWang.Lin@Sun.COM return (0);
24099999SWang.Lin@Sun.COM }
24109999SWang.Lin@Sun.COM
24119999SWang.Lin@Sun.COM static int
arn_m_unicst(void * arg,const uint8_t * macaddr)24129999SWang.Lin@Sun.COM arn_m_unicst(void *arg, const uint8_t *macaddr)
24139999SWang.Lin@Sun.COM {
24149999SWang.Lin@Sun.COM struct arn_softc *sc = arg;
24159999SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
24169999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc;
24179999SWang.Lin@Sun.COM
24189999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_XMIT, "ath: ath_gld_saddr(): "
24199999SWang.Lin@Sun.COM "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
24209999SWang.Lin@Sun.COM macaddr[0], macaddr[1], macaddr[2],
24219999SWang.Lin@Sun.COM macaddr[3], macaddr[4], macaddr[5]));
24229999SWang.Lin@Sun.COM
24239999SWang.Lin@Sun.COM ARN_LOCK(sc);
24249999SWang.Lin@Sun.COM IEEE80211_ADDR_COPY(sc->sc_isc.ic_macaddr, macaddr);
24259999SWang.Lin@Sun.COM (void) ath9k_hw_setmac(ah, sc->sc_isc.ic_macaddr);
24269999SWang.Lin@Sun.COM (void) arn_reset(ic);
24279999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
24289999SWang.Lin@Sun.COM return (0);
24299999SWang.Lin@Sun.COM }
24309999SWang.Lin@Sun.COM
24319999SWang.Lin@Sun.COM static mblk_t *
arn_m_tx(void * arg,mblk_t * mp)24329999SWang.Lin@Sun.COM arn_m_tx(void *arg, mblk_t *mp)
24339999SWang.Lin@Sun.COM {
24349999SWang.Lin@Sun.COM struct arn_softc *sc = arg;
24359999SWang.Lin@Sun.COM int error = 0;
24369999SWang.Lin@Sun.COM mblk_t *next;
24379999SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc;
24389999SWang.Lin@Sun.COM
24399999SWang.Lin@Sun.COM /*
24409999SWang.Lin@Sun.COM * No data frames go out unless we're associated; this
24419999SWang.Lin@Sun.COM * should not happen as the 802.11 layer does not enable
24429999SWang.Lin@Sun.COM * the xmit queue until we enter the RUN state.
24439999SWang.Lin@Sun.COM */
24449999SWang.Lin@Sun.COM if (ic->ic_state != IEEE80211_S_RUN) {
24459999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_XMIT, "arn: arn_m_tx(): "
24469999SWang.Lin@Sun.COM "discard, state %u\n", ic->ic_state));
24479999SWang.Lin@Sun.COM sc->sc_stats.ast_tx_discard++;
24489999SWang.Lin@Sun.COM freemsgchain(mp);
24499999SWang.Lin@Sun.COM return (NULL);
24509999SWang.Lin@Sun.COM }
24519999SWang.Lin@Sun.COM
24529999SWang.Lin@Sun.COM while (mp != NULL) {
24539999SWang.Lin@Sun.COM next = mp->b_next;
24549999SWang.Lin@Sun.COM mp->b_next = NULL;
24559999SWang.Lin@Sun.COM error = arn_tx(ic, mp, IEEE80211_FC0_TYPE_DATA);
24569999SWang.Lin@Sun.COM if (error != 0) {
24579999SWang.Lin@Sun.COM mp->b_next = next;
24589999SWang.Lin@Sun.COM if (error == ENOMEM) {
24599999SWang.Lin@Sun.COM break;
24609999SWang.Lin@Sun.COM } else {
24619999SWang.Lin@Sun.COM freemsgchain(mp);
24629999SWang.Lin@Sun.COM return (NULL);
24639999SWang.Lin@Sun.COM }
24649999SWang.Lin@Sun.COM }
24659999SWang.Lin@Sun.COM mp = next;
24669999SWang.Lin@Sun.COM }
24679999SWang.Lin@Sun.COM
24689999SWang.Lin@Sun.COM return (mp);
24699999SWang.Lin@Sun.COM }
24709999SWang.Lin@Sun.COM
24719999SWang.Lin@Sun.COM static void
arn_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)24729999SWang.Lin@Sun.COM arn_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
24739999SWang.Lin@Sun.COM {
24749999SWang.Lin@Sun.COM struct arn_softc *sc = arg;
24759999SWang.Lin@Sun.COM int32_t err;
24769999SWang.Lin@Sun.COM
24779999SWang.Lin@Sun.COM err = ieee80211_ioctl(&sc->sc_isc, wq, mp);
24789999SWang.Lin@Sun.COM
24799999SWang.Lin@Sun.COM ARN_LOCK(sc);
24809999SWang.Lin@Sun.COM if (err == ENETRESET) {
24819999SWang.Lin@Sun.COM if (!(sc->sc_flags & SC_OP_INVALID)) {
24829999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
24839999SWang.Lin@Sun.COM
24849999SWang.Lin@Sun.COM (void) arn_m_start(sc);
24859999SWang.Lin@Sun.COM
24869999SWang.Lin@Sun.COM (void) ieee80211_new_state(&sc->sc_isc,
24879999SWang.Lin@Sun.COM IEEE80211_S_SCAN, -1);
24889999SWang.Lin@Sun.COM ARN_LOCK(sc);
24899999SWang.Lin@Sun.COM }
24909999SWang.Lin@Sun.COM }
24919999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
24929999SWang.Lin@Sun.COM }
24939999SWang.Lin@Sun.COM
24949999SWang.Lin@Sun.COM static int
arn_m_setprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,const void * wldp_buf)24959999SWang.Lin@Sun.COM arn_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
24969999SWang.Lin@Sun.COM uint_t wldp_length, const void *wldp_buf)
24979999SWang.Lin@Sun.COM {
24989999SWang.Lin@Sun.COM struct arn_softc *sc = arg;
24999999SWang.Lin@Sun.COM int err;
25009999SWang.Lin@Sun.COM
25019999SWang.Lin@Sun.COM err = ieee80211_setprop(&sc->sc_isc, pr_name, wldp_pr_num,
25029999SWang.Lin@Sun.COM wldp_length, wldp_buf);
25039999SWang.Lin@Sun.COM
25049999SWang.Lin@Sun.COM ARN_LOCK(sc);
25059999SWang.Lin@Sun.COM
25069999SWang.Lin@Sun.COM if (err == ENETRESET) {
25079999SWang.Lin@Sun.COM if (!(sc->sc_flags & SC_OP_INVALID)) {
25089999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
25099999SWang.Lin@Sun.COM (void) arn_m_start(sc);
25109999SWang.Lin@Sun.COM (void) ieee80211_new_state(&sc->sc_isc,
25119999SWang.Lin@Sun.COM IEEE80211_S_SCAN, -1);
25129999SWang.Lin@Sun.COM ARN_LOCK(sc);
25139999SWang.Lin@Sun.COM }
25149999SWang.Lin@Sun.COM err = 0;
25159999SWang.Lin@Sun.COM }
25169999SWang.Lin@Sun.COM
25179999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
25189999SWang.Lin@Sun.COM
25199999SWang.Lin@Sun.COM return (err);
25209999SWang.Lin@Sun.COM }
25219999SWang.Lin@Sun.COM
25229999SWang.Lin@Sun.COM /* ARGSUSED */
25239999SWang.Lin@Sun.COM static int
arn_m_getprop(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,uint_t wldp_length,void * wldp_buf)25249999SWang.Lin@Sun.COM arn_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2525*11878SVenu.Iyer@Sun.COM uint_t wldp_length, void *wldp_buf)
25269999SWang.Lin@Sun.COM {
25279999SWang.Lin@Sun.COM struct arn_softc *sc = arg;
25289999SWang.Lin@Sun.COM int err = 0;
25299999SWang.Lin@Sun.COM
25309999SWang.Lin@Sun.COM err = ieee80211_getprop(&sc->sc_isc, pr_name, wldp_pr_num,
2531*11878SVenu.Iyer@Sun.COM wldp_length, wldp_buf);
25329999SWang.Lin@Sun.COM
25339999SWang.Lin@Sun.COM return (err);
25349999SWang.Lin@Sun.COM }
25359999SWang.Lin@Sun.COM
2536*11878SVenu.Iyer@Sun.COM static void
arn_m_propinfo(void * arg,const char * pr_name,mac_prop_id_t wldp_pr_num,mac_prop_info_handle_t prh)2537*11878SVenu.Iyer@Sun.COM arn_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
2538*11878SVenu.Iyer@Sun.COM mac_prop_info_handle_t prh)
2539*11878SVenu.Iyer@Sun.COM {
2540*11878SVenu.Iyer@Sun.COM struct arn_softc *sc = arg;
2541*11878SVenu.Iyer@Sun.COM
2542*11878SVenu.Iyer@Sun.COM ieee80211_propinfo(&sc->sc_isc, pr_name, wldp_pr_num, prh);
2543*11878SVenu.Iyer@Sun.COM }
2544*11878SVenu.Iyer@Sun.COM
25459999SWang.Lin@Sun.COM /* return bus cachesize in 4B word units */
25469999SWang.Lin@Sun.COM static void
arn_pci_config_cachesize(struct arn_softc * sc)25479999SWang.Lin@Sun.COM arn_pci_config_cachesize(struct arn_softc *sc)
25489999SWang.Lin@Sun.COM {
25499999SWang.Lin@Sun.COM uint8_t csz;
25509999SWang.Lin@Sun.COM
25519999SWang.Lin@Sun.COM /*
25529999SWang.Lin@Sun.COM * Cache line size is used to size and align various
25539999SWang.Lin@Sun.COM * structures used to communicate with the hardware.
25549999SWang.Lin@Sun.COM */
25559999SWang.Lin@Sun.COM csz = pci_config_get8(sc->sc_cfg_handle, PCI_CONF_CACHE_LINESZ);
25569999SWang.Lin@Sun.COM if (csz == 0) {
25579999SWang.Lin@Sun.COM /*
25589999SWang.Lin@Sun.COM * We must have this setup properly for rx buffer
25599999SWang.Lin@Sun.COM * DMA to work so force a reasonable value here if it
25609999SWang.Lin@Sun.COM * comes up zero.
25619999SWang.Lin@Sun.COM */
25629999SWang.Lin@Sun.COM csz = ATH_DEF_CACHE_BYTES / sizeof (uint32_t);
25639999SWang.Lin@Sun.COM pci_config_put8(sc->sc_cfg_handle, PCI_CONF_CACHE_LINESZ,
25649999SWang.Lin@Sun.COM csz);
25659999SWang.Lin@Sun.COM }
25669999SWang.Lin@Sun.COM sc->sc_cachelsz = csz << 2;
25679999SWang.Lin@Sun.COM }
25689999SWang.Lin@Sun.COM
25699999SWang.Lin@Sun.COM static int
arn_pci_setup(struct arn_softc * sc)25709999SWang.Lin@Sun.COM arn_pci_setup(struct arn_softc *sc)
25719999SWang.Lin@Sun.COM {
25729999SWang.Lin@Sun.COM uint16_t command;
25739999SWang.Lin@Sun.COM
25749999SWang.Lin@Sun.COM /*
25759999SWang.Lin@Sun.COM * Enable memory mapping and bus mastering
25769999SWang.Lin@Sun.COM */
25779999SWang.Lin@Sun.COM ASSERT(sc != NULL);
25789999SWang.Lin@Sun.COM command = pci_config_get16(sc->sc_cfg_handle, PCI_CONF_COMM);
25799999SWang.Lin@Sun.COM command |= PCI_COMM_MAE | PCI_COMM_ME;
25809999SWang.Lin@Sun.COM pci_config_put16(sc->sc_cfg_handle, PCI_CONF_COMM, command);
25819999SWang.Lin@Sun.COM command = pci_config_get16(sc->sc_cfg_handle, PCI_CONF_COMM);
25829999SWang.Lin@Sun.COM if ((command & PCI_COMM_MAE) == 0) {
25839999SWang.Lin@Sun.COM arn_problem("arn: arn_pci_setup(): "
25849999SWang.Lin@Sun.COM "failed to enable memory mapping\n");
25859999SWang.Lin@Sun.COM return (EIO);
25869999SWang.Lin@Sun.COM }
25879999SWang.Lin@Sun.COM if ((command & PCI_COMM_ME) == 0) {
25889999SWang.Lin@Sun.COM arn_problem("arn: arn_pci_setup(): "
25899999SWang.Lin@Sun.COM "failed to enable bus mastering\n");
25909999SWang.Lin@Sun.COM return (EIO);
25919999SWang.Lin@Sun.COM }
25929999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INIT, "arn: arn_pci_setup(): "
25939999SWang.Lin@Sun.COM "set command reg to 0x%x \n", command));
25949999SWang.Lin@Sun.COM
25959999SWang.Lin@Sun.COM return (0);
25969999SWang.Lin@Sun.COM }
25979999SWang.Lin@Sun.COM
25989999SWang.Lin@Sun.COM static void
arn_get_hw_encap(struct arn_softc * sc)25999999SWang.Lin@Sun.COM arn_get_hw_encap(struct arn_softc *sc)
26009999SWang.Lin@Sun.COM {
26019999SWang.Lin@Sun.COM ieee80211com_t *ic;
26029999SWang.Lin@Sun.COM struct ath_hal *ah;
26039999SWang.Lin@Sun.COM
26049999SWang.Lin@Sun.COM ic = (ieee80211com_t *)sc;
26059999SWang.Lin@Sun.COM ah = sc->sc_ah;
26069999SWang.Lin@Sun.COM
26079999SWang.Lin@Sun.COM if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
26089999SWang.Lin@Sun.COM ATH9K_CIPHER_AES_CCM, NULL))
26099999SWang.Lin@Sun.COM ic->ic_caps |= IEEE80211_C_AES_CCM;
26109999SWang.Lin@Sun.COM if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
26119999SWang.Lin@Sun.COM ATH9K_CIPHER_AES_OCB, NULL))
26129999SWang.Lin@Sun.COM ic->ic_caps |= IEEE80211_C_AES;
26139999SWang.Lin@Sun.COM if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
26149999SWang.Lin@Sun.COM ATH9K_CIPHER_TKIP, NULL))
26159999SWang.Lin@Sun.COM ic->ic_caps |= IEEE80211_C_TKIP;
26169999SWang.Lin@Sun.COM if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
26179999SWang.Lin@Sun.COM ATH9K_CIPHER_WEP, NULL))
26189999SWang.Lin@Sun.COM ic->ic_caps |= IEEE80211_C_WEP;
26199999SWang.Lin@Sun.COM if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
26209999SWang.Lin@Sun.COM ATH9K_CIPHER_MIC, NULL))
26219999SWang.Lin@Sun.COM ic->ic_caps |= IEEE80211_C_TKIPMIC;
26229999SWang.Lin@Sun.COM }
26239999SWang.Lin@Sun.COM
262411729SWang.Lin@Sun.COM static void
arn_setup_ht_cap(struct arn_softc * sc)262511729SWang.Lin@Sun.COM arn_setup_ht_cap(struct arn_softc *sc)
262611729SWang.Lin@Sun.COM {
262711729SWang.Lin@Sun.COM #define ATH9K_HT_CAP_MAXRXAMPDU_65536 0x3 /* 2 ^ 16 */
262811729SWang.Lin@Sun.COM #define ATH9K_HT_CAP_MPDUDENSITY_8 0x6 /* 8 usec */
262911729SWang.Lin@Sun.COM
263011729SWang.Lin@Sun.COM /* LINTED E_FUNC_SET_NOT_USED */
263111729SWang.Lin@Sun.COM uint8_t tx_streams;
263211729SWang.Lin@Sun.COM uint8_t rx_streams;
263311729SWang.Lin@Sun.COM
263411729SWang.Lin@Sun.COM arn_ht_conf *ht_info = &sc->sc_ht_conf;
263511729SWang.Lin@Sun.COM
263611729SWang.Lin@Sun.COM ht_info->ht_supported = B_TRUE;
263711729SWang.Lin@Sun.COM
263811729SWang.Lin@Sun.COM /* Todo: IEEE80211_HTCAP_SMPS */
263911729SWang.Lin@Sun.COM ht_info->cap = IEEE80211_HTCAP_CHWIDTH40|
264011729SWang.Lin@Sun.COM IEEE80211_HTCAP_SHORTGI40 |
264111729SWang.Lin@Sun.COM IEEE80211_HTCAP_DSSSCCK40;
264211729SWang.Lin@Sun.COM
264311729SWang.Lin@Sun.COM ht_info->ampdu_factor = ATH9K_HT_CAP_MAXRXAMPDU_65536;
264411729SWang.Lin@Sun.COM ht_info->ampdu_density = ATH9K_HT_CAP_MPDUDENSITY_8;
264511729SWang.Lin@Sun.COM
264611729SWang.Lin@Sun.COM /* set up supported mcs set */
264711729SWang.Lin@Sun.COM (void) memset(&ht_info->rx_mcs_mask, 0, sizeof (ht_info->rx_mcs_mask));
264811729SWang.Lin@Sun.COM tx_streams =
264911729SWang.Lin@Sun.COM !(sc->sc_ah->ah_caps.tx_chainmask &
265011729SWang.Lin@Sun.COM (sc->sc_ah->ah_caps.tx_chainmask - 1)) ? 1 : 2;
265111729SWang.Lin@Sun.COM rx_streams =
265211729SWang.Lin@Sun.COM !(sc->sc_ah->ah_caps.rx_chainmask &
265311729SWang.Lin@Sun.COM (sc->sc_ah->ah_caps.rx_chainmask - 1)) ? 1 : 2;
265411729SWang.Lin@Sun.COM
265511729SWang.Lin@Sun.COM ht_info->rx_mcs_mask[0] = 0xff;
265611729SWang.Lin@Sun.COM if (rx_streams >= 2)
265711729SWang.Lin@Sun.COM ht_info->rx_mcs_mask[1] = 0xff;
265811729SWang.Lin@Sun.COM }
265911729SWang.Lin@Sun.COM
266011729SWang.Lin@Sun.COM /* xxx should be used for ht rate set negotiating ? */
266111729SWang.Lin@Sun.COM static void
arn_overwrite_11n_rateset(struct arn_softc * sc)266211729SWang.Lin@Sun.COM arn_overwrite_11n_rateset(struct arn_softc *sc)
266311729SWang.Lin@Sun.COM {
266411729SWang.Lin@Sun.COM uint8_t *ht_rs = sc->sc_ht_conf.rx_mcs_mask;
266511729SWang.Lin@Sun.COM int mcs_idx, mcs_count = 0;
266611729SWang.Lin@Sun.COM int i, j;
266711729SWang.Lin@Sun.COM
266811729SWang.Lin@Sun.COM (void) memset(&ieee80211_rateset_11n, 0,
266911729SWang.Lin@Sun.COM sizeof (ieee80211_rateset_11n));
267011729SWang.Lin@Sun.COM for (i = 0; i < 10; i++) {
267111729SWang.Lin@Sun.COM for (j = 0; j < 8; j++) {
267211729SWang.Lin@Sun.COM if (ht_rs[i] & (1 << j)) {
267311729SWang.Lin@Sun.COM mcs_idx = i * 8 + j;
267411729SWang.Lin@Sun.COM if (mcs_idx >= IEEE80211_HTRATE_MAXSIZE) {
267511729SWang.Lin@Sun.COM break;
267611729SWang.Lin@Sun.COM }
267711729SWang.Lin@Sun.COM
267811729SWang.Lin@Sun.COM ieee80211_rateset_11n.rs_rates[mcs_idx] =
267911729SWang.Lin@Sun.COM (uint8_t)mcs_idx;
268011729SWang.Lin@Sun.COM mcs_count++;
268111729SWang.Lin@Sun.COM }
268211729SWang.Lin@Sun.COM }
268311729SWang.Lin@Sun.COM }
268411729SWang.Lin@Sun.COM
268511729SWang.Lin@Sun.COM ieee80211_rateset_11n.rs_nrates = (uint8_t)mcs_count;
268611729SWang.Lin@Sun.COM
268711729SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_RATE, "arn_overwrite_11n_rateset(): "
268811729SWang.Lin@Sun.COM "MCS rate set supported by this station is as follows:\n"));
268911729SWang.Lin@Sun.COM
269011729SWang.Lin@Sun.COM for (i = 0; i < ieee80211_rateset_11n.rs_nrates; i++) {
269111729SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_RATE, "MCS rate %d is %d\n",
269211729SWang.Lin@Sun.COM i, ieee80211_rateset_11n.rs_rates[i]));
269311729SWang.Lin@Sun.COM }
269411729SWang.Lin@Sun.COM
269511729SWang.Lin@Sun.COM }
269611729SWang.Lin@Sun.COM
269711729SWang.Lin@Sun.COM /*
269811729SWang.Lin@Sun.COM * Update WME parameters for a transmit queue.
269911729SWang.Lin@Sun.COM */
270011729SWang.Lin@Sun.COM static int
arn_tx_queue_update(struct arn_softc * sc,int ac)270111729SWang.Lin@Sun.COM arn_tx_queue_update(struct arn_softc *sc, int ac)
270211729SWang.Lin@Sun.COM {
270311729SWang.Lin@Sun.COM #define ATH_EXPONENT_TO_VALUE(v) ((1<<v)-1)
270411729SWang.Lin@Sun.COM #define ATH_TXOP_TO_US(v) (v<<5)
270511729SWang.Lin@Sun.COM ieee80211com_t *ic = (ieee80211com_t *)sc;
270611729SWang.Lin@Sun.COM struct ath_txq *txq;
270711729SWang.Lin@Sun.COM struct wmeParams *wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac];
270811729SWang.Lin@Sun.COM struct ath_hal *ah = sc->sc_ah;
270911729SWang.Lin@Sun.COM struct ath9k_tx_queue_info qi;
271011729SWang.Lin@Sun.COM
271111729SWang.Lin@Sun.COM txq = &sc->sc_txq[arn_get_hal_qnum(ac, sc)];
271211729SWang.Lin@Sun.COM (void) ath9k_hw_get_txq_props(ah, txq->axq_qnum, &qi);
271311729SWang.Lin@Sun.COM
271411729SWang.Lin@Sun.COM /*
271511729SWang.Lin@Sun.COM * TXQ_FLAG_TXOKINT_ENABLE = 0x0001
271611729SWang.Lin@Sun.COM * TXQ_FLAG_TXERRINT_ENABLE = 0x0001
271711729SWang.Lin@Sun.COM * TXQ_FLAG_TXDESCINT_ENABLE = 0x0002
271811729SWang.Lin@Sun.COM * TXQ_FLAG_TXEOLINT_ENABLE = 0x0004
271911729SWang.Lin@Sun.COM * TXQ_FLAG_TXURNINT_ENABLE = 0x0008
272011729SWang.Lin@Sun.COM * TXQ_FLAG_BACKOFF_DISABLE = 0x0010
272111729SWang.Lin@Sun.COM * TXQ_FLAG_COMPRESSION_ENABLE = 0x0020
272211729SWang.Lin@Sun.COM * TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE = 0x0040
272311729SWang.Lin@Sun.COM * TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE = 0x0080
272411729SWang.Lin@Sun.COM */
272511729SWang.Lin@Sun.COM
272611729SWang.Lin@Sun.COM /* xxx should update these flags here? */
272711729SWang.Lin@Sun.COM #if 0
272811729SWang.Lin@Sun.COM qi.tqi_qflags = TXQ_FLAG_TXOKINT_ENABLE |
272911729SWang.Lin@Sun.COM TXQ_FLAG_TXERRINT_ENABLE |
273011729SWang.Lin@Sun.COM TXQ_FLAG_TXDESCINT_ENABLE |
273111729SWang.Lin@Sun.COM TXQ_FLAG_TXURNINT_ENABLE;
273211729SWang.Lin@Sun.COM #endif
273311729SWang.Lin@Sun.COM
273411729SWang.Lin@Sun.COM qi.tqi_aifs = wmep->wmep_aifsn;
273511729SWang.Lin@Sun.COM qi.tqi_cwmin = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin);
273611729SWang.Lin@Sun.COM qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax);
273711729SWang.Lin@Sun.COM qi.tqi_readyTime = 0;
273811729SWang.Lin@Sun.COM qi.tqi_burstTime = ATH_TXOP_TO_US(wmep->wmep_txopLimit);
273911729SWang.Lin@Sun.COM
274011729SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INIT,
274111729SWang.Lin@Sun.COM "%s:"
274211729SWang.Lin@Sun.COM "Q%u"
274311729SWang.Lin@Sun.COM "qflags 0x%x"
274411729SWang.Lin@Sun.COM "aifs %u"
274511729SWang.Lin@Sun.COM "cwmin %u"
274611729SWang.Lin@Sun.COM "cwmax %u"
274711729SWang.Lin@Sun.COM "burstTime %u\n",
274811729SWang.Lin@Sun.COM __func__,
274911729SWang.Lin@Sun.COM txq->axq_qnum,
275011729SWang.Lin@Sun.COM qi.tqi_qflags,
275111729SWang.Lin@Sun.COM qi.tqi_aifs,
275211729SWang.Lin@Sun.COM qi.tqi_cwmin,
275311729SWang.Lin@Sun.COM qi.tqi_cwmax,
275411729SWang.Lin@Sun.COM qi.tqi_burstTime));
275511729SWang.Lin@Sun.COM
275611729SWang.Lin@Sun.COM if (!ath9k_hw_set_txq_props(ah, txq->axq_qnum, &qi)) {
275711729SWang.Lin@Sun.COM arn_problem("unable to update hardware queue "
275811729SWang.Lin@Sun.COM "parameters for %s traffic!\n",
275911729SWang.Lin@Sun.COM ieee80211_wme_acnames[ac]);
276011729SWang.Lin@Sun.COM return (0);
276111729SWang.Lin@Sun.COM } else {
276211729SWang.Lin@Sun.COM /* push to H/W */
276311729SWang.Lin@Sun.COM (void) ath9k_hw_resettxqueue(ah, txq->axq_qnum);
276411729SWang.Lin@Sun.COM return (1);
276511729SWang.Lin@Sun.COM }
276611729SWang.Lin@Sun.COM
276711729SWang.Lin@Sun.COM #undef ATH_TXOP_TO_US
276811729SWang.Lin@Sun.COM #undef ATH_EXPONENT_TO_VALUE
276911729SWang.Lin@Sun.COM }
277011729SWang.Lin@Sun.COM
277111729SWang.Lin@Sun.COM /* Update WME parameters */
277211729SWang.Lin@Sun.COM static int
arn_wme_update(ieee80211com_t * ic)277311729SWang.Lin@Sun.COM arn_wme_update(ieee80211com_t *ic)
277411729SWang.Lin@Sun.COM {
277511729SWang.Lin@Sun.COM struct arn_softc *sc = (struct arn_softc *)ic;
277611729SWang.Lin@Sun.COM
277711729SWang.Lin@Sun.COM /* updateing */
277811729SWang.Lin@Sun.COM return (!arn_tx_queue_update(sc, WME_AC_BE) ||
277911729SWang.Lin@Sun.COM !arn_tx_queue_update(sc, WME_AC_BK) ||
278011729SWang.Lin@Sun.COM !arn_tx_queue_update(sc, WME_AC_VI) ||
278111729SWang.Lin@Sun.COM !arn_tx_queue_update(sc, WME_AC_VO) ? EIO : 0);
278211729SWang.Lin@Sun.COM }
278311729SWang.Lin@Sun.COM
278411729SWang.Lin@Sun.COM /*
278511729SWang.Lin@Sun.COM * Update tx/rx chainmask. For legacy association,
278611729SWang.Lin@Sun.COM * hard code chainmask to 1x1, for 11n association, use
278711729SWang.Lin@Sun.COM * the chainmask configuration.
278811729SWang.Lin@Sun.COM */
278911729SWang.Lin@Sun.COM void
arn_update_chainmask(struct arn_softc * sc)279011729SWang.Lin@Sun.COM arn_update_chainmask(struct arn_softc *sc)
279111729SWang.Lin@Sun.COM {
279211729SWang.Lin@Sun.COM boolean_t is_ht = B_FALSE;
279311729SWang.Lin@Sun.COM sc->sc_flags |= SC_OP_CHAINMASK_UPDATE;
279411729SWang.Lin@Sun.COM
279511729SWang.Lin@Sun.COM is_ht = sc->sc_ht_conf.ht_supported;
279611729SWang.Lin@Sun.COM if (is_ht) {
279711729SWang.Lin@Sun.COM sc->sc_tx_chainmask = sc->sc_ah->ah_caps.tx_chainmask;
279811729SWang.Lin@Sun.COM sc->sc_rx_chainmask = sc->sc_ah->ah_caps.rx_chainmask;
279911729SWang.Lin@Sun.COM } else {
280011729SWang.Lin@Sun.COM sc->sc_tx_chainmask = 1;
280111729SWang.Lin@Sun.COM sc->sc_rx_chainmask = 1;
280211729SWang.Lin@Sun.COM }
280311729SWang.Lin@Sun.COM
280411729SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
280511729SWang.Lin@Sun.COM "tx_chainmask = %d, rx_chainmask = %d\n",
280611729SWang.Lin@Sun.COM sc->sc_tx_chainmask, sc->sc_rx_chainmask));
280711729SWang.Lin@Sun.COM }
280811729SWang.Lin@Sun.COM
28099999SWang.Lin@Sun.COM static int
arn_resume(dev_info_t * devinfo)28109999SWang.Lin@Sun.COM arn_resume(dev_info_t *devinfo)
28119999SWang.Lin@Sun.COM {
28129999SWang.Lin@Sun.COM struct arn_softc *sc;
28139999SWang.Lin@Sun.COM int ret = DDI_SUCCESS;
28149999SWang.Lin@Sun.COM
28159999SWang.Lin@Sun.COM sc = ddi_get_soft_state(arn_soft_state_p, ddi_get_instance(devinfo));
28169999SWang.Lin@Sun.COM if (sc == NULL) {
28179999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INIT, "ath: ath_resume(): "
28189999SWang.Lin@Sun.COM "failed to get soft state\n"));
28199999SWang.Lin@Sun.COM return (DDI_FAILURE);
28209999SWang.Lin@Sun.COM }
28219999SWang.Lin@Sun.COM
28229999SWang.Lin@Sun.COM ARN_LOCK(sc);
28239999SWang.Lin@Sun.COM /*
28249999SWang.Lin@Sun.COM * Set up config space command register(s). Refuse
28259999SWang.Lin@Sun.COM * to resume on failure.
28269999SWang.Lin@Sun.COM */
28279999SWang.Lin@Sun.COM if (arn_pci_setup(sc) != 0) {
28289999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_INIT, "ath: ath_resume(): "
28299999SWang.Lin@Sun.COM "ath_pci_setup() failed\n"));
28309999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
28319999SWang.Lin@Sun.COM return (DDI_FAILURE);
28329999SWang.Lin@Sun.COM }
28339999SWang.Lin@Sun.COM
28349999SWang.Lin@Sun.COM if (!(sc->sc_flags & SC_OP_INVALID))
28359999SWang.Lin@Sun.COM ret = arn_open(sc);
28369999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
28379999SWang.Lin@Sun.COM
28389999SWang.Lin@Sun.COM return (ret);
28399999SWang.Lin@Sun.COM }
28409999SWang.Lin@Sun.COM
28419999SWang.Lin@Sun.COM static int
arn_attach(dev_info_t * devinfo,ddi_attach_cmd_t cmd)28429999SWang.Lin@Sun.COM arn_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
28439999SWang.Lin@Sun.COM {
28449999SWang.Lin@Sun.COM struct arn_softc *sc;
28459999SWang.Lin@Sun.COM int instance;
28469999SWang.Lin@Sun.COM int status;
28479999SWang.Lin@Sun.COM int32_t err;
28489999SWang.Lin@Sun.COM uint16_t vendor_id;
28499999SWang.Lin@Sun.COM uint16_t device_id;
28509999SWang.Lin@Sun.COM uint32_t i;
28519999SWang.Lin@Sun.COM uint32_t val;
28529999SWang.Lin@Sun.COM char strbuf[32];
28539999SWang.Lin@Sun.COM ieee80211com_t *ic;
28549999SWang.Lin@Sun.COM struct ath_hal *ah;
28559999SWang.Lin@Sun.COM wifi_data_t wd = { 0 };
28569999SWang.Lin@Sun.COM mac_register_t *macp;
28579999SWang.Lin@Sun.COM
28589999SWang.Lin@Sun.COM switch (cmd) {
28599999SWang.Lin@Sun.COM case DDI_ATTACH:
28609999SWang.Lin@Sun.COM break;
28619999SWang.Lin@Sun.COM case DDI_RESUME:
28629999SWang.Lin@Sun.COM return (arn_resume(devinfo));
28639999SWang.Lin@Sun.COM default:
28649999SWang.Lin@Sun.COM return (DDI_FAILURE);
28659999SWang.Lin@Sun.COM }
28669999SWang.Lin@Sun.COM
28679999SWang.Lin@Sun.COM instance = ddi_get_instance(devinfo);
28689999SWang.Lin@Sun.COM if (ddi_soft_state_zalloc(arn_soft_state_p, instance) != DDI_SUCCESS) {
28699999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: "
28709999SWang.Lin@Sun.COM "%s: Unable to alloc softstate\n", __func__));
28719999SWang.Lin@Sun.COM return (DDI_FAILURE);
28729999SWang.Lin@Sun.COM }
28739999SWang.Lin@Sun.COM
28749999SWang.Lin@Sun.COM sc = ddi_get_soft_state(arn_soft_state_p, ddi_get_instance(devinfo));
28759999SWang.Lin@Sun.COM ic = (ieee80211com_t *)sc;
28769999SWang.Lin@Sun.COM sc->sc_dev = devinfo;
28779999SWang.Lin@Sun.COM
28789999SWang.Lin@Sun.COM mutex_init(&sc->sc_genlock, NULL, MUTEX_DRIVER, NULL);
28799999SWang.Lin@Sun.COM mutex_init(&sc->sc_serial_rw, NULL, MUTEX_DRIVER, NULL);
28809999SWang.Lin@Sun.COM mutex_init(&sc->sc_txbuflock, NULL, MUTEX_DRIVER, NULL);
28819999SWang.Lin@Sun.COM mutex_init(&sc->sc_rxbuflock, NULL, MUTEX_DRIVER, NULL);
28829999SWang.Lin@Sun.COM mutex_init(&sc->sc_resched_lock, NULL, MUTEX_DRIVER, NULL);
28839999SWang.Lin@Sun.COM #ifdef ARN_IBSS
28849999SWang.Lin@Sun.COM mutex_init(&sc->sc_bcbuflock, NULL, MUTEX_DRIVER, NULL);
28859999SWang.Lin@Sun.COM #endif
28869999SWang.Lin@Sun.COM
28879999SWang.Lin@Sun.COM sc->sc_flags |= SC_OP_INVALID;
28889999SWang.Lin@Sun.COM
28899999SWang.Lin@Sun.COM err = pci_config_setup(devinfo, &sc->sc_cfg_handle);
28909999SWang.Lin@Sun.COM if (err != DDI_SUCCESS) {
28919999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
28929999SWang.Lin@Sun.COM "pci_config_setup() failed"));
28939999SWang.Lin@Sun.COM goto attach_fail0;
28949999SWang.Lin@Sun.COM }
28959999SWang.Lin@Sun.COM
28969999SWang.Lin@Sun.COM if (arn_pci_setup(sc) != 0)
28979999SWang.Lin@Sun.COM goto attach_fail1;
28989999SWang.Lin@Sun.COM
28999999SWang.Lin@Sun.COM /* Cache line size set up */
29009999SWang.Lin@Sun.COM arn_pci_config_cachesize(sc);
29019999SWang.Lin@Sun.COM
29029999SWang.Lin@Sun.COM vendor_id = pci_config_get16(sc->sc_cfg_handle, PCI_CONF_VENID);
29039999SWang.Lin@Sun.COM device_id = pci_config_get16(sc->sc_cfg_handle, PCI_CONF_DEVID);
29049999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): vendor 0x%x, "
29059999SWang.Lin@Sun.COM "device id 0x%x, cache size %d\n",
29069999SWang.Lin@Sun.COM vendor_id, device_id,
29079999SWang.Lin@Sun.COM pci_config_get8(sc->sc_cfg_handle, PCI_CONF_CACHE_LINESZ)));
29089999SWang.Lin@Sun.COM
29099999SWang.Lin@Sun.COM pci_config_put8(sc->sc_cfg_handle, PCI_CONF_LATENCY_TIMER, 0xa8);
29109999SWang.Lin@Sun.COM val = pci_config_get32(sc->sc_cfg_handle, 0x40);
29119999SWang.Lin@Sun.COM if ((val & 0x0000ff00) != 0)
29129999SWang.Lin@Sun.COM pci_config_put32(sc->sc_cfg_handle, 0x40, val & 0xffff00ff);
29139999SWang.Lin@Sun.COM
29149999SWang.Lin@Sun.COM err = ddi_regs_map_setup(devinfo, 1,
29159999SWang.Lin@Sun.COM &sc->mem, 0, 0, &arn_reg_accattr, &sc->sc_io_handle);
29169999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
29179999SWang.Lin@Sun.COM "regs map1 = %x err=%d\n", sc->mem, err));
29189999SWang.Lin@Sun.COM if (err != DDI_SUCCESS) {
29199999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
29209999SWang.Lin@Sun.COM "ddi_regs_map_setup() failed"));
29219999SWang.Lin@Sun.COM goto attach_fail1;
29229999SWang.Lin@Sun.COM }
29239999SWang.Lin@Sun.COM
29249999SWang.Lin@Sun.COM ah = ath9k_hw_attach(device_id, sc, sc->mem, &status);
29259999SWang.Lin@Sun.COM if (ah == NULL) {
29269999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
29279999SWang.Lin@Sun.COM "unable to attach hw: H/W status %u\n",
29289999SWang.Lin@Sun.COM status));
29299999SWang.Lin@Sun.COM goto attach_fail2;
29309999SWang.Lin@Sun.COM }
29319999SWang.Lin@Sun.COM sc->sc_ah = ah;
29329999SWang.Lin@Sun.COM
29339999SWang.Lin@Sun.COM ath9k_hw_getmac(ah, ic->ic_macaddr);
29349999SWang.Lin@Sun.COM
29359999SWang.Lin@Sun.COM /* Get the hardware key cache size. */
29369999SWang.Lin@Sun.COM sc->sc_keymax = ah->ah_caps.keycache_size;
29379999SWang.Lin@Sun.COM if (sc->sc_keymax > ATH_KEYMAX) {
29389999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
29399999SWang.Lin@Sun.COM "Warning, using only %u entries in %u key cache\n",
29409999SWang.Lin@Sun.COM ATH_KEYMAX, sc->sc_keymax));
29419999SWang.Lin@Sun.COM sc->sc_keymax = ATH_KEYMAX;
29429999SWang.Lin@Sun.COM }
29439999SWang.Lin@Sun.COM
29449999SWang.Lin@Sun.COM /*
29459999SWang.Lin@Sun.COM * Reset the key cache since some parts do not
29469999SWang.Lin@Sun.COM * reset the contents on initial power up.
29479999SWang.Lin@Sun.COM */
29489999SWang.Lin@Sun.COM for (i = 0; i < sc->sc_keymax; i++)
29499999SWang.Lin@Sun.COM (void) ath9k_hw_keyreset(ah, (uint16_t)i);
29509999SWang.Lin@Sun.COM /*
29519999SWang.Lin@Sun.COM * Mark key cache slots associated with global keys
29529999SWang.Lin@Sun.COM * as in use. If we knew TKIP was not to be used we
29539999SWang.Lin@Sun.COM * could leave the +32, +64, and +32+64 slots free.
29549999SWang.Lin@Sun.COM * XXX only for splitmic.
29559999SWang.Lin@Sun.COM */
29569999SWang.Lin@Sun.COM for (i = 0; i < IEEE80211_WEP_NKID; i++) {
29579999SWang.Lin@Sun.COM set_bit(i, sc->sc_keymap);
29589999SWang.Lin@Sun.COM set_bit(i + 32, sc->sc_keymap);
29599999SWang.Lin@Sun.COM set_bit(i + 64, sc->sc_keymap);
29609999SWang.Lin@Sun.COM set_bit(i + 32 + 64, sc->sc_keymap);
29619999SWang.Lin@Sun.COM }
29629999SWang.Lin@Sun.COM
29639999SWang.Lin@Sun.COM /* Collect the channel list using the default country code */
29649999SWang.Lin@Sun.COM err = arn_setup_channels(sc);
29659999SWang.Lin@Sun.COM if (err == EINVAL) {
29669999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
29679999SWang.Lin@Sun.COM "ERR:arn_setup_channels\n"));
29689999SWang.Lin@Sun.COM goto attach_fail3;
29699999SWang.Lin@Sun.COM }
29709999SWang.Lin@Sun.COM
29719999SWang.Lin@Sun.COM /* default to STA mode */
29729999SWang.Lin@Sun.COM sc->sc_ah->ah_opmode = ATH9K_M_STA;
29739999SWang.Lin@Sun.COM
29749999SWang.Lin@Sun.COM /* Setup rate tables */
29759999SWang.Lin@Sun.COM arn_rate_attach(sc);
29769999SWang.Lin@Sun.COM arn_setup_rates(sc, IEEE80211_MODE_11A);
29779999SWang.Lin@Sun.COM arn_setup_rates(sc, IEEE80211_MODE_11B);
29789999SWang.Lin@Sun.COM arn_setup_rates(sc, IEEE80211_MODE_11G);
29799999SWang.Lin@Sun.COM
29809999SWang.Lin@Sun.COM /* Setup current mode here */
29819999SWang.Lin@Sun.COM arn_setcurmode(sc, ATH9K_MODE_11G);
29829999SWang.Lin@Sun.COM
29839999SWang.Lin@Sun.COM /* 802.11g features */
29849999SWang.Lin@Sun.COM if (sc->sc_have11g)
29859999SWang.Lin@Sun.COM ic->ic_caps |= IEEE80211_C_SHPREAMBLE |
29869999SWang.Lin@Sun.COM IEEE80211_C_SHSLOT; /* short slot time */
29879999SWang.Lin@Sun.COM
298811729SWang.Lin@Sun.COM /* Temp workaround */
29899999SWang.Lin@Sun.COM sc->sc_mrretry = 1;
299011729SWang.Lin@Sun.COM sc->sc_config.ath_aggr_prot = 0;
29919999SWang.Lin@Sun.COM
29929999SWang.Lin@Sun.COM /* Setup tx/rx descriptors */
29939999SWang.Lin@Sun.COM err = arn_desc_alloc(devinfo, sc);
29949999SWang.Lin@Sun.COM if (err != DDI_SUCCESS) {
29959999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
29969999SWang.Lin@Sun.COM "failed to allocate descriptors: %d\n", err));
29979999SWang.Lin@Sun.COM goto attach_fail3;
29989999SWang.Lin@Sun.COM }
29999999SWang.Lin@Sun.COM
30009999SWang.Lin@Sun.COM if ((sc->sc_tq = ddi_taskq_create(devinfo, "ath_taskq", 1,
30019999SWang.Lin@Sun.COM TASKQ_DEFAULTPRI, 0)) == NULL) {
30029999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
30039999SWang.Lin@Sun.COM "ERR:ddi_taskq_create\n"));
30049999SWang.Lin@Sun.COM goto attach_fail4;
30059999SWang.Lin@Sun.COM }
30069999SWang.Lin@Sun.COM
30079999SWang.Lin@Sun.COM /*
30089999SWang.Lin@Sun.COM * Allocate hardware transmit queues: one queue for
30099999SWang.Lin@Sun.COM * beacon frames and one data queue for each QoS
30109999SWang.Lin@Sun.COM * priority. Note that the hal handles reseting
30119999SWang.Lin@Sun.COM * these queues at the needed time.
30129999SWang.Lin@Sun.COM */
30139999SWang.Lin@Sun.COM #ifdef ARN_IBSS
30149999SWang.Lin@Sun.COM sc->sc_beaconq = arn_beaconq_setup(ah);
30159999SWang.Lin@Sun.COM if (sc->sc_beaconq == (-1)) {
30169999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
30179999SWang.Lin@Sun.COM "unable to setup a beacon xmit queue\n"));
30189999SWang.Lin@Sun.COM goto attach_fail4;
30199999SWang.Lin@Sun.COM }
30209999SWang.Lin@Sun.COM #endif
30219999SWang.Lin@Sun.COM #ifdef ARN_HOSTAP
30229999SWang.Lin@Sun.COM sc->sc_cabq = arn_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0);
30239999SWang.Lin@Sun.COM if (sc->sc_cabq == NULL) {
30249999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
30259999SWang.Lin@Sun.COM "unable to setup CAB xmit queue\n"));
30269999SWang.Lin@Sun.COM goto attach_fail4;
30279999SWang.Lin@Sun.COM }
30289999SWang.Lin@Sun.COM
30299999SWang.Lin@Sun.COM sc->sc_config.cabqReadytime = ATH_CABQ_READY_TIME;
30309999SWang.Lin@Sun.COM ath_cabq_update(sc);
30319999SWang.Lin@Sun.COM #endif
30329999SWang.Lin@Sun.COM
30339999SWang.Lin@Sun.COM for (i = 0; i < ARRAY_SIZE(sc->sc_haltype2q); i++)
30349999SWang.Lin@Sun.COM sc->sc_haltype2q[i] = -1;
30359999SWang.Lin@Sun.COM
30369999SWang.Lin@Sun.COM /* Setup data queues */
30379999SWang.Lin@Sun.COM /* NB: ensure BK queue is the lowest priority h/w queue */
30389999SWang.Lin@Sun.COM if (!arn_tx_setup(sc, ATH9K_WME_AC_BK)) {
30399999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
30409999SWang.Lin@Sun.COM "unable to setup xmit queue for BK traffic\n"));
30419999SWang.Lin@Sun.COM goto attach_fail4;
30429999SWang.Lin@Sun.COM }
30439999SWang.Lin@Sun.COM if (!arn_tx_setup(sc, ATH9K_WME_AC_BE)) {
30449999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
30459999SWang.Lin@Sun.COM "unable to setup xmit queue for BE traffic\n"));
30469999SWang.Lin@Sun.COM goto attach_fail4;
30479999SWang.Lin@Sun.COM }
30489999SWang.Lin@Sun.COM if (!arn_tx_setup(sc, ATH9K_WME_AC_VI)) {
30499999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
30509999SWang.Lin@Sun.COM "unable to setup xmit queue for VI traffic\n"));
30519999SWang.Lin@Sun.COM goto attach_fail4;
30529999SWang.Lin@Sun.COM }
30539999SWang.Lin@Sun.COM if (!arn_tx_setup(sc, ATH9K_WME_AC_VO)) {
30549999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
30559999SWang.Lin@Sun.COM "unable to setup xmit queue for VO traffic\n"));
30569999SWang.Lin@Sun.COM goto attach_fail4;
30579999SWang.Lin@Sun.COM }
30589999SWang.Lin@Sun.COM
30599999SWang.Lin@Sun.COM /*
30609999SWang.Lin@Sun.COM * Initializes the noise floor to a reasonable default value.
30619999SWang.Lin@Sun.COM * Later on this will be updated during ANI processing.
30629999SWang.Lin@Sun.COM */
30639999SWang.Lin@Sun.COM
30649999SWang.Lin@Sun.COM sc->sc_ani.sc_noise_floor = ATH_DEFAULT_NOISE_FLOOR;
30659999SWang.Lin@Sun.COM
30669999SWang.Lin@Sun.COM
30679999SWang.Lin@Sun.COM if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
30689999SWang.Lin@Sun.COM ATH9K_CIPHER_TKIP, NULL)) {
30699999SWang.Lin@Sun.COM /*
30709999SWang.Lin@Sun.COM * Whether we should enable h/w TKIP MIC.
30719999SWang.Lin@Sun.COM * XXX: if we don't support WME TKIP MIC, then we wouldn't
30729999SWang.Lin@Sun.COM * report WMM capable, so it's always safe to turn on
30739999SWang.Lin@Sun.COM * TKIP MIC in this case.
30749999SWang.Lin@Sun.COM */
30759999SWang.Lin@Sun.COM (void) ath9k_hw_setcapability(sc->sc_ah, ATH9K_CAP_TKIP_MIC,
30769999SWang.Lin@Sun.COM 0, 1, NULL);
30779999SWang.Lin@Sun.COM }
30789999SWang.Lin@Sun.COM
30799999SWang.Lin@Sun.COM /* Get cipher releated capability information */
30809999SWang.Lin@Sun.COM arn_get_hw_encap(sc);
30819999SWang.Lin@Sun.COM
30829999SWang.Lin@Sun.COM /*
30839999SWang.Lin@Sun.COM * Check whether the separate key cache entries
30849999SWang.Lin@Sun.COM * are required to handle both tx+rx MIC keys.
30859999SWang.Lin@Sun.COM * With split mic keys the number of stations is limited
30869999SWang.Lin@Sun.COM * to 27 otherwise 59.
30879999SWang.Lin@Sun.COM */
30889999SWang.Lin@Sun.COM if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
30899999SWang.Lin@Sun.COM ATH9K_CIPHER_TKIP, NULL) &&
30909999SWang.Lin@Sun.COM ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
30919999SWang.Lin@Sun.COM ATH9K_CIPHER_MIC, NULL) &&
30929999SWang.Lin@Sun.COM ath9k_hw_getcapability(ah, ATH9K_CAP_TKIP_SPLIT,
30939999SWang.Lin@Sun.COM 0, NULL))
30949999SWang.Lin@Sun.COM sc->sc_splitmic = 1;
30959999SWang.Lin@Sun.COM
30969999SWang.Lin@Sun.COM /* turn on mcast key search if possible */
30979999SWang.Lin@Sun.COM if (!ath9k_hw_getcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL))
30989999SWang.Lin@Sun.COM (void) ath9k_hw_setcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 1,
30999999SWang.Lin@Sun.COM 1, NULL);
31009999SWang.Lin@Sun.COM
31019999SWang.Lin@Sun.COM sc->sc_config.txpowlimit = ATH_TXPOWER_MAX;
31029999SWang.Lin@Sun.COM sc->sc_config.txpowlimit_override = 0;
31039999SWang.Lin@Sun.COM
31049999SWang.Lin@Sun.COM /* 11n Capabilities */
31059999SWang.Lin@Sun.COM if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) {
31069999SWang.Lin@Sun.COM sc->sc_flags |= SC_OP_TXAGGR;
31079999SWang.Lin@Sun.COM sc->sc_flags |= SC_OP_RXAGGR;
310811729SWang.Lin@Sun.COM arn_setup_ht_cap(sc);
310911729SWang.Lin@Sun.COM arn_overwrite_11n_rateset(sc);
31109999SWang.Lin@Sun.COM }
311111729SWang.Lin@Sun.COM
31129999SWang.Lin@Sun.COM sc->sc_tx_chainmask = 1;
31139999SWang.Lin@Sun.COM sc->sc_rx_chainmask = 1;
31149999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
31159999SWang.Lin@Sun.COM "tx_chainmask = %d, rx_chainmask = %d\n",
31169999SWang.Lin@Sun.COM sc->sc_tx_chainmask, sc->sc_rx_chainmask));
31179999SWang.Lin@Sun.COM
311811729SWang.Lin@Sun.COM /* arn_update_chainmask(sc); */
311911729SWang.Lin@Sun.COM
31209999SWang.Lin@Sun.COM (void) ath9k_hw_setcapability(ah, ATH9K_CAP_DIVERSITY, 1, B_TRUE, NULL);
31219999SWang.Lin@Sun.COM sc->sc_defant = ath9k_hw_getdefantenna(ah);
31229999SWang.Lin@Sun.COM
31239999SWang.Lin@Sun.COM ath9k_hw_getmac(ah, sc->sc_myaddr);
31249999SWang.Lin@Sun.COM if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) {
31259999SWang.Lin@Sun.COM ath9k_hw_getbssidmask(ah, sc->sc_bssidmask);
31269999SWang.Lin@Sun.COM ATH_SET_VAP_BSSID_MASK(sc->sc_bssidmask);
31279999SWang.Lin@Sun.COM (void) ath9k_hw_setbssidmask(ah, sc->sc_bssidmask);
31289999SWang.Lin@Sun.COM }
31299999SWang.Lin@Sun.COM
31309999SWang.Lin@Sun.COM /* set default value to short slot time */
31319999SWang.Lin@Sun.COM sc->sc_slottime = ATH9K_SLOT_TIME_9;
31329999SWang.Lin@Sun.COM (void) ath9k_hw_setslottime(ah, ATH9K_SLOT_TIME_9);
31339999SWang.Lin@Sun.COM
31349999SWang.Lin@Sun.COM /* initialize beacon slots */
31359999SWang.Lin@Sun.COM for (i = 0; i < ARRAY_SIZE(sc->sc_bslot); i++)
31369999SWang.Lin@Sun.COM sc->sc_bslot[i] = ATH_IF_ID_ANY;
31379999SWang.Lin@Sun.COM
313811729SWang.Lin@Sun.COM /* Save MISC configurations */
31399999SWang.Lin@Sun.COM sc->sc_config.swBeaconProcess = 1;
31409999SWang.Lin@Sun.COM
314111729SWang.Lin@Sun.COM /* Support QoS/WME */
314211729SWang.Lin@Sun.COM ic->ic_caps |= IEEE80211_C_WME;
314311729SWang.Lin@Sun.COM ic->ic_wme.wme_update = arn_wme_update;
314411729SWang.Lin@Sun.COM
314511729SWang.Lin@Sun.COM /* Support 802.11n/HT */
314611729SWang.Lin@Sun.COM if (sc->sc_ht_conf.ht_supported) {
314711729SWang.Lin@Sun.COM ic->ic_htcaps =
314811729SWang.Lin@Sun.COM IEEE80211_HTCAP_CHWIDTH40 |
314911729SWang.Lin@Sun.COM IEEE80211_HTCAP_SHORTGI40 |
315011729SWang.Lin@Sun.COM IEEE80211_HTCAP_DSSSCCK40 |
315111729SWang.Lin@Sun.COM IEEE80211_HTCAP_MAXAMSDU_7935 |
315211729SWang.Lin@Sun.COM IEEE80211_HTC_HT |
315311729SWang.Lin@Sun.COM IEEE80211_HTC_AMSDU |
315411729SWang.Lin@Sun.COM IEEE80211_HTCAP_RXSTBC_2STREAM;
315511729SWang.Lin@Sun.COM
315611729SWang.Lin@Sun.COM #ifdef ARN_TX_AGGREGATION
315711729SWang.Lin@Sun.COM ic->ic_htcaps |= IEEE80211_HTC_AMPDU;
315811729SWang.Lin@Sun.COM #endif
315911729SWang.Lin@Sun.COM }
316011729SWang.Lin@Sun.COM
316111729SWang.Lin@Sun.COM /* Header padding requested by driver */
316211729SWang.Lin@Sun.COM ic->ic_flags |= IEEE80211_F_DATAPAD;
316311729SWang.Lin@Sun.COM /* Support WPA/WPA2 */
316411729SWang.Lin@Sun.COM ic->ic_caps |= IEEE80211_C_WPA;
316511729SWang.Lin@Sun.COM #if 0
316611729SWang.Lin@Sun.COM ic->ic_caps |= IEEE80211_C_TXFRAG; /* handle tx frags */
316711729SWang.Lin@Sun.COM ic->ic_caps |= IEEE80211_C_BGSCAN; /* capable of bg scanning */
316811729SWang.Lin@Sun.COM #endif
316911729SWang.Lin@Sun.COM ic->ic_phytype = IEEE80211_T_HT;
31709999SWang.Lin@Sun.COM ic->ic_opmode = IEEE80211_M_STA;
31719999SWang.Lin@Sun.COM ic->ic_state = IEEE80211_S_INIT;
31729999SWang.Lin@Sun.COM ic->ic_maxrssi = ARN_MAX_RSSI;
31739999SWang.Lin@Sun.COM ic->ic_set_shortslot = arn_set_shortslot;
31749999SWang.Lin@Sun.COM ic->ic_xmit = arn_tx;
31759999SWang.Lin@Sun.COM ieee80211_attach(ic);
31769999SWang.Lin@Sun.COM
31779999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
31789999SWang.Lin@Sun.COM "ic->ic_curchan->ich_freq: %d\n", ic->ic_curchan->ich_freq));
31799999SWang.Lin@Sun.COM
31809999SWang.Lin@Sun.COM /* different instance has different WPA door */
31819999SWang.Lin@Sun.COM (void) snprintf(ic->ic_wpadoor, MAX_IEEE80211STR, "%s_%s%d", WPA_DOOR,
31829999SWang.Lin@Sun.COM ddi_driver_name(devinfo),
31839999SWang.Lin@Sun.COM ddi_get_instance(devinfo));
31849999SWang.Lin@Sun.COM
318511729SWang.Lin@Sun.COM if (sc->sc_ht_conf.ht_supported) {
318611729SWang.Lin@Sun.COM sc->sc_recv_action = ic->ic_recv_action;
318711729SWang.Lin@Sun.COM ic->ic_recv_action = arn_ampdu_recv_action;
318811729SWang.Lin@Sun.COM // sc->sc_send_action = ic->ic_send_action;
318911729SWang.Lin@Sun.COM // ic->ic_send_action = arn_ampdu_send_action;
319011729SWang.Lin@Sun.COM
319111729SWang.Lin@Sun.COM ic->ic_ampdu_rxmax = sc->sc_ht_conf.ampdu_factor;
319211729SWang.Lin@Sun.COM ic->ic_ampdu_density = sc->sc_ht_conf.ampdu_density;
319311729SWang.Lin@Sun.COM ic->ic_ampdu_limit = ic->ic_ampdu_rxmax;
319411729SWang.Lin@Sun.COM }
319511729SWang.Lin@Sun.COM
31969999SWang.Lin@Sun.COM /* Override 80211 default routines */
31979999SWang.Lin@Sun.COM sc->sc_newstate = ic->ic_newstate;
31989999SWang.Lin@Sun.COM ic->ic_newstate = arn_newstate;
31999999SWang.Lin@Sun.COM #ifdef ARN_IBSS
32009999SWang.Lin@Sun.COM sc->sc_recv_mgmt = ic->ic_recv_mgmt;
32019999SWang.Lin@Sun.COM ic->ic_recv_mgmt = arn_recv_mgmt;
32029999SWang.Lin@Sun.COM #endif
32039999SWang.Lin@Sun.COM ic->ic_watchdog = arn_watchdog;
32049999SWang.Lin@Sun.COM ic->ic_node_alloc = arn_node_alloc;
32059999SWang.Lin@Sun.COM ic->ic_node_free = arn_node_free;
32069999SWang.Lin@Sun.COM ic->ic_crypto.cs_key_alloc = arn_key_alloc;
32079999SWang.Lin@Sun.COM ic->ic_crypto.cs_key_delete = arn_key_delete;
32089999SWang.Lin@Sun.COM ic->ic_crypto.cs_key_set = arn_key_set;
32099999SWang.Lin@Sun.COM
32109999SWang.Lin@Sun.COM ieee80211_media_init(ic);
32119999SWang.Lin@Sun.COM
32129999SWang.Lin@Sun.COM /*
32139999SWang.Lin@Sun.COM * initialize default tx key
32149999SWang.Lin@Sun.COM */
32159999SWang.Lin@Sun.COM ic->ic_def_txkey = 0;
32169999SWang.Lin@Sun.COM
32179999SWang.Lin@Sun.COM sc->sc_rx_pend = 0;
32189999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(sc->sc_ah, 0);
32199999SWang.Lin@Sun.COM err = ddi_add_softintr(devinfo, DDI_SOFTINT_LOW,
32209999SWang.Lin@Sun.COM &sc->sc_softint_id, NULL, 0, arn_softint_handler, (caddr_t)sc);
32219999SWang.Lin@Sun.COM if (err != DDI_SUCCESS) {
32229999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
32239999SWang.Lin@Sun.COM "ddi_add_softintr() failed....\n"));
32249999SWang.Lin@Sun.COM goto attach_fail5;
32259999SWang.Lin@Sun.COM }
32269999SWang.Lin@Sun.COM
32279999SWang.Lin@Sun.COM if (ddi_get_iblock_cookie(devinfo, 0, &sc->sc_iblock)
32289999SWang.Lin@Sun.COM != DDI_SUCCESS) {
32299999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
32309999SWang.Lin@Sun.COM "Can not get iblock cookie for INT\n"));
32319999SWang.Lin@Sun.COM goto attach_fail6;
32329999SWang.Lin@Sun.COM }
32339999SWang.Lin@Sun.COM
32349999SWang.Lin@Sun.COM if (ddi_add_intr(devinfo, 0, NULL, NULL, arn_isr,
32359999SWang.Lin@Sun.COM (caddr_t)sc) != DDI_SUCCESS) {
32369999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
32379999SWang.Lin@Sun.COM "Can not set intr for ARN driver\n"));
32389999SWang.Lin@Sun.COM goto attach_fail6;
32399999SWang.Lin@Sun.COM }
32409999SWang.Lin@Sun.COM
32419999SWang.Lin@Sun.COM /*
32429999SWang.Lin@Sun.COM * Provide initial settings for the WiFi plugin; whenever this
32439999SWang.Lin@Sun.COM * information changes, we need to call mac_plugindata_update()
32449999SWang.Lin@Sun.COM */
32459999SWang.Lin@Sun.COM wd.wd_opmode = ic->ic_opmode;
32469999SWang.Lin@Sun.COM wd.wd_secalloc = WIFI_SEC_NONE;
32479999SWang.Lin@Sun.COM IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
32489999SWang.Lin@Sun.COM
32499999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
32509999SWang.Lin@Sun.COM "IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid)"
32519999SWang.Lin@Sun.COM "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
32529999SWang.Lin@Sun.COM wd.wd_bssid[0], wd.wd_bssid[1], wd.wd_bssid[2],
32539999SWang.Lin@Sun.COM wd.wd_bssid[3], wd.wd_bssid[4], wd.wd_bssid[5]));
32549999SWang.Lin@Sun.COM
32559999SWang.Lin@Sun.COM if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
32569999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
32579999SWang.Lin@Sun.COM "MAC version mismatch\n"));
32589999SWang.Lin@Sun.COM goto attach_fail7;
32599999SWang.Lin@Sun.COM }
32609999SWang.Lin@Sun.COM
32619999SWang.Lin@Sun.COM macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI;
32629999SWang.Lin@Sun.COM macp->m_driver = sc;
32639999SWang.Lin@Sun.COM macp->m_dip = devinfo;
32649999SWang.Lin@Sun.COM macp->m_src_addr = ic->ic_macaddr;
32659999SWang.Lin@Sun.COM macp->m_callbacks = &arn_m_callbacks;
32669999SWang.Lin@Sun.COM macp->m_min_sdu = 0;
32679999SWang.Lin@Sun.COM macp->m_max_sdu = IEEE80211_MTU;
32689999SWang.Lin@Sun.COM macp->m_pdata = &wd;
32699999SWang.Lin@Sun.COM macp->m_pdata_size = sizeof (wd);
32709999SWang.Lin@Sun.COM
32719999SWang.Lin@Sun.COM err = mac_register(macp, &ic->ic_mach);
32729999SWang.Lin@Sun.COM mac_free(macp);
32739999SWang.Lin@Sun.COM if (err != 0) {
32749999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
32759999SWang.Lin@Sun.COM "mac_register err %x\n", err));
32769999SWang.Lin@Sun.COM goto attach_fail7;
32779999SWang.Lin@Sun.COM }
32789999SWang.Lin@Sun.COM
32799999SWang.Lin@Sun.COM /* Create minor node of type DDI_NT_NET_WIFI */
32809999SWang.Lin@Sun.COM (void) snprintf(strbuf, sizeof (strbuf), "%s%d",
32819999SWang.Lin@Sun.COM ARN_NODENAME, instance);
32829999SWang.Lin@Sun.COM err = ddi_create_minor_node(devinfo, strbuf, S_IFCHR,
32839999SWang.Lin@Sun.COM instance + 1, DDI_NT_NET_WIFI, 0);
32849999SWang.Lin@Sun.COM if (err != DDI_SUCCESS)
32859999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "WARN: arn: arn_attach(): "
32869999SWang.Lin@Sun.COM "Create minor node failed - %d\n", err));
32879999SWang.Lin@Sun.COM
328811729SWang.Lin@Sun.COM /* Notify link is down now */
32899999SWang.Lin@Sun.COM mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
32909999SWang.Lin@Sun.COM
32919999SWang.Lin@Sun.COM sc->sc_promisc = B_FALSE;
32929999SWang.Lin@Sun.COM bzero(sc->sc_mcast_refs, sizeof (sc->sc_mcast_refs));
32939999SWang.Lin@Sun.COM bzero(sc->sc_mcast_hash, sizeof (sc->sc_mcast_hash));
32949999SWang.Lin@Sun.COM
32959999SWang.Lin@Sun.COM ARN_DBG((ARN_DBG_ATTACH, "arn: arn_attach(): "
32969999SWang.Lin@Sun.COM "Atheros AR%s MAC/BB Rev:%x "
32979999SWang.Lin@Sun.COM "AR%s RF Rev:%x: mem=0x%lx\n",
32989999SWang.Lin@Sun.COM arn_mac_bb_name(ah->ah_macVersion),
32999999SWang.Lin@Sun.COM ah->ah_macRev,
33009999SWang.Lin@Sun.COM arn_rf_name((ah->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR)),
33019999SWang.Lin@Sun.COM ah->ah_phyRev,
33029999SWang.Lin@Sun.COM (unsigned long)sc->mem));
33039999SWang.Lin@Sun.COM
33049999SWang.Lin@Sun.COM /* XXX: hardware will not be ready until arn_open() being called */
33059999SWang.Lin@Sun.COM sc->sc_flags |= SC_OP_INVALID;
33069999SWang.Lin@Sun.COM sc->sc_isrunning = 0;
33079999SWang.Lin@Sun.COM
33089999SWang.Lin@Sun.COM return (DDI_SUCCESS);
33099999SWang.Lin@Sun.COM
33109999SWang.Lin@Sun.COM attach_fail7:
33119999SWang.Lin@Sun.COM ddi_remove_intr(devinfo, 0, sc->sc_iblock);
33129999SWang.Lin@Sun.COM attach_fail6:
33139999SWang.Lin@Sun.COM ddi_remove_softintr(sc->sc_softint_id);
33149999SWang.Lin@Sun.COM attach_fail5:
33159999SWang.Lin@Sun.COM (void) ieee80211_detach(ic);
33169999SWang.Lin@Sun.COM attach_fail4:
33179999SWang.Lin@Sun.COM arn_desc_free(sc);
33189999SWang.Lin@Sun.COM if (sc->sc_tq)
33199999SWang.Lin@Sun.COM ddi_taskq_destroy(sc->sc_tq);
33209999SWang.Lin@Sun.COM attach_fail3:
33219999SWang.Lin@Sun.COM ath9k_hw_detach(ah);
33229999SWang.Lin@Sun.COM attach_fail2:
33239999SWang.Lin@Sun.COM ddi_regs_map_free(&sc->sc_io_handle);
33249999SWang.Lin@Sun.COM attach_fail1:
33259999SWang.Lin@Sun.COM pci_config_teardown(&sc->sc_cfg_handle);
33269999SWang.Lin@Sun.COM attach_fail0:
33279999SWang.Lin@Sun.COM sc->sc_flags |= SC_OP_INVALID;
33289999SWang.Lin@Sun.COM /* cleanup tx queues */
33299999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_txbuflock);
33309999SWang.Lin@Sun.COM for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
33319999SWang.Lin@Sun.COM if (ARN_TXQ_SETUP(sc, i)) {
33329999SWang.Lin@Sun.COM /* arn_tx_cleanupq(asc, &asc->sc_txq[i]); */
33339999SWang.Lin@Sun.COM mutex_destroy(&((&sc->sc_txq[i])->axq_lock));
33349999SWang.Lin@Sun.COM }
33359999SWang.Lin@Sun.COM }
33369999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_rxbuflock);
33379999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_serial_rw);
33389999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_genlock);
33399999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_resched_lock);
33409999SWang.Lin@Sun.COM #ifdef ARN_IBSS
33419999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_bcbuflock);
33429999SWang.Lin@Sun.COM #endif
33439999SWang.Lin@Sun.COM
33449999SWang.Lin@Sun.COM ddi_soft_state_free(arn_soft_state_p, instance);
33459999SWang.Lin@Sun.COM
33469999SWang.Lin@Sun.COM return (DDI_FAILURE);
33479999SWang.Lin@Sun.COM
33489999SWang.Lin@Sun.COM }
33499999SWang.Lin@Sun.COM
33509999SWang.Lin@Sun.COM /*
33519999SWang.Lin@Sun.COM * Suspend transmit/receive for powerdown
33529999SWang.Lin@Sun.COM */
33539999SWang.Lin@Sun.COM static int
arn_suspend(struct arn_softc * sc)33549999SWang.Lin@Sun.COM arn_suspend(struct arn_softc *sc)
33559999SWang.Lin@Sun.COM {
33569999SWang.Lin@Sun.COM ARN_LOCK(sc);
33579999SWang.Lin@Sun.COM arn_close(sc);
33589999SWang.Lin@Sun.COM ARN_UNLOCK(sc);
33599999SWang.Lin@Sun.COM
33609999SWang.Lin@Sun.COM return (DDI_SUCCESS);
33619999SWang.Lin@Sun.COM }
33629999SWang.Lin@Sun.COM
33639999SWang.Lin@Sun.COM static int32_t
arn_detach(dev_info_t * devinfo,ddi_detach_cmd_t cmd)33649999SWang.Lin@Sun.COM arn_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
33659999SWang.Lin@Sun.COM {
33669999SWang.Lin@Sun.COM struct arn_softc *sc;
33679999SWang.Lin@Sun.COM int i;
33689999SWang.Lin@Sun.COM
33699999SWang.Lin@Sun.COM sc = ddi_get_soft_state(arn_soft_state_p, ddi_get_instance(devinfo));
33709999SWang.Lin@Sun.COM ASSERT(sc != NULL);
33719999SWang.Lin@Sun.COM
33729999SWang.Lin@Sun.COM switch (cmd) {
33739999SWang.Lin@Sun.COM case DDI_DETACH:
33749999SWang.Lin@Sun.COM break;
33759999SWang.Lin@Sun.COM
33769999SWang.Lin@Sun.COM case DDI_SUSPEND:
33779999SWang.Lin@Sun.COM return (arn_suspend(sc));
33789999SWang.Lin@Sun.COM
33799999SWang.Lin@Sun.COM default:
33809999SWang.Lin@Sun.COM return (DDI_FAILURE);
33819999SWang.Lin@Sun.COM }
33829999SWang.Lin@Sun.COM
33839999SWang.Lin@Sun.COM if (mac_disable(sc->sc_isc.ic_mach) != 0)
33849999SWang.Lin@Sun.COM return (DDI_FAILURE);
33859999SWang.Lin@Sun.COM
33869999SWang.Lin@Sun.COM arn_stop_scantimer(sc);
33879999SWang.Lin@Sun.COM arn_stop_caltimer(sc);
33889999SWang.Lin@Sun.COM
33899999SWang.Lin@Sun.COM /* disable interrupts */
33909999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(sc->sc_ah, 0);
33919999SWang.Lin@Sun.COM
33929999SWang.Lin@Sun.COM /*
33939999SWang.Lin@Sun.COM * Unregister from the MAC layer subsystem
33949999SWang.Lin@Sun.COM */
33959999SWang.Lin@Sun.COM (void) mac_unregister(sc->sc_isc.ic_mach);
33969999SWang.Lin@Sun.COM
33979999SWang.Lin@Sun.COM /* free intterrupt resources */
33989999SWang.Lin@Sun.COM ddi_remove_intr(devinfo, 0, sc->sc_iblock);
33999999SWang.Lin@Sun.COM ddi_remove_softintr(sc->sc_softint_id);
34009999SWang.Lin@Sun.COM
34019999SWang.Lin@Sun.COM /*
34029999SWang.Lin@Sun.COM * NB: the order of these is important:
34039999SWang.Lin@Sun.COM * o call the 802.11 layer before detaching the hal to
34049999SWang.Lin@Sun.COM * insure callbacks into the driver to delete global
34059999SWang.Lin@Sun.COM * key cache entries can be handled
34069999SWang.Lin@Sun.COM * o reclaim the tx queue data structures after calling
34079999SWang.Lin@Sun.COM * the 802.11 layer as we'll get called back to reclaim
34089999SWang.Lin@Sun.COM * node state and potentially want to use them
34099999SWang.Lin@Sun.COM * o to cleanup the tx queues the hal is called, so detach
34109999SWang.Lin@Sun.COM * it last
34119999SWang.Lin@Sun.COM */
34129999SWang.Lin@Sun.COM ieee80211_detach(&sc->sc_isc);
34139999SWang.Lin@Sun.COM
34149999SWang.Lin@Sun.COM arn_desc_free(sc);
34159999SWang.Lin@Sun.COM
34169999SWang.Lin@Sun.COM ddi_taskq_destroy(sc->sc_tq);
34179999SWang.Lin@Sun.COM
34189999SWang.Lin@Sun.COM if (!(sc->sc_flags & SC_OP_INVALID))
34199999SWang.Lin@Sun.COM (void) ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
34209999SWang.Lin@Sun.COM
34219999SWang.Lin@Sun.COM /* cleanup tx queues */
34229999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_txbuflock);
34239999SWang.Lin@Sun.COM for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
34249999SWang.Lin@Sun.COM if (ARN_TXQ_SETUP(sc, i)) {
34259999SWang.Lin@Sun.COM arn_tx_cleanupq(sc, &sc->sc_txq[i]);
34269999SWang.Lin@Sun.COM mutex_destroy(&((&sc->sc_txq[i])->axq_lock));
34279999SWang.Lin@Sun.COM }
34289999SWang.Lin@Sun.COM }
34299999SWang.Lin@Sun.COM
34309999SWang.Lin@Sun.COM ath9k_hw_detach(sc->sc_ah);
34319999SWang.Lin@Sun.COM
34329999SWang.Lin@Sun.COM /* free io handle */
34339999SWang.Lin@Sun.COM ddi_regs_map_free(&sc->sc_io_handle);
34349999SWang.Lin@Sun.COM pci_config_teardown(&sc->sc_cfg_handle);
34359999SWang.Lin@Sun.COM
34369999SWang.Lin@Sun.COM /* destroy locks */
34379999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_genlock);
34389999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_serial_rw);
34399999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_rxbuflock);
34409999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_resched_lock);
34419999SWang.Lin@Sun.COM #ifdef ARN_IBSS
34429999SWang.Lin@Sun.COM mutex_destroy(&sc->sc_bcbuflock);
34439999SWang.Lin@Sun.COM #endif
34449999SWang.Lin@Sun.COM
34459999SWang.Lin@Sun.COM ddi_remove_minor_node(devinfo, NULL);
34469999SWang.Lin@Sun.COM ddi_soft_state_free(arn_soft_state_p, ddi_get_instance(devinfo));
34479999SWang.Lin@Sun.COM
34489999SWang.Lin@Sun.COM return (DDI_SUCCESS);
34499999SWang.Lin@Sun.COM }
34509999SWang.Lin@Sun.COM
34519999SWang.Lin@Sun.COM /*
34529999SWang.Lin@Sun.COM * quiesce(9E) entry point.
34539999SWang.Lin@Sun.COM *
34549999SWang.Lin@Sun.COM * This function is called when the system is single-threaded at high
34559999SWang.Lin@Sun.COM * PIL with preemption disabled. Therefore, this function must not be
34569999SWang.Lin@Sun.COM * blocked.
34579999SWang.Lin@Sun.COM *
34589999SWang.Lin@Sun.COM * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
34599999SWang.Lin@Sun.COM * DDI_FAILURE indicates an error condition and should almost never happen.
34609999SWang.Lin@Sun.COM */
34619999SWang.Lin@Sun.COM static int32_t
arn_quiesce(dev_info_t * devinfo)34629999SWang.Lin@Sun.COM arn_quiesce(dev_info_t *devinfo)
34639999SWang.Lin@Sun.COM {
34649999SWang.Lin@Sun.COM struct arn_softc *sc;
34659999SWang.Lin@Sun.COM int i;
34669999SWang.Lin@Sun.COM struct ath_hal *ah;
34679999SWang.Lin@Sun.COM
34689999SWang.Lin@Sun.COM sc = ddi_get_soft_state(arn_soft_state_p, ddi_get_instance(devinfo));
34699999SWang.Lin@Sun.COM
34709999SWang.Lin@Sun.COM if (sc == NULL || (ah = sc->sc_ah) == NULL)
34719999SWang.Lin@Sun.COM return (DDI_FAILURE);
34729999SWang.Lin@Sun.COM
34739999SWang.Lin@Sun.COM /*
34749999SWang.Lin@Sun.COM * Disable interrupts
34759999SWang.Lin@Sun.COM */
34769999SWang.Lin@Sun.COM (void) ath9k_hw_set_interrupts(ah, 0);
34779999SWang.Lin@Sun.COM
34789999SWang.Lin@Sun.COM /*
34799999SWang.Lin@Sun.COM * Disable TX HW
34809999SWang.Lin@Sun.COM */
34819999SWang.Lin@Sun.COM for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
34829999SWang.Lin@Sun.COM if (ARN_TXQ_SETUP(sc, i))
34839999SWang.Lin@Sun.COM (void) ath9k_hw_stoptxdma(ah, sc->sc_txq[i].axq_qnum);
34849999SWang.Lin@Sun.COM }
34859999SWang.Lin@Sun.COM
34869999SWang.Lin@Sun.COM /*
34879999SWang.Lin@Sun.COM * Disable RX HW
34889999SWang.Lin@Sun.COM */
34899999SWang.Lin@Sun.COM ath9k_hw_stoppcurecv(ah);
34909999SWang.Lin@Sun.COM ath9k_hw_setrxfilter(ah, 0);
34919999SWang.Lin@Sun.COM (void) ath9k_hw_stopdmarecv(ah);
34929999SWang.Lin@Sun.COM drv_usecwait(3000);
34939999SWang.Lin@Sun.COM
34949999SWang.Lin@Sun.COM /*
34959999SWang.Lin@Sun.COM * Power down HW
34969999SWang.Lin@Sun.COM */
34979999SWang.Lin@Sun.COM (void) ath9k_hw_phy_disable(ah);
34989999SWang.Lin@Sun.COM
34999999SWang.Lin@Sun.COM return (DDI_SUCCESS);
35009999SWang.Lin@Sun.COM }
35019999SWang.Lin@Sun.COM
35029999SWang.Lin@Sun.COM DDI_DEFINE_STREAM_OPS(arn_dev_ops, nulldev, nulldev, arn_attach, arn_detach,
35039999SWang.Lin@Sun.COM nodev, NULL, D_MP, NULL, arn_quiesce);
35049999SWang.Lin@Sun.COM
35059999SWang.Lin@Sun.COM static struct modldrv arn_modldrv = {
35069999SWang.Lin@Sun.COM &mod_driverops, /* Type of module. This one is a driver */
350711729SWang.Lin@Sun.COM "arn-Atheros 9000 series driver:2.0", /* short description */
35089999SWang.Lin@Sun.COM &arn_dev_ops /* driver specific ops */
35099999SWang.Lin@Sun.COM };
35109999SWang.Lin@Sun.COM
35119999SWang.Lin@Sun.COM static struct modlinkage modlinkage = {
35129999SWang.Lin@Sun.COM MODREV_1, (void *)&arn_modldrv, NULL
35139999SWang.Lin@Sun.COM };
35149999SWang.Lin@Sun.COM
35159999SWang.Lin@Sun.COM int
_info(struct modinfo * modinfop)35169999SWang.Lin@Sun.COM _info(struct modinfo *modinfop)
35179999SWang.Lin@Sun.COM {
35189999SWang.Lin@Sun.COM return (mod_info(&modlinkage, modinfop));
35199999SWang.Lin@Sun.COM }
35209999SWang.Lin@Sun.COM
35219999SWang.Lin@Sun.COM int
_init(void)35229999SWang.Lin@Sun.COM _init(void)
35239999SWang.Lin@Sun.COM {
35249999SWang.Lin@Sun.COM int status;
35259999SWang.Lin@Sun.COM
35269999SWang.Lin@Sun.COM status = ddi_soft_state_init
35279999SWang.Lin@Sun.COM (&arn_soft_state_p, sizeof (struct arn_softc), 1);
35289999SWang.Lin@Sun.COM if (status != 0)
35299999SWang.Lin@Sun.COM return (status);
35309999SWang.Lin@Sun.COM
35319999SWang.Lin@Sun.COM mutex_init(&arn_loglock, NULL, MUTEX_DRIVER, NULL);
35329999SWang.Lin@Sun.COM mac_init_ops(&arn_dev_ops, "arn");
35339999SWang.Lin@Sun.COM status = mod_install(&modlinkage);
35349999SWang.Lin@Sun.COM if (status != 0) {
35359999SWang.Lin@Sun.COM mac_fini_ops(&arn_dev_ops);
35369999SWang.Lin@Sun.COM mutex_destroy(&arn_loglock);
35379999SWang.Lin@Sun.COM ddi_soft_state_fini(&arn_soft_state_p);
35389999SWang.Lin@Sun.COM }
35399999SWang.Lin@Sun.COM
35409999SWang.Lin@Sun.COM return (status);
35419999SWang.Lin@Sun.COM }
35429999SWang.Lin@Sun.COM
35439999SWang.Lin@Sun.COM int
_fini(void)35449999SWang.Lin@Sun.COM _fini(void)
35459999SWang.Lin@Sun.COM {
35469999SWang.Lin@Sun.COM int status;
35479999SWang.Lin@Sun.COM
35489999SWang.Lin@Sun.COM status = mod_remove(&modlinkage);
35499999SWang.Lin@Sun.COM if (status == 0) {
35509999SWang.Lin@Sun.COM mac_fini_ops(&arn_dev_ops);
35519999SWang.Lin@Sun.COM mutex_destroy(&arn_loglock);
35529999SWang.Lin@Sun.COM ddi_soft_state_fini(&arn_soft_state_p);
35539999SWang.Lin@Sun.COM }
35549999SWang.Lin@Sun.COM return (status);
35559999SWang.Lin@Sun.COM }
3556