iPXE
Defines | Functions
ath9k_recv.c File Reference
#include <ipxe/io.h>
#include "ath9k.h"
#include "ar9003_mac.h"

Go to the source code of this file.

Defines

#define RX_FILTER_PRESERVE   (ATH9K_RX_FILTER_PHYERR | ATH9K_RX_FILTER_PHYRADAR)

Functions

static void ath_rx_buf_link (struct ath_softc *sc, struct ath_buf *bf)
static void ath_setdefantenna (struct ath_softc *sc, u32 antenna)
static void ath_opmode_init (struct ath_softc *sc)
int ath_rx_init (struct ath_softc *sc, int nbufs)
void ath_rx_cleanup (struct ath_softc *sc)
u32 ath_calcrxfilter (struct ath_softc *sc)
int ath_startrecv (struct ath_softc *sc)
int ath_stoprecv (struct ath_softc *sc)
void ath_flushrecv (struct ath_softc *sc)
static struct ath_bufath_get_next_rx_buf (struct ath_softc *sc, struct ath_rx_status *rs)
static int ath9k_rx_accept (struct ath_common *common, struct ath_rx_status *rx_stats, int *decrypt_error)
static int ath9k_process_rate (struct ath_common *common __unused, struct net80211_device *dev, struct ath_rx_status *rx_stats, int *rix)
static int ath9k_rx_iob_preprocess (struct ath_common *common, struct net80211_device *dev, struct ath_rx_status *rx_stats, int *rix, int *decrypt_error)
int ath_rx_tasklet (struct ath_softc *sc, int flush, int hp __unused)

Define Documentation

Referenced by ath_calcrxfilter().


Function Documentation

static void ath_rx_buf_link ( struct ath_softc sc,
struct ath_buf bf 
) [static]

Definition at line 33 of file ath9k_recv.c.

References ah, ath9k_hw_common(), ath9k_hw_putrxbuf(), ath9k_hw_setuprxdesc(), ATH_RXBUF_RESET, ath_buf::bf_buf_addr, ath_buf::bf_daddr, ath_buf::bf_desc, common, ds, ath_desc::ds_data, ath_desc::ds_link, NULL, ath_softc::rx, ath_common::rx_bufsize, ath_rx::rxlink, and ath_softc::sc_ah.

Referenced by ath_rx_tasklet(), and ath_startrecv().

{
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        struct ath_desc *ds;
//      struct io_buffer *iob;

        ATH_RXBUF_RESET(bf);

        ds = bf->bf_desc;
        ds->ds_link = 0; /* link to null */
        ds->ds_data = bf->bf_buf_addr;

//      /* virtual addr of the beginning of the buffer. */
//      iob = bf->bf_mpdu;
//      ds->ds_vdata = iob->data;

        /*
         * setup rx descriptors. The rx_bufsize here tells the hardware
         * how much data it can DMA to us and that we are prepared
         * to process
         */
        ath9k_hw_setuprxdesc(ah, ds,
                             common->rx_bufsize,
                             0);

        if (sc->rx.rxlink == NULL)
                ath9k_hw_putrxbuf(ah, bf->bf_daddr);
        else
                *sc->rx.rxlink = bf->bf_daddr;

        sc->rx.rxlink = &ds->ds_link;
}
static void ath_setdefantenna ( struct ath_softc sc,
u32  antenna 
) [static]

Definition at line 67 of file ath9k_recv.c.

References ath9k_hw_setantenna(), ath_rx::defant, ath_softc::rx, ath_rx::rxotherant, and ath_softc::sc_ah.

Referenced by ath_rx_tasklet().

{
        /* XXX block beacon interrupts */
        ath9k_hw_setantenna(sc->sc_ah, antenna);
        sc->rx.defant = antenna;
        sc->rx.rxotherant = 0;
}
static void ath_opmode_init ( struct ath_softc sc) [static]

Definition at line 75 of file ath9k_recv.c.

References ah, ath9k_hw_common(), ath9k_hw_setmcastfilter(), ath9k_hw_setopmode(), ath9k_hw_setrxfilter(), ath_calcrxfilter(), ath_hw_setbssidmask(), common, and ath_softc::sc_ah.

Referenced by ath_startrecv().

{
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);

        u32 rfilt, mfilt[2];

        /* configure rx filter */
        rfilt = ath_calcrxfilter(sc);
        ath9k_hw_setrxfilter(ah, rfilt);

        /* configure bssid mask */
        ath_hw_setbssidmask(common);

        /* configure operational mode */
        ath9k_hw_setopmode(ah);

        /* calculate and install multicast filter */
        mfilt[0] = mfilt[1] = ~0;
        ath9k_hw_setmcastfilter(ah, mfilt[0], mfilt[1]);
}
int ath_rx_init ( struct ath_softc sc,
int  nbufs 
)

Definition at line 97 of file ath9k_recv.c.

References alloc_iob_raw(), ath9k_hw_common(), ath_descdma_setup(), ath_rx_cleanup(), ath_buf::bf_buf_addr, ath_buf::bf_mpdu, ath_common::cachelsz, ath_hw::caps, common, io_buffer::data, DBG, DBG2, ENOMEM, error, IEEE80211_MAX_MPDU_LEN, ath_buf::list, list_for_each_entry, NULL, ath_softc::rx, ath_common::rx_bufsize, ath9k_hw_capabilities::rx_status_len, ath_rx::rxbuf, ath_rx::rxdma, ath_rx::rxlink, ath_softc::sc_ah, ath_softc::sc_flags, SC_OP_RXFLUSH, and virt_to_bus().

Referenced by ath9k_init_device().

{
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct io_buffer *iob;
        struct ath_buf *bf;
        int error = 0;

        sc->sc_flags &= ~SC_OP_RXFLUSH;

        common->rx_bufsize = IEEE80211_MAX_MPDU_LEN / 2 +
                             sc->sc_ah->caps.rx_status_len;

        DBG2("ath9k: cachelsz %d rxbufsize %d\n",
                common->cachelsz, common->rx_bufsize);

        /* Initialize rx descriptors */

        error = ath_descdma_setup(sc, &sc->rx.rxdma, &sc->rx.rxbuf,
                        "rx", nbufs, 1, 0);
        if (error != 0) {
                DBG("ath9k: "
                        "failed to allocate rx descriptors: %d\n",
                        error);
                goto err;
        }

        list_for_each_entry(bf, &sc->rx.rxbuf, list) {
                iob = alloc_iob_raw ( common->rx_bufsize, common->cachelsz, 0 );
                if (iob == NULL) {
                        error = -ENOMEM;
                        goto err;
                }

                bf->bf_mpdu = iob;
                bf->bf_buf_addr = virt_to_bus ( iob->data );
        }
        sc->rx.rxlink = NULL;

err:
        if (error)
                ath_rx_cleanup(sc);

        return error;
}
void ath_rx_cleanup ( struct ath_softc sc)
u32 ath_calcrxfilter ( struct ath_softc sc)

Definition at line 179 of file ath9k_recv.c.

References ath9k_hw_getrxfilter(), ATH9K_RX_FILTER_BCAST, ATH9K_RX_FILTER_BEACON, ATH9K_RX_FILTER_MCAST, ATH9K_RX_FILTER_UCAST, RX_FILTER_PRESERVE, and ath_softc::sc_ah.

Referenced by ath_opmode_init().

{
#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 | ATH9K_RX_FILTER_BEACON;

        return rfilt;

#undef RX_FILTER_PRESERVE
}
int ath_startrecv ( struct ath_softc sc)

Definition at line 194 of file ath9k_recv.c.

References ah, ath9k_hw_putrxbuf(), ath9k_hw_rxena(), ath9k_hw_startpcureceive(), ath_opmode_init(), ath_rx_buf_link(), ath_buf::bf_daddr, ath_buf::list, list_empty, list_first_entry, list_for_each_entry_safe, NULL, ath_softc::rx, ath_rx::rxbuf, ath_rx::rxlink, ath_softc::sc_ah, ath_softc::sc_flags, and SC_OP_OFFCHANNEL.

Referenced by ath9k_start(), ath_reset(), and ath_set_channel().

{
        struct ath_hw *ah = sc->sc_ah;
        struct ath_buf *bf, *tbf;

        if (list_empty(&sc->rx.rxbuf))
                goto start_recv;

        sc->rx.rxlink = NULL;
        list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) {
                ath_rx_buf_link(sc, bf);
        }

        /* We could have deleted elements so the list may be empty now */
        if (list_empty(&sc->rx.rxbuf))
                goto start_recv;

        bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
        ath9k_hw_putrxbuf(ah, bf->bf_daddr);
        ath9k_hw_rxena(ah);

start_recv:
        ath_opmode_init(sc);
        ath9k_hw_startpcureceive(ah, (sc->sc_flags & SC_OP_OFFCHANNEL));

        return 0;
}
int ath_stoprecv ( struct ath_softc sc)

Definition at line 222 of file ath9k_recv.c.

References ah, ath_hw::ah_flags, AH_UNPLUGGED, ath9k_hw_abortpcurecv(), ath9k_hw_setrxfilter(), ath9k_hw_stopdmarecv(), DBG, NULL, ath_softc::rx, ath_rx::rxlink, and ath_softc::sc_ah.

Referenced by ath9k_stop(), ath_radio_disable(), ath_reset(), and ath_set_channel().

{
        struct ath_hw *ah = sc->sc_ah;
        int stopped, reset = 0;

        ath9k_hw_abortpcurecv(ah);
        ath9k_hw_setrxfilter(ah, 0);
        stopped = ath9k_hw_stopdmarecv(ah, &reset);

        sc->rx.rxlink = NULL;

        if (!(ah->ah_flags & AH_UNPLUGGED) &&
            !stopped) {
                DBG("ath9k: "
                        "Could not stop RX, we could be "
                        "confusing the DMA engine when we start RX up\n");
        }
        return stopped && !reset;
}
void ath_flushrecv ( struct ath_softc sc)

Definition at line 242 of file ath9k_recv.c.

References ath_rx_tasklet(), ath_softc::sc_flags, and SC_OP_RXFLUSH.

Referenced by ath_radio_disable(), and ath_reset().

static struct ath_buf* ath_get_next_rx_buf ( struct ath_softc sc,
struct ath_rx_status rs 
) [static, read]

Definition at line 249 of file ath9k_recv.c.

References ah, ath9k_hw_rxprocdesc(), ath_buf::bf_desc, ath_buf::bf_mpdu, ds, EINPROGRESS, ath_buf::list, list_empty, list_entry, list_first_entry, memset(), list_head::next, NULL, ret, ath_softc::rx, ath_rx::rxbuf, ath_rx::rxlink, and ath_softc::sc_ah.

Referenced by ath_rx_tasklet().

{
        struct ath_hw *ah = sc->sc_ah;
        struct ath_desc *ds;
        struct ath_buf *bf;
        int ret;

        if (list_empty(&sc->rx.rxbuf)) {
                sc->rx.rxlink = NULL;
                return NULL;
        }

        bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
        ds = bf->bf_desc;

        /*
         * 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.
         */
        ret = ath9k_hw_rxprocdesc(ah, ds, rs, 0);
        if (ret == -EINPROGRESS) {
                struct ath_rx_status trs;
                struct ath_buf *tbf;
                struct ath_desc *tds;

                memset(&trs, 0, sizeof(trs));
                if ((&bf->list)->next == &sc->rx.rxbuf) {
                        sc->rx.rxlink = NULL;
                        return NULL;
                }

                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;
                ret = ath9k_hw_rxprocdesc(ah, tds, &trs, 0);
                if (ret == -EINPROGRESS)
                        return NULL;
        }

        if (!bf->bf_mpdu)
                return bf;

        return bf;
}
static int ath9k_rx_accept ( struct ath_common common,
struct ath_rx_status rx_stats,
int *  decrypt_error 
) [static]

Definition at line 314 of file ath9k_recv.c.

References ah, ath_common::ah, ATH9K_RXERR_CRC, ATH9K_RXERR_DECRYPT, ATH9K_RXERR_MIC, ATH9K_RXERR_PHY, ath_hw::caps, ath_hw::is_monitoring, ath_rx_status::rs_datalen, ath_rx_status::rs_more, ath_rx_status::rs_status, ath_common::rx_bufsize, and ath9k_hw_capabilities::rx_status_len.

Referenced by ath9k_rx_iob_preprocess().

{
        struct ath_hw *ah = common->ah;
        u8 rx_status_len = ah->caps.rx_status_len;


        if (!rx_stats->rs_datalen)
                return 0;
        /*
         * rs_status follows rs_datalen so if rs_datalen is too large
         * we can take a hint that hardware corrupted it, so ignore
         * those frames.
         */
        if (rx_stats->rs_datalen > (common->rx_bufsize - rx_status_len))
                return 0;

        /* Only use error bits from the last fragment */
        if (rx_stats->rs_more)
                return 1;

        /*
         * The rx_stats->rs_status will not be set until the end of the
         * chained descriptors so it can be ignored if rs_more is set. The
         * rs_more will be false at the last element of the chained
         * descriptors.
         */
        if (rx_stats->rs_status != 0) {
                if (rx_stats->rs_status & ATH9K_RXERR_PHY)
                        return 0;

                if (rx_stats->rs_status & ATH9K_RXERR_DECRYPT) {
                        *decrypt_error = 1;
                }
                /*
                 * Reject error frames with the exception of
                 * decryption and MIC failures. For monitor mode,
                 * we also ignore the CRC error.
                 */
                if (ah->is_monitoring) {
                        if (rx_stats->rs_status &
                            ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC |
                              ATH9K_RXERR_CRC))
                                return 0;
                } else {
                        if (rx_stats->rs_status &
                            ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC)) {
                                return 0;
                        }
                }
        }
        return 1;
}
static int ath9k_process_rate ( struct ath_common *common  __unused,
struct net80211_device dev,
struct ath_rx_status rx_stats,
int *  rix 
) [static]

Definition at line 369 of file ath9k_recv.c.

References net80211_device::channel, net80211_device::channels, DBG, ath_softc::dev, EINVAL, ath9k_legacy_rate::hw_value, ath9k_legacy_rate::hw_value_short, ath_softc::hwinfo, net80211_hw_info::nr_rates, net80211_device::priv, ath_softc::rates, and ath_rx_status::rs_rate.

Referenced by ath9k_rx_iob_preprocess().

{
        struct ath_softc *sc = (struct ath_softc *)dev->priv;
        int band;
        int i = 0;

        band = (dev->channels + sc->dev->channel)->band;

        for (i = 0; i < sc->hwinfo->nr_rates[band]; i++) {
                if (sc->rates[i].hw_value == rx_stats->rs_rate) {
                        *rix = i;
                        return 0;
                }
                if (sc->rates[i].hw_value_short == rx_stats->rs_rate) {
                        *rix = i;
                        return 0;
                }
        }

        /*
         * No valid hardware bitrate found -- we should not get here
         * because hardware has already validated this frame as OK.
         */
        DBG("ath9k: "
                "unsupported hw bitrate detected 0x%02x using 1 Mbit\n",
                rx_stats->rs_rate);

        return -EINVAL;
}
static int ath9k_rx_iob_preprocess ( struct ath_common common,
struct net80211_device dev,
struct ath_rx_status rx_stats,
int *  rix,
int *  decrypt_error 
) [static]

Definition at line 407 of file ath9k_recv.c.

References ath9k_process_rate(), ath9k_rx_accept(), EINVAL, and ath_rx_status::rs_more.

Referenced by ath_rx_tasklet().

{
        /*
         * everything but the rate is checked here, the rate check is done
         * separately to avoid doing two lookups for a rate for each frame.
         */
        if (!ath9k_rx_accept(common, rx_stats, decrypt_error))
                return -EINVAL;

        /* Only use status info from the last fragment */
        if (rx_stats->rs_more)
                return 0;

        if (ath9k_process_rate(common, dev, rx_stats, rix))
                return -EINVAL;

        return 0;
}
int ath_rx_tasklet ( struct ath_softc sc,
int  flush,
int hp  __unused 
)

Definition at line 430 of file ath9k_recv.c.

References ah, alloc_iob_raw(), ath9k_hw_common(), ath9k_hw_rxena(), ath9k_rx_iob_preprocess(), ath_get_next_rx_buf(), ath_rx_buf_link(), ath_setdefantenna(), ath_buf::bf_buf_addr, ath_buf::bf_mpdu, ath9k_legacy_rate::bitrate, ath_common::cachelsz, ath_hw::caps, common, DBGIO, ath_rx::defant, ath_softc::dev, iob_pull, iob_put, ath_buf::list, list_add_tail, list_del, memset(), net80211_rx(), NULL, ath_softc::rates, ath_rx_status::rs_antenna, ath_rx_status::rs_datalen, ath_rx_status::rs_rate, ath_rx_status::rs_rssi, ath_softc::rx, ath_common::rx_bufsize, ath9k_hw_capabilities::rx_status_len, ath_rx::rxbuf, ath_rx::rxotherant, ath_softc::sc_ah, ath_softc::sc_flags, SC_OP_RXFLUSH, and virt_to_bus().

Referenced by ath9k_tasklet(), and ath_flushrecv().

{
        struct ath_buf *bf;
        struct io_buffer *iob = NULL, *requeue_iob;
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        /*
         * The hw can technically differ from common->hw when using ath9k
         * virtual wiphy so to account for that we iterate over the active
         * wiphys and find the appropriate wiphy and therefore hw.
         */
        struct net80211_device *dev = sc->dev;
        int retval;
        int decrypt_error = 0;
        struct ath_rx_status rs;
        int rix = 0;

        do {
                /* If handling rx interrupt and flush is in progress => exit */
                if ((sc->sc_flags & SC_OP_RXFLUSH) && (flush == 0))
                        break;

                memset(&rs, 0, sizeof(rs));
                bf = ath_get_next_rx_buf(sc, &rs);

                if (!bf)
                        break;

                iob = bf->bf_mpdu;
                if (!iob)
                        continue;

                /*
                 * If we're asked to flush receive queue, directly
                 * chain it back at the queue without processing it.
                 */
                if (flush)
                        goto requeue_drop_frag;

                retval = ath9k_rx_iob_preprocess(common, dev, &rs,
                                                 &rix, &decrypt_error);
                if (retval)
                        goto requeue_drop_frag;

                /* Ensure we always have an iob to requeue once we are done
                 * processing the current buffer's iob */
                requeue_iob = alloc_iob_raw ( common->rx_bufsize,
                                              common->cachelsz, 0 );

                /* If there is no memory we ignore the current RX'd frame,
                 * tell hardware it can give us a new frame using the old
                 * iob and put it at the tail of the sc->rx.rxbuf list for
                 * processing. */
                if (!requeue_iob)
                        goto requeue_drop_frag;

                iob_put(iob, rs.rs_datalen + ah->caps.rx_status_len);
                if (ah->caps.rx_status_len)
                        iob_pull(iob, ah->caps.rx_status_len);

                /* We will now give hardware our shiny new allocated iob */
                bf->bf_mpdu = requeue_iob;
                bf->bf_buf_addr = virt_to_bus ( requeue_iob->data );

                /*
                 * change the default rx antenna if rx diversity chooses the
                 * other antenna 3 times in a row.
                 */
                if (sc->rx.defant != rs.rs_antenna) {
                        if (++sc->rx.rxotherant >= 3)
                                ath_setdefantenna(sc, rs.rs_antenna);
                } else {
                        sc->rx.rxotherant = 0;
                }

                DBGIO("ath9k: rx %d bytes, signal %d, bitrate %d, hw_value %d\n", rs.rs_datalen,
                                                rs.rs_rssi, sc->rates[rix].bitrate, rs.rs_rate);

                net80211_rx(dev, iob, rs.rs_rssi,
                                sc->rates[rix].bitrate);

requeue_drop_frag:
                list_del(&bf->list);
                list_add_tail(&bf->list, &sc->rx.rxbuf);
                ath_rx_buf_link(sc, bf);
                ath9k_hw_rxena(ah);
        } while (1);

        return 0;
}