diff options
Diffstat (limited to 'drivers/net/wireless/ath9k/recv.c')
-rw-r--r-- | drivers/net/wireless/ath9k/recv.c | 1318 |
1 files changed, 1318 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath9k/recv.c b/drivers/net/wireless/ath9k/recv.c new file mode 100644 index 000000000000..2fe806175c01 --- /dev/null +++ b/drivers/net/wireless/ath9k/recv.c @@ -0,0 +1,1318 @@ +/* + * Copyright (c) 2008 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Implementation of receive path. + */ + +#include "core.h" + +/* + * Setup and link descriptors. + * + * 11N: we can no longer afford to self link the last descriptor. + * MAC acknowledges BA status as long as it copies frames to host + * buffer (or rx fifo). This can incorrectly acknowledge packets + * to a sender if last desc is self-linked. + * + * NOTE: Caller should hold the rxbuf lock. + */ + +static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath_desc *ds; + struct sk_buff *skb; + + ATH_RXBUF_RESET(bf); + + ds = bf->bf_desc; + ds->ds_link = 0; /* link to null */ + ds->ds_data = bf->bf_buf_addr; + + /* XXX For RADAR? + * virtual addr of the beginning of the buffer. */ + skb = bf->bf_mpdu; + ASSERT(skb != NULL); + ds->ds_vdata = skb->data; + + /* setup rx descriptors */ + ath9k_hw_setuprxdesc(ah, + ds, + skb_tailroom(skb), /* buffer size */ + 0); + + if (sc->sc_rxlink == NULL) + ath9k_hw_putrxbuf(ah, bf->bf_daddr); + else + *sc->sc_rxlink = bf->bf_daddr; + + sc->sc_rxlink = &ds->ds_link; + ath9k_hw_rxena(ah); +} + +/* Process received BAR frame */ + +static int ath_bar_rx(struct ath_softc *sc, + struct ath_node *an, + struct sk_buff *skb) +{ + struct ieee80211_bar *bar; + struct ath_arx_tid *rxtid; + struct sk_buff *tskb; + struct ath_recv_status *rx_status; + int tidno, index, cindex; + u16 seqno; + + /* look at BAR contents */ + + bar = (struct ieee80211_bar *)skb->data; + tidno = (le16_to_cpu(bar->control) & IEEE80211_BAR_CTL_TID_M) + >> IEEE80211_BAR_CTL_TID_S; + seqno = le16_to_cpu(bar->start_seq_num) >> IEEE80211_SEQ_SEQ_SHIFT; + + /* process BAR - indicate all pending RX frames till the BAR seqno */ + + rxtid = &an->an_aggr.rx.tid[tidno]; + + spin_lock_bh(&rxtid->tidlock); + + /* get relative index */ + + index = ATH_BA_INDEX(rxtid->seq_next, seqno); + + /* drop BAR if old sequence (index is too large) */ + + if ((index > rxtid->baw_size) && + (index > (IEEE80211_SEQ_MAX - (rxtid->baw_size << 2)))) + /* discard frame, ieee layer may not treat frame as a dup */ + goto unlock_and_free; + + /* complete receive processing for all pending frames upto BAR seqno */ + + cindex = (rxtid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); + while ((rxtid->baw_head != rxtid->baw_tail) && + (rxtid->baw_head != cindex)) { + tskb = rxtid->rxbuf[rxtid->baw_head].rx_wbuf; + rx_status = &rxtid->rxbuf[rxtid->baw_head].rx_status; + rxtid->rxbuf[rxtid->baw_head].rx_wbuf = NULL; + + if (tskb != NULL) + ath_rx_subframe(an, tskb, rx_status); + + INCR(rxtid->baw_head, ATH_TID_MAX_BUFS); + INCR(rxtid->seq_next, IEEE80211_SEQ_MAX); + } + + /* ... and indicate rest of the frames in-order */ + + while (rxtid->baw_head != rxtid->baw_tail && + rxtid->rxbuf[rxtid->baw_head].rx_wbuf != NULL) { + tskb = rxtid->rxbuf[rxtid->baw_head].rx_wbuf; + rx_status = &rxtid->rxbuf[rxtid->baw_head].rx_status; + rxtid->rxbuf[rxtid->baw_head].rx_wbuf = NULL; + + ath_rx_subframe(an, tskb, rx_status); + + INCR(rxtid->baw_head, ATH_TID_MAX_BUFS); + INCR(rxtid->seq_next, IEEE80211_SEQ_MAX); + } + +unlock_and_free: + spin_unlock_bh(&rxtid->tidlock); + /* free bar itself */ + dev_kfree_skb(skb); + return IEEE80211_FTYPE_CTL; +} + +/* Function to handle a subframe of aggregation when HT is enabled */ + +static int ath_ampdu_input(struct ath_softc *sc, + struct ath_node *an, + struct sk_buff *skb, + struct ath_recv_status *rx_status) +{ + struct ieee80211_hdr *hdr; + struct ath_arx_tid *rxtid; + struct ath_rxbuf *rxbuf; + u8 type, subtype; + u16 rxseq; + int tid = 0, index, cindex, rxdiff; + __le16 fc; + u8 *qc; + + hdr = (struct ieee80211_hdr *)skb->data; + fc = hdr->frame_control; + + /* collect stats of frames with non-zero version */ + + if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_VERS) != 0) { + dev_kfree_skb(skb); + return -1; + } + + type = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_FTYPE; + subtype = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_STYPE; + + if (ieee80211_is_back_req(fc)) + return ath_bar_rx(sc, an, skb); + + /* special aggregate processing only for qos unicast data frames */ + + if (!ieee80211_is_data(fc) || + !ieee80211_is_data_qos(fc) || + is_multicast_ether_addr(hdr->addr1)) + return ath_rx_subframe(an, skb, rx_status); + + /* lookup rx tid state */ + + if (ieee80211_is_data_qos(fc)) { + qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & 0xf; + } + + if (sc->sc_opmode == ATH9K_M_STA) { + /* Drop the frame not belonging to me. */ + if (memcmp(hdr->addr1, sc->sc_myaddr, ETH_ALEN)) { + dev_kfree_skb(skb); + return -1; + } + } + + rxtid = &an->an_aggr.rx.tid[tid]; + + spin_lock(&rxtid->tidlock); + + rxdiff = (rxtid->baw_tail - rxtid->baw_head) & + (ATH_TID_MAX_BUFS - 1); + + /* + * If the ADDBA exchange has not been completed by the source, + * process via legacy path (i.e. no reordering buffer is needed) + */ + if (!rxtid->addba_exchangecomplete) { + spin_unlock(&rxtid->tidlock); + return ath_rx_subframe(an, skb, rx_status); + } + + /* extract sequence number from recvd frame */ + + rxseq = le16_to_cpu(hdr->seq_ctrl) >> IEEE80211_SEQ_SEQ_SHIFT; + + if (rxtid->seq_reset) { + rxtid->seq_reset = 0; + rxtid->seq_next = rxseq; + } + + index = ATH_BA_INDEX(rxtid->seq_next, rxseq); + + /* drop frame if old sequence (index is too large) */ + + if (index > (IEEE80211_SEQ_MAX - (rxtid->baw_size << 2))) { + /* discard frame, ieee layer may not treat frame as a dup */ + spin_unlock(&rxtid->tidlock); + dev_kfree_skb(skb); + return IEEE80211_FTYPE_DATA; + } + + /* sequence number is beyond block-ack window */ + + if (index >= rxtid->baw_size) { + + /* complete receive processing for all pending frames */ + + while (index >= rxtid->baw_size) { + + rxbuf = rxtid->rxbuf + rxtid->baw_head; + + if (rxbuf->rx_wbuf != NULL) { + ath_rx_subframe(an, rxbuf->rx_wbuf, + &rxbuf->rx_status); + rxbuf->rx_wbuf = NULL; + } + + INCR(rxtid->baw_head, ATH_TID_MAX_BUFS); + INCR(rxtid->seq_next, IEEE80211_SEQ_MAX); + + index--; + } + } + + /* add buffer to the recv ba window */ + + cindex = (rxtid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); + rxbuf = rxtid->rxbuf + cindex; + + if (rxbuf->rx_wbuf != NULL) { + spin_unlock(&rxtid->tidlock); + /* duplicate frame */ + dev_kfree_skb(skb); + return IEEE80211_FTYPE_DATA; + } + + rxbuf->rx_wbuf = skb; + rxbuf->rx_time = get_timestamp(); + rxbuf->rx_status = *rx_status; + + /* advance tail if sequence received is newer + * than any received so far */ + + if (index >= rxdiff) { + rxtid->baw_tail = cindex; + INCR(rxtid->baw_tail, ATH_TID_MAX_BUFS); + } + + /* indicate all in-order received frames */ + + while (rxtid->baw_head != rxtid->baw_tail) { + rxbuf = rxtid->rxbuf + rxtid->baw_head; + if (!rxbuf->rx_wbuf) + break; + + ath_rx_subframe(an, rxbuf->rx_wbuf, &rxbuf->rx_status); + rxbuf->rx_wbuf = NULL; + + INCR(rxtid->baw_head, ATH_TID_MAX_BUFS); + INCR(rxtid->seq_next, IEEE80211_SEQ_MAX); + } + + /* + * start a timer to flush all received frames if there are pending + * receive frames + */ + if (rxtid->baw_head != rxtid->baw_tail) + mod_timer(&rxtid->timer, ATH_RX_TIMEOUT); + else + del_timer_sync(&rxtid->timer); + + spin_unlock(&rxtid->tidlock); + return IEEE80211_FTYPE_DATA; +} + +/* Timer to flush all received sub-frames */ + +static void ath_rx_timer(unsigned long data) +{ + struct ath_arx_tid *rxtid = (struct ath_arx_tid *)data; + struct ath_node *an = rxtid->an; + struct ath_rxbuf *rxbuf; + int nosched; + + spin_lock_bh(&rxtid->tidlock); + while (rxtid->baw_head != rxtid->baw_tail) { + rxbuf = rxtid->rxbuf + rxtid->baw_head; + if (!rxbuf->rx_wbuf) { + INCR(rxtid->baw_head, ATH_TID_MAX_BUFS); + INCR(rxtid->seq_next, IEEE80211_SEQ_MAX); + continue; + } + + /* + * Stop if the next one is a very recent frame. + * + * Call get_timestamp in every iteration to protect against the + * case in which a new frame is received while we are executing + * this function. Using a timestamp obtained before entering + * the loop could lead to a very large time interval + * (a negative value typecast to unsigned), breaking the + * function's logic. + */ + if ((get_timestamp() - rxbuf->rx_time) < + (ATH_RX_TIMEOUT * HZ / 1000)) + break; + + ath_rx_subframe(an, rxbuf->rx_wbuf, + &rxbuf->rx_status); + rxbuf->rx_wbuf = NULL; + + INCR(rxtid->baw_head, ATH_TID_MAX_BUFS); + INCR(rxtid->seq_next, IEEE80211_SEQ_MAX); + } + + /* + * start a timer to flush all received frames if there are pending + * receive frames + */ + if (rxtid->baw_head != rxtid->baw_tail) + nosched = 0; + else + nosched = 1; /* no need to re-arm the timer again */ + + spin_unlock_bh(&rxtid->tidlock); +} + +/* Free all pending sub-frames in the re-ordering buffer */ + +static void ath_rx_flush_tid(struct ath_softc *sc, + struct ath_arx_tid *rxtid, int drop) +{ + struct ath_rxbuf *rxbuf; + + spin_lock_bh(&rxtid->tidlock); + while (rxtid->baw_head != rxtid->baw_tail) { + rxbuf = rxtid->rxbuf + rxtid->baw_head; + if (!rxbuf->rx_wbuf) { + INCR(rxtid->baw_head, ATH_TID_MAX_BUFS); + INCR(rxtid->seq_next, IEEE80211_SEQ_MAX); + continue; + } + + if (drop) + dev_kfree_skb(rxbuf->rx_wbuf); + else + ath_rx_subframe(rxtid->an, + rxbuf->rx_wbuf, + &rxbuf->rx_status); + + rxbuf->rx_wbuf = NULL; + + INCR(rxtid->baw_head, ATH_TID_MAX_BUFS); + INCR(rxtid->seq_next, IEEE80211_SEQ_MAX); + } + spin_unlock_bh(&rxtid->tidlock); +} + +static struct sk_buff *ath_rxbuf_alloc(struct ath_softc *sc, + u32 len) +{ + struct sk_buff *skb; + u32 off; + + /* + * Cache-line-align. This is important (for the + * 5210 at least) as not doing so causes bogus data + * in rx'd frames. + */ + + skb = dev_alloc_skb(len + sc->sc_cachelsz - 1); + if (skb != NULL) { + off = ((unsigned long) skb->data) % sc->sc_cachelsz; + if (off != 0) + skb_reserve(skb, sc->sc_cachelsz - off); + } else { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: skbuff alloc of size %u failed\n", + __func__, len); + return NULL; + } + + return skb; +} + +static void ath_rx_requeue(struct ath_softc *sc, struct sk_buff *skb) +{ + struct ath_buf *bf = ATH_RX_CONTEXT(skb)->ctx_rxbuf; + + ASSERT(bf != NULL); + + spin_lock_bh(&sc->sc_rxbuflock); + if (bf->bf_status & ATH_BUFSTATUS_STALE) { + /* + * This buffer is still held for hw acess. + * Mark it as free to be re-queued it later. + */ + bf->bf_status |= ATH_BUFSTATUS_FREE; + } else { + /* XXX: we probably never enter here, remove after + * verification */ + list_add_tail(&bf->list, &sc->sc_rxbuf); + ath_rx_buf_link(sc, bf); + } + spin_unlock_bh(&sc->sc_rxbuflock); +} + +/* + * The skb indicated to upper stack won't be returned to us. + * So we have to allocate a new one and queue it by ourselves. + */ +static int ath_rx_indicate(struct ath_softc *sc, + struct sk_buff *skb, + struct ath_recv_status *status, + u16 keyix) +{ + struct ath_buf *bf = ATH_RX_CONTEXT(skb)->ctx_rxbuf; + struct sk_buff *nskb; + int type; + + /* indicate frame to the stack, which will free the old skb. */ + type = ath__rx_indicate(sc, skb, status, keyix); + + /* allocate a new skb and queue it to for H/W processing */ + nskb = ath_rxbuf_alloc(sc, sc->sc_rxbufsize); + if (nskb != NULL) { + bf->bf_mpdu = nskb; + bf->bf_buf_addr = ath_skb_map_single(sc, + nskb, + PCI_DMA_FROMDEVICE, + /* XXX: Remove get_dma_mem_context() */ + get_dma_mem_context(bf, bf_dmacontext)); + ATH_RX_CONTEXT(nskb)->ctx_rxbuf = bf; + + /* queue the new wbuf to H/W */ + ath_rx_requeue(sc, nskb); + } + + return type; +} + +static void ath_opmode_init(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + u32 rfilt, mfilt[2]; + + /* configure rx filter */ + rfilt = ath_calcrxfilter(sc); + ath9k_hw_setrxfilter(ah, rfilt); + + /* configure bssid mask */ + if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) + ath9k_hw_setbssidmask(ah, sc->sc_bssidmask); + + /* configure operational mode */ + ath9k_hw_setopmode(ah); + + /* Handle any link-level address change. */ + ath9k_hw_setmac(ah, sc->sc_myaddr); + + /* calculate and install multicast filter */ + mfilt[0] = mfilt[1] = ~0; + + ath9k_hw_setmcastfilter(ah, mfilt[0], mfilt[1]); + DPRINTF(sc, ATH_DBG_CONFIG , + "%s: RX filter 0x%x, MC filter %08x:%08x\n", + __func__, rfilt, mfilt[0], mfilt[1]); +} + +int ath_rx_init(struct ath_softc *sc, int nbufs) +{ + struct sk_buff *skb; + struct ath_buf *bf; + int error = 0; + + do { + spin_lock_init(&sc->sc_rxflushlock); + sc->sc_rxflush = 0; + spin_lock_init(&sc->sc_rxbuflock); + + /* + * Cisco's VPN software requires that drivers be able to + * receive encapsulated frames that are larger than the MTU. + * Since we can't be sure how large a frame we'll get, setup + * to handle the larges on possible. + */ + sc->sc_rxbufsize = roundup(IEEE80211_MAX_MPDU_LEN, + min(sc->sc_cachelsz, + (u16)64)); + + DPRINTF(sc, ATH_DBG_CONFIG, "%s: cachelsz %u rxbufsize %u\n", + __func__, sc->sc_cachelsz, sc->sc_rxbufsize); + + /* Initialize rx descriptors */ + + error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf, + "rx", nbufs, 1); + if (error != 0) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: failed to allocate rx descriptors: %d\n", + __func__, error); + break; + } + + /* Pre-allocate a wbuf for each rx buffer */ + + list_for_each_entry(bf, &sc->sc_rxbuf, list) { + skb = ath_rxbuf_alloc(sc, sc->sc_rxbufsize); + if (skb == NULL) { + error = -ENOMEM; + break; + } + + bf->bf_mpdu = skb; + bf->bf_buf_addr = + ath_skb_map_single(sc, skb, PCI_DMA_FROMDEVICE, + get_dma_mem_context(bf, bf_dmacontext)); + ATH_RX_CONTEXT(skb)->ctx_rxbuf = bf; + } + sc->sc_rxlink = NULL; + + } while (0); + + if (error) + ath_rx_cleanup(sc); + + return error; +} + +/* Reclaim all rx queue resources */ + +void ath_rx_cleanup(struct ath_softc *sc) +{ + struct sk_buff *skb; + struct ath_buf *bf; + + list_for_each_entry(bf, &sc->sc_rxbuf, list) { + skb = bf->bf_mpdu; + if (skb) + dev_kfree_skb(skb); + } + + /* cleanup rx descriptors */ + + if (sc->sc_rxdma.dd_desc_len != 0) + ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf); +} + +/* + * Calculate the receive filter according to the + * operating mode and state: + * + * o always accept unicast, broadcast, and multicast traffic + * o maintain current state of phy error reception (the hal + * may enable phy error frames for noise immunity work) + * o probe request frames are accepted only when operating in + * hostap, adhoc, or monitor modes + * o enable promiscuous mode according to the interface state + * o accept beacons: + * - when operating in adhoc mode so the 802.11 layer creates + * node table entries for peers, + * - when operating in station mode for collecting rssi data when + * the station is otherwise quiet, or + * - when operating as a repeater so we see repeater-sta beacons + * - when scanning + */ + +u32 ath_calcrxfilter(struct ath_softc *sc) +{ +#define RX_FILTER_PRESERVE (ATH9K_RX_FILTER_PHYERR | ATH9K_RX_FILTER_PHYRADAR) + u32 rfilt; + + rfilt = (ath9k_hw_getrxfilter(sc->sc_ah) & RX_FILTER_PRESERVE) + | ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST + | ATH9K_RX_FILTER_MCAST; + + /* If not a STA, enable processing of Probe Requests */ + if (sc->sc_opmode != ATH9K_M_STA) + rfilt |= ATH9K_RX_FILTER_PROBEREQ; + + /* Can't set HOSTAP into promiscous mode */ + if (sc->sc_opmode == ATH9K_M_MONITOR) { + rfilt |= ATH9K_RX_FILTER_PROM; + /* ??? To prevent from sending ACK */ + rfilt &= ~ATH9K_RX_FILTER_UCAST; + } + + if (sc->sc_opmode == ATH9K_M_STA || sc->sc_opmode == ATH9K_M_IBSS || + sc->sc_scanning) + rfilt |= ATH9K_RX_FILTER_BEACON; + + /* If in HOSTAP mode, want to enable reception of PSPOLL frames + & beacon frames */ + if (sc->sc_opmode == ATH9K_M_HOSTAP) + rfilt |= (ATH9K_RX_FILTER_BEACON | ATH9K_RX_FILTER_PSPOLL); + return rfilt; +#undef RX_FILTER_PRESERVE +} + +/* Enable the receive h/w following a reset. */ + +int ath_startrecv(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath_buf *bf, *tbf; + + spin_lock_bh(&sc->sc_rxbuflock); + if (list_empty(&sc->sc_rxbuf)) + goto start_recv; + + sc->sc_rxlink = NULL; + list_for_each_entry_safe(bf, tbf, &sc->sc_rxbuf, list) { + if (bf->bf_status & ATH_BUFSTATUS_STALE) { + /* restarting h/w, no need for holding descriptors */ + bf->bf_status &= ~ATH_BUFSTATUS_STALE; + /* + * Upper layer may not be done with the frame yet so + * we can't just re-queue it to hardware. Remove it + * from h/w queue. It'll be re-queued when upper layer + * returns the frame and ath_rx_requeue_mpdu is called. + */ + if (!(bf->bf_status & ATH_BUFSTATUS_FREE)) { + list_del(&bf->list); + continue; + } + } + /* chain descriptors */ + ath_rx_buf_link(sc, bf); + } + + /* We could have deleted elements so the list may be empty now */ + if (list_empty(&sc->sc_rxbuf)) + goto start_recv; + + bf = list_first_entry(&sc->sc_rxbuf, struct ath_buf, list); + ath9k_hw_putrxbuf(ah, bf->bf_daddr); + ath9k_hw_rxena(ah); /* enable recv descriptors */ + +start_recv: + spin_unlock_bh(&sc->sc_rxbuflock); + ath_opmode_init(sc); /* set filters, etc. */ + ath9k_hw_startpcureceive(ah); /* re-enable PCU/DMA engine */ + return 0; +} + +/* Disable the receive h/w in preparation for a reset. */ + +bool ath_stoprecv(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + u64 tsf; + bool stopped; + + ath9k_hw_stoppcurecv(ah); /* disable PCU */ + ath9k_hw_setrxfilter(ah, 0); /* clear recv filter */ + stopped = ath9k_hw_stopdmarecv(ah); /* disable DMA engine */ + mdelay(3); /* 3ms is long enough for 1 frame */ + tsf = ath9k_hw_gettsf64(ah); + sc->sc_rxlink = NULL; /* just in case */ + return stopped; +} + +/* Flush receive queue */ + +void ath_flushrecv(struct ath_softc *sc) +{ + /* + * ath_rx_tasklet may be used to handle rx interrupt and flush receive + * queue at the same time. Use a lock to serialize the access of rx + * queue. + * ath_rx_tasklet cannot hold the spinlock while indicating packets. + * Instead, do not claim the spinlock but check for a flush in + * progress (see references to sc_rxflush) + */ + spin_lock_bh(&sc->sc_rxflushlock); + sc->sc_rxflush = 1; + + ath_rx_tasklet(sc, 1); + + sc->sc_rxflush = 0; + spin_unlock_bh(&sc->sc_rxflushlock); +} + +/* Process an individual frame */ + +int ath_rx_input(struct ath_softc *sc, + struct ath_node *an, + int is_ampdu, + struct sk_buff *skb, + struct ath_recv_status *rx_status, + enum ATH_RX_TYPE *status) +{ + if (is_ampdu && sc->sc_rxaggr) { + *status = ATH_RX_CONSUMED; + return ath_ampdu_input(sc, an, skb, rx_status); + } else { + *status = ATH_RX_NON_CONSUMED; + return -1; + } +} + +/* Process receive queue, as well as LED, etc. */ + +int ath_rx_tasklet(struct ath_softc *sc, int flush) +{ +#define PA2DESC(_sc, _pa) \ + ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \ + ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) + + struct ath_buf *bf, *bf_held = NULL; + struct ath_desc *ds; + struct ieee80211_hdr *hdr; + struct sk_buff *skb = NULL; + struct ath_recv_status rx_status; + struct ath_hal *ah = sc->sc_ah; + int type, rx_processed = 0; + u32 phyerr; + u8 chainreset = 0; + int retval; + __le16 fc; + + do { + /* If handling rx interrupt and flush is in progress => exit */ + if (sc->sc_rxflush && (flush == 0)) + break; + + spin_lock_bh(&sc->sc_rxbuflock); + if (list_empty(&sc->sc_rxbuf)) { + sc->sc_rxlink = NULL; + spin_unlock_bh(&sc->sc_rxbuflock); + break; + } + + bf = list_first_entry(&sc->sc_rxbuf, struct ath_buf, list); + + /* + * There is a race condition that BH gets scheduled after sw + * writes RxE and before hw re-load the last descriptor to get + * the newly chained one. Software must keep the last DONE + * descriptor as a holding descriptor - software does so by + * marking it with the STALE flag. + */ + if (bf->bf_status & ATH_BUFSTATUS_STALE) { + bf_held = bf; + if (list_is_last(&bf_held->list, &sc->sc_rxbuf)) { + /* + * The holding descriptor is the last + * descriptor in queue. It's safe to + * remove the last holding descriptor + * in BH context. + */ + list_del(&bf_held->list); + bf_held->bf_status &= ~ATH_BUFSTATUS_STALE; + sc->sc_rxlink = NULL; + + if (bf_held->bf_status & ATH_BUFSTATUS_FREE) { + list_add_tail(&bf_held->list, + &sc->sc_rxbuf); + ath_rx_buf_link(sc, bf_held); + } + spin_unlock_bh(&sc->sc_rxbuflock); + break; + } + bf = list_entry(bf->list.next, struct ath_buf, list); + } + + ds = bf->bf_desc; + ++rx_processed; + + /* + * Must provide the virtual address of the current + * descriptor, the physical address, and the virtual + * address of the next descriptor in the h/w chain. + * This allows the HAL to look ahead to see if the + * hardware is done with a descriptor by checking the + * done bit in the following descriptor and the address + * of the current descriptor the DMA engine is working + * on. All this is necessary because of our use of + * a self-linked list to avoid rx overruns. + */ + retval = ath9k_hw_rxprocdesc(ah, + ds, + bf->bf_daddr, + PA2DESC(sc, ds->ds_link), + 0); + if (retval == -EINPROGRESS) { + struct ath_buf *tbf; + struct ath_desc *tds; + + if (list_is_last(&bf->list, &sc->sc_rxbuf)) { + spin_unlock_bh(&sc->sc_rxbuflock); + break; + } + + tbf = list_entry(bf->list.next, struct ath_buf, list); + + /* + * On some hardware the descriptor status words could + * get corrupted, including the done bit. Because of + * this, check if the next descriptor's done bit is + * set or not. + * + * If the next descriptor's done bit is set, the current + * descriptor has been corrupted. Force s/w to discard + * this descriptor and continue... + */ + + tds = tbf->bf_desc; + retval = ath9k_hw_rxprocdesc(ah, + tds, tbf->bf_daddr, + PA2DESC(sc, tds->ds_link), 0); + if (retval == -EINPROGRESS) { + spin_unlock_bh(&sc->sc_rxbuflock); + break; + } + } + + /* XXX: we do not support frames spanning + * multiple descriptors */ + bf->bf_status |= ATH_BUFSTATUS_DONE; + + skb = bf->bf_mpdu; + if (skb == NULL) { /* XXX ??? can this happen */ + spin_unlock_bh(&sc->sc_rxbuflock); + continue; + } + /* + * Now we know it's a completed frame, we can indicate the + * frame. Remove the previous holding descriptor and leave + * this one in the queue as the new holding descriptor. + */ + if (bf_held) { + list_del(&bf_held->list); + bf_held->bf_status &= ~ATH_BUFSTATUS_STALE; + if (bf_held->bf_status & ATH_BUFSTATUS_FREE) { + list_add_tail(&bf_held->list, &sc->sc_rxbuf); + /* try to requeue this descriptor */ + ath_rx_buf_link(sc, bf_held); + } + } + + bf->bf_status |= ATH_BUFSTATUS_STALE; + bf_held = bf; + /* + * Release the lock here in case ieee80211_input() return + * the frame immediately by calling ath_rx_mpdu_requeue(). + */ + spin_unlock_bh(&sc->sc_rxbuflock); + + if (flush) { + /* + * If we're asked to flush receive queue, directly + * chain it back at the queue without processing it. + */ + goto rx_next; + } + + hdr = (struct ieee80211_hdr *)skb->data; + fc = hdr->frame_control; + memzero(&rx_status, sizeof(struct ath_recv_status)); + + if (ds->ds_rxstat.rs_more) { + /* + * Frame spans multiple descriptors; this + * cannot happen yet as we don't support + * jumbograms. If not in monitor mode, + * discard the frame. + */ +#ifndef ERROR_FRAMES + /* + * Enable this if you want to see + * error frames in Monitor mode. + */ + if (sc->sc_opmode != ATH9K_M_MONITOR) + goto rx_next; +#endif + /* fall thru for monitor mode handling... */ + } else if (ds->ds_rxstat.rs_status != 0) { + if (ds->ds_rxstat.rs_status & ATH9K_RXERR_CRC) + rx_status.flags |= ATH_RX_FCS_ERROR; + if (ds->ds_rxstat.rs_status & ATH9K_RXERR_PHY) { + phyerr = ds->ds_rxstat.rs_phyerr & 0x1f; + goto rx_next; + } + + if (ds->ds_rxstat.rs_status & ATH9K_RXERR_DECRYPT) { + /* + * Decrypt error. We only mark packet status + * here and always push up the frame up to let + * mac80211 handle the actual error case, be + * it no decryption key or real decryption + * error. This let us keep statistics there. + */ + rx_status.flags |= ATH_RX_DECRYPT_ERROR; + } else if (ds->ds_rxstat.rs_status & ATH9K_RXERR_MIC) { + /* + * Demic error. We only mark frame status here + * and always push up the frame up to let + * mac80211 handle the actual error case. This + * let us keep statistics there. Hardware may + * post a false-positive MIC error. + */ + if (ieee80211_is_ctl(fc)) + /* + * Sometimes, we get invalid + * MIC failures on valid control frames. + * Remove these mic errors. + */ + ds->ds_rxstat.rs_status &= + ~ATH9K_RXERR_MIC; + else + rx_status.flags |= ATH_RX_MIC_ERROR; + } + /* + * Reject error frames with the exception of + * decryption and MIC failures. For monitor mode, + * we also ignore the CRC error. + */ + if (sc->sc_opmode == ATH9K_M_MONITOR) { + if (ds->ds_rxstat.rs_status & + ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC | + ATH9K_RXERR_CRC)) + goto rx_next; + } else { + if (ds->ds_rxstat.rs_status & + ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC)) { + goto rx_next; + } + } + } + /* + * The status portion of the descriptor could get corrupted. + */ + if (sc->sc_rxbufsize < ds->ds_rxstat.rs_datalen) + goto rx_next; + /* + * Sync and unmap the frame. At this point we're + * committed to passing the sk_buff somewhere so + * clear buf_skb; this means a new sk_buff must be + * allocated when the rx descriptor is setup again + * to receive another frame. + */ + skb_put(skb, ds->ds_rxstat.rs_datalen); + skb->protocol = cpu_to_be16(ETH_P_CONTROL); + rx_status.tsf = ath_extend_tsf(sc, ds->ds_rxstat.rs_tstamp); + rx_status.rateieee = + sc->sc_hwmap[ds->ds_rxstat.rs_rate].ieeerate; + rx_status.rateKbps = + sc->sc_hwmap[ds->ds_rxstat.rs_rate].rateKbps; + rx_status.ratecode = ds->ds_rxstat.rs_rate; + + /* HT rate */ + if (rx_status.ratecode & 0x80) { + /* TODO - add table to avoid division */ + if (ds->ds_rxstat.rs_flags & ATH9K_RX_2040) { + rx_status.flags |= ATH_RX_40MHZ; + rx_status.rateKbps = + (rx_status.rateKbps * 27) / 13; + } + if (ds->ds_rxstat.rs_flags & ATH9K_RX_GI) + rx_status.rateKbps = + (rx_status.rateKbps * 10) / 9; + else + rx_status.flags |= ATH_RX_SHORT_GI; + } + + /* sc->sc_noise_floor is only available when the station + attaches to an AP, so we use a default value + if we are not yet attached. */ + + /* XXX we should use either sc->sc_noise_floor or + * ath_hal_getChanNoise(ah, &sc->sc_curchan) + * to calculate the noise floor. + * However, the value returned by ath_hal_getChanNoise + * seems to be incorrect (-31dBm on the last test), + * so we will use a hard-coded value until we + * figure out what is going on. + */ + rx_status.abs_rssi = + ds->ds_rxstat.rs_rssi + ATH_DEFAULT_NOISE_FLOOR; + + pci_dma_sync_single_for_cpu(sc->pdev, + bf->bf_buf_addr, + skb_tailroom(skb), + PCI_DMA_FROMDEVICE); + pci_unmap_single(sc->pdev, + bf->bf_buf_addr, + sc->sc_rxbufsize, + PCI_DMA_FROMDEVICE); + + /* XXX: Ah! make me more readable, use a helper */ + if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) { + if (ds->ds_rxstat.rs_moreaggr == 0) { + rx_status.rssictl[0] = + ds->ds_rxstat.rs_rssi_ctl0; + rx_status.rssictl[1] = + ds->ds_rxstat.rs_rssi_ctl1; + rx_status.rssictl[2] = + ds->ds_rxstat.rs_rssi_ctl2; + rx_status.rssi = ds->ds_rxstat.rs_rssi; + if (ds->ds_rxstat.rs_flags & ATH9K_RX_2040) { + rx_status.rssiextn[0] = + ds->ds_rxstat.rs_rssi_ext0; + rx_status.rssiextn[1] = + ds->ds_rxstat.rs_rssi_ext1; + rx_status.rssiextn[2] = + ds->ds_rxstat.rs_rssi_ext2; + rx_status.flags |= + ATH_RX_RSSI_EXTN_VALID; + } + rx_status.flags |= ATH_RX_RSSI_VALID | + ATH_RX_CHAIN_RSSI_VALID; + } + } else { + /* + * Need to insert the "combined" rssi into the + * status structure for upper layer processing + */ + rx_status.rssi = ds->ds_rxstat.rs_rssi; + rx_status.flags |= ATH_RX_RSSI_VALID; + } + + /* Pass frames up to the stack. */ + + type = ath_rx_indicate(sc, skb, + &rx_status, ds->ds_rxstat.rs_keyix); + + /* + * change the default rx antenna if rx diversity chooses the + * other antenna 3 times in a row. + */ + if (sc->sc_defant != ds->ds_rxstat.rs_antenna) { + if (++sc->sc_rxotherant >= 3) + ath_setdefantenna(sc, + ds->ds_rxstat.rs_antenna); + } else { + sc->sc_rxotherant = 0; + } + +#ifdef CONFIG_SLOW_ANT_DIV + if ((rx_status.flags & ATH_RX_RSSI_VALID) && + ieee80211_is_beacon(fc)) { + ath_slow_ant_div(&sc->sc_antdiv, hdr, &ds->ds_rxstat); + } +#endif + /* + * For frames successfully indicated, the buffer will be + * returned to us by upper layers by calling + * ath_rx_mpdu_requeue, either synchronusly or asynchronously. + * So we don't want to do it here in this loop. + */ + continue; + +rx_next: + bf->bf_status |= ATH_BUFSTATUS_FREE; + } while (TRUE); + + if (chainreset) { + DPRINTF(sc, ATH_DBG_CONFIG, + "%s: Reset rx chain mask. " + "Do internal reset\n", __func__); + ASSERT(flush == 0); + ath_internal_reset(sc); + } + + return 0; +#undef PA2DESC +} + +/* Process ADDBA request in per-TID data structure */ + +int ath_rx_aggr_start(struct ath_softc *sc, + const u8 *addr, + u16 tid, + u16 *ssn) +{ + struct ath_arx_tid *rxtid; + struct ath_node *an; + struct ieee80211_hw *hw = sc->hw; + struct ieee80211_supported_band *sband; + u16 buffersize = 0; + + spin_lock_bh(&sc->node_lock); + an = ath_node_find(sc, (u8 *) addr); + spin_unlock_bh(&sc->node_lock); + + if (!an) { + DPRINTF(sc, ATH_DBG_AGGR, + "%s: Node not found to initialize RX aggregation\n", + __func__); + return -1; + } + + sband = hw->wiphy->bands[hw->conf.channel->band]; + buffersize = IEEE80211_MIN_AMPDU_BUF << + sband->ht_info.ampdu_factor; /* FIXME */ + + rxtid = &an->an_aggr.rx.tid[tid]; + + spin_lock_bh(&rxtid->tidlock); + if (sc->sc_rxaggr) { + /* Allow aggregation reception + * Adjust rx BA window size. Peer might indicate a + * zero buffer size for a _dont_care_ condition. + */ + if (buffersize) + rxtid->baw_size = min(buffersize, rxtid->baw_size); + + /* set rx sequence number */ + rxtid->seq_next = *ssn; + + /* Allocate the receive buffers for this TID */ + DPRINTF(sc, ATH_DBG_AGGR, + "%s: Allcating rxbuffer for TID %d\n", __func__, tid); + + if (rxtid->rxbuf == NULL) { + /* + * If the rxbuff is not NULL at this point, we *probably* + * already allocated the buffer on a previous ADDBA, + * and this is a subsequent ADDBA that got through. + * Don't allocate, but use the value in the pointer, + * we zero it out when we de-allocate. + */ + rxtid->rxbuf = kmalloc(ATH_TID_MAX_BUFS * + sizeof(struct ath_rxbuf), GFP_ATOMIC); + } + if (rxtid->rxbuf == NULL) { + DPRINTF(sc, ATH_DBG_AGGR, + "%s: Unable to allocate RX buffer, " + "refusing ADDBA\n", __func__); + } else { + /* Ensure the memory is zeroed out (all internal + * pointers are null) */ + memzero(rxtid->rxbuf, ATH_TID_MAX_BUFS * + sizeof(struct ath_rxbuf)); + DPRINTF(sc, ATH_DBG_AGGR, + "%s: Allocated @%p\n", __func__, rxtid->rxbuf); + + /* Allow aggregation reception */ + rxtid->addba_exchangecomplete = 1; + } + } + spin_unlock_bh(&rxtid->tidlock); + + return 0; +} + +/* Process DELBA */ + +int ath_rx_aggr_stop(struct ath_softc *sc, + const u8 *addr, + u16 tid) +{ + struct ath_node *an; + + spin_lock_bh(&sc->node_lock); + an = ath_node_find(sc, (u8 *) addr); + spin_unlock_bh(&sc->node_lock); + + if (!an) { + DPRINTF(sc, ATH_DBG_AGGR, + "%s: RX aggr stop for non-existent node\n", __func__); + return -1; + } + + ath_rx_aggr_teardown(sc, an, tid); + return 0; +} + +/* Rx aggregation tear down */ + +void ath_rx_aggr_teardown(struct ath_softc *sc, + struct ath_node *an, u8 tid) +{ + struct ath_arx_tid *rxtid = &an->an_aggr.rx.tid[tid]; + + if (!rxtid->addba_exchangecomplete) + return; + + del_timer_sync(&rxtid->timer); + ath_rx_flush_tid(sc, rxtid, 0); + rxtid->addba_exchangecomplete = 0; + + /* De-allocate the receive buffer array allocated when addba started */ + + if (rxtid->rxbuf) { + DPRINTF(sc, ATH_DBG_AGGR, + "%s: Deallocating TID %d rxbuff @%p\n", + __func__, tid, rxtid->rxbuf); + kfree(rxtid->rxbuf); + + /* Set pointer to null to avoid reuse*/ + rxtid->rxbuf = NULL; + } +} + +/* Initialize per-node receive state */ + +void ath_rx_node_init(struct ath_softc *sc, struct ath_node *an) +{ + if (sc->sc_rxaggr) { + struct ath_arx_tid *rxtid; + int tidno; + + /* Init per tid rx state */ + for (tidno = 0, rxtid = &an->an_aggr.rx.tid[tidno]; + tidno < WME_NUM_TID; + tidno++, rxtid++) { + rxtid->an = an; + rxtid->seq_reset = 1; + rxtid->seq_next = 0; + rxtid->baw_size = WME_MAX_BA; + rxtid->baw_head = rxtid->baw_tail = 0; + + /* + * Ensure the buffer pointer is null at this point + * (needs to be allocated when addba is received) + */ + + rxtid->rxbuf = NULL; + setup_timer(&rxtid->timer, ath_rx_timer, + (unsigned long)rxtid); + spin_lock_init(&rxtid->tidlock); + + /* ADDBA state */ + rxtid->addba_exchangecomplete = 0; + } + } +} + +void ath_rx_node_cleanup(struct ath_softc *sc, struct ath_node *an) +{ + if (sc->sc_rxaggr) { + struct ath_arx_tid *rxtid; + int tidno, i; + + /* Init per tid rx state */ + for (tidno = 0, rxtid = &an->an_aggr.rx.tid[tidno]; + tidno < WME_NUM_TID; + tidno++, rxtid++) { + + if (!rxtid->addba_exchangecomplete) + continue; + + /* must cancel timer first */ + del_timer_sync(&rxtid->timer); + + /* drop any pending sub-frames */ + ath_rx_flush_tid(sc, rxtid, 1); + + for (i = 0; i < ATH_TID_MAX_BUFS; i++) + ASSERT(rxtid->rxbuf[i].rx_wbuf == NULL); + + rxtid->addba_exchangecomplete = 0; + } + } + +} + +/* Cleanup per-node receive state */ + +void ath_rx_node_free(struct ath_softc *sc, struct ath_node *an) +{ + ath_rx_node_cleanup(sc, an); +} + +dma_addr_t ath_skb_map_single(struct ath_softc *sc, + struct sk_buff *skb, + int direction, + dma_addr_t *pa) +{ + /* + * NB: do NOT use skb->len, which is 0 on initialization. + * Use skb's entire data area instead. + */ + *pa = pci_map_single(sc->pdev, skb->data, + skb_end_pointer(skb) - skb->head, direction); + return *pa; +} + +void ath_skb_unmap_single(struct ath_softc *sc, + struct sk_buff *skb, + int direction, + dma_addr_t *pa) +{ + /* Unmap skb's entire data area */ + pci_unmap_single(sc->pdev, *pa, + skb_end_pointer(skb) - skb->head, direction); +} |