diff options
author | Damien Bergamini <damien@cvs.openbsd.org> | 2010-06-05 18:43:58 +0000 |
---|---|---|
committer | Damien Bergamini <damien@cvs.openbsd.org> | 2010-06-05 18:43:58 +0000 |
commit | 8246ce26cd8453f17900e5779d801ab364994f07 (patch) | |
tree | 2ddc0803a31d7320e4392810f5223f6f0653a35a /sys | |
parent | c554b451afe77faf07ac03eaa7f08c2761deab88 (diff) |
- fix athn_set_key() on big-endian machines (function is not used yet)
- change sign extension such that we do not rely on >> being an
arithmetic shift on signed integers
- various changes to AR9003 code, fix Tx path, enable Tx IQ calibration
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/ic/ar5008.c | 16 | ||||
-rw-r--r-- | sys/dev/ic/ar9003.c | 89 | ||||
-rw-r--r-- | sys/dev/ic/ar9003reg.h | 4 | ||||
-rw-r--r-- | sys/dev/ic/athn.c | 90 | ||||
-rw-r--r-- | sys/dev/ic/athnvar.h | 7 |
5 files changed, 116 insertions, 90 deletions
diff --git a/sys/dev/ic/ar5008.c b/sys/dev/ic/ar5008.c index 655888f6002..2c06e35feca 100644 --- a/sys/dev/ic/ar5008.c +++ b/sys/dev/ic/ar5008.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ar5008.c,v 1.7 2010/05/16 14:34:19 damien Exp $ */ +/* $OpenBSD: ar5008.c,v 1.8 2010/06/05 18:43:57 damien Exp $ */ /*- * Copyright (c) 2009 Damien Bergamini <damien.bergamini@free.fr> @@ -597,8 +597,8 @@ ar5008_rx_alloc(struct athn_softc *sc) goto fail; } - bus_dmamap_sync(sc->sc_dmat, bf->bf_map, 0, - bf->bf_map->dm_mapsize, BUS_DMASYNC_PREREAD); + bus_dmamap_sync(sc->sc_dmat, bf->bf_map, 0, ATHN_RXBUFSZ, + BUS_DMASYNC_PREREAD); bf->bf_desc = ds; bf->bf_daddr = rxq->map->dm_segs[0].ds_addr + @@ -818,7 +818,7 @@ ar5008_rx_process(struct athn_softc *sc) } len = MS(ds->ds_status1, AR_RXS1_DATA_LEN); - if (__predict_false(len == 0 || len > ATHN_RXBUFSZ)) { + if (__predict_false(len < IEEE80211_MIN_LEN || len > ATHN_RXBUFSZ)) { DPRINTF(("corrupted descriptor length=%d\n", len)); ifp->if_ierrors++; goto skip; @@ -874,7 +874,7 @@ ar5008_rx_process(struct athn_softc *sc) u_int hdrlen = ieee80211_get_hdrlen(wh); if (hdrlen & 3) { ovbcopy(wh, (caddr_t)wh + 2, hdrlen); - m_adj(m, 2); /* XXX sure? */ + m_adj(m, 2); } } #if NBPFILTER > 0 @@ -931,7 +931,7 @@ ar5008_tx_process(struct athn_softc *sc, int qid) uint8_t failcnt; bf = SIMPLEQ_FIRST(&txq->head); - if (__predict_false(bf == NULL)) + if (bf == NULL) return (ENOENT); /* Get descriptor of last DMA segment. */ ds = &((struct ar_tx_desc *)bf->bf_descs)[bf->bf_map->dm_nsegs - 1]; @@ -1709,8 +1709,8 @@ ar5008_set_rxchains(struct athn_softc *sc) void ar5008_read_noisefloor(struct athn_softc *sc, int16_t *nf, int16_t *nf_ext) { -/* Sign-extend 9-bit value to 16-bit. */ -#define SIGN_EXT(v) ((((int16_t)(v)) << 7) >> 7) +/* Sign-extends 9-bit value (assumes upper bits are zeroes.) */ +#define SIGN_EXT(v) (((v) ^ 0x100) - 0x100) uint32_t reg; int i; diff --git a/sys/dev/ic/ar9003.c b/sys/dev/ic/ar9003.c index bddcb8f7912..35b756def54 100644 --- a/sys/dev/ic/ar9003.c +++ b/sys/dev/ic/ar9003.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ar9003.c,v 1.11 2010/05/16 14:34:19 damien Exp $ */ +/* $OpenBSD: ar9003.c,v 1.12 2010/06/05 18:43:57 damien Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> @@ -652,8 +652,8 @@ ar9003_rx_alloc(struct athn_softc *sc, int qid, int count) bf->bf_desc = ds; bf->bf_daddr = bf->bf_map->dm_segs[0].ds_addr; - bus_dmamap_sync(sc->sc_dmat, bf->bf_map, 0, - bf->bf_map->dm_mapsize, BUS_DMASYNC_PREREAD); + bus_dmamap_sync(sc->sc_dmat, bf->bf_map, 0, ATHN_RXBUFSZ, + BUS_DMASYNC_PREREAD); } return (0); fail: @@ -852,7 +852,8 @@ ar9003_rx_process(struct athn_softc *sc, int qid) } len = MS(ds->ds_status2, AR_RXS2_DATA_LEN); - if (__predict_false(len == 0 || len > ATHN_RXBUFSZ)) { + if (__predict_false(len < IEEE80211_MIN_LEN || + len > ATHN_RXBUFSZ - sizeof(*ds))) { DPRINTF(("corrupted descriptor length=%d\n", len)); ifp->if_ierrors++; goto skip; @@ -892,7 +893,7 @@ ar9003_rx_process(struct athn_softc *sc, int qid) /* Finalize mbuf. */ m->m_pkthdr.rcvif = ifp; - /* Strip Rx descriptor from head. */ + /* Strip Rx status descriptor from head. */ m->m_data = (caddr_t)&ds[1]; m->m_pkthdr.len = m->m_len = len; @@ -900,6 +901,14 @@ ar9003_rx_process(struct athn_softc *sc, int qid) wh = mtod(m, struct ieee80211_frame *); ni = ieee80211_find_rxnode(ic, wh); + /* Remove any HW padding after the 802.11 header. */ + if (!(wh->i_fc[0] & IEEE80211_FC0_TYPE_CTL)) { + u_int hdrlen = ieee80211_get_hdrlen(wh); + if (hdrlen & 3) { + ovbcopy(wh, (caddr_t)wh + 2, hdrlen); + m_adj(m, 2); + } + } #if NBPFILTER > 0 if (__predict_false(sc->sc_drvbpf != NULL)) ar9003_rx_radiotap(sc, m, ds); @@ -970,7 +979,7 @@ ar9003_tx_process(struct athn_softc *sc) txq = &sc->txq[qid]; bf = SIMPLEQ_FIRST(&txq->head); - if (__predict_false(bf == NULL)) { + if (bf == NULL || bf == txq->wait) { memset(ds, 0, sizeof(*ds)); return (0); } @@ -1018,13 +1027,27 @@ ar9003_tx_process(struct athn_softc *sc) /* Link Tx buffer back to global free list. */ SIMPLEQ_INSERT_TAIL(&sc->txbufs, bf, bf_list); + + /* Queue buffers that are waiting if there is new room. */ + if (--txq->queued < AR9003_TX_QDEPTH && txq->wait != NULL) { + AR_WRITE(sc, AR_QTXDP(qid), txq->wait->bf_daddr); + txq->wait = SIMPLEQ_NEXT(txq->wait, bf_list); + } return (0); } void ar9003_tx_intr(struct athn_softc *sc) { + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + while (ar9003_tx_process(sc) == 0); + + if (!SIMPLEQ_EMPTY(&sc->txbufs)) { + ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_start(ifp); + } } #ifndef IEEE80211_STA_ONLY @@ -1553,20 +1576,18 @@ ar9003_tx(struct athn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) bus_dmamap_sync(sc->sc_dmat, bf->bf_map, 0, bf->bf_map->dm_mapsize, BUS_DMASYNC_PREWRITE); - if (!SIMPLEQ_EMPTY(&txq->head)) - ((struct ar_tx_desc *)txq->lastds)->ds_link = bf->bf_daddr; - else - AR_WRITE(sc, AR_QTXDP(qid), bf->bf_daddr); - txq->lastds = ds; - SIMPLEQ_REMOVE_HEAD(&sc->txbufs, bf_list); - SIMPLEQ_INSERT_TAIL(&txq->head, bf, bf_list); - DPRINTFN(6, ("Tx qid=%d nsegs=%d ctl11=0x%x ctl12=0x%x ctl14=0x%x\n", qid, bf->bf_map->dm_nsegs, ds->ds_ctl11, ds->ds_ctl12, ds->ds_ctl14)); - /* Kick Tx. */ - AR_WRITE(sc, AR_Q_TXE, 1 << qid); + SIMPLEQ_REMOVE_HEAD(&sc->txbufs, bf_list); + SIMPLEQ_INSERT_TAIL(&txq->head, bf, bf_list); + + /* Queue buffer unless hardware FIFO is already full. */ + if (++txq->queued <= AR9003_TX_QDEPTH) + AR_WRITE(sc, AR_QTXDP(qid), bf->bf_daddr); + else if (txq->wait == NULL) + txq->wait = bf; return (0); } @@ -1728,8 +1749,8 @@ ar9003_set_rxchains(struct athn_softc *sc) void ar9003_read_noisefloor(struct athn_softc *sc, int16_t *nf, int16_t *nf_ext) { -/* Sign-extend 9-bit value to 16-bit. */ -#define SIGN_EXT(v) ((((int16_t)(v)) << 7) >> 7) +/* Sign-extends 9-bit value (assumes upper bits are zeroes.) */ +#define SIGN_EXT(v) (((v) ^ 0x100) - 0x100) uint32_t reg; int i; @@ -1863,6 +1884,13 @@ ar9003_init_calib(struct athn_softc *sc) txchainmask = rxchainmask = 0x7; ar9003_init_chains(sc); + /* Perform Tx IQ calibration. */ + ar9003_calib_tx_iq(sc); + /* Disable and re-enable the PHY chips. */ + AR_WRITE(sc, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); + DELAY(5); + AR_WRITE(sc, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); + /* Calibrate the AGC. */ AR_SETBITS(sc, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL); /* Poll for offset calibration completion. */ @@ -1875,11 +1903,6 @@ ar9003_init_calib(struct athn_softc *sc) if (ntries == 10000) return (ETIMEDOUT); -#ifdef notyet - /* Perform Tx IQ calibration. */ - ar9003_calib_tx_iq(sc); -#endif - /* Restore chains masks. */ sc->txchainmask = txchainmask; sc->rxchainmask = rxchainmask; @@ -1895,8 +1918,7 @@ ar9003_do_calib(struct athn_softc *sc) if (sc->cur_calib_mask & ATHN_CAL_IQ) { reg = AR_READ(sc, AR_PHY_TIMING4); - reg = RW(reg, AR_PHY_TIMING4_IQCAL_LOG_COUNT_MAX, - AR_MAX_LOG_CAL); + reg = RW(reg, AR_PHY_TIMING4_IQCAL_LOG_COUNT_MAX, 10); AR_WRITE(sc, AR_PHY_TIMING4, reg); AR_WRITE(sc, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ); AR_SETBITS(sc, AR_PHY_TIMING4, AR_PHY_TIMING4_DO_CAL); @@ -1931,17 +1953,12 @@ ar9003_calib_iq(struct athn_softc *sc) for (i = 0; i < AR_MAX_CHAINS; i++) { cal = &sc->calib.iq[i]; - /* Accumulate IQ calibration measures (clear on read). */ - cal->pwr_meas_i += AR_READ(sc, AR_PHY_IQ_ADC_MEAS_0_B(i)); - cal->pwr_meas_q += AR_READ(sc, AR_PHY_IQ_ADC_MEAS_1_B(i)); - cal->iq_corr_meas += + /* Read IQ calibration measures (clear on read). */ + cal->pwr_meas_i = AR_READ(sc, AR_PHY_IQ_ADC_MEAS_0_B(i)); + cal->pwr_meas_q = AR_READ(sc, AR_PHY_IQ_ADC_MEAS_1_B(i)); + cal->iq_corr_meas = (int32_t)AR_READ(sc, AR_PHY_IQ_ADC_MEAS_2_B(i)); } - if (++sc->calib.nsamples < AR_CAL_SAMPLES) { - /* Not enough samples accumulated, continue. */ - ar9003_do_calib(sc); - return; - } for (i = 0; i < sc->nrxchains; i++) { cal = &sc->calib.iq[i]; @@ -1994,8 +2011,8 @@ ar9003_calib_iq(struct athn_softc *sc) int ar9003_get_iq_corr(struct athn_softc *sc, int32_t res[6], int32_t coeff[2]) { -/* Sign-extend 12-bit values to 32-bit. */ -#define SIGN_EXT(v) ((((int32_t)(v)) << 20) >> 20) +/* Sign-extends 12-bit value (assumes upper bits are zeroes.) */ +#define SIGN_EXT(v) (((v) ^ 0x800) - 0x800) #define SCALE (1 << 15) #define SHIFT (1 << 8) struct { diff --git a/sys/dev/ic/ar9003reg.h b/sys/dev/ic/ar9003reg.h index 83419e804fa..4bf3fb24690 100644 --- a/sys/dev/ic/ar9003reg.h +++ b/sys/dev/ic/ar9003reg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ar9003reg.h,v 1.1 2010/05/10 17:44:21 damien Exp $ */ +/* $OpenBSD: ar9003reg.h,v 1.2 2010/06/05 18:43:57 damien Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> @@ -676,7 +676,7 @@ #define AR_PHY_65NM_CH0_THERM_START 0x20000000 #define AR_PHY_65NM_CH0_THERM_LOCAL 0x80000000 - +#define AR9003_TX_QDEPTH 8 #define AR9003_RX_LP_QDEPTH 128 #define AR9003_RX_HP_QDEPTH 16 diff --git a/sys/dev/ic/athn.c b/sys/dev/ic/athn.c index 66ae40549d5..0a88c70e91d 100644 --- a/sys/dev/ic/athn.c +++ b/sys/dev/ic/athn.c @@ -1,4 +1,4 @@ -/* $OpenBSD: athn.c,v 1.48 2010/06/03 18:02:50 damien Exp $ */ +/* $OpenBSD: athn.c,v 1.49 2010/06/05 18:43:57 damien Exp $ */ /*- * Copyright (c) 2009 Damien Bergamini <damien.bergamini@free.fr> @@ -921,10 +921,9 @@ athn_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, struct ieee80211_key *k) { struct athn_softc *sc = ic->ic_softc; - uint32_t type, lo, hi; - uint16_t keybuf[8], micbuf[8]; - const uint8_t *addr; + const uint8_t *txmic, *rxmic, *key, *addr; uintptr_t entry, micentry; + uint32_t type, lo, hi; switch (k->k_cipher) { case IEEE80211_CIPHER_WEP40: @@ -944,9 +943,6 @@ athn_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, return (ieee80211_set_key(ic, ni, k)); } - memset(keybuf, 0, sizeof(keybuf)); - memcpy(keybuf, k->k_key, MIN(k->k_len, 16)); - if (!(k->k_flags & IEEE80211_KEY_GROUP)) entry = IEEE80211_WEP_NKID + IEEE80211_AID(ni->ni_associd); else @@ -954,55 +950,65 @@ athn_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, k->k_priv = (void *)entry; /* NB: See note about key cache registers access above. */ + key = k->k_key; if (type == AR_KEYTABLE_TYPE_TKIP) { +#ifndef IEEE80211_STA_ONLY + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + txmic = &key[16]; + rxmic = &key[24]; + } else +#endif + { + rxmic = &key[16]; + txmic = &key[24]; + } if (sc->flags & ATHN_FLAG_SPLIT_TKIP_MIC) { /* Tx MIC is at entry + 64. */ micentry = entry + 64; - memcpy(micbuf, &k->k_key[16], 8); AR_WRITE(sc, AR_KEYTABLE_KEY0(micentry), - micbuf[0] | micbuf[1] << 16); + LE_READ_4(&txmic[0])); + AR_WRITE(sc, AR_KEYTABLE_KEY1(micentry), 0); + AR_WRITE(sc, AR_KEYTABLE_KEY2(micentry), - micbuf[2] | micbuf[3] << 16); - AR_WRITE(sc, AR_KEYTABLE_TYPE(micentry), - AR_KEYTABLE_TYPE_CLR); + LE_READ_4(&txmic[4])); + AR_WRITE(sc, AR_KEYTABLE_KEY3(micentry), 0); - /* Rx MIC is at entry + 64 + 32. */ + /* Rx MIC key is at entry + 64 + 32. */ micentry = entry + 64 + 32; - memcpy(micbuf, &k->k_key[24], 8); AR_WRITE(sc, AR_KEYTABLE_KEY0(micentry), - micbuf[0] | micbuf[1] << 16); + LE_READ_4(&rxmic[0])); + AR_WRITE(sc, AR_KEYTABLE_KEY1(micentry), 0); + AR_WRITE(sc, AR_KEYTABLE_KEY2(micentry), - micbuf[2] | micbuf[3] << 16); - AR_WRITE(sc, AR_KEYTABLE_TYPE(micentry), - AR_KEYTABLE_TYPE_CLR); + LE_READ_4(&rxmic[4])); + AR_WRITE(sc, AR_KEYTABLE_KEY3(micentry), 0); } else { - /* Tx+Rx MIC is at entry + 64. */ + /* Tx+Rx MIC key is at entry + 64. */ micentry = entry + 64; - memcpy(micbuf, &k->k_key[16], 16); AR_WRITE(sc, AR_KEYTABLE_KEY0(micentry), - micbuf[0] | micbuf[1] << 16); + LE_READ_4(&rxmic[0])); AR_WRITE(sc, AR_KEYTABLE_KEY1(micentry), - micbuf[5]); + LE_READ_2(&txmic[2])); + AR_WRITE(sc, AR_KEYTABLE_KEY2(micentry), - micbuf[2] | micbuf[3] << 16); + LE_READ_4(&rxmic[4])); AR_WRITE(sc, AR_KEYTABLE_KEY3(micentry), - micbuf[4]); + LE_READ_2(&txmic[0])); + AR_WRITE(sc, AR_KEYTABLE_KEY4(micentry), - micbuf[6] | micbuf[7] << 16); + LE_READ_4(&txmic[4])); AR_WRITE(sc, AR_KEYTABLE_TYPE(micentry), AR_KEYTABLE_TYPE_CLR); } } - AR_WRITE(sc, AR_KEYTABLE_KEY0(entry), keybuf[0] | keybuf[1] << 16); - AR_WRITE(sc, AR_KEYTABLE_KEY1(entry), keybuf[2]); - AR_WRITE(sc, AR_KEYTABLE_KEY2(entry), keybuf[3] | keybuf[4] << 16); - AR_WRITE(sc, AR_KEYTABLE_KEY3(entry), keybuf[5]); - AR_WRITE(sc, AR_KEYTABLE_KEY4(entry), keybuf[6] | keybuf[7] << 16); - AR_WRITE(sc, AR_KEYTABLE_TYPE(entry), type); + AR_WRITE(sc, AR_KEYTABLE_KEY0(entry), LE_READ_4(&key[ 0])); + AR_WRITE(sc, AR_KEYTABLE_KEY1(entry), LE_READ_2(&key[ 4])); - /* Clear keys from the stack. */ - memset(keybuf, 0, sizeof(keybuf)); - memset(micbuf, 0, sizeof(micbuf)); + AR_WRITE(sc, AR_KEYTABLE_KEY2(entry), LE_READ_4(&key[ 6])); + AR_WRITE(sc, AR_KEYTABLE_KEY3(entry), LE_READ_2(&key[10])); + + AR_WRITE(sc, AR_KEYTABLE_KEY4(entry), LE_READ_4(&key[12])); + AR_WRITE(sc, AR_KEYTABLE_TYPE(entry), type); if (!(k->k_flags & IEEE80211_KEY_GROUP)) { addr = ni->ni_macaddr; @@ -1724,6 +1730,8 @@ athn_init_tx_queues(struct athn_softc *sc) for (qid = 0; qid < ATHN_QID_COUNT; qid++) { SIMPLEQ_INIT(&sc->txq[qid].head); sc->txq[qid].lastds = NULL; + sc->txq[qid].wait = NULL; + sc->txq[qid].queued = 0; AR_WRITE(sc, AR_DRETRY_LIMIT(qid), SM(AR_D_RETRY_LIMIT_STA_SH, 32) | @@ -1908,9 +1916,8 @@ athn_set_bss(struct athn_softc *sc, struct ieee80211_node *ni) { const uint8_t *bssid = ni->ni_bssid; - AR_WRITE(sc, AR_BSS_ID0, bssid[0] | bssid[1] << 8 | - bssid[2] << 16 | bssid[3] << 24); - AR_WRITE(sc, AR_BSS_ID1, bssid[4] | bssid[5] << 8 | + AR_WRITE(sc, AR_BSS_ID0, LE_READ_4(&bssid[0])); + AR_WRITE(sc, AR_BSS_ID1, LE_READ_2(&bssid[4]) | SM(AR_BSS_ID1_AID, IEEE80211_AID(ni->ni_associd))); } @@ -2082,12 +2089,9 @@ athn_hw_reset(struct athn_softc *sc, struct ieee80211_channel *c, ops->spur_mitigate(sc, c, extc); ops->init_from_rom(sc, c, extc); - AR_WRITE(sc, AR_STA_ID0, - ic->ic_myaddr[0] << 0 | ic->ic_myaddr[1] << 8 | - ic->ic_myaddr[2] << 16 | ic->ic_myaddr[3] << 24); /* XXX */ - AR_WRITE(sc, AR_STA_ID1, - ic->ic_myaddr[4] << 0 | ic->ic_myaddr[5] << 8 | + AR_WRITE(sc, AR_STA_ID0, LE_READ_4(&ic->ic_myaddr[0])); + AR_WRITE(sc, AR_STA_ID1, LE_READ_2(&ic->ic_myaddr[4]) | sta_id1 | AR_STA_ID1_RTS_USE_DEF | AR_STA_ID1_CRPT_MIC_ENABLE); athn_set_opmode(sc); @@ -2652,7 +2656,7 @@ athn_init(struct ifnet *ifp) if (ic->ic_flags & IEEE80211_F_WEPON) { /* Configure WEP keys. */ for (i = 0; i < IEEE80211_WEP_NKID; i++) - (void)athn_set_key(ic, NULL, &ic->ic_nw_keys[i]); + athn_set_key(ic, NULL, &ic->ic_nw_keys[i]); } #endif if (ic->ic_opmode == IEEE80211_M_MONITOR) diff --git a/sys/dev/ic/athnvar.h b/sys/dev/ic/athnvar.h index 4ed94430fdf..d3452556b07 100644 --- a/sys/dev/ic/athnvar.h +++ b/sys/dev/ic/athnvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: athnvar.h,v 1.14 2010/05/16 14:34:19 damien Exp $ */ +/* $OpenBSD: athnvar.h,v 1.15 2010/06/05 18:43:57 damien Exp $ */ /*- * Copyright (c) 2009 Damien Bergamini <damien.bergamini@free.fr> @@ -28,6 +28,9 @@ extern int athn_debug; #define DPRINTFN(n, x) #endif +#define LE_READ_4(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24) +#define LE_READ_2(p) ((p)[0] | (p)[1] << 8) + #define ATHN_RXBUFSZ 3872 #define ATHN_TXBUFSZ 4096 @@ -82,6 +85,8 @@ struct athn_tx_buf { struct athn_txq { SIMPLEQ_HEAD(, athn_tx_buf) head; void *lastds; + struct athn_tx_buf *wait; + int queued; }; struct athn_rx_buf { |