Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/iwn/if_iwn.c
Show First 20 Lines • Show All 162 Lines • ▼ Show 20 Lines | |||||
static void iwn_free_fwmem(struct iwn_softc *); | static void iwn_free_fwmem(struct iwn_softc *); | ||||
static int iwn_alloc_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); | static int iwn_alloc_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); | ||||
static void iwn_reset_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); | static void iwn_reset_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); | ||||
static void iwn_free_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); | static void iwn_free_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); | ||||
static int iwn_alloc_tx_ring(struct iwn_softc *, struct iwn_tx_ring *, | static int iwn_alloc_tx_ring(struct iwn_softc *, struct iwn_tx_ring *, | ||||
int); | int); | ||||
static void iwn_reset_tx_ring(struct iwn_softc *, struct iwn_tx_ring *); | static void iwn_reset_tx_ring(struct iwn_softc *, struct iwn_tx_ring *); | ||||
static void iwn_free_tx_ring(struct iwn_softc *, struct iwn_tx_ring *); | static void iwn_free_tx_ring(struct iwn_softc *, struct iwn_tx_ring *); | ||||
static void iwn_check_tx_ring(struct iwn_softc *, int); | |||||
static void iwn5000_ict_reset(struct iwn_softc *); | static void iwn5000_ict_reset(struct iwn_softc *); | ||||
static int iwn_read_eeprom(struct iwn_softc *, | static int iwn_read_eeprom(struct iwn_softc *, | ||||
uint8_t macaddr[IEEE80211_ADDR_LEN]); | uint8_t macaddr[IEEE80211_ADDR_LEN]); | ||||
static void iwn4965_read_eeprom(struct iwn_softc *); | static void iwn4965_read_eeprom(struct iwn_softc *); | ||||
#ifdef IWN_DEBUG | #ifdef IWN_DEBUG | ||||
static void iwn4965_print_power_group(struct iwn_softc *, int); | static void iwn4965_print_power_group(struct iwn_softc *, int); | ||||
#endif | #endif | ||||
static void iwn5000_read_eeprom(struct iwn_softc *); | static void iwn5000_read_eeprom(struct iwn_softc *); | ||||
Show All 15 Lines | static struct ieee80211_node *iwn_node_alloc(struct ieee80211vap *, | ||||
const uint8_t mac[IEEE80211_ADDR_LEN]); | const uint8_t mac[IEEE80211_ADDR_LEN]); | ||||
static void iwn_newassoc(struct ieee80211_node *, int); | static void iwn_newassoc(struct ieee80211_node *, int); | ||||
static int iwn_media_change(struct ifnet *); | static int iwn_media_change(struct ifnet *); | ||||
static int iwn_newstate(struct ieee80211vap *, enum ieee80211_state, int); | static int iwn_newstate(struct ieee80211vap *, enum ieee80211_state, int); | ||||
static void iwn_calib_timeout(void *); | static void iwn_calib_timeout(void *); | ||||
static void iwn_rx_phy(struct iwn_softc *, struct iwn_rx_desc *); | static void iwn_rx_phy(struct iwn_softc *, struct iwn_rx_desc *); | ||||
static void iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *, | static void iwn_rx_done(struct iwn_softc *, struct iwn_rx_desc *, | ||||
struct iwn_rx_data *); | struct iwn_rx_data *); | ||||
static void iwn_agg_tx_complete(struct iwn_softc *, struct iwn_tx_ring *, | |||||
int, int, int); | |||||
static void iwn_rx_compressed_ba(struct iwn_softc *, struct iwn_rx_desc *); | static void iwn_rx_compressed_ba(struct iwn_softc *, struct iwn_rx_desc *); | ||||
static void iwn5000_rx_calib_results(struct iwn_softc *, | static void iwn5000_rx_calib_results(struct iwn_softc *, | ||||
struct iwn_rx_desc *); | struct iwn_rx_desc *); | ||||
static void iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *); | static void iwn_rx_statistics(struct iwn_softc *, struct iwn_rx_desc *); | ||||
static void iwn4965_tx_done(struct iwn_softc *, struct iwn_rx_desc *, | static void iwn4965_tx_done(struct iwn_softc *, struct iwn_rx_desc *, | ||||
struct iwn_rx_data *); | struct iwn_rx_data *); | ||||
static void iwn5000_tx_done(struct iwn_softc *, struct iwn_rx_desc *, | static void iwn5000_tx_done(struct iwn_softc *, struct iwn_rx_desc *, | ||||
struct iwn_rx_data *); | struct iwn_rx_data *); | ||||
static void iwn_adj_ampdu_ptr(struct iwn_softc *, struct iwn_tx_ring *); | |||||
static void iwn_tx_done(struct iwn_softc *, struct iwn_rx_desc *, int, int, | static void iwn_tx_done(struct iwn_softc *, struct iwn_rx_desc *, int, int, | ||||
uint8_t); | uint8_t); | ||||
static void iwn_ampdu_tx_done(struct iwn_softc *, int, int, int, int, int, | static int iwn_ampdu_check_bitmap(uint64_t, int, int); | ||||
void *); | static int iwn_ampdu_index_check(struct iwn_softc *, struct iwn_tx_ring *, | ||||
uint64_t, int, int); | |||||
static void iwn_ampdu_tx_done(struct iwn_softc *, int, int, int, void *); | |||||
static void iwn_cmd_done(struct iwn_softc *, struct iwn_rx_desc *); | static void iwn_cmd_done(struct iwn_softc *, struct iwn_rx_desc *); | ||||
static void iwn_notif_intr(struct iwn_softc *); | static void iwn_notif_intr(struct iwn_softc *); | ||||
static void iwn_wakeup_intr(struct iwn_softc *); | static void iwn_wakeup_intr(struct iwn_softc *); | ||||
static void iwn_rftoggle_task(void *, int); | static void iwn_rftoggle_task(void *, int); | ||||
static void iwn_fatal_intr(struct iwn_softc *); | static void iwn_fatal_intr(struct iwn_softc *); | ||||
static void iwn_intr(void *); | static void iwn_intr(void *); | ||||
static void iwn4965_update_sched(struct iwn_softc *, int, int, uint8_t, | static void iwn4965_update_sched(struct iwn_softc *, int, int, uint8_t, | ||||
uint16_t); | uint16_t); | ||||
▲ Show 20 Lines • Show All 1,848 Lines • ▼ Show 20 Lines | if (data->m != NULL) { | ||||
bus_dmamap_unload(ring->data_dmat, data->map); | bus_dmamap_unload(ring->data_dmat, data->map); | ||||
m_freem(data->m); | m_freem(data->m); | ||||
data->m = NULL; | data->m = NULL; | ||||
} | } | ||||
if (data->ni != NULL) { | if (data->ni != NULL) { | ||||
ieee80211_free_node(data->ni); | ieee80211_free_node(data->ni); | ||||
data->ni = NULL; | data->ni = NULL; | ||||
} | } | ||||
data->remapped = 0; | |||||
data->long_retries = 0; | |||||
} | } | ||||
/* Clear TX descriptors. */ | /* Clear TX descriptors. */ | ||||
memset(ring->desc, 0, ring->desc_dma.size); | memset(ring->desc, 0, ring->desc_dma.size); | ||||
bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, | bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, | ||||
BUS_DMASYNC_PREWRITE); | BUS_DMASYNC_PREWRITE); | ||||
sc->qfullmsk &= ~(1 << ring->qid); | sc->qfullmsk &= ~(1 << ring->qid); | ||||
ring->queued = 0; | ring->queued = 0; | ||||
ring->cur = 0; | ring->cur = 0; | ||||
Show All 23 Lines | iwn_free_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) | ||||
} | } | ||||
if (ring->data_dmat != NULL) { | if (ring->data_dmat != NULL) { | ||||
bus_dma_tag_destroy(ring->data_dmat); | bus_dma_tag_destroy(ring->data_dmat); | ||||
ring->data_dmat = NULL; | ring->data_dmat = NULL; | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
iwn_check_tx_ring(struct iwn_softc *sc, int qid) | |||||
{ | |||||
struct iwn_tx_ring *ring = &sc->txq[qid]; | |||||
KASSERT(ring->queued >= 0, ("%s: ring->queued (%d) for queue %d < 0!", | |||||
__func__, ring->queued, qid)); | |||||
if (qid >= sc->firstaggqueue) { | |||||
struct iwn_ops *ops = &sc->ops; | |||||
struct ieee80211_tx_ampdu *tap = sc->qid2tap[qid]; | |||||
if (ring->queued == 0 && !IEEE80211_AMPDU_RUNNING(tap)) { | |||||
uint16_t ssn = tap->txa_start & 0xfff; | |||||
uint8_t tid = tap->txa_tid; | |||||
int *res = tap->txa_private; | |||||
iwn_nic_lock(sc); | |||||
ops->ampdu_tx_stop(sc, qid, tid, ssn); | |||||
iwn_nic_unlock(sc); | |||||
sc->qid2tap[qid] = NULL; | |||||
free(res, M_DEVBUF); | |||||
} | |||||
} | |||||
if (ring->queued < IWN_TX_RING_LOMARK) { | |||||
sc->qfullmsk &= ~(1 << qid); | |||||
if (ring->queued == 0) | |||||
sc->sc_tx_timer = 0; | |||||
else | |||||
sc->sc_tx_timer = 5; | |||||
} | |||||
} | |||||
static void | |||||
iwn5000_ict_reset(struct iwn_softc *sc) | iwn5000_ict_reset(struct iwn_softc *sc) | ||||
{ | { | ||||
/* Disable interrupts. */ | /* Disable interrupts. */ | ||||
IWN_WRITE(sc, IWN_INT_MASK, 0); | IWN_WRITE(sc, IWN_INT_MASK, 0); | ||||
/* Reset ICT table. */ | /* Reset ICT table. */ | ||||
memset(sc->ict, 0, IWN_ICT_SIZE); | memset(sc->ict, 0, IWN_ICT_SIZE); | ||||
sc->ict_cur = 0; | sc->ict_cur = 0; | ||||
▲ Show 20 Lines • Show All 1,039 Lines • ▼ Show 20 Lines | if (ni != NULL) { | ||||
(void)ieee80211_input_all(ic, m, rssi - nf, nf); | (void)ieee80211_input_all(ic, m, rssi - nf, nf); | ||||
IWN_LOCK(sc); | IWN_LOCK(sc); | ||||
DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__); | DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__); | ||||
} | } | ||||
/* Process an incoming Compressed BlockAck. */ | |||||
static void | static void | ||||
iwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc) | iwn_agg_tx_complete(struct iwn_softc *sc, struct iwn_tx_ring *ring, int tid, | ||||
int idx, int success) | |||||
{ | { | ||||
struct ieee80211_ratectl_tx_status *txs = &sc->sc_txs; | struct ieee80211_ratectl_tx_status *txs = &sc->sc_txs; | ||||
struct iwn_ops *ops = &sc->ops; | struct iwn_tx_data *data = &ring->data[idx]; | ||||
struct iwn_node *wn; | struct iwn_node *wn; | ||||
struct mbuf *m; | |||||
struct ieee80211_node *ni; | struct ieee80211_node *ni; | ||||
KASSERT(data->ni != NULL, ("idx %d: no node", idx)); | |||||
KASSERT(data->m != NULL, ("idx %d: no mbuf", idx)); | |||||
/* Unmap and free mbuf. */ | |||||
bus_dmamap_sync(ring->data_dmat, data->map, | |||||
BUS_DMASYNC_POSTWRITE); | |||||
bus_dmamap_unload(ring->data_dmat, data->map); | |||||
m = data->m, data->m = NULL; | |||||
ni = data->ni, data->ni = NULL; | |||||
wn = (void *)ni; | |||||
#if 0 | |||||
/* XXX causes significant performance degradation. */ | |||||
txs->flags = IEEE80211_RATECTL_STATUS_SHORT_RETRY | | |||||
IEEE80211_RATECTL_STATUS_LONG_RETRY; | |||||
txs->long_retries = data->long_retries - 1; | |||||
#else | |||||
txs->flags = IEEE80211_RATECTL_STATUS_SHORT_RETRY; | |||||
#endif | |||||
txs->short_retries = wn->agg[tid].short_retries; | |||||
if (success) | |||||
txs->status = IEEE80211_RATECTL_TX_SUCCESS; | |||||
else | |||||
txs->status = IEEE80211_RATECTL_TX_FAIL_UNSPECIFIED; | |||||
wn->agg[tid].short_retries = 0; | |||||
data->long_retries = 0; | |||||
DPRINTF(sc, IWN_DEBUG_AMPDU, "%s: freeing m %p ni %p idx %d qid %d\n", | |||||
__func__, m, ni, idx, ring->qid); | |||||
ieee80211_ratectl_tx_complete(ni, txs); | |||||
ieee80211_tx_complete(ni, m, !success); | |||||
} | |||||
/* Process an incoming Compressed BlockAck. */ | |||||
static void | |||||
iwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc) | |||||
{ | |||||
struct iwn_tx_ring *ring; | |||||
struct iwn_tx_data *data; | |||||
struct iwn_node *wn; | |||||
struct iwn_compressed_ba *ba = (struct iwn_compressed_ba *)(desc + 1); | struct iwn_compressed_ba *ba = (struct iwn_compressed_ba *)(desc + 1); | ||||
struct iwn_tx_ring *txq; | |||||
struct iwn_tx_data *txdata; | |||||
struct ieee80211_tx_ampdu *tap; | struct ieee80211_tx_ampdu *tap; | ||||
struct mbuf *m; | |||||
uint64_t bitmap; | uint64_t bitmap; | ||||
uint16_t ssn; | |||||
uint8_t tid; | uint8_t tid; | ||||
int i, lastidx, qid, *res, shift; | int i, qid, shift; | ||||
int tx_ok = 0, tx_err = 0; | int tx_ok = 0; | ||||
DPRINTF(sc, IWN_DEBUG_TRACE | IWN_DEBUG_XMIT, "->%s begin\n", __func__); | DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); | ||||
qid = le16toh(ba->qid); | qid = le16toh(ba->qid); | ||||
txq = &sc->txq[ba->qid]; | tap = sc->qid2tap[qid]; | ||||
tap = sc->qid2tap[ba->qid]; | ring = &sc->txq[qid]; | ||||
tid = tap->txa_tid; | tid = tap->txa_tid; | ||||
wn = (void *)tap->txa_ni; | wn = (void *)tap->txa_ni; | ||||
res = NULL; | DPRINTF(sc, IWN_DEBUG_AMPDU, "%s: qid %d tid %d seq %04X ssn %04X\n" | ||||
ssn = 0; | "bitmap: ba %016jX wn %016jX, start %d\n", | ||||
if (!IEEE80211_AMPDU_RUNNING(tap)) { | __func__, qid, tid, le16toh(ba->seq), le16toh(ba->ssn), | ||||
res = tap->txa_private; | (uintmax_t)le64toh(ba->bitmap), (uintmax_t)wn->agg[tid].bitmap, | ||||
ssn = tap->txa_start & 0xfff; | wn->agg[tid].startidx); | ||||
} | |||||
for (lastidx = le16toh(ba->ssn) & 0xff; txq->read != lastidx;) { | |||||
txdata = &txq->data[txq->read]; | |||||
/* Unmap and free mbuf. */ | |||||
bus_dmamap_sync(txq->data_dmat, txdata->map, | |||||
BUS_DMASYNC_POSTWRITE); | |||||
bus_dmamap_unload(txq->data_dmat, txdata->map); | |||||
m = txdata->m, txdata->m = NULL; | |||||
ni = txdata->ni, txdata->ni = NULL; | |||||
KASSERT(ni != NULL, ("no node")); | |||||
KASSERT(m != NULL, ("no mbuf")); | |||||
DPRINTF(sc, IWN_DEBUG_XMIT, "%s: freeing m=%p\n", __func__, m); | |||||
ieee80211_tx_complete(ni, m, 1); | |||||
txq->queued--; | |||||
txq->read = (txq->read + 1) % IWN_TX_RING_COUNT; | |||||
} | |||||
if (txq->queued == 0 && res != NULL) { | |||||
iwn_nic_lock(sc); | |||||
ops->ampdu_tx_stop(sc, qid, tid, ssn); | |||||
iwn_nic_unlock(sc); | |||||
sc->qid2tap[qid] = NULL; | |||||
free(res, M_DEVBUF); | |||||
return; | |||||
} | |||||
if (wn->agg[tid].bitmap == 0) | if (wn->agg[tid].bitmap == 0) | ||||
return; | return; | ||||
shift = wn->agg[tid].startidx - ((le16toh(ba->seq) >> 4) & 0xff); | shift = wn->agg[tid].startidx - ((le16toh(ba->seq) >> 4) & 0xff); | ||||
if (shift < 0) | if (shift <= -64) | ||||
shift += 0x100; | shift += 0x100; | ||||
if (wn->agg[tid].nframes > (64 - shift)) | |||||
return; | |||||
/* | /* | ||||
* Walk the bitmap and calculate how many successful and failed | * Walk the bitmap and calculate how many successful attempts | ||||
* attempts are made. | * are made. | ||||
* | * | ||||
* Yes, the rate control code doesn't know these are A-MPDU | * Yes, the rate control code doesn't know these are A-MPDU | ||||
* subframes and that it's okay to fail some of these. | * subframes; due to that long_retries stats are not used here. | ||||
*/ | */ | ||||
ni = tap->txa_ni; | bitmap = le64toh(ba->bitmap); | ||||
bitmap = (le64toh(ba->bitmap) >> shift) & wn->agg[tid].bitmap; | if (shift >= 0) | ||||
for (i = 0; bitmap; i++) { | bitmap >>= shift; | ||||
txs->flags = 0; /* XXX TODO */ | else | ||||
if ((bitmap & 1) == 0) { | bitmap <<= -shift; | ||||
tx_err ++; | bitmap &= wn->agg[tid].bitmap; | ||||
txs->status = IEEE80211_RATECTL_TX_FAIL_UNSPECIFIED; | wn->agg[tid].bitmap = 0; | ||||
} else { | |||||
for (i = wn->agg[tid].startidx; | |||||
bitmap; | |||||
bitmap >>= 1, i = (i + 1) % IWN_TX_RING_COUNT) { | |||||
if ((bitmap & 1) == 0) | |||||
continue; | |||||
data = &ring->data[i]; | |||||
if (__predict_false(data->m == NULL)) { | |||||
/* | |||||
* There is no frame; skip this entry. | |||||
* | |||||
* NB: it is "ok" to have both | |||||
* 'tx done' + 'compressed BA' replies for frame | |||||
* with STATE_SCD_QUERY status. | |||||
*/ | |||||
DPRINTF(sc, IWN_DEBUG_AMPDU, | |||||
"%s: ring %d: no entry %d\n", __func__, qid, i); | |||||
continue; | |||||
} | |||||
tx_ok ++; | tx_ok++; | ||||
txs->status = IEEE80211_RATECTL_TX_SUCCESS; | iwn_agg_tx_complete(sc, ring, tid, i, 1); | ||||
} | } | ||||
ieee80211_ratectl_tx_complete(ni, txs); | |||||
bitmap >>= 1; | |||||
} | |||||
DPRINTF(sc, IWN_DEBUG_TRACE | IWN_DEBUG_XMIT, | ring->queued -= tx_ok; | ||||
"->%s: end; %d ok; %d err\n",__func__, tx_ok, tx_err); | iwn_check_tx_ring(sc, qid); | ||||
DPRINTF(sc, IWN_DEBUG_TRACE | IWN_DEBUG_AMPDU, | |||||
"->%s: end; %d ok\n",__func__, tx_ok); | |||||
} | } | ||||
/* | /* | ||||
* Process a CALIBRATION_RESULT notification sent by the initialization | * Process a CALIBRATION_RESULT notification sent by the initialization | ||||
* firmware on response to a CMD_CALIB_CONFIG command (5000 only). | * firmware on response to a CMD_CALIB_CONFIG command (5000 only). | ||||
*/ | */ | ||||
static void | static void | ||||
iwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc) | iwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc) | ||||
▲ Show 20 Lines • Show All 231 Lines • ▼ Show 20 Lines | DPRINTF(sc, IWN_DEBUG_XMIT, "%s: " | ||||
"qid %d idx %d RTS retries %d ACK retries %d nkill %d rate %x duration %d status %x\n", | "qid %d idx %d RTS retries %d ACK retries %d nkill %d rate %x duration %d status %x\n", | ||||
__func__, desc->qid, desc->idx, | __func__, desc->qid, desc->idx, | ||||
stat->rtsfailcnt, | stat->rtsfailcnt, | ||||
stat->ackfailcnt, | stat->ackfailcnt, | ||||
stat->btkillcnt, | stat->btkillcnt, | ||||
stat->rate, le16toh(stat->duration), | stat->rate, le16toh(stat->duration), | ||||
le32toh(stat->status)); | le32toh(stat->status)); | ||||
if (qid >= sc->firstaggqueue) { | if (qid >= sc->firstaggqueue && stat->nframes != 1) { | ||||
iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes, | iwn_ampdu_tx_done(sc, qid, stat->nframes, stat->rtsfailcnt, | ||||
stat->rtsfailcnt, stat->ackfailcnt, &stat->status); | &stat->status); | ||||
} else { | } else { | ||||
iwn_tx_done(sc, desc, stat->rtsfailcnt, stat->ackfailcnt, | iwn_tx_done(sc, desc, stat->rtsfailcnt, stat->ackfailcnt, | ||||
le32toh(stat->status) & 0xff); | le32toh(stat->status) & 0xff); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
iwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, | iwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, | ||||
Show All 11 Lines | DPRINTF(sc, IWN_DEBUG_XMIT, "%s: " | ||||
stat->rate, le16toh(stat->duration), | stat->rate, le16toh(stat->duration), | ||||
le32toh(stat->status)); | le32toh(stat->status)); | ||||
#ifdef notyet | #ifdef notyet | ||||
/* Reset TX scheduler slot. */ | /* Reset TX scheduler slot. */ | ||||
iwn5000_reset_sched(sc, qid, desc->idx); | iwn5000_reset_sched(sc, qid, desc->idx); | ||||
#endif | #endif | ||||
if (qid >= sc->firstaggqueue) { | if (qid >= sc->firstaggqueue && stat->nframes != 1) { | ||||
iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes, | iwn_ampdu_tx_done(sc, qid, stat->nframes, stat->rtsfailcnt, | ||||
stat->rtsfailcnt, stat->ackfailcnt, &stat->status); | &stat->status); | ||||
} else { | } else { | ||||
iwn_tx_done(sc, desc, stat->rtsfailcnt, stat->ackfailcnt, | iwn_tx_done(sc, desc, stat->rtsfailcnt, stat->ackfailcnt, | ||||
le16toh(stat->status) & 0xff); | le16toh(stat->status) & 0xff); | ||||
} | } | ||||
} | } | ||||
static void | |||||
iwn_adj_ampdu_ptr(struct iwn_softc *sc, struct iwn_tx_ring *ring) | |||||
{ | |||||
int i; | |||||
for (i = ring->read; i != ring->cur; i = (i + 1) % IWN_TX_RING_COUNT) { | |||||
struct iwn_tx_data *data = &ring->data[i]; | |||||
if (data->m != NULL) | |||||
break; | |||||
data->remapped = 0; | |||||
} | |||||
ring->read = i; | |||||
} | |||||
/* | /* | ||||
* Adapter-independent backend for TX_DONE firmware notifications. | * Adapter-independent backend for TX_DONE firmware notifications. | ||||
*/ | */ | ||||
static void | static void | ||||
iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int rtsfailcnt, | iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int rtsfailcnt, | ||||
int ackfailcnt, uint8_t status) | int ackfailcnt, uint8_t status) | ||||
{ | { | ||||
struct ieee80211_ratectl_tx_status *txs = &sc->sc_txs; | struct ieee80211_ratectl_tx_status *txs = &sc->sc_txs; | ||||
struct iwn_tx_ring *ring = &sc->txq[desc->qid & IWN_RX_DESC_QID_MSK]; | struct iwn_tx_ring *ring = &sc->txq[desc->qid & IWN_RX_DESC_QID_MSK]; | ||||
struct iwn_tx_data *data = &ring->data[desc->idx]; | struct iwn_tx_data *data = &ring->data[desc->idx]; | ||||
struct mbuf *m; | struct mbuf *m; | ||||
struct ieee80211_node *ni; | struct ieee80211_node *ni; | ||||
if (__predict_false(data->m == NULL && | |||||
ring->qid >= sc->firstaggqueue)) { | |||||
/* | |||||
* There is no frame; skip this entry. | |||||
*/ | |||||
DPRINTF(sc, IWN_DEBUG_AMPDU, "%s: ring %d: no entry %d\n", | |||||
__func__, ring->qid, desc->idx); | |||||
return; | |||||
} | |||||
KASSERT(data->ni != NULL, ("no node")); | KASSERT(data->ni != NULL, ("no node")); | ||||
KASSERT(data->m != NULL, ("no mbuf")); | |||||
DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); | DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); | ||||
/* Unmap and free mbuf. */ | /* Unmap and free mbuf. */ | ||||
bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); | bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); | ||||
bus_dmamap_unload(ring->data_dmat, data->map); | bus_dmamap_unload(ring->data_dmat, data->map); | ||||
m = data->m, data->m = NULL; | m = data->m, data->m = NULL; | ||||
ni = data->ni, data->ni = NULL; | ni = data->ni, data->ni = NULL; | ||||
data->long_retries = 0; | |||||
if (ring->qid >= sc->firstaggqueue) | |||||
iwn_adj_ampdu_ptr(sc, ring); | |||||
/* | /* | ||||
* XXX f/w may hang (device timeout) when desc->idx - ring->read == 64 | |||||
* (aggregation queues only). | |||||
*/ | |||||
ring->queued--; | |||||
iwn_check_tx_ring(sc, ring->qid); | |||||
/* | |||||
* Update rate control statistics for the node. | * Update rate control statistics for the node. | ||||
*/ | */ | ||||
txs->flags = IEEE80211_RATECTL_STATUS_SHORT_RETRY | | txs->flags = IEEE80211_RATECTL_STATUS_SHORT_RETRY | | ||||
IEEE80211_RATECTL_STATUS_LONG_RETRY; | IEEE80211_RATECTL_STATUS_LONG_RETRY; | ||||
txs->short_retries = rtsfailcnt; | txs->short_retries = rtsfailcnt; | ||||
txs->long_retries = ackfailcnt; | txs->long_retries = ackfailcnt; | ||||
if (!(status & IWN_TX_FAIL)) | if (!(status & IWN_TX_FAIL)) | ||||
txs->status = IEEE80211_RATECTL_TX_SUCCESS; | txs->status = IEEE80211_RATECTL_TX_SUCCESS; | ||||
Show All 31 Lines | iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int rtsfailcnt, | ||||
*/ | */ | ||||
if (status == IWN_TX_FAIL_TX_LOCKED && | if (status == IWN_TX_FAIL_TX_LOCKED && | ||||
ni->ni_vap->iv_state == IEEE80211_S_AUTH) | ni->ni_vap->iv_state == IEEE80211_S_AUTH) | ||||
ieee80211_tx_complete(ni, m, 0); | ieee80211_tx_complete(ni, m, 0); | ||||
else | else | ||||
ieee80211_tx_complete(ni, m, | ieee80211_tx_complete(ni, m, | ||||
(status & IWN_TX_FAIL) != 0); | (status & IWN_TX_FAIL) != 0); | ||||
sc->sc_tx_timer = 0; | |||||
if (--ring->queued < IWN_TX_RING_LOMARK) | |||||
sc->qfullmsk &= ~(1 << ring->qid); | |||||
DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__); | DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__); | ||||
} | } | ||||
/* | /* | ||||
* Process a "command done" firmware notification. This is where we wakeup | * Process a "command done" firmware notification. This is where we wakeup | ||||
* processes waiting for a synchronous command completion. | * processes waiting for a synchronous command completion. | ||||
*/ | */ | ||||
static void | static void | ||||
Show All 20 Lines | bus_dmamap_sync(ring->data_dmat, data->map, | ||||
BUS_DMASYNC_POSTWRITE); | BUS_DMASYNC_POSTWRITE); | ||||
bus_dmamap_unload(ring->data_dmat, data->map); | bus_dmamap_unload(ring->data_dmat, data->map); | ||||
m_freem(data->m); | m_freem(data->m); | ||||
data->m = NULL; | data->m = NULL; | ||||
} | } | ||||
wakeup(&ring->desc[desc->idx]); | wakeup(&ring->desc[desc->idx]); | ||||
} | } | ||||
static int | |||||
iwn_ampdu_check_bitmap(uint64_t bitmap, int start, int idx) | |||||
{ | |||||
int bit, shift; | |||||
bit = idx - start; | |||||
shift = 0; | |||||
if (bit >= 64) { | |||||
shift = 0x100 - bit; | |||||
bit = 0; | |||||
} else if (bit <= -64) | |||||
bit = 0x100 + bit; | |||||
else if (bit < 0) { | |||||
shift = -bit; | |||||
bit = 0; | |||||
} | |||||
if (bit - shift >= 64) | |||||
return (0); | |||||
return ((bitmap & (1ULL << (bit - shift))) != 0); | |||||
} | |||||
/* | |||||
* Firmware bug workaround: in case if 'retries' counter | |||||
* overflows 'seqno' field will be incremented: | |||||
* status|sequence|status|sequence|status|sequence | |||||
* 0000 0A48 0001 0A49 0000 0A6A | |||||
* 1000 0A48 1000 0A49 1000 0A6A | |||||
* 2000 0A48 2000 0A49 2000 0A6A | |||||
* ... | |||||
* E000 0A48 E000 0A49 E000 0A6A | |||||
* F000 0A48 F000 0A49 F000 0A6A | |||||
* 0000 0A49 0000 0A49 0000 0A6B | |||||
* 1000 0A49 1000 0A49 1000 0A6B | |||||
* ... | |||||
* D000 0A49 D000 0A49 D000 0A6B | |||||
* E000 0A49 E001 0A49 E000 0A6B | |||||
* F000 0A49 F001 0A49 F000 0A6B | |||||
* 0000 0A4A 0000 0A4B 0000 0A6A | |||||
* 1000 0A4A 1000 0A4B 1000 0A6A | |||||
* ... | |||||
* | |||||
* Odd 'seqno' numbers are incremened by 2 every 2 overflows. | |||||
* For even 'seqno' % 4 != 0 overflow is cyclic (0 -> +1 -> 0). | |||||
* Not checked with nretries >= 64. | |||||
* | |||||
*/ | |||||
static int | |||||
iwn_ampdu_index_check(struct iwn_softc *sc, struct iwn_tx_ring *ring, | |||||
uint64_t bitmap, int start, int idx) | |||||
{ | |||||
struct ieee80211com *ic = &sc->sc_ic; | |||||
struct iwn_tx_data *data; | |||||
int diff, min_retries, max_retries, new_idx, loop_end; | |||||
new_idx = idx - IWN_LONG_RETRY_LIMIT_LOG; | |||||
if (new_idx < 0) | |||||
new_idx += IWN_TX_RING_COUNT; | |||||
/* | |||||
* Corner case: check if retry count is not too big; | |||||
* reset device otherwise. | |||||
*/ | |||||
if (!iwn_ampdu_check_bitmap(bitmap, start, new_idx)) { | |||||
data = &ring->data[new_idx]; | |||||
if (data->long_retries > IWN_LONG_RETRY_LIMIT) { | |||||
device_printf(sc->sc_dev, | |||||
"%s: retry count (%d) for idx %d/%d overflow, " | |||||
"resetting...\n", __func__, data->long_retries, | |||||
ring->qid, new_idx); | |||||
ieee80211_restart_all(ic); | |||||
return (-1); | |||||
} | |||||
} | |||||
/* Correct index if needed. */ | |||||
loop_end = idx; | |||||
do { | |||||
data = &ring->data[new_idx]; | |||||
diff = idx - new_idx; | |||||
if (diff < 0) | |||||
diff += IWN_TX_RING_COUNT; | |||||
min_retries = IWN_LONG_RETRY_FW_OVERFLOW * diff; | |||||
if ((new_idx % 2) == 0) | |||||
max_retries = IWN_LONG_RETRY_FW_OVERFLOW * (diff + 1); | |||||
else | |||||
max_retries = IWN_LONG_RETRY_FW_OVERFLOW * (diff + 2); | |||||
if (!iwn_ampdu_check_bitmap(bitmap, start, new_idx) && | |||||
((data->long_retries >= min_retries && | |||||
data->long_retries < max_retries) || | |||||
(diff == 1 && | |||||
(new_idx & 0x03) == 0x02 && | |||||
data->long_retries >= IWN_LONG_RETRY_FW_OVERFLOW))) { | |||||
DPRINTF(sc, IWN_DEBUG_AMPDU, | |||||
"%s: correcting index %d -> %d in queue %d" | |||||
" (retries %d)\n", __func__, idx, new_idx, | |||||
ring->qid, data->long_retries); | |||||
return (new_idx); | |||||
} | |||||
new_idx = (new_idx + 1) % IWN_TX_RING_COUNT; | |||||
} while (new_idx != loop_end); | |||||
return (idx); | |||||
} | |||||
static void | static void | ||||
iwn_ampdu_tx_done(struct iwn_softc *sc, int qid, int idx, int nframes, | iwn_ampdu_tx_done(struct iwn_softc *sc, int qid, int nframes, int rtsfailcnt, | ||||
int rtsfailcnt, int ackfailcnt, void *stat) | void *stat) | ||||
{ | { | ||||
struct iwn_ops *ops = &sc->ops; | |||||
struct iwn_tx_ring *ring = &sc->txq[qid]; | struct iwn_tx_ring *ring = &sc->txq[qid]; | ||||
struct ieee80211_ratectl_tx_status *txs = &sc->sc_txs; | struct ieee80211_tx_ampdu *tap = sc->qid2tap[qid]; | ||||
struct iwn_node *wn = (void *)tap->txa_ni; | |||||
struct iwn_tx_data *data; | struct iwn_tx_data *data; | ||||
struct mbuf *m; | uint64_t bitmap = 0; | ||||
struct iwn_node *wn; | |||||
struct ieee80211_node *ni; | |||||
struct ieee80211_tx_ampdu *tap; | |||||
uint64_t bitmap; | |||||
uint32_t *status = stat; | |||||
uint16_t *aggstatus = stat; | uint16_t *aggstatus = stat; | ||||
uint16_t ssn; | uint8_t tid = tap->txa_tid; | ||||
uint8_t tid; | int bit, i, idx, shift, start, tx_err; | ||||
int bit, i, lastidx, *res, seqno, shift, start; | |||||
/* XXX TODO: status is le16 field! Grr */ | |||||
DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); | DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); | ||||
DPRINTF(sc, IWN_DEBUG_XMIT, "%s: nframes=%d, status=0x%08x\n", | |||||
__func__, | |||||
nframes, | |||||
*status); | |||||
tap = sc->qid2tap[qid]; | start = le16toh(*(aggstatus + nframes * 2)) & 0xff; | ||||
tid = tap->txa_tid; | |||||
wn = (void *)tap->txa_ni; | |||||
ni = tap->txa_ni; | |||||
for (i = 0; i < nframes; i++) { | |||||
uint16_t status = le16toh(aggstatus[i * 2]); | |||||
if (status & IWN_AGG_TX_STATE_IGNORE_MASK) | |||||
continue; | |||||
idx = le16toh(aggstatus[i * 2 + 1]) & 0xff; | |||||
data = &ring->data[idx]; | |||||
if (data->remapped) { | |||||
idx = iwn_ampdu_index_check(sc, ring, bitmap, start, idx); | |||||
if (idx == -1) { | |||||
/* skip error (device will be restarted anyway). */ | |||||
continue; | |||||
} | |||||
/* Index may have changed. */ | |||||
data = &ring->data[idx]; | |||||
} | |||||
/* | /* | ||||
* XXX TODO: ACK and RTS failures would be nice here! | * XXX Sometimes (rarely) some frames are excluded from events. | ||||
* XXX Due to that long_retries counter may be wrong. | |||||
*/ | */ | ||||
data->long_retries &= ~0x0f; | |||||
data->long_retries += IWN_AGG_TX_TRY_COUNT(status) + 1; | |||||
if (data->long_retries >= IWN_LONG_RETRY_FW_OVERFLOW) { | |||||
int diff, wrong_idx; | |||||
diff = data->long_retries / IWN_LONG_RETRY_FW_OVERFLOW; | |||||
wrong_idx = (idx + diff) % IWN_TX_RING_COUNT; | |||||
/* | /* | ||||
* A-MPDU single frame status - if we failed to transmit it | * Mark the entry so the above code will check it | ||||
* in A-MPDU, then it may be a permanent failure. | * next time. | ||||
* | |||||
* XXX TODO: check what the Linux iwlwifi driver does here; | |||||
* there's some permanent and temporary failures that may be | |||||
* handled differently. | |||||
*/ | */ | ||||
if (nframes == 1) { | ring->data[wrong_idx].remapped = 1; | ||||
txs->flags = IEEE80211_RATECTL_STATUS_SHORT_RETRY | | } | ||||
IEEE80211_RATECTL_STATUS_LONG_RETRY; | |||||
txs->short_retries = rtsfailcnt; | if (status & IWN_AGG_TX_STATE_UNDERRUN_MSK) { | ||||
txs->long_retries = ackfailcnt; | |||||
if ((*status & 0xff) != 1 && (*status & 0xff) != 2) { | |||||
#ifdef NOT_YET | |||||
printf("ieee80211_send_bar()\n"); | |||||
#endif | |||||
/* | /* | ||||
* If we completely fail a transmit, make sure a | * NB: count retries but postpone - it was not | ||||
* notification is pushed up to the rate control | * transmitted. | ||||
* layer. | |||||
*/ | */ | ||||
/* XXX */ | continue; | ||||
txs->status = IEEE80211_RATECTL_TX_FAIL_UNSPECIFIED; | |||||
} else { | |||||
/* | |||||
* If nframes=1, then we won't be getting a BA for | |||||
* this frame. Ensure that we correctly update the | |||||
* rate control code with how many retries were | |||||
* needed to send it. | |||||
*/ | |||||
txs->status = IEEE80211_RATECTL_TX_SUCCESS; | |||||
} | } | ||||
ieee80211_ratectl_tx_complete(ni, txs); | |||||
} | |||||
bitmap = 0; | |||||
start = idx; | |||||
for (i = 0; i < nframes; i++) { | |||||
if (le16toh(aggstatus[i * 2]) & 0xc) | |||||
continue; | |||||
idx = le16toh(aggstatus[2*i + 1]) & 0xff; | |||||
bit = idx - start; | bit = idx - start; | ||||
shift = 0; | shift = 0; | ||||
if (bit >= 64) { | if (bit >= 64) { | ||||
shift = 0x100 - idx + start; | shift = 0x100 - bit; | ||||
bit = 0; | bit = 0; | ||||
start = idx; | |||||
} else if (bit <= -64) | } else if (bit <= -64) | ||||
bit = 0x100 - start + idx; | bit = 0x100 + bit; | ||||
else if (bit < 0) { | else if (bit < 0) { | ||||
shift = start - idx; | shift = -bit; | ||||
start = idx; | |||||
bit = 0; | bit = 0; | ||||
} | } | ||||
bitmap = bitmap << shift; | bitmap = bitmap << shift; | ||||
bitmap |= 1ULL << bit; | bitmap |= 1ULL << bit; | ||||
} | } | ||||
tap = sc->qid2tap[qid]; | |||||
tid = tap->txa_tid; | |||||
wn = (void *)tap->txa_ni; | |||||
wn->agg[tid].bitmap = bitmap; | |||||
wn->agg[tid].startidx = start; | wn->agg[tid].startidx = start; | ||||
wn->agg[tid].nframes = nframes; | wn->agg[tid].bitmap = bitmap; | ||||
wn->agg[tid].short_retries = rtsfailcnt; | |||||
res = NULL; | DPRINTF(sc, IWN_DEBUG_AMPDU, "%s: nframes %d start %d bitmap %016jX\n", | ||||
ssn = 0; | __func__, nframes, start, (uintmax_t)bitmap); | ||||
if (!IEEE80211_AMPDU_RUNNING(tap)) { | |||||
res = tap->txa_private; | |||||
ssn = tap->txa_start & 0xfff; | |||||
} | |||||
/* This is going nframes DWORDS into the descriptor? */ | i = ring->read; | ||||
seqno = le32toh(*(status + nframes)) & 0xfff; | |||||
for (lastidx = (seqno & 0xff); ring->read != lastidx;) { | |||||
data = &ring->data[ring->read]; | |||||
/* Unmap and free mbuf. */ | for (tx_err = 0; | ||||
bus_dmamap_sync(ring->data_dmat, data->map, | i != wn->agg[tid].startidx; | ||||
BUS_DMASYNC_POSTWRITE); | i = (i + 1) % IWN_TX_RING_COUNT) { | ||||
bus_dmamap_unload(ring->data_dmat, data->map); | data = &ring->data[i]; | ||||
m = data->m, data->m = NULL; | data->remapped = 0; | ||||
ni = data->ni, data->ni = NULL; | if (data->m == NULL) | ||||
continue; | |||||
KASSERT(ni != NULL, ("no node")); | tx_err++; | ||||
KASSERT(m != NULL, ("no mbuf")); | iwn_agg_tx_complete(sc, ring, tid, i, 0); | ||||
DPRINTF(sc, IWN_DEBUG_XMIT, "%s: freeing m=%p\n", __func__, m); | |||||
ieee80211_tx_complete(ni, m, 1); | |||||
ring->queued--; | |||||
ring->read = (ring->read + 1) % IWN_TX_RING_COUNT; | |||||
} | } | ||||
if (ring->queued == 0 && res != NULL) { | ring->read = wn->agg[tid].startidx; | ||||
iwn_nic_lock(sc); | ring->queued -= tx_err; | ||||
ops->ampdu_tx_stop(sc, qid, tid, ssn); | |||||
iwn_nic_unlock(sc); | |||||
sc->qid2tap[qid] = NULL; | |||||
free(res, M_DEVBUF); | |||||
return; | |||||
} | |||||
sc->sc_tx_timer = 0; | iwn_check_tx_ring(sc, qid); | ||||
if (ring->queued < IWN_TX_RING_LOMARK) | |||||
sc->qfullmsk &= ~(1 << ring->qid); | |||||
DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__); | DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__); | ||||
} | } | ||||
/* | /* | ||||
* Process an INT_FH_RX or INT_SW_RX interrupt. | * Process an INT_FH_RX or INT_SW_RX interrupt. | ||||
*/ | */ | ||||
static void | static void | ||||
▲ Show 20 Lines • Show All 547 Lines • ▼ Show 20 Lines | iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) | ||||
struct ieee80211com *ic = ni->ni_ic; | struct ieee80211com *ic = ni->ni_ic; | ||||
struct iwn_node *wn = (void *)ni; | struct iwn_node *wn = (void *)ni; | ||||
struct iwn_tx_ring *ring; | struct iwn_tx_ring *ring; | ||||
struct iwn_tx_cmd *cmd; | struct iwn_tx_cmd *cmd; | ||||
struct iwn_cmd_data *tx; | struct iwn_cmd_data *tx; | ||||
struct ieee80211_frame *wh; | struct ieee80211_frame *wh; | ||||
struct ieee80211_key *k = NULL; | struct ieee80211_key *k = NULL; | ||||
uint32_t flags; | uint32_t flags; | ||||
uint16_t seqno, qos; | uint16_t qos; | ||||
uint8_t tid, type; | uint8_t tid, type; | ||||
int ac, totlen, rate; | int ac, totlen, rate; | ||||
DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); | DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); | ||||
IWN_LOCK_ASSERT(sc); | IWN_LOCK_ASSERT(sc); | ||||
wh = mtod(m, struct ieee80211_frame *); | wh = mtod(m, struct ieee80211_frame *); | ||||
Show All 25 Lines | iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) | ||||
/* | /* | ||||
* XXX TODO: Group addressed frames aren't aggregated and must | * XXX TODO: Group addressed frames aren't aggregated and must | ||||
* go to the normal non-aggregation queue, and have a NONQOS TID | * go to the normal non-aggregation queue, and have a NONQOS TID | ||||
* assigned from net80211. | * assigned from net80211. | ||||
*/ | */ | ||||
ac = M_WME_GETAC(m); | ac = M_WME_GETAC(m); | ||||
seqno = ni->ni_txseqs[tid]; | |||||
if (m->m_flags & M_AMPDU_MPDU) { | if (m->m_flags & M_AMPDU_MPDU) { | ||||
struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac]; | struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac]; | ||||
if (!IEEE80211_AMPDU_RUNNING(tap)) { | if (!IEEE80211_AMPDU_RUNNING(tap)) | ||||
return (EINVAL); | return (EINVAL); | ||||
} | |||||
/* | /* NB: clear Fragment Number field. */ | ||||
* Queue this frame to the hardware ring that we've | /* XXX move this to net80211 */ | ||||
* negotiated AMPDU TX on. | *(uint16_t *)wh->i_seq = 0; | ||||
* | |||||
* Note that the sequence number must match the TX slot | |||||
* being used! | |||||
*/ | |||||
ac = *(int *)tap->txa_private; | ac = *(int *)tap->txa_private; | ||||
*(uint16_t *)wh->i_seq = | |||||
htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); | |||||
ni->ni_txseqs[tid]++; | |||||
} | } | ||||
/* Encrypt the frame if need be. */ | /* Encrypt the frame if need be. */ | ||||
if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { | if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { | ||||
/* Retrieve key for TX. */ | /* Retrieve key for TX. */ | ||||
k = ieee80211_crypto_encap(ni, m); | k = ieee80211_crypto_encap(ni, m); | ||||
if (k == NULL) { | if (k == NULL) { | ||||
return ENOBUFS; | return ENOBUFS; | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | if (flags & (IWN_TX_NEED_RTS | IWN_TX_NEED_CTS)) { | ||||
flags &= ~(IWN_TX_NEED_RTS | IWN_TX_NEED_CTS); | flags &= ~(IWN_TX_NEED_RTS | IWN_TX_NEED_CTS); | ||||
flags |= IWN_TX_NEED_PROTECTION; | flags |= IWN_TX_NEED_PROTECTION; | ||||
} else | } else | ||||
flags |= IWN_TX_FULL_TXOP; | flags |= IWN_TX_FULL_TXOP; | ||||
} | } | ||||
} | } | ||||
ring = &sc->txq[ac]; | ring = &sc->txq[ac]; | ||||
if ((m->m_flags & M_AMPDU_MPDU) != 0 && | if (m->m_flags & M_AMPDU_MPDU) { | ||||
(seqno % 256) != ring->cur) { | uint16_t seqno = ni->ni_txseqs[tid]; | ||||
if (ring->queued > IWN_TX_RING_COUNT / 2 && | |||||
(ring->cur + 1) % IWN_TX_RING_COUNT == ring->read) { | |||||
DPRINTF(sc, IWN_DEBUG_AMPDU, "%s: no more space " | |||||
"(queued %d) left in %d queue!\n", | |||||
__func__, ring->queued, ac); | |||||
return (ENOBUFS); | |||||
} | |||||
/* | |||||
* Queue this frame to the hardware ring that we've | |||||
* negotiated AMPDU TX on. | |||||
* | |||||
* Note that the sequence number must match the TX slot | |||||
* being used! | |||||
*/ | |||||
if ((seqno % 256) != ring->cur) { | |||||
device_printf(sc->sc_dev, | device_printf(sc->sc_dev, | ||||
"%s: m=%p: seqno (%d) (%d) != ring index (%d) !\n", | "%s: m=%p: seqno (%d) (%d) != ring index (%d) !\n", | ||||
__func__, | __func__, | ||||
m, | m, | ||||
seqno, | seqno, | ||||
seqno % 256, | seqno % 256, | ||||
ring->cur); | ring->cur); | ||||
/* XXX until D9195 will not be committed */ | |||||
ni->ni_txseqs[tid] &= ~0xff; | |||||
ni->ni_txseqs[tid] += ring->cur; | |||||
seqno = ni->ni_txseqs[tid]; | |||||
} | } | ||||
*(uint16_t *)wh->i_seq = | |||||
htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); | |||||
ni->ni_txseqs[tid]++; | |||||
} | |||||
/* Prepare TX firmware command. */ | /* Prepare TX firmware command. */ | ||||
cmd = &ring->cmd[ring->cur]; | cmd = &ring->cmd[ring->cur]; | ||||
tx = (struct iwn_cmd_data *)cmd->data; | tx = (struct iwn_cmd_data *)cmd->data; | ||||
/* NB: No need to clear tx, all fields are reinitialized here. */ | /* NB: No need to clear tx, all fields are reinitialized here. */ | ||||
tx->scratch = 0; /* clear "scratch" area */ | tx->scratch = 0; /* clear "scratch" area */ | ||||
if (IEEE80211_IS_MULTICAST(wh->i_addr1) || | if (IEEE80211_IS_MULTICAST(wh->i_addr1) || | ||||
▲ Show 20 Lines • Show All 142 Lines • ▼ Show 20 Lines | iwn_tx_cmd(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, | ||||
int totlen, error, pad, nsegs = 0, i; | int totlen, error, pad, nsegs = 0, i; | ||||
wh = mtod(m, struct ieee80211_frame *); | wh = mtod(m, struct ieee80211_frame *); | ||||
hdrlen = ieee80211_anyhdrsize(wh); | hdrlen = ieee80211_anyhdrsize(wh); | ||||
totlen = m->m_pkthdr.len; | totlen = m->m_pkthdr.len; | ||||
desc = &ring->desc[ring->cur]; | desc = &ring->desc[ring->cur]; | ||||
data = &ring->data[ring->cur]; | data = &ring->data[ring->cur]; | ||||
if (__predict_false(data->m != NULL || data->ni != NULL)) { | |||||
device_printf(sc->sc_dev, "%s: ni (%p) or m (%p) for idx %d " | |||||
"in queue %d is not NULL!\n", __func__, data->ni, data->m, | |||||
ring->cur, ring->qid); | |||||
return EIO; | |||||
} | |||||
/* Prepare TX firmware command. */ | /* Prepare TX firmware command. */ | ||||
cmd = &ring->cmd[ring->cur]; | cmd = &ring->cmd[ring->cur]; | ||||
cmd->code = IWN_CMD_TX_DATA; | cmd->code = IWN_CMD_TX_DATA; | ||||
cmd->flags = 0; | cmd->flags = 0; | ||||
cmd->qid = ring->qid; | cmd->qid = ring->qid; | ||||
cmd->idx = ring->cur; | cmd->idx = ring->cur; | ||||
▲ Show 20 Lines • Show All 4,355 Lines • Show Last 20 Lines |