summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/ath/ath_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/io/ath/ath_main.c')
-rw-r--r--usr/src/uts/common/io/ath/ath_main.c1445
1 files changed, 721 insertions, 724 deletions
diff --git a/usr/src/uts/common/io/ath/ath_main.c b/usr/src/uts/common/io/ath/ath_main.c
index 4add9a873b..936c6cdaf6 100644
--- a/usr/src/uts/common/io/ath/ath_main.c
+++ b/usr/src/uts/common/io/ath/ath_main.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -41,66 +41,57 @@
/*
* Driver for the Atheros Wireless LAN controller.
*
- * The Atheros driver can be devided into 2 parts: H/W related(we called LLD:
- * Low Level Driver) and IEEE80211 protocol related(we called IEEE80211),
- * and each part has several sub modules.
+ * The Atheros driver calls into net80211 module for IEEE80211 protocol
+ * management functionalities. The driver includes a LLD(Low Level Driver)
+ * part to implement H/W related operations.
* The following is the high level structure of ath driver.
* (The arrows between modules indicate function call direction.)
*
*
- * ^ |
- * | | GLD thread
- * | V
- * ================== =========================================
- * |[1] | |[2] |
- * | | | GLD Callback functions registered |
- * | IEEE80211 | ========================= by |
- * | | | | IEEE80211 |
- * | Internal | V | |
- * ========= | |======================== | |
- * |[3] | | Functions | | |
- * | | | | | |
- * |Multi- | ========================================== =================
- * | | ^ | | |
- * |Func | | V V V
- * | | ====================== ------------------------------------
- * |Thread | |[4] | |[5] |
- * | |-->| Functions exported | | IEEE80211 Callback functions |
- * | | | by IEEE80211 | | registered by LLD |
- * ========= ====================== ------------------------------------
- * ^ |
- * | V
- * -------------------------------------------------------------
- * |[6] |
- * | LLD Internal functions |
- * | |
- * -------------------------------------------------------------
- * ^
- * | Software interrupt thread
- * |
+ * |
+ * | GLD thread
+ * V
+ * ================== =========================================
+ * | | |[1] |
+ * | | | GLDv3 Callback functions registered |
+ * | Net80211 | ========================= by |
+ * | module | | | driver |
+ * | | V | |
+ * | |======================== | |
+ * | Functions exported by net80211 | | |
+ * | | | |
+ * ========================================== =================
+ * | |
+ * V |
+ * +----------------------------------+ |
+ * |[2] | |
+ * | Net80211 Callback functions | |
+ * | registered by LLD | |
+ * +----------------------------------+ |
+ * | |
+ * V v
+ * +-----------------------------------------------------------+
+ * |[3] |
+ * | LLD Internal functions |
+ * | |
+ * +-----------------------------------------------------------+
+ * ^
+ * | Software interrupt thread
+ * |
*
- * Modules 1/2/3/4 constitute part IEEE80211, and modules 5/6 constitute LLD.
* The short description of each module is as below:
- * Module 1: IEEE80211 Internal functions, including ieee80211 state
- * machine, convert functions between 802.3 frame and
- * 802.11 frame, and node maintain function, etc.
- * Module 2: GLD callback functions, which are intercepting the calls from
- * GLD to LLD, and adding IEEE80211's mutex protection.
- * Module 3: Multi-func thread, which is responsible for scan timing,
- * rate control timing and calibrate timing.
- * Module 4: Functions exported by IEEE80211, which can be called from
- * other modules.
- * Module 5: IEEE80211 callback functions registered by LLD, which include
- * GLD related callbacks and some other functions needed by
- * IEEE80211.
- * Module 6: LLD Internal functions, which are responsible for allocing
+ * Module 1: GLD callback functions, which are intercepting the calls from
+ * GLD to LLD.
+ * Module 2: Net80211 callback functions registered by LLD, which
+ * calls into LLD for H/W related functions needed by net80211.
+ * Module 3: LLD Internal functions, which are responsible for allocing
* descriptor/buffer, handling interrupt and other H/W
* operations.
*
* All functions are running in 3 types of thread:
* 1. GLD callbacks threads, such as ioctl, intr, etc.
- * 2. Multi-Func thread in IEEE80211 which is responsible for scan,
- * rate control and calibrate.
+ * 2. Clock interruptt thread which is responsible for scan, rate control and
+ * calibration.
* 3. Software Interrupt thread originated in LLD.
*
* The lock strategy is as below:
@@ -117,40 +108,9 @@
* operational data in ath_softc struct and HAL accesses.
* It is acquired by the interupt handler and most "mode-ctrl" routines.
*
- * In ieee80211com struct, isc_genlock is a general lock to protect
- * necessary data and functions in ieee80211_com struct. Some data in
- * ieee802.11_com don't need protection. For example, isc_dev is writen
- * only in ath_attach(), but read in many other functions, so protection
- * is not necessary.
- *
* Any of the locks can be acquired singly, but where multiple
* locks are acquired, they *must* be in the order:
- *
- * isc_genlock >> asc_genlock >> asc_txqlock[i] >>
- * asc_txbuflock >> asc_rxbuflock
- *
- * Note:
- * 1. All the IEEE80211 callback functions(except isc_gld_intr)
- * registered by LLD in module [5] are protected by isc_genlock before
- * calling from IEEE80211.
- * 2. Module [4] have 3 important functions ieee80211_input(),
- * ieee80211_new_state() and _ieee80211_new_state().
- * The functions in module [6] should avoid holding mutex or other locks
- * during the call to ieee80211_input().
- * In particular, the soft interrupt thread that calls ieee80211_input()
- * may in some cases carry out processing that includes sending an outgoing
- * packet, resulting in a call to the driver's ath_mgmt_send() routine.
- * If the ath_mgmt_send() routine were to try to acquire a mutex being held
- * by soft interrupt thread at the time it calls ieee80211_input(),
- * this could result in a panic due to recursive mutex entry.
- * ieee80211_new_state() and _ieee80211_new_state() are almost the same
- * except that the latter function asserts isc_genlock is owned in its entry.
- * so ieee80211_new_state() is only called by ath_bmiss_handler()
- * from soft interrupt handler thread.
- * As the same reason to ieee80211_input, we can't hold any other mutex.
- * 3. *None* of these locks may be held across calls out to the
- * GLD routines gld_recv() in ieee80211_input().
- *
+ * asc_genlock >> asc_txqlock[i] >> asc_txbuflock >> asc_rxbuflock
*/
#include <sys/param.h>
@@ -174,7 +134,7 @@
#include <sys/sunddi.h>
#include <sys/pci.h>
#include <sys/errno.h>
-#include <sys/gld.h>
+#include <sys/mac.h>
#include <sys/dlpi.h>
#include <sys/ethernet.h>
#include <sys/list.h>
@@ -185,11 +145,14 @@
#include <inet/nd.h>
#include <inet/mi.h>
#include <inet/wifi_ioctl.h>
+#include <sys/mac_wifi.h>
#include "ath_hal.h"
#include "ath_impl.h"
#include "ath_aux.h"
#include "ath_rate.h"
+#define ATH_MAX_RSSI 63 /* max rssi */
+
extern void ath_halfix_init(void);
extern void ath_halfix_finit(void);
extern int32_t ath_getset(ath_t *asc, mblk_t *mp, uint32_t cmd);
@@ -230,12 +193,31 @@ static ddi_dma_attr_t dma_attr = {
0 /* dma_attr_flags */
};
-static uint8_t ath_broadcast_addr[] = {
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
-};
-
static kmutex_t ath_loglock;
static void *ath_soft_state_p = NULL;
+static int ath_dwelltime = 150; /* scan interval, ms */
+
+static int ath_m_stat(void *, uint_t, uint64_t *);
+static int ath_m_start(void *);
+static void ath_m_stop(void *);
+static int ath_m_promisc(void *, boolean_t);
+static int ath_m_multicst(void *, boolean_t, const uint8_t *);
+static int ath_m_unicst(void *, const uint8_t *);
+static mblk_t *ath_m_tx(void *, mblk_t *);
+static void ath_m_ioctl(void *, queue_t *, mblk_t *);
+static mac_callbacks_t ath_m_callbacks = {
+ MC_IOCTL,
+ ath_m_stat,
+ ath_m_start,
+ ath_m_stop,
+ ath_m_promisc,
+ ath_m_multicst,
+ ath_m_unicst,
+ ath_m_tx,
+ NULL, /* mc_resources; */
+ ath_m_ioctl,
+ NULL /* mc_getcapab */
+};
/*
* Available debug flags:
@@ -300,6 +282,7 @@ ath_setup_desc(ath_t *asc, struct ath_buf *bf)
ds = bf->bf_desc;
ds->ds_link = bf->bf_daddr;
ds->ds_data = bf->bf_dma.cookie.dmac_address;
+ ds->ds_vdata = bf->bf_dma.mem_va;
ATH_HAL_SETUPRXDESC(asc->asc_ah, ds,
bf->bf_dma.alength, /* buffer size */
0);
@@ -434,7 +417,8 @@ ath_desc_alloc(dev_info_t *devinfo, ath_t *asc)
list_insert_tail(&asc->asc_txbuf_list, bf);
/* alloc DMA memory */
- err = ath_alloc_dma_mem(devinfo, size, &ath_desc_accattr,
+ err = ath_alloc_dma_mem(devinfo, asc->asc_dmabuf_size,
+ &ath_desc_accattr,
DDI_DMA_STREAMING, DDI_DMA_STREAMING, &bf->bf_dma);
if (err != DDI_SUCCESS)
return (err);
@@ -490,16 +474,17 @@ ath_printrxbuf(struct ath_buf *bf, int32_t done)
static void
ath_rx_handler(ath_t *asc)
{
- ieee80211com_t *isc = (ieee80211com_t *)asc;
+ ieee80211com_t *ic = (ieee80211com_t *)asc;
struct ath_buf *bf;
struct ath_hal *ah = asc->asc_ah;
struct ath_desc *ds;
mblk_t *rx_mp;
- struct ieee80211_frame *wh, whbuf;
+ struct ieee80211_frame *wh;
int32_t len, loop = 1;
uint8_t phyerr;
HAL_STATUS status;
HAL_NODE_STATS hal_node_stats;
+ struct ieee80211_node *in;
do {
mutex_enter(&asc->asc_rxbuflock);
@@ -564,7 +549,7 @@ ath_rx_handler(ath_t *asc)
rx_mp->b_wptr += len;
wh = (struct ieee80211_frame *)rx_mp->b_rptr;
- if ((wh->ifrm_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
+ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
IEEE80211_FC0_TYPE_CTL) {
/*
* Ignore control frame received in promisc mode.
@@ -574,32 +559,25 @@ ath_rx_handler(ath_t *asc)
}
/* Remove the CRC at the end of IEEE80211 frame */
rx_mp->b_wptr -= IEEE80211_CRC_LEN;
- if (wh->ifrm_fc[1] & IEEE80211_FC1_WEP) {
- /*
- * WEP is decrypted by hardware. Clear WEP bit
- * and trim WEP header for ieee80211_input().
- */
- wh->ifrm_fc[1] &= ~IEEE80211_FC1_WEP;
- bcopy(wh, &whbuf, sizeof (whbuf));
- /*
- * Remove WEP related fields between
- * header and payload.
- */
- rx_mp->b_rptr += IEEE80211_WEP_IVLEN +
- IEEE80211_WEP_KIDLEN;
- bcopy(&whbuf, rx_mp->b_rptr, sizeof (whbuf));
- /*
- * Remove WEP CRC from the tail.
- */
- rx_mp->b_wptr -= IEEE80211_WEP_CRCLEN;
- }
#ifdef DEBUG
ath_printrxbuf(bf, status == HAL_OK);
#endif /* DEBUG */
- ieee80211_input(isc, rx_mp,
+ /*
+ * Locate the node for sender, track state, and then
+ * pass the (referenced) node up to the 802.11 layer
+ * for its use.
+ */
+ in = ieee80211_find_rxnode(ic, wh);
+
+ /*
+ * Send frame up for processing.
+ */
+ (void) ieee80211_input(ic, rx_mp, in,
ds->ds_rxstat.rs_rssi,
- ds->ds_rxstat.rs_tstamp,
- ds->ds_rxstat.rs_antenna);
+ ds->ds_rxstat.rs_tstamp);
+
+ ieee80211_free_node(in);
+
rx_next:
mutex_enter(&asc->asc_rxbuflock);
list_insert_tail(&asc->asc_rxbuf_list, bf);
@@ -608,8 +586,7 @@ rx_next:
} while (loop);
/* rx signal state monitoring */
- ATH_HAL_RXMONITOR(ah, &hal_node_stats);
- ATH_HAL_RXENA(ah); /* in case of RXEOL */
+ ATH_HAL_RXMONITOR(ah, &hal_node_stats, &asc->asc_curchan);
}
static void
@@ -628,27 +605,28 @@ ath_printtxbuf(struct ath_buf *bf, int done)
/*
* The input parameter mp has following assumption:
- * the first mblk is for ieee80211 header, and there has enough space left
- * for WEP option at the end of this mblk.
- * The continue mblks are for payload.
+ * For data packets, GLDv3 mac_wifi plugin allocates and fills the
+ * ieee80211 header. For management packets, net80211 allocates and
+ * fills the ieee80211 header. In both cases, enough spaces in the
+ * header are left for encryption option.
*/
static int32_t
-ath_xmit(ath_t *asc, struct ieee80211_node *in,
- struct ath_buf *bf, mblk_t *mp, mblk_t *mp_header)
+ath_tx_start(ath_t *asc, struct ieee80211_node *in, struct ath_buf *bf,
+ mblk_t *mp)
{
- ieee80211com_t *isc = (ieee80211com_t *)asc;
+ ieee80211com_t *ic = (ieee80211com_t *)asc;
struct ieee80211_frame *wh;
struct ath_hal *ah = asc->asc_ah;
- uint32_t subtype, flags, ctsduration, antenna;
+ uint32_t subtype, flags, ctsduration;
int32_t keyix, iswep, hdrlen, pktlen, mblen, mbslen, try0;
- uint8_t rix, cix, txrate, ctsrate, *tmp_ptr;
+ uint8_t rix, cix, txrate, ctsrate;
struct ath_desc *ds;
struct ath_txq *txq;
HAL_PKT_TYPE atype;
const HAL_RATE_TABLE *rt;
HAL_BOOL shortPreamble;
- mblk_t *mp0;
struct ath_node *an;
+ caddr_t dest;
/*
* CRC are added by H/W, not encaped by driver,
@@ -656,62 +634,71 @@ ath_xmit(ath_t *asc, struct ieee80211_node *in,
*/
pktlen = IEEE80211_CRC_LEN;
- wh = (struct ieee80211_frame *)mp_header->b_rptr;
- iswep = wh->ifrm_fc[1] & IEEE80211_FC1_WEP;
+ wh = (struct ieee80211_frame *)mp->b_rptr;
+ iswep = wh->i_fc[1] & IEEE80211_FC1_WEP;
keyix = HAL_TXKEYIX_INVALID;
hdrlen = sizeof (struct ieee80211_frame);
- if (iswep) {
- hdrlen += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
- pktlen += IEEE80211_WEP_CRCLEN;
- keyix = isc->isc_wep_txkey;
- }
- tmp_ptr = (uint8_t *)bf->bf_dma.mem_va;
+ if (iswep != 0) {
+ const struct ieee80211_cipher *cip;
+ struct ieee80211_key *k;
- /* Copy 80211 header from mblk to DMA txbuf */
- mblen = mp_header->b_wptr - mp_header->b_rptr;
- bcopy(mp_header->b_rptr, tmp_ptr, mblen);
- tmp_ptr += mblen;
- pktlen += mblen;
- mbslen = mblen;
-
- /*
- * If mp==NULL, then it's a management frame,
- * else it's a data frame.
- */
- if (mp != NULL) {
/*
- * Copy the first mblk to DMA txbuf
- * (this mblk includes ether header).
+ * Construct the 802.11 header+trailer for an encrypted
+ * frame. The only reason this can fail is because of an
+ * unknown or unsupported cipher/key type.
*/
- mblen = mp->b_wptr - mp->b_rptr - sizeof (struct ether_header);
- bcopy(mp->b_rptr + sizeof (struct ether_header),
- tmp_ptr, mblen);
- tmp_ptr += mblen;
- pktlen += mblen;
- mbslen += mblen;
-
- /* Copy subsequent mblks to DMA txbuf */
- for (mp0 = mp->b_cont; mp0 != NULL; mp0 = mp0->b_cont) {
- mblen = mp0->b_wptr - mp0->b_rptr;
- bcopy(mp0->b_rptr, tmp_ptr, mblen);
- tmp_ptr += mblen;
- pktlen += mblen;
- mbslen += mblen;
+ k = ieee80211_crypto_encap(ic, mp);
+ if (k == NULL) {
+ ATH_DEBUG((ATH_DBG_AUX, "crypto_encap failed\n"));
+ /*
+ * This can happen when the key is yanked after the
+ * frame was queued. Just discard the frame; the
+ * 802.11 layer counts failures and provides
+ * debugging/diagnostics.
+ */
+ return (EIO);
}
+ cip = k->wk_cipher;
+ /*
+ * Adjust the packet + header lengths for the crypto
+ * additions and calculate the h/w key index. When
+ * a s/w mic is done the frame will have had any mic
+ * added to it prior to entry so m0->m_pkthdr.len above will
+ * account for it. Otherwise we need to add it to the
+ * packet length.
+ */
+ hdrlen += cip->ic_header;
+ pktlen += cip->ic_header + cip->ic_trailer;
+ if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0)
+ pktlen += cip->ic_miclen;
+ keyix = k->wk_keyix;
+
+ /* packet header may have moved, reset our local pointer */
+ wh = (struct ieee80211_frame *)mp->b_rptr;
}
+ dest = bf->bf_dma.mem_va;
+ for (; mp != NULL; mp = mp->b_cont) {
+ mblen = MBLKL(mp);
+ bcopy(mp->b_rptr, dest, mblen);
+ dest += mblen;
+ }
+ mbslen = dest - bf->bf_dma.mem_va;
+ pktlen += mbslen;
+
bf->bf_in = in;
/* setup descriptors */
ds = bf->bf_desc;
rt = asc->asc_currates;
+ ASSERT(rt != NULL);
/*
* The 802.11 layer marks whether or not we should
* use short preamble based on the current mode and
* negotiated parameters.
*/
- if ((isc->isc_flags & IEEE80211_F_SHPREAMBLE) &&
+ if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
(in->in_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
shortPreamble = AH_TRUE;
asc->asc_stats.ast_tx_shortpre++;
@@ -725,9 +712,9 @@ ath_xmit(ath_t *asc, struct ieee80211_node *in,
* Calculate Atheros packet type from IEEE80211 packet header
* and setup for rate calculations.
*/
- switch (wh->ifrm_fc[0] & IEEE80211_FC0_TYPE_MASK) {
+ switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
case IEEE80211_FC0_TYPE_MGT:
- subtype = wh->ifrm_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
if (subtype == IEEE80211_FC0_SUBTYPE_BEACON)
atype = HAL_PKT_TYPE_BEACON;
else if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
@@ -747,7 +734,7 @@ ath_xmit(ath_t *asc, struct ieee80211_node *in,
break;
case IEEE80211_FC0_TYPE_CTL:
atype = HAL_PKT_TYPE_PSPOLL;
- subtype = wh->ifrm_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+ subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
rix = 0; /* lowest rate */
try0 = ATH_TXMAXTRY;
if (shortPreamble)
@@ -777,10 +764,10 @@ ath_xmit(ath_t *asc, struct ieee80211_node *in,
* Calculate miscellaneous flags.
*/
flags = HAL_TXDESC_CLRDMASK;
- if (IEEE80211_IS_MULTICAST(wh->ifrm_addr1)) {
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
flags |= HAL_TXDESC_NOACK; /* no ack on broad/multicast */
asc->asc_stats.ast_tx_noack++;
- } else if (pktlen > isc->isc_rtsthreshold) {
+ } else if (pktlen > ic->ic_rtsthreshold) {
flags |= HAL_TXDESC_RTSENA; /* RTS based on frame length */
asc->asc_stats.ast_tx_rts++;
}
@@ -790,12 +777,12 @@ ath_xmit(ath_t *asc, struct ieee80211_node *in,
* layer but it lacks sufficient information to calculate it.
*/
if ((flags & HAL_TXDESC_NOACK) == 0 &&
- (wh->ifrm_fc[0] & IEEE80211_FC0_TYPE_MASK) !=
+ (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) !=
IEEE80211_FC0_TYPE_CTL) {
uint16_t dur;
dur = ath_hal_computetxtime(ah, rt, IEEE80211_ACK_SIZE,
rix, shortPreamble);
- *(uint16_t *)wh->ifrm_dur = LE_16(dur);
+ *(uint16_t *)wh->i_dur = LE_16(dur);
}
/*
@@ -832,17 +819,6 @@ ath_xmit(ath_t *asc, struct ieee80211_node *in,
} else
ctsrate = 0;
- /*
- * For now use the antenna on which the last good
- * frame was received on. We assume this field is
- * initialized to 0 which gives us ``auto'' or the
- * ``default'' antenna.
- */
- if (an->an_tx_antenna)
- antenna = an->an_tx_antenna;
- else
- antenna = in->in_recv_hist[in->in_hist_cur].irh_rantenna;
-
if (++txq->axq_intrcnt >= ATH_TXINTR_PERIOD) {
flags |= HAL_TXDESC_INTREQ;
txq->axq_intrcnt = 0;
@@ -857,18 +833,19 @@ ath_xmit(ath_t *asc, struct ieee80211_node *in,
atype, /* Atheros packet type */
MIN(in->in_txpower, 60), /* txpower */
txrate, try0, /* series 0 rate/tries */
- keyix,
- antenna, /* antenna mode */
+ keyix, /* key cache index */
+ an->an_tx_antenna, /* antenna mode */
flags, /* flags */
ctsrate, /* rts/cts rate */
ctsduration); /* rts/cts duration */
+ bf->bf_flags = flags;
ATH_DEBUG((ATH_DBG_SEND, "ath: ath_xmit(): to %s totlen=%d "
"an->an_tx_rate1sp=%d tx_rate2sp=%d tx_rate3sp=%d "
"qnum=%d rix=%d sht=%d dur = %d\n",
- ieee80211_ether_sprintf(wh->ifrm_addr1), mbslen, an->an_tx_rate1sp,
+ ieee80211_macaddr_sprintf(wh->i_addr1), mbslen, an->an_tx_rate1sp,
an->an_tx_rate2sp, an->an_tx_rate3sp,
- txq->axq_qnum, rix, shortPreamble, *(uint16_t *)wh->ifrm_dur));
+ txq->axq_qnum, rix, shortPreamble, *(uint16_t *)wh->i_dur));
/*
* Setup the multi-rate retry state only when we're
@@ -905,94 +882,153 @@ ath_xmit(ath_t *asc, struct ieee80211_node *in,
ATH_HAL_TXSTART(ah, txq->axq_qnum);
+ ic->ic_stats.is_tx_frags++;
+ ic->ic_stats.is_tx_bytes += pktlen;
+
return (0);
}
-
+/*
+ * Transmit a management frame. On failure we reclaim the skbuff.
+ * Note that management frames come directly from the 802.11 layer
+ * and do not honor the send queue flow control. Need to investigate
+ * using priority queueing so management frames can bypass data.
+ */
static int
-ath_gld_send(gld_mac_info_t *gld_p, mblk_t *mp)
+ath_xmit(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
{
- int err;
- ath_t *asc = ATH_STATE(gld_p);
- ieee80211com_t *isc = (ieee80211com_t *)asc;
- struct ieee80211_node *in;
- mblk_t *mp_header;
+ ath_t *asc = (ath_t *)ic;
+ struct ath_hal *ah = asc->asc_ah;
+ struct ieee80211_node *in = NULL;
struct ath_buf *bf = NULL;
+ struct ieee80211_frame *wh;
+ int error = 0;
- /*
- * No data frames go out unless we're associated; this
- * should not happen as the 802.11 layer does not enable
- * the xmit queue until we enter the RUN state.
- */
- if (isc->isc_state != IEEE80211_S_RUN) {
- ATH_DEBUG((ATH_DBG_SEND, "ath: ath_gld_send(): "
- "discard, state %u\n", isc->isc_state));
- asc->asc_stats.ast_tx_discard++;
- return (GLD_NOLINK);
- }
-
- /*
- * Only supports STA mode
- */
- if (isc->isc_opmode != IEEE80211_M_STA)
- return (GLD_NOLINK);
+ ASSERT(mp->b_next == NULL);
- /*
- * Locate AP information, so we can fill MAC address.
- */
- in = isc->isc_bss;
- in->in_inact = 0;
-
- /*
- * Grab a TX buffer.
- */
+ /* Grab a TX buffer */
mutex_enter(&asc->asc_txbuflock);
bf = list_head(&asc->asc_txbuf_list);
-
if (bf != NULL)
list_remove(&asc->asc_txbuf_list, bf);
+ if (list_empty(&asc->asc_txbuf_list)) {
+ ATH_DEBUG((ATH_DBG_SEND, "ath: ath_mgmt_send(): "
+ "stop queue\n"));
+ asc->asc_stats.ast_tx_qstop++;
+ }
mutex_exit(&asc->asc_txbuflock);
-
if (bf == NULL) {
- ATH_DEBUG((ATH_DBG_SEND, "ath: ath_gld_send(): "
- "no TX DMA buffer available: 100 times\n"));
- asc->asc_stats.ast_tx_nobuf++;
-
- mutex_enter(&asc->asc_gld_sched_lock);
- asc->asc_need_gld_sched = 1;
- mutex_exit(&asc->asc_gld_sched_lock);
- return (GLD_NORESOURCES);
+ ATH_DEBUG((ATH_DBG_SEND, "ath: ath_mgmt_send(): discard, "
+ "no xmit buf\n"));
+ ic->ic_stats.is_tx_nobuf++;
+ if ((type & IEEE80211_FC0_TYPE_MASK) ==
+ IEEE80211_FC0_TYPE_DATA) {
+ asc->asc_stats.ast_tx_nobuf++;
+ mutex_enter(&asc->asc_resched_lock);
+ asc->asc_resched_needed = B_TRUE;
+ mutex_exit(&asc->asc_resched_lock);
+ } else {
+ asc->asc_stats.ast_tx_nobufmgt++;
+ freemsg(mp);
+ }
+ return (ENOMEM);
}
- mp_header = ieee80211_fill_header(isc, mp, isc->isc_wep_txkey, in);
- if (mp_header == NULL) {
- /* Push back the TX buf */
- mutex_enter(&asc->asc_txbuflock);
- list_insert_tail(&asc->asc_txbuf_list, bf);
- mutex_exit(&asc->asc_txbuflock);
- return (GLD_FAILURE);
+ wh = (struct ieee80211_frame *)mp->b_rptr;
+
+ /* Locate node */
+ in = ieee80211_find_txnode(ic, wh->i_addr1);
+ if (in == NULL) {
+ error = EIO;
+ goto bad;
}
- err = ath_xmit(asc, in, bf, mp, mp_header);
- freemsg(mp_header);
+ in->in_inact = 0;
+ switch (type & IEEE80211_FC0_TYPE_MASK) {
+ case IEEE80211_FC0_TYPE_DATA:
+ (void) ieee80211_encap(ic, mp, in);
+ break;
+ default:
+ if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+ IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
+ /* fill time stamp */
+ uint64_t tsf;
+ uint32_t *tstamp;
+
+ tsf = ATH_HAL_GETTSF64(ah);
+ /* adjust 100us delay to xmit */
+ tsf += 100;
+ tstamp = (uint32_t *)&wh[1];
+ tstamp[0] = LE_32(tsf & 0xffffffff);
+ tstamp[1] = LE_32(tsf >> 32);
+ }
+ asc->asc_stats.ast_tx_mgmt++;
+ break;
+ }
- if (!err) {
+ error = ath_tx_start(asc, in, bf, mp);
+ if (error != 0) {
+bad:
+ ic->ic_stats.is_tx_failed++;
+ if (bf != NULL) {
+ mutex_enter(&asc->asc_txbuflock);
+ list_insert_tail(&asc->asc_txbuf_list, bf);
+ mutex_exit(&asc->asc_txbuflock);
+ }
+ }
+ if (in != NULL)
+ ieee80211_free_node(in);
+ if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA ||
+ error == 0) {
freemsg(mp);
- return (GLD_SUCCESS);
- } else {
- return (GLD_FAILURE);
}
+
+ return (error);
}
-static void
+static mblk_t *
+ath_m_tx(void *arg, mblk_t *mp)
+{
+ ath_t *asc = arg;
+ ieee80211com_t *ic = (ieee80211com_t *)asc;
+ mblk_t *next;
+
+ /*
+ * No data frames go out unless we're associated; this
+ * should not happen as the 802.11 layer does not enable
+ * the xmit queue until we enter the RUN state.
+ */
+ if (ic->ic_state != IEEE80211_S_RUN) {
+ ATH_DEBUG((ATH_DBG_SEND, "ath: ath_m_tx(): "
+ "discard, state %u\n", ic->ic_state));
+ asc->asc_stats.ast_tx_discard ++;
+ return (mp);
+ }
+
+ while (mp != NULL) {
+ next = mp->b_next;
+ mp->b_next = NULL;
+
+ if (ath_xmit(ic, mp, IEEE80211_FC0_TYPE_DATA) != 0) {
+ mp->b_next = next;
+ break;
+ }
+ mp = next;
+ }
+
+ return (mp);
+
+}
+
+static int
ath_tx_processq(ath_t *asc, struct ath_txq *txq)
{
- ieee80211com_t *isc = (ieee80211com_t *)asc;
+ ieee80211com_t *ic = (ieee80211com_t *)asc;
struct ath_hal *ah = asc->asc_ah;
struct ath_buf *bf;
struct ath_desc *ds;
struct ieee80211_node *in;
- int32_t sr, lr;
+ int32_t sr, lr, nacked = 0;
HAL_STATUS status;
struct ath_node *an;
@@ -1050,22 +1086,39 @@ ath_tx_processq(ath_t *asc, struct ath_txq *txq)
lr = ds->ds_txstat.ts_longretry;
asc->asc_stats.ast_tx_shortretry += sr;
asc->asc_stats.ast_tx_longretry += lr;
- an->an_tx_retr += sr + lr;
+ /*
+ * Hand the descriptor to the rate control algorithm.
+ */
+ if ((ds->ds_txstat.ts_status & HAL_TXERR_FILT) == 0 &&
+ (bf->bf_flags & HAL_TXDESC_NOACK) == 0) {
+ /*
+ * If frame was ack'd update the last rx time
+ * used to workaround phantom bmiss interrupts.
+ */
+ if (ds->ds_txstat.ts_status == 0) {
+ nacked++;
+ an->an_tx_ok++;
+ } else {
+ an->an_tx_err++;
+ }
+ an->an_tx_retr += sr + lr;
+ }
}
bf->bf_in = NULL;
mutex_enter(&asc->asc_txbuflock);
list_insert_tail(&asc->asc_txbuf_list, bf);
mutex_exit(&asc->asc_txbuflock);
- mutex_enter(&asc->asc_gld_sched_lock);
/*
* Reschedule stalled outbound packets
*/
- if (asc->asc_need_gld_sched) {
- asc->asc_need_gld_sched = 0;
- gld_sched(isc->isc_dev);
+ mutex_enter(&asc->asc_resched_lock);
+ if (asc->asc_resched_needed) {
+ asc->asc_resched_needed = B_FALSE;
+ mac_tx_update(ic->ic_mach);
}
- mutex_exit(&asc->asc_gld_sched_lock);
+ mutex_exit(&asc->asc_resched_lock);
}
+ return (nacked);
}
@@ -1079,16 +1132,16 @@ ath_tx_handler(ath_t *asc)
*/
for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
if (ATH_TXQ_SETUP(asc, i)) {
- ath_tx_processq(asc, &asc->asc_txq[i]);
+ (void) ath_tx_processq(asc, &asc->asc_txq[i]);
}
}
}
static struct ieee80211_node *
-ath_node_alloc(ieee80211com_t *isc)
+ath_node_alloc(ieee80211com_t *ic)
{
struct ath_node *an;
- ath_t *asc = (ath_t *)isc;
+ ath_t *asc = (ath_t *)ic;
an = kmem_zalloc(sizeof (struct ath_node), KM_SLEEP);
ath_rate_update(asc, &an->an_node, 0);
@@ -1096,9 +1149,10 @@ ath_node_alloc(ieee80211com_t *isc)
}
static void
-ath_node_free(ieee80211com_t *isc, struct ieee80211_node *in)
+ath_node_free(struct ieee80211_node *in)
{
- ath_t *asc = (ath_t *)isc;
+ ieee80211com_t *ic = in->in_ic;
+ ath_t *asc = (ath_t *)ic;
struct ath_buf *bf;
struct ath_txq *txq;
int32_t i;
@@ -1117,93 +1171,40 @@ ath_node_free(ieee80211com_t *isc, struct ieee80211_node *in)
mutex_exit(&txq->axq_lock);
}
}
+ ic->ic_node_cleanup(in);
kmem_free(in, sizeof (struct ath_node));
}
static void
-ath_node_copy(struct ieee80211_node *dst, const struct ieee80211_node *src)
+ath_next_scan(void *arg)
{
- bcopy(src, dst, sizeof (struct ieee80211_node));
+ ieee80211com_t *ic = arg;
+ ath_t *asc = (ath_t *)ic;
+
+ asc->asc_scan_timer = 0;
+ if (ic->ic_state == IEEE80211_S_SCAN) {
+ asc->asc_scan_timer = timeout(ath_next_scan, (void *)asc,
+ drv_usectohz(ath_dwelltime * 1000));
+ ieee80211_next_scan(ic);
+ }
}
-/*
- * Transmit a management frame. On failure we reclaim the skbuff.
- * Note that management frames come directly from the 802.11 layer
- * and do not honor the send queue flow control. Need to investigate
- * using priority queueing so management frames can bypass data.
- */
-static int32_t
-ath_mgmt_send(ieee80211com_t *isc, mblk_t *mp)
+static void
+ath_stop_scantimer(ath_t *asc)
{
- ath_t *asc = (ath_t *)isc;
- struct ath_hal *ah = asc->asc_ah;
- struct ieee80211_node *in;
- struct ath_buf *bf = NULL;
- struct ieee80211_frame *wh;
- int32_t error = 0;
-
- /* Grab a TX buffer */
- mutex_enter(&asc->asc_txbuflock);
- bf = list_head(&asc->asc_txbuf_list);
- if (bf != NULL)
- list_remove(&asc->asc_txbuf_list, bf);
- if (list_empty(&asc->asc_txbuf_list)) {
- ATH_DEBUG((ATH_DBG_SEND, "ath: ath_mgmt_send(): "
- "stop queue\n"));
- asc->asc_stats.ast_tx_qstop++;
- }
- mutex_exit(&asc->asc_txbuflock);
- if (bf == NULL) {
- ATH_DEBUG((ATH_DBG_SEND, "ath: ath_mgmt_send(): discard, "
- "no xmit buf\n"));
- asc->asc_stats.ast_tx_nobufmgt++;
- goto bad;
- }
- wh = (struct ieee80211_frame *)mp->b_rptr;
- if ((wh->ifrm_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
- IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
- /* fill time stamp */
- uint64_t tsf;
- uint32_t *tstamp;
-
- tsf = ATH_HAL_GETTSF64(ah);
- /* adjust 100us delay to xmit */
- tsf += 100;
- tstamp = (uint32_t *)&wh[1];
- tstamp[0] = LE_32(tsf & 0xffffffff);
- tstamp[1] = LE_32(tsf >> 32);
- }
- /*
- * Locate node state. When operating
- * in station mode we always use ic_bss.
- */
- if (isc->isc_opmode != IEEE80211_M_STA) {
- in = ieee80211_find_node(isc, wh->ifrm_addr1);
- if (in == NULL)
- in = isc->isc_bss;
- } else
- in = isc->isc_bss;
+ timeout_id_t tmp_id = 0;
- error = ath_xmit(asc, in, bf, NULL, mp);
- if (error == 0) {
- asc->asc_stats.ast_tx_mgmt++;
- freemsg(mp);
- return (0);
+ while ((asc->asc_scan_timer != 0) && (tmp_id != asc->asc_scan_timer)) {
+ tmp_id = asc->asc_scan_timer;
+ (void) untimeout(tmp_id);
}
-bad:
- if (bf != NULL) {
- mutex_enter(&asc->asc_txbuflock);
- list_insert_tail(&asc->asc_txbuf_list, bf);
- mutex_exit(&asc->asc_txbuflock);
- }
- freemsg(mp);
- return (error);
+ asc->asc_scan_timer = 0;
}
static int32_t
-ath_new_state(ieee80211com_t *isc, enum ieee80211_state nstate)
+ath_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
{
- ath_t *asc = (ath_t *)isc;
+ ath_t *asc = (ath_t *)ic;
struct ath_hal *ah = asc->asc_ah;
struct ieee80211_node *in;
int32_t i, error;
@@ -1218,36 +1219,44 @@ ath_new_state(ieee80211com_t *isc, enum ieee80211_state nstate)
HAL_LED_ASSOC, /* IEEE80211_S_ASSOC */
HAL_LED_RUN, /* IEEE80211_S_RUN */
};
- if (asc->asc_invalid == 1)
+ if (!ATH_IS_RUNNING(asc))
return (0);
- ostate = isc->isc_state;
+ ostate = ic->ic_state;
+ if (nstate != IEEE80211_S_SCAN)
+ ath_stop_scantimer(asc);
+ ATH_LOCK(asc);
ATH_HAL_SETLEDSTATE(ah, leds[nstate]); /* set LED */
if (nstate == IEEE80211_S_INIT) {
asc->asc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
- ATH_HAL_INTRSET(ah, asc->asc_imask);
- error = 0; /* cheat + use error return */
- goto bad;
+ ATH_HAL_INTRSET(ah, asc->asc_imask &~ HAL_INT_GLOBAL);
+ ATH_UNLOCK(asc);
+ goto done;
+ }
+ in = ic->ic_bss;
+ error = ath_chan_set(asc, ic->ic_curchan);
+ if (error != 0) {
+ if (nstate != IEEE80211_S_SCAN) {
+ ATH_UNLOCK(asc);
+ ieee80211_reset_chan(ic);
+ goto bad;
+ }
}
- in = isc->isc_bss;
- error = ath_chan_set(asc, in->in_chan);
- if (error != 0)
- goto bad;
rfilt = ath_calcrxfilter(asc);
if (nstate == IEEE80211_S_SCAN)
- bssid = isc->isc_macaddr;
+ bssid = ic->ic_macaddr;
else
bssid = in->in_bssid;
ATH_HAL_SETRXFILTER(ah, rfilt);
- if (nstate == IEEE80211_S_RUN && isc->isc_opmode != IEEE80211_M_IBSS)
+ if (nstate == IEEE80211_S_RUN && ic->ic_opmode != IEEE80211_M_IBSS)
ATH_HAL_SETASSOCID(ah, bssid, in->in_associd);
else
ATH_HAL_SETASSOCID(ah, bssid, 0);
- if (isc->isc_flags & IEEE80211_F_WEPON) {
+ if (ic->ic_flags & IEEE80211_F_PRIVACY) {
for (i = 0; i < IEEE80211_WEP_NKID; i++) {
if (ATH_HAL_KEYISVALID(ah, i))
ATH_HAL_KEYSETMAC(ah, i, bssid);
@@ -1267,7 +1276,7 @@ ath_new_state(ieee80211com_t *isc, enum ieee80211_state nstate)
*/
ath_rate_ctl_reset(asc, nstate);
- if (nstate == IEEE80211_S_RUN) {
+ if (nstate == IEEE80211_S_RUN && (ostate != IEEE80211_S_RUN)) {
nvlist_t *attr_list = NULL;
sysevent_id_t eid;
int32_t err = 0;
@@ -1277,11 +1286,11 @@ ath_new_state(ieee80211com_t *isc, enum ieee80211_state nstate)
ATH_DEBUG((ATH_DBG_80211, "ath: ath new state(RUN): "
"ic_flags=0x%08x iv=%d"
" bssid=%s capinfo=0x%04x chan=%d\n",
- isc->isc_flags,
+ ic->ic_flags,
in->in_intval,
- ieee80211_ether_sprintf(in->in_bssid),
+ ieee80211_macaddr_sprintf(in->in_bssid),
in->in_capinfo,
- ieee80211_chan2ieee(isc, in->in_chan)));
+ ieee80211_chan2ieee(ic, in->in_chan)));
(void) sprintf(str_value, "%s%s%d", "-i ",
ddi_driver_name(asc->asc_dev),
@@ -1304,7 +1313,23 @@ ath_new_state(ieee80211com_t *isc, enum ieee80211_state nstate)
}
}
- return (0);
+ ATH_UNLOCK(asc);
+done:
+ /*
+ * Invoke the parent method to complete the work.
+ */
+ error = asc->asc_newstate(ic, nstate, arg);
+ /*
+ * Finally, start any timers.
+ */
+ if (nstate == IEEE80211_S_RUN) {
+ ieee80211_start_watchdog(ic, 1);
+ } else if ((nstate == IEEE80211_S_SCAN) && (ostate != nstate)) {
+ /* start ap/neighbor scan timer */
+ ASSERT(asc->asc_scan_timer == 0);
+ asc->asc_scan_timer = timeout(ath_next_scan, (void *)asc,
+ drv_usectohz(ath_dwelltime * 1000));
+ }
bad:
return (error);
}
@@ -1314,23 +1339,13 @@ bad:
* for temperature/environment changes.
*/
static void
-ath_calibrate(ieee80211com_t *isc)
+ath_calibrate(ath_t *asc)
{
- ath_t *asc = (ath_t *)isc;
struct ath_hal *ah = asc->asc_ah;
- struct ieee80211channel *ch;
- HAL_CHANNEL hchan;
+ HAL_BOOL iqcaldone;
asc->asc_stats.ast_per_cal++;
- /*
- * Convert to a HAL channel description with the flags
- * constrained to reflect the current operating mode.
- */
- ch = isc->isc_ibss_chan;
- hchan.channel = ch->ich_freq;
- hchan.channelFlags = ath_chan2flags(isc, ch);
-
if (ATH_HAL_GETRFGAIN(ah) == HAL_RFGAIN_NEED_CHANGE) {
/*
* Rfgain is out of bounds, reset the chip
@@ -1339,29 +1354,77 @@ ath_calibrate(ieee80211com_t *isc)
ATH_DEBUG((ATH_DBG_HAL, "ath: ath_calibrate(): "
"Need change RFgain\n"));
asc->asc_stats.ast_per_rfgain++;
- ath_reset(asc);
+ (void) ath_reset(&asc->asc_isc);
}
- if (!ATH_HAL_CALIBRATE(ah, &hchan)) {
+ if (!ATH_HAL_CALIBRATE(ah, &asc->asc_curchan, &iqcaldone)) {
ATH_DEBUG((ATH_DBG_HAL, "ath: ath_calibrate(): "
"calibration of channel %u failed\n",
- ch->ich_freq));
+ asc->asc_curchan.channel));
asc->asc_stats.ast_per_calfail++;
}
}
+static void
+ath_watchdog(void *arg)
+{
+ ath_t *asc = arg;
+ ieee80211com_t *ic = &asc->asc_isc;
+ int ntimer = 0;
+
+ ATH_LOCK(asc);
+ ic->ic_watchdog_timer = 0;
+ if (!ATH_IS_RUNNING(asc)) {
+ ATH_UNLOCK(asc);
+ return;
+ }
+
+ if (ic->ic_state == IEEE80211_S_RUN) {
+ /* periodic recalibration */
+ ath_calibrate(asc);
+
+ /*
+ * Start the background rate control thread if we
+ * are not configured to use a fixed xmit rate.
+ */
+ if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
+ asc->asc_stats.ast_rate_calls ++;
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ ath_rate_ctl(ic, ic->ic_bss);
+ else
+ ieee80211_iterate_nodes(&ic->ic_sta,
+ ath_rate_cb, asc);
+ }
+
+ ntimer = 1;
+ }
+ ATH_UNLOCK(asc);
+
+ ieee80211_watchdog(ic);
+ if (ntimer != 0)
+ ieee80211_start_watchdog(ic, ntimer);
+}
+
static uint_t
-ath_gld_intr(gld_mac_info_t *gld_p)
+ath_intr(caddr_t arg)
{
- ath_t *asc = ATH_STATE(gld_p);
+ ath_t *asc = (ath_t *)arg;
struct ath_hal *ah = asc->asc_ah;
HAL_INT status;
- enum ieee80211_state isc_state;
- ieee80211com_t *isc = (ieee80211com_t *)asc;
+ ieee80211com_t *ic = (ieee80211com_t *)asc;
- mutex_enter(&asc->asc_genlock);
+ ATH_LOCK(asc);
+
+ if (!ATH_IS_RUNNING(asc)) {
+ /*
+ * The hardware is not ready/present, don't touch anything.
+ * Note this can happen early on if the IRQ is shared.
+ */
+ ATH_UNLOCK(asc);
+ return (DDI_INTR_UNCLAIMED);
+ }
if (!ATH_HAL_INTRPEND(ah)) { /* shared irq, not for us */
- mutex_exit(&asc->asc_genlock);
+ ATH_UNLOCK(asc);
return (DDI_INTR_UNCLAIMED);
}
@@ -1369,11 +1432,9 @@ ath_gld_intr(gld_mac_info_t *gld_p)
status &= asc->asc_imask;
if (status & HAL_INT_FATAL) {
asc->asc_stats.ast_hardware++;
- mutex_exit(&asc->asc_genlock);
goto reset;
} else if (status & HAL_INT_RXORN) {
asc->asc_stats.ast_rxorn++;
- mutex_exit(&asc->asc_genlock);
goto reset;
} else {
if (status & HAL_INT_RXEOL) {
@@ -1384,6 +1445,7 @@ ath_gld_intr(gld_mac_info_t *gld_p)
asc->asc_stats.ast_txurn++;
ATH_HAL_UPDATETXTRIGLEVEL(ah, AH_TRUE);
}
+
if (status & HAL_INT_RX) {
asc->asc_rx_pend = 1;
ddi_trigger_softintr(asc->asc_softint_id);
@@ -1391,19 +1453,15 @@ ath_gld_intr(gld_mac_info_t *gld_p)
if (status & HAL_INT_TX) {
ath_tx_handler(asc);
}
-
- mutex_exit(&asc->asc_genlock);
+ ATH_UNLOCK(asc);
if (status & HAL_INT_SWBA) {
/* This will occur only in Host-AP or Ad-Hoc mode */
return (DDI_INTR_CLAIMED);
}
if (status & HAL_INT_BMISS) {
- mutex_enter(&isc->isc_genlock);
- isc_state = isc->isc_state;
- mutex_exit(&isc->isc_genlock);
- if (isc_state == IEEE80211_S_RUN) {
- (void) ieee80211_new_state(isc,
+ if (ic->ic_state == IEEE80211_S_RUN) {
+ (void) ieee80211_new_state(ic,
IEEE80211_S_ASSOC, -1);
}
}
@@ -1411,9 +1469,8 @@ ath_gld_intr(gld_mac_info_t *gld_p)
return (DDI_INTR_CLAIMED);
reset:
- mutex_enter(&isc->isc_genlock);
- ath_reset(asc);
- mutex_exit(&isc->isc_genlock);
+ (void) ath_reset(ic);
+ ATH_UNLOCK(asc);
return (DDI_INTR_CLAIMED);
}
@@ -1426,14 +1483,14 @@ ath_softint_handler(caddr_t data)
* Check if the soft interrupt is triggered by another
* driver at the same level.
*/
- mutex_enter(&asc->asc_genlock);
+ ATH_LOCK(asc);
if (asc->asc_rx_pend) { /* Soft interrupt for this driver */
asc->asc_rx_pend = 0;
- mutex_exit(&asc->asc_genlock);
- ath_rx_handler((ath_t *)data);
+ ATH_UNLOCK(asc);
+ ath_rx_handler(asc);
return (DDI_INTR_CLAIMED);
}
- mutex_exit(&asc->asc_genlock);
+ ATH_UNLOCK(asc);
return (DDI_INTR_UNCLAIMED);
}
@@ -1448,56 +1505,69 @@ ath_softint_handler(caddr_t data)
* and to reset the hardware when rf gain settings must be reset.
*/
-static int
-ath_gld_reset(gld_mac_info_t *gld_p)
-{
- ath_t *asc = ATH_STATE(gld_p);
-
- ath_reset(asc);
- return (GLD_SUCCESS);
-}
-
-
-static int
-ath_gld_stop(gld_mac_info_t *gld_p)
+static void
+ath_stop_locked(ath_t *asc)
{
- ath_t *asc = ATH_STATE(gld_p);
- ieee80211com_t *isc = (ieee80211com_t *)asc;
+ ieee80211com_t *ic = (ieee80211com_t *)asc;
struct ath_hal *ah = asc->asc_ah;
- (void) _ieee80211_new_state(isc, IEEE80211_S_INIT, -1);
+ ATH_LOCK_ASSERT(asc);
+ /*
+ * Shutdown the hardware and driver:
+ * reset 802.11 state machine
+ * turn off timers
+ * disable interrupts
+ * turn off the radio
+ * clear transmit machinery
+ * clear receive machinery
+ * drain and release tx queues
+ * reclaim beacon resources
+ * power down hardware
+ *
+ * Note that some of this work is not possible if the
+ * hardware is gone (invalid).
+ */
+ ATH_UNLOCK(asc);
+ ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+ ieee80211_stop_watchdog(ic);
+ ATH_LOCK(asc);
ATH_HAL_INTRSET(ah, 0);
ath_draintxq(asc);
- if (! asc->asc_invalid)
+ if (ATH_IS_RUNNING(asc)) {
ath_stoprecv(asc);
- else
+ ATH_HAL_PHYDISABLE(ah);
+ } else {
asc->asc_rxlink = NULL;
- ATH_HAL_SETPOWER(ah, HAL_PM_FULL_SLEEP, 0);
+ }
+}
- asc->asc_invalid = 1;
+static void
+ath_m_stop(void *arg)
+{
+ ath_t *asc = arg;
+ struct ath_hal *ah = asc->asc_ah;
- return (GLD_SUCCESS);
+ ATH_LOCK(asc);
+ ath_stop_locked(asc);
+ ATH_HAL_SETPOWER(ah, HAL_PM_AWAKE);
+ asc->asc_invalid = 1;
+ ATH_UNLOCK(asc);
}
int
-ath_gld_start(gld_mac_info_t *gld_p)
+ath_m_start(void *arg)
{
- int ret;
- ath_t *asc = ATH_STATE(gld_p);
- ieee80211com_t *isc = (ieee80211com_t *)asc;
- struct ieee80211_node *in;
- enum ieee80211_phymode mode;
+ ath_t *asc = arg;
+ ieee80211com_t *ic = (ieee80211com_t *)asc;
struct ath_hal *ah = asc->asc_ah;
HAL_STATUS status;
- HAL_CHANNEL hchan;
+ ATH_LOCK(asc);
/*
* Stop anything previously setup. This is safe
* whether this is the first time through or not.
*/
- ret = ath_gld_stop(gld_p);
- if (ret != GLD_SUCCESS)
- return (ret);
+ ath_stop_locked(asc);
/*
* The basic interface to setting the hardware in a good
@@ -1506,25 +1576,17 @@ ath_gld_start(gld_mac_info_t *gld_p)
* be followed by initialization of the appropriate bits
* and then setup of the interrupt mask.
*/
- hchan.channel = isc->isc_ibss_chan->ich_freq;
- hchan.channelFlags = ath_chan2flags(isc, isc->isc_ibss_chan);
- if (!ATH_HAL_RESET(ah, (HAL_OPMODE)isc->isc_opmode,
- &hchan, AH_FALSE, &status)) {
- ATH_DEBUG((ATH_DBG_HAL, "ath: ath_gld_start(): "
- "unable to reset hardware, hal status %u\n", status));
- return (GLD_FAILURE);
+ asc->asc_curchan.channel = ic->ic_curchan->ich_freq;
+ asc->asc_curchan.channelFlags = ath_chan2flags(ic, ic->ic_curchan);
+ if (!ATH_HAL_RESET(ah, (HAL_OPMODE)ic->ic_opmode,
+ &asc->asc_curchan, AH_FALSE, &status)) {
+ ATH_DEBUG((ATH_DBG_HAL, "ath: ath_m_start(): "
+ "reset hardware failed, hal status %u\n", status));
+ ATH_UNLOCK(asc);
+ return (ENOTACTIVE);
}
- /*
- * Setup the hardware after reset: the key cache
- * is filled as needed and the receive engine is
- * set going. Frame transmit is handled entirely
- * in the frame output path; there's nothing to do
- * here except setup the interrupt mask.
- */
- ath_initkeytable(asc);
- if (ath_startrecv(asc))
- return (GLD_FAILURE);
+ (void) ath_startrecv(asc);
/*
* Enable interrupts.
@@ -1534,27 +1596,24 @@ ath_gld_start(gld_mac_info_t *gld_p)
| HAL_INT_FATAL | HAL_INT_GLOBAL;
ATH_HAL_INTRSET(ah, asc->asc_imask);
- isc->isc_state = IEEE80211_S_INIT;
+ ic->ic_state = IEEE80211_S_INIT;
/*
* The hardware should be ready to go now so it's safe
* to kick the 802.11 state machine as it's likely to
* immediately call back to us to send mgmt frames.
*/
- in = isc->isc_bss;
- in->in_chan = isc->isc_ibss_chan;
- mode = ieee80211_chan2mode(isc, in->in_chan);
- if (mode != asc->asc_curmode)
- ath_setcurmode(asc, mode);
+ ath_chan_change(asc, ic->ic_curchan);
asc->asc_invalid = 0;
- return (GLD_SUCCESS);
+ ATH_UNLOCK(asc);
+ return (0);
}
-static int32_t
-ath_gld_saddr(gld_mac_info_t *gld_p, unsigned char *macaddr)
+static int
+ath_m_unicst(void *arg, const uint8_t *macaddr)
{
- ath_t *asc = ATH_STATE(gld_p);
+ ath_t *asc = arg;
struct ath_hal *ah = asc->asc_ah;
ATH_DEBUG((ATH_DBG_GLD, "ath: ath_gld_saddr(): "
@@ -1562,54 +1621,50 @@ ath_gld_saddr(gld_mac_info_t *gld_p, unsigned char *macaddr)
macaddr[0], macaddr[1], macaddr[2],
macaddr[3], macaddr[4], macaddr[5]));
- IEEE80211_ADDR_COPY(asc->asc_isc.isc_macaddr, macaddr);
- ATH_HAL_SETMAC(ah, asc->asc_isc.isc_macaddr);
+ ATH_LOCK(asc);
+ IEEE80211_ADDR_COPY(asc->asc_isc.ic_macaddr, macaddr);
+ ATH_HAL_SETMAC(ah, asc->asc_isc.ic_macaddr);
- ath_reset(asc);
- return (GLD_SUCCESS);
+ (void) ath_reset(&asc->asc_isc);
+ ATH_UNLOCK(asc);
+ return (0);
}
static int
-ath_gld_set_promiscuous(gld_mac_info_t *macinfo, int mode)
+ath_m_promisc(void *arg, boolean_t on)
{
- ath_t *asc = ATH_STATE(macinfo);
+ ath_t *asc = arg;
struct ath_hal *ah = asc->asc_ah;
uint32_t rfilt;
+ ATH_LOCK(asc);
rfilt = ATH_HAL_GETRXFILTER(ah);
- switch (mode) {
- case GLD_MAC_PROMISC_PHYS:
- ATH_HAL_SETRXFILTER(ah, rfilt | HAL_RX_FILTER_PROM);
- break;
- case GLD_MAC_PROMISC_MULTI:
- rfilt |= HAL_RX_FILTER_MCAST;
+ if (on)
+ rfilt |= HAL_RX_FILTER_PROM;
+ else
rfilt &= ~HAL_RX_FILTER_PROM;
- ATH_HAL_SETRXFILTER(ah, rfilt);
- break;
- case GLD_MAC_PROMISC_NONE:
- ATH_HAL_SETRXFILTER(ah, rfilt & (~HAL_RX_FILTER_PROM));
- break;
- default:
- break;
- }
+ ATH_HAL_SETRXFILTER(ah, rfilt);
+ ATH_UNLOCK(asc);
- return (GLD_SUCCESS);
+ return (0);
}
static int
-ath_gld_set_multicast(gld_mac_info_t *macinfo, uchar_t *mca, int flag)
+ath_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
{
+ ath_t *asc = arg;
+ struct ath_hal *ah = asc->asc_ah;
uint32_t mfilt[2], val, rfilt;
uint8_t pos;
- ath_t *asc = ATH_STATE(macinfo);
- struct ath_hal *ah = asc->asc_ah;
+ ATH_LOCK(asc);
rfilt = ATH_HAL_GETRXFILTER(ah);
/* disable multicast */
- if (flag == GLD_MULTI_DISABLE) {
+ if (!add) {
ATH_HAL_SETRXFILTER(ah, rfilt & (~HAL_RX_FILTER_MCAST));
- return (GLD_SUCCESS);
+ ATH_UNLOCK(asc);
+ return (0);
}
/* enable multicast */
@@ -1626,117 +1681,104 @@ ath_gld_set_multicast(gld_mac_info_t *macinfo, uchar_t *mca, int flag)
mfilt[pos / 32] |= (1 << (pos % 32));
ATH_HAL_SETMCASTFILTER(ah, mfilt[0], mfilt[1]);
- return (GLD_SUCCESS);
+ ATH_UNLOCK(asc);
+ return (0);
}
static void
-ath_wlan_ioctl(ath_t *asc, queue_t *wq, mblk_t *mp, uint32_t cmd)
+ath_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
{
-
- struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
- uint32_t len, ret;
- mblk_t *mp1;
-
- /* sanity check */
- if (iocp->ioc_count == 0 || !(mp1 = mp->b_cont)) {
- miocnak(wq, mp, 0, EINVAL);
- return;
- }
-
- /* assuming single data block */
- if (mp1->b_cont) {
- freemsg(mp1->b_cont);
- mp1->b_cont = NULL;
+ ath_t *asc = arg;
+ int32_t err;
+
+ err = ieee80211_ioctl(&asc->asc_isc, wq, mp);
+ ATH_LOCK(asc);
+ if (err == ENETRESET) {
+ if (ATH_IS_RUNNING(asc)) {
+ ATH_UNLOCK(asc);
+ (void) ath_m_start(asc);
+ (void) ieee80211_new_state(&asc->asc_isc,
+ IEEE80211_S_SCAN, -1);
+ ATH_LOCK(asc);
+ }
}
-
- /* we will overwrite everything */
- mp1->b_wptr = mp1->b_rptr;
-
- ret = ath_getset(asc, mp1, cmd);
-
- len = msgdsize(mp1);
-
- miocack(wq, mp, len, ret);
+ ATH_UNLOCK(asc);
}
static int
-ath_gld_ioctl(gld_mac_info_t *gld_p, queue_t *wq, mblk_t *mp)
+ath_m_stat(void *arg, uint_t stat, uint64_t *val)
{
- struct iocblk *iocp;
- int32_t cmd, err;
- ath_t *asc = ATH_STATE(gld_p);
- boolean_t need_privilege;
+ ath_t *asc = arg;
+ ieee80211com_t *ic = (ieee80211com_t *)asc;
+ struct ieee80211_node *in = ic->ic_bss;
+ struct ieee80211_rateset *rs = &in->in_rates;
- /*
- * Validate the command before bothering with the mutexen ...
- */
- iocp = (struct iocblk *)mp->b_rptr;
- cmd = iocp->ioc_cmd;
- need_privilege = B_TRUE;
- switch (cmd) {
- case WLAN_SET_PARAM:
- case WLAN_COMMAND:
+ ATH_LOCK(asc);
+ switch (stat) {
+ case MAC_STAT_IFSPEED:
+ *val = (rs->ir_rates[in->in_txrate] & IEEE80211_RATE_VAL) / 2 *
+ 1000000ull;
+ break;
+ case MAC_STAT_NOXMTBUF:
+ *val = asc->asc_stats.ast_tx_nobuf +
+ asc->asc_stats.ast_tx_nobufmgt;
break;
- case WLAN_GET_PARAM:
- need_privilege = B_FALSE;
+ case MAC_STAT_IERRORS:
+ *val = asc->asc_stats.ast_rx_tooshort;
break;
+ case MAC_STAT_OERRORS:
+ *val = asc->asc_stats.ast_tx_fifoerr +
+ asc->asc_stats.ast_tx_xretries;
+ break;
+ case MAC_STAT_RBYTES:
+ *val = ic->ic_stats.is_rx_bytes;
+ break;
+ case MAC_STAT_IPACKETS:
+ *val = ic->ic_stats.is_rx_frags;
+ break;
+ case MAC_STAT_OBYTES:
+ *val = ic->ic_stats.is_tx_bytes;
+ break;
+ case MAC_STAT_OPACKETS:
+ *val = ic->ic_stats.is_tx_frags;
+ break;
+ case WIFI_STAT_TX_FAILED:
+ *val = asc->asc_stats.ast_tx_fifoerr +
+ asc->asc_stats.ast_tx_xretries;
+ break;
+ case WIFI_STAT_TX_RETRANS:
+ *val = asc->asc_stats.ast_tx_xretries;
+ break;
+ case WIFI_STAT_FCS_ERRORS:
+ *val = asc->asc_stats.ast_rx_crcerr;
+ break;
+ case WIFI_STAT_WEP_ERRORS:
+ *val = asc->asc_stats.ast_rx_badcrypt;
+ break;
+ case WIFI_STAT_TX_FRAGS:
+ case WIFI_STAT_MCAST_TX:
+ case WIFI_STAT_RTS_SUCCESS:
+ case WIFI_STAT_RTS_FAILURE:
+ case WIFI_STAT_ACK_FAILURE:
+ case WIFI_STAT_RX_FRAGS:
+ case WIFI_STAT_MCAST_RX:
+ case WIFI_STAT_RX_DUPS:
+ ATH_UNLOCK(asc);
+ return (ieee80211_stat(ic, stat, val));
default:
- ATH_DEBUG((ATH_DBG_GLD, "ath: ath_gld_ioctl(): "
- "unknown cmd 0x%x", cmd));
- miocnak(wq, mp, 0, EINVAL);
- return (GLD_SUCCESS);
- }
-
- if (need_privilege) {
- /*
- * Check for specific net_config privilege on Solaris 10+.
- * Otherwise just check for root access ...
- */
- if (secpolicy_net_config != NULL)
- err = secpolicy_net_config(iocp->ioc_cr, B_FALSE);
- else
- err = drv_priv(iocp->ioc_cr);
- if (err != 0) {
- miocnak(wq, mp, 0, err);
- return (GLD_SUCCESS);
- }
+ ATH_UNLOCK(asc);
+ return (ENOTSUP);
}
+ ATH_UNLOCK(asc);
- ath_wlan_ioctl(asc, wq, mp, cmd);
- return (GLD_SUCCESS);
-}
-
-static int
-ath_gld_gstat(gld_mac_info_t *gld_p, struct gld_stats *glds_p)
-{
- ath_t *asc = ATH_STATE(gld_p);
- ieee80211com_t *isc = (ieee80211com_t *)asc;
- struct ieee80211_node *in = isc->isc_bss;
- struct ath_node *an = ATH_NODE(in);
- struct ieee80211_rateset *rs = &in->in_rates;
-
- glds_p->glds_crc = asc->asc_stats.ast_rx_crcerr;
- glds_p->glds_multircv = 0;
- glds_p->glds_multixmt = 0;
- glds_p->glds_excoll = 0;
- glds_p->glds_xmtretry = an->an_tx_retr;
- glds_p->glds_defer = 0;
- glds_p->glds_noxmtbuf = asc->asc_stats.ast_tx_nobuf;
- glds_p->glds_norcvbuf = asc->asc_stats.ast_rx_fifoerr;
- glds_p->glds_short = asc->asc_stats.ast_rx_tooshort;
- glds_p->glds_missed = asc->asc_stats.ast_rx_badcrypt;
- glds_p->glds_speed = 1000000*(rs->ir_rates[in->in_txrate] &
- IEEE80211_RATE_VAL) / 2;
- glds_p->glds_duplex = GLD_DUPLEX_FULL;
-
- return (GLD_SUCCESS);
+ return (0);
}
static int
ath_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
{
ath_t *asc;
- ieee80211com_t *isc;
+ ieee80211com_t *ic;
struct ath_hal *ah;
uint8_t csz;
HAL_STATUS status;
@@ -1747,33 +1789,28 @@ ath_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
int32_t ath_countrycode = CTRY_DEFAULT; /* country code */
int32_t err, ath_regdomain = 0; /* regulatory domain */
char strbuf[32];
+ int instance;
+ wifi_data_t wd = { 0 };
+ mac_register_t *macp;
- switch (cmd) {
- case DDI_RESUME:
+ if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
- case DDI_ATTACH:
- break;
- default:
- return (DDI_FAILURE);
- }
- if (ddi_soft_state_zalloc(ath_soft_state_p,
- ddi_get_instance(devinfo)) != DDI_SUCCESS) {
+ instance = ddi_get_instance(devinfo);
+ if (ddi_soft_state_zalloc(ath_soft_state_p, instance) != DDI_SUCCESS) {
ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
"Unable to alloc softstate\n"));
return (DDI_FAILURE);
}
asc = ddi_get_soft_state(ath_soft_state_p, ddi_get_instance(devinfo));
- isc = (ieee80211com_t *)asc;
+ ic = (ieee80211com_t *)asc;
asc->asc_dev = devinfo;
- ath_halfix_init();
-
mutex_init(&asc->asc_genlock, NULL, MUTEX_DRIVER, NULL);
mutex_init(&asc->asc_txbuflock, NULL, MUTEX_DRIVER, NULL);
mutex_init(&asc->asc_rxbuflock, NULL, MUTEX_DRIVER, NULL);
- mutex_init(&asc->asc_gld_sched_lock, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&asc->asc_resched_lock, NULL, MUTEX_DRIVER, NULL);
err = pci_config_setup(devinfo, &asc->asc_cfg_handle);
if (err != DDI_SUCCESS) {
@@ -1883,7 +1920,7 @@ ath_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
ath_rate_setup(asc, IEEE80211_MODE_11A);
ath_rate_setup(asc, IEEE80211_MODE_11B);
ath_rate_setup(asc, IEEE80211_MODE_11G);
- ath_rate_setup(asc, IEEE80211_MODE_TURBO);
+ ath_rate_setup(asc, IEEE80211_MODE_TURBO_A);
/* Setup here so ath_rate_update is happy */
ath_setcurmode(asc, IEEE80211_MODE_11A);
@@ -1899,55 +1936,56 @@ ath_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
if (ath_txq_setup(asc))
goto attach_fail4;
- ATH_HAL_GETMAC(ah, asc->asc_isc.isc_macaddr);
+ ATH_HAL_GETMAC(ah, ic->ic_macaddr);
- /* setup gld */
- if ((isc->isc_dev = gld_mac_alloc(devinfo)) == NULL) {
- ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
- "gld_mac_alloc = %p\n", (void *)isc->isc_dev));
- goto attach_fail4;
- }
-
- /* pre initialize some variables for isc */
- isc->isc_dev->gldm_private = (caddr_t)asc;
-
- isc->isc_gld_reset = ath_gld_reset;
- isc->isc_gld_start = ath_gld_start;
- isc->isc_gld_stop = ath_gld_stop;
- isc->isc_gld_saddr = ath_gld_saddr;
- isc->isc_gld_send = ath_gld_send;
- isc->isc_gld_set_promiscuous = ath_gld_set_promiscuous;
- isc->isc_gld_gstat = ath_gld_gstat;
- isc->isc_gld_ioctl = ath_gld_ioctl;
- isc->isc_gld_set_multicast = ath_gld_set_multicast;
- isc->isc_gld_intr = ath_gld_intr;
-
- isc->isc_mgmt_send = ath_mgmt_send;
- isc->isc_new_state = ath_new_state;
- isc->isc_phytype = IEEE80211_T_OFDM;
- isc->isc_opmode = IEEE80211_M_STA;
- isc->isc_caps = IEEE80211_C_WEP | IEEE80211_C_IBSS |
- IEEE80211_C_HOSTAP;
+ /*
+ * Initialize pointers to device specific functions which
+ * will be used by the generic layer.
+ */
/* 11g support is identified when we fetch the channel set */
if (asc->asc_have11g)
- isc->isc_caps |= IEEE80211_C_SHPREAMBLE;
- isc->isc_node_alloc = ath_node_alloc;
- isc->isc_node_free = ath_node_free;
- isc->isc_node_copy = ath_node_copy;
- isc->isc_rate_ctl = ath_rate_ctl;
- isc->isc_calibrate = ath_calibrate;
- (void) ieee80211_ifattach(isc->isc_dev);
-
- isc->isc_dev->gldm_devinfo = devinfo;
- isc->isc_dev->gldm_vendor_addr = asc->asc_isc.isc_macaddr;
- isc->isc_dev->gldm_broadcast_addr = ath_broadcast_addr;
- isc->isc_dev->gldm_ident = "Atheros driver";
- isc->isc_dev->gldm_type = DL_ETHER;
- isc->isc_dev->gldm_minpkt = 0;
- isc->isc_dev->gldm_maxpkt = 1500;
- isc->isc_dev->gldm_addrlen = ETHERADDRL;
- isc->isc_dev->gldm_saplen = -2;
- isc->isc_dev->gldm_ppa = ddi_get_instance(devinfo);
+ ic->ic_caps |= IEEE80211_C_SHPREAMBLE;
+ /*
+ * Query the hal to figure out h/w crypto support.
+ */
+ if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_WEP))
+ ic->ic_caps |= IEEE80211_C_WEP;
+ if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_AES_OCB))
+ ic->ic_caps |= IEEE80211_C_AES;
+ if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_AES_CCM))
+ ic->ic_caps |= IEEE80211_C_AES_CCM;
+ if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_CKIP)) {
+ ic->ic_caps |= IEEE80211_C_CKIP;
+ /*
+ * Check if h/w does the MIC and/or whether the
+ * separate key cache entries are required to
+ * handle both tx+rx MIC keys.
+ */
+ if (ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_MIC))
+ ic->ic_caps |= IEEE80211_C_TKIPMIC;
+ if (ATH_HAL_TKIPSPLIT(ah))
+ asc->asc_splitmic = 1;
+ }
+ asc->asc_hasclrkey = ATH_HAL_CIPHERSUPPORTED(ah, HAL_CIPHER_CLR);
+ ic->ic_phytype = IEEE80211_T_OFDM;
+ ic->ic_opmode = IEEE80211_M_STA;
+ ic->ic_state = IEEE80211_S_INIT;
+ ic->ic_maxrssi = ATH_MAX_RSSI;
+ ic->ic_set_shortslot = ath_set_shortslot;
+ ic->ic_xmit = ath_xmit;
+ ieee80211_attach(ic);
+
+ /* Override 80211 default routines */
+ ic->ic_reset = ath_reset;
+ asc->asc_newstate = ic->ic_newstate;
+ ic->ic_newstate = ath_newstate;
+ ic->ic_watchdog = ath_watchdog;
+ ic->ic_node_alloc = ath_node_alloc;
+ ic->ic_node_free = ath_node_free;
+ ic->ic_crypto.cs_key_alloc = ath_key_alloc;
+ ic->ic_crypto.cs_key_delete = ath_key_delete;
+ ic->ic_crypto.cs_key_set = ath_key_set;
+ ieee80211_media_init(ic);
asc->asc_rx_pend = 0;
ATH_HAL_INTRSET(ah, 0);
@@ -1955,7 +1993,7 @@ ath_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
&asc->asc_softint_id, NULL, 0, ath_softint_handler, (caddr_t)asc);
if (err != DDI_SUCCESS) {
ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
- "ddi_add_softintr() failed"));
+ "ddi_add_softintr() failed\n"));
goto attach_fail5;
}
@@ -1966,29 +2004,55 @@ ath_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
goto attach_fail6;
}
- if (ddi_add_intr(devinfo, 0, NULL, NULL, gld_intr,
- (caddr_t)asc->asc_isc.isc_dev) != DDI_SUCCESS) {
+ if (ddi_add_intr(devinfo, 0, NULL, NULL, ath_intr,
+ (caddr_t)asc) != DDI_SUCCESS) {
ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
"Can not set intr for ATH driver\n"));
goto attach_fail6;
}
- isc->isc_dev->gldm_cookie = asc->asc_iblock;
- if (err = gld_register(devinfo, "ath", isc->isc_dev)) {
+ /*
+ * Provide initial settings for the WiFi plugin; whenever this
+ * information changes, we need to call mac_plugindata_update()
+ */
+ wd.wd_opmode = ic->ic_opmode;
+ wd.wd_secalloc = WIFI_SEC_NONE;
+ IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid);
+
+ if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
- "gld_register err %x\n", err));
+ "MAC version mismatch\n"));
+ goto attach_fail7;
+ }
+
+ macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI;
+ macp->m_driver = asc;
+ macp->m_dip = devinfo;
+ macp->m_src_addr = ic->ic_macaddr;
+ macp->m_callbacks = &ath_m_callbacks;
+ macp->m_min_sdu = 0;
+ macp->m_max_sdu = IEEE80211_MTU;
+ macp->m_pdata = &wd;
+ macp->m_pdata_size = sizeof (wd);
+
+ err = mac_register(macp, &ic->ic_mach);
+ mac_free(macp);
+ if (err != 0) {
+ ATH_DEBUG((ATH_DBG_ATTACH, "ath: ath_attach(): "
+ "mac_register err %x\n", err));
goto attach_fail7;
}
/* Create minor node of type DDI_NT_NET_WIFI */
(void) snprintf(strbuf, sizeof (strbuf), "%s%d",
- ATH_NODENAME, isc->isc_dev->gldm_ppa);
+ ATH_NODENAME, instance);
err = ddi_create_minor_node(devinfo, strbuf, S_IFCHR,
- isc->isc_dev->gldm_ppa + 1, DDI_NT_NET_WIFI, 0);
+ instance + 1, DDI_NT_NET_WIFI, 0);
if (err != DDI_SUCCESS)
ATH_DEBUG((ATH_DBG_ATTACH, "WARN: ath: ath_attach(): "
"Create minor node failed - %d\n", err));
+ mac_link_update(ic->ic_mach, LINK_STATE_DOWN);
asc->asc_invalid = 1;
return (DDI_SUCCESS);
attach_fail7:
@@ -1996,7 +2060,7 @@ attach_fail7:
attach_fail6:
ddi_remove_softintr(asc->asc_softint_id);
attach_fail5:
- gld_mac_free(isc->isc_dev);
+ (void) ieee80211_detach(ic);
attach_fail4:
ath_desc_free(asc);
attach_fail3:
@@ -2016,8 +2080,8 @@ attach_fail0:
}
mutex_destroy(&asc->asc_rxbuflock);
mutex_destroy(&asc->asc_genlock);
- mutex_destroy(&asc->asc_gld_sched_lock);
- ddi_soft_state_free(ath_soft_state_p, ddi_get_instance(devinfo));
+ mutex_destroy(&asc->asc_resched_lock);
+ ddi_soft_state_free(ath_soft_state_p, instance);
return (DDI_FAILURE);
}
@@ -2026,57 +2090,52 @@ static int32_t
ath_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
{
ath_t *asc;
- int32_t i;
asc = ddi_get_soft_state(ath_soft_state_p, ddi_get_instance(devinfo));
ASSERT(asc != NULL);
- switch (cmd) {
- default:
+ if (cmd != DDI_DETACH)
return (DDI_FAILURE);
- case DDI_SUSPEND:
- return (DDI_FAILURE);
-
- case DDI_DETACH:
- break;
- }
-
- ASSERT(asc->asc_isc.isc_mf_thread == NULL);
+ ath_stop_scantimer(asc);
/* disable interrupts */
ATH_HAL_INTRSET(asc->asc_ah, 0);
+ /*
+ * Unregister from the MAC layer subsystem
+ */
+ if (mac_unregister(asc->asc_isc.ic_mach) != 0)
+ return (DDI_FAILURE);
+
/* free intterrupt resources */
ddi_remove_intr(devinfo, 0, asc->asc_iblock);
ddi_remove_softintr(asc->asc_softint_id);
- /* detach 802.11 and Atheros HAL */
- ieee80211_ifdetach(asc->asc_isc.isc_dev);
+ /*
+ * NB: the order of these is important:
+ * o call the 802.11 layer before detaching the hal to
+ * insure callbacks into the driver to delete global
+ * key cache entries can be handled
+ * o reclaim the tx queue data structures after calling
+ * the 802.11 layer as we'll get called back to reclaim
+ * node state and potentially want to use them
+ * o to cleanup the tx queues the hal is called, so detach
+ * it last
+ */
+ ieee80211_detach(&asc->asc_isc);
ath_desc_free(asc);
+ ath_txq_cleanup(asc);
asc->asc_ah->ah_detach(asc->asc_ah);
- ath_halfix_finit();
-
- /* detach gld */
- if (gld_unregister(asc->asc_isc.isc_dev) != 0)
- return (DDI_FAILURE);
- gld_mac_free(asc->asc_isc.isc_dev);
/* free io handle */
ddi_regs_map_free(&asc->asc_io_handle);
pci_config_teardown(&asc->asc_cfg_handle);
/* destroy locks */
- mutex_destroy(&asc->asc_txbuflock);
- for (i = 0; i < HAL_NUM_TX_QUEUES; i++) {
- if (ATH_TXQ_SETUP(asc, i)) {
- struct ath_txq *txq = &asc->asc_txq[i];
- mutex_destroy(&txq->axq_lock);
- }
- }
mutex_destroy(&asc->asc_rxbuflock);
mutex_destroy(&asc->asc_genlock);
- mutex_destroy(&asc->asc_gld_sched_lock);
+ mutex_destroy(&asc->asc_resched_lock);
ddi_remove_minor_node(devinfo, NULL);
ddi_soft_state_free(ath_soft_state_p, ddi_get_instance(devinfo));
@@ -2084,80 +2143,12 @@ ath_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd)
return (DDI_SUCCESS);
}
-static struct module_info ath_module_info = {
- 0, /* ATH_IDNUM, */
- "ath", /* ATH_DRIVER_NAME, */
- 0,
- INFPSZ,
- 4096, /* ATH_HIWAT, */
- 128, /* ATH_LOWAT */
-};
-
-static struct qinit ath_r_qinit = { /* read queues */
- NULL,
- gld_rsrv,
- gld_open,
- gld_close,
- NULL,
- &ath_module_info,
- NULL
-};
-
-static struct qinit ath_w_qinit = { /* write queues */
- gld_wput,
- gld_wsrv,
- NULL,
- NULL,
- NULL,
- &ath_module_info,
- NULL
-};
-
-static struct streamtab ath_streamtab = {
- &ath_r_qinit,
- &ath_w_qinit,
- NULL,
- NULL
-};
-
-static struct cb_ops ath_cb_ops = {
- nulldev, /* cb_open */
- nulldev, /* cb_close */
- nodev, /* cb_strategy */
- nodev, /* cb_print */
- nodev, /* cb_dump */
- nodev, /* cb_read */
- nodev, /* cb_write */
- nodev, /* cb_ioctl */
- nodev, /* cb_devmap */
- nodev, /* cb_mmap */
- nodev, /* cb_segmap */
- nochpoll, /* cb_chpoll */
- ddi_prop_op, /* cb_prop_op */
- &ath_streamtab, /* cb_stream */
- D_MP, /* cb_flag */
- 0, /* cb_rev */
- nodev, /* cb_aread */
- nodev /* cb_awrite */
-};
-
-static struct dev_ops ath_dev_ops = {
- DEVO_REV, /* devo_rev */
- 0, /* devo_refcnt */
- gld_getinfo, /* devo_getinfo */
- nulldev, /* devo_identify */
- nulldev, /* devo_probe */
- ath_attach, /* devo_attach */
- ath_detach, /* devo_detach */
- nodev, /* devo_reset */
- &ath_cb_ops, /* devo_cb_ops */
- (struct bus_ops *)NULL, /* devo_bus_ops */
- NULL /* devo_power */
-};
+DDI_DEFINE_STREAM_OPS(ath_dev_ops, nulldev, nulldev, ath_attach, ath_detach,
+ nodev, NULL, D_MP, NULL);
static struct modldrv ath_modldrv = {
&mod_driverops, /* Type of module. This one is a driver */
- "ath driver 1.1", /* short description */
+ "ath driver 1.2/HAL 0.9.17.2", /* short description */
&ath_dev_ops /* driver specific ops */
};
@@ -2182,10 +2173,14 @@ _init(void)
return (status);
mutex_init(&ath_loglock, NULL, MUTEX_DRIVER, NULL);
+ ath_halfix_init();
+ mac_init_ops(&ath_dev_ops, "ath");
status = mod_install(&modlinkage);
if (status != 0) {
- ddi_soft_state_fini(&ath_soft_state_p);
+ mac_fini_ops(&ath_dev_ops);
+ ath_halfix_finit();
mutex_destroy(&ath_loglock);
+ ddi_soft_state_fini(&ath_soft_state_p);
}
return (status);
@@ -2198,8 +2193,10 @@ _fini(void)
status = mod_remove(&modlinkage);
if (status == 0) {
- ddi_soft_state_fini(&ath_soft_state_p);
+ mac_fini_ops(&ath_dev_ops);
+ ath_halfix_finit();
mutex_destroy(&ath_loglock);
+ ddi_soft_state_fini(&ath_soft_state_p);
}
return (status);
}