Begin abstracting out the RX path in preparation for RX EDMA support.

The RX EDMA support requires a modified approach to the RX descriptor
handling.

Specifically:

* There's now two RX queues - high and low priority;
* The RX queues are implemented as FIFOs; they're now an array of pointers
  to buffers;
* .. and the RX buffer and descriptor are in the same "buffer", rather than
  being separate.

So to that end, this commit abstracts out most of the RX related functions
from the bulk of the driver.  Notably, the RX DMA/buffer allocation isn't
updated, primarily because I haven't yet fleshed out what it should look
like.

Whilst I'm here, create a set of matching but mostly unimplemented EDMA
stubs.

Tested:

  * AR9280, station mode

TODO:

  * Thorough AP and other mode testing for non-EDMA chips;
  * Figure out how to allocate RX buffers suitable for RX EDMA, including
    correctly setting the mbuf length to compensate for the RX descriptor
    and completion status area.
This commit is contained in:
Adrian Chadd 2012-07-03 06:59:12 +00:00
parent 9a0b948f98
commit f8cc9b09b0
6 changed files with 321 additions and 34 deletions

View File

@ -108,6 +108,7 @@ __FBSDID("$FreeBSD$");
#include <dev/ath/if_ath_led.h>
#include <dev/ath/if_ath_keycache.h>
#include <dev/ath/if_ath_rx.h>
#include <dev/ath/if_ath_rx_edma.h>
#include <dev/ath/if_ath_beacon.h>
#include <dev/ath/if_athdfs.h>
@ -301,6 +302,17 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
sc->sc_debug = ath_debug;
#endif
/*
* Setup the DMA/EDMA functions based on the current
* hardware support.
*
* This is required before the descriptors are allocated.
*/
if (ath_hal_hasedma(sc->sc_ah))
ath_recv_setup_edma(sc);
else
ath_recv_setup_legacy(sc);
/*
* Check if the MAC has multi-rate retry support.
* We do this by trying to setup a fake extended
@ -376,7 +388,7 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
taskqueue_start_threads(&sc->sc_tq, 1, PI_NET,
"%s taskq", ifp->if_xname);
TASK_INIT(&sc->sc_rxtask, 0, ath_rx_tasklet, sc);
TASK_INIT(&sc->sc_rxtask, 0, sc->sc_rx.recv_tasklet, sc);
TASK_INIT(&sc->sc_bmisstask, 0, ath_bmiss_proc, sc);
TASK_INIT(&sc->sc_bstucktask,0, ath_bstuck_proc, sc);
TASK_INIT(&sc->sc_resettask,0, ath_reset_proc, sc);
@ -2100,7 +2112,7 @@ ath_reset(struct ifnet *ifp, ATH_RESET_TYPE reset_type)
* That way frames aren't dropped which shouldn't be.
*/
ath_stoprecv(sc, (reset_type != ATH_RESET_NOLOSS));
ath_rx_proc(sc, 0);
ath_rx_flush(sc);
ath_settkipmic(sc); /* configure TKIP MIC handling */
/* NB: indicate channel change so we do a full reset */
@ -4018,7 +4030,7 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
/*
* First, handle completed TX/RX frames.
*/
ath_rx_proc(sc, 0);
ath_rx_flush(sc);
ath_draintxq(sc, ATH_RESET_NOLOSS);
/*
* Next, flush the non-scheduled frames.

View File

@ -214,8 +214,8 @@ ath_calcrxfilter(struct ath_softc *sc)
return rfilt;
}
int
ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf)
static int
ath_legacy_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf)
{
struct ath_hal *ah = sc->sc_ah;
int error;
@ -463,29 +463,6 @@ ath_handle_micerror(struct ieee80211com *ic,
}
}
/*
* Only run the RX proc if it's not already running.
* Since this may get run as part of the reset/flush path,
* the task can't clash with an existing, running tasklet.
*/
void
ath_rx_tasklet(void *arg, int npending)
{
struct ath_softc *sc = arg;
CTR1(ATH_KTR_INTR, "ath_rx_proc: pending=%d", npending);
DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: pending %u\n", __func__, npending);
ATH_PCU_LOCK(sc);
if (sc->sc_inreset_cnt > 0) {
device_printf(sc->sc_dev,
"%s: sc_inreset_cnt > 0; skipping\n", __func__);
ATH_PCU_UNLOCK(sc);
return;
}
ATH_PCU_UNLOCK(sc);
ath_rx_proc(sc, 1);
}
static int
ath_rx_pkt(struct ath_softc *sc, struct ath_rx_status *rs, HAL_STATUS status,
uint64_t tsf, int nf, struct ath_buf *bf)
@ -814,7 +791,7 @@ rx_next:
return (is_good);
}
void
static void
ath_rx_proc(struct ath_softc *sc, int resched)
{
#define PA2DESC(_sc, _pa) \
@ -964,11 +941,42 @@ rx_proc_next:
ATH_PCU_UNLOCK(sc);
}
/*
* Only run the RX proc if it's not already running.
* Since this may get run as part of the reset/flush path,
* the task can't clash with an existing, running tasklet.
*/
static void
ath_legacy_rx_tasklet(void *arg, int npending)
{
struct ath_softc *sc = arg;
CTR1(ATH_KTR_INTR, "ath_rx_proc: pending=%d", npending);
DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: pending %u\n", __func__, npending);
ATH_PCU_LOCK(sc);
if (sc->sc_inreset_cnt > 0) {
device_printf(sc->sc_dev,
"%s: sc_inreset_cnt > 0; skipping\n", __func__);
ATH_PCU_UNLOCK(sc);
return;
}
ATH_PCU_UNLOCK(sc);
ath_rx_proc(sc, 1);
}
static void
ath_legacy_flushrecv(struct ath_softc *sc)
{
ath_rx_proc(sc, 0);
}
/*
* Disable the receive h/w in preparation for a reset.
*/
void
ath_stoprecv(struct ath_softc *sc, int dodelay)
static void
ath_legacy_stoprecv(struct ath_softc *sc, int dodelay)
{
#define PA2DESC(_sc, _pa) \
((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \
@ -1019,8 +1027,8 @@ ath_stoprecv(struct ath_softc *sc, int dodelay)
/*
* Enable the receive h/w following a reset.
*/
int
ath_startrecv(struct ath_softc *sc)
static int
ath_legacy_startrecv(struct ath_softc *sc)
{
struct ath_hal *ah = sc->sc_ah;
struct ath_buf *bf;
@ -1044,3 +1052,17 @@ ath_startrecv(struct ath_softc *sc)
ath_hal_startpcurecv(ah); /* re-enable PCU/DMA engine */
return 0;
}
void
ath_recv_setup_legacy(struct ath_softc *sc)
{
device_printf(sc->sc_dev, "DMA setup: legacy\n");
sc->sc_rx.recv_start = ath_legacy_startrecv;
sc->sc_rx.recv_stop = ath_legacy_stoprecv;
sc->sc_rx.recv_flush = ath_legacy_flushrecv;
sc->sc_rx.recv_tasklet = ath_legacy_rx_tasklet;
sc->sc_rx.recv_rxbuf_init = ath_legacy_rxbuf_init;
}

View File

@ -32,12 +32,26 @@
#define __IF_ATH_RX_H__
extern u_int32_t ath_calcrxfilter(struct ath_softc *sc);
extern int ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf);
extern void ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
int subtype, int rssi, int nf);
#define ath_stoprecv(_sc, _dodelay) \
(_sc)->sc_rx.recv_stop((_sc), (_dodelay))
#define ath_startrecv(_sc) \
(_sc)->sc_rx.recv_start((_sc))
#define ath_rx_flush(_sc) \
(_sc)->sc_rx.recv_flush((_sc))
#define ath_rxbuf_init(_sc, _bf) \
(_sc)->sc_rx.recv_rxbuf_init((_sc), (_bf))
#if 0
extern int ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf);
extern void ath_rx_tasklet(void *arg, int npending);
extern void ath_rx_proc(struct ath_softc *sc, int resched);
extern void ath_stoprecv(struct ath_softc *sc, int dodelay);
extern int ath_startrecv(struct ath_softc *sc);
#endif
extern void ath_recv_setup_legacy(struct ath_softc *sc);
#endif

View File

@ -0,0 +1,192 @@
/*-
* Copyright (c) 2012 Adrian Chadd <adrian@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* Driver for the Atheros Wireless LAN controller.
*
* This software is derived from work of Atsushi Onoe; his contribution
* is greatly appreciated.
*/
#include "opt_inet.h"
#include "opt_ath.h"
/*
* This is needed for register operations which are performed
* by the driver - eg, calls to ath_hal_gettsf32().
*
* It's also required for any AH_DEBUG checks in here, eg the
* module dependencies.
*/
#include "opt_ah.h"
#include "opt_wlan.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/errno.h>
#include <sys/callout.h>
#include <sys/bus.h>
#include <sys/endian.h>
#include <sys/kthread.h>
#include <sys/taskqueue.h>
#include <sys/priv.h>
#include <sys/module.h>
#include <sys/ktr.h>
#include <sys/smp.h> /* for mp_ncpus */
#include <machine/bus.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/if_arp.h>
#include <net/ethernet.h>
#include <net/if_llc.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_regdomain.h>
#ifdef IEEE80211_SUPPORT_SUPERG
#include <net80211/ieee80211_superg.h>
#endif
#ifdef IEEE80211_SUPPORT_TDMA
#include <net80211/ieee80211_tdma.h>
#endif
#include <net/bpf.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/if_ether.h>
#endif
#include <dev/ath/if_athvar.h>
#include <dev/ath/ath_hal/ah_devid.h> /* XXX for softled */
#include <dev/ath/ath_hal/ah_diagcodes.h>
#include <dev/ath/if_ath_debug.h>
#include <dev/ath/if_ath_misc.h>
#include <dev/ath/if_ath_tsf.h>
#include <dev/ath/if_ath_tx.h>
#include <dev/ath/if_ath_sysctl.h>
#include <dev/ath/if_ath_led.h>
#include <dev/ath/if_ath_keycache.h>
#include <dev/ath/if_ath_rx.h>
#include <dev/ath/if_ath_beacon.h>
#include <dev/ath/if_athdfs.h>
#ifdef ATH_TX99_DIAG
#include <dev/ath/ath_tx99/ath_tx99.h>
#endif
#include <dev/ath/if_ath_rx_edma.h>
static void
ath_edma_stoprecv(struct ath_softc *sc, int dodelay)
{
struct ath_hal *ah = sc->sc_ah;
ath_hal_stoppcurecv(ah);
ath_hal_setrxfilter(ah, 0);
ath_hal_stopdmarecv(ah);
DELAY(3000);
if (sc->sc_rxpending != NULL) {
m_freem(sc->sc_rxpending);
sc->sc_rxpending = NULL;
}
sc->sc_rxlink = NULL;
}
static int
ath_edma_startrecv(struct ath_softc *sc)
{
struct ath_hal *ah = sc->sc_ah;
sc->sc_rxlink = NULL;
sc->sc_rxpending = NULL;
/* XXX setup HP RX queue FIFO pointer */
/* XXX setup LP RX queue FIFO pointer */
/* XXX ath_hal_rxena() */
ath_mode_init(sc);
ath_hal_startpcurecv(ah);
return (0);
}
static void
ath_edma_recv_flush(struct ath_softc *sc)
{
device_printf(sc->sc_dev, "%s: called\n", __func__);
}
static void
ath_edma_recv_tasklet(void *arg, int npending)
{
struct ath_softc *sc = (struct ath_softc *) arg;
device_printf(sc->sc_dev, "%s: called; npending=%d\n",
__func__,
npending);
/* XXX TODO */
}
static int
ath_edma_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf)
{
device_printf(sc->sc_dev, "%s: called; bf=%p\n", __func__, bf);
return (EIO);
}
void
ath_recv_setup_edma(struct ath_softc *sc)
{
device_printf(sc->sc_dev, "DMA setup: EDMA\n");
sc->sc_rx.recv_stop = ath_edma_stoprecv;
sc->sc_rx.recv_start = ath_edma_startrecv;
sc->sc_rx.recv_flush = ath_edma_recv_flush;
sc->sc_rx.recv_tasklet = ath_edma_recv_tasklet;
sc->sc_rx.recv_rxbuf_init = ath_edma_rxbuf_init;
}

View File

@ -0,0 +1,36 @@
/*-
* Copyright (c) 2012 Adrian Chadd <adrian@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
* redistribution must be conditioned upon including a substantially
* similar Disclaimer requirement for further binary redistribution.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGES.
*
* $FreeBSD$
*/
#ifndef __IF_ATH_RX_EDMA_H__
#define __IF_ATH_RX_EDMA_H__
extern void ath_recv_setup_edma(struct ath_softc *sc);
#endif

View File

@ -372,6 +372,15 @@ typedef enum {
ATH_RESET_FULL = 2,
} ATH_RESET_TYPE;
struct ath_rx_methods {
void (*recv_stop)(struct ath_softc *sc, int dodelay);
int (*recv_start)(struct ath_softc *sc);
void (*recv_flush)(struct ath_softc *sc);
void (*recv_tasklet)(void *arg, int npending);
int (*recv_rxbuf_init)(struct ath_softc *sc,
struct ath_buf *bf);
};
struct ath_softc {
struct ifnet *sc_ifp; /* interface common */
struct ath_stats sc_stats; /* interface statistics */
@ -385,6 +394,8 @@ struct ath_softc {
u_int8_t sc_nbssid0; /* # vap's using base mac */
uint32_t sc_bssidmask; /* bssid mask */
struct ath_rx_methods sc_rx;
void (*sc_node_cleanup)(struct ieee80211_node *);
void (*sc_node_free)(struct ieee80211_node *);
device_t sc_dev;