summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2015-02-09 08:33:29 -0600
committerGregory Nutt <gnutt@nuttx.org>2015-02-09 08:33:29 -0600
commit0aa6421dec16ae22870525edd6d6652cef671efb (patch)
treeeec891b7b6d52f0f453081bc3618130cb70d58d8
parente1f5738a8df9845a03ffc086f70ec3ecd52300f0 (diff)
downloadnuttx-0aa6421dec16ae22870525edd6d6652cef671efb.tar.gz
nuttx-0aa6421dec16ae22870525edd6d6652cef671efb.tar.bz2
nuttx-0aa6421dec16ae22870525edd6d6652cef671efb.zip
Add logic so that STM32 Ethernet drivier can avoid interrupt level processing and, instead, execute on the work thread.
-rw-r--r--nuttx/arch/arm/src/stm32/stm32_eth.c440
1 files changed, 402 insertions, 38 deletions
diff --git a/nuttx/arch/arm/src/stm32/stm32_eth.c b/nuttx/arch/arm/src/stm32/stm32_eth.c
index aa163733f..c7547e7f8 100644
--- a/nuttx/arch/arm/src/stm32/stm32_eth.c
+++ b/nuttx/arch/arm/src/stm32/stm32_eth.c
@@ -54,6 +54,10 @@
#include <nuttx/irq.h>
#include <nuttx/wdog.h>
+#ifdef CONFIG_NET_NOINTS
+# include <nuttx/wqueue.h>
+#endif
+
#include <nuttx/net/mii.h>
#include <nuttx/net/arp.h>
#include <nuttx/net/netdev.h>
@@ -89,6 +93,14 @@
# error "Logic to support multiple Ethernet interfaces is incomplete"
#endif
+/* If processing is not done at the interrupt level, then high priority
+ * work queue support is required.
+ */
+
+#if defined(CONFIG_NET_NOINTS) && !defined(CONFIG_SCHED_HPWORK)
+# error High priority work queue support is required
+#endif
+
#if !defined(CONFIG_STM32_SYSCFG) && !defined(CONFIG_STM32_CONNECTIVITYLINE)
# error "CONFIG_STM32_SYSCFG must be defined in the NuttX configuration"
#endif
@@ -570,6 +582,9 @@ struct stm32_ethmac_s
uint8_t fduplex : 1; /* Full (vs. half) duplex */
WDOG_ID txpoll; /* TX poll timer */
WDOG_ID txtimeout; /* TX timeout timer */
+#ifdef CONFIG_NET_NOINTS
+ struct work_s work; /* For deferring work to the work queue */
+#endif
/* This holds the information visible to uIP/NuttX */
@@ -642,17 +657,34 @@ static int stm32_recvframe(FAR struct stm32_ethmac_s *priv);
static void stm32_receive(FAR struct stm32_ethmac_s *priv);
static void stm32_freeframe(FAR struct stm32_ethmac_s *priv);
static void stm32_txdone(FAR struct stm32_ethmac_s *priv);
+#ifdef CONFIG_NET_NOINTS
+static void stm32_interrupt_work(FAR void *arg);
+#endif
static int stm32_interrupt(int irq, FAR void *context);
/* Watchdog timer expirations */
-static void stm32_polltimer(int argc, uint32_t arg, ...);
-static void stm32_txtimeout(int argc, uint32_t arg, ...);
+static inline void stm32_txtimeout_process(FAR struct stm32_ethmac_s *priv);
+#ifdef CONFIG_NET_NOINTS
+static void stm32_txtimeout_work(FAR void *arg);
+#endif
+static void stm32_txtimeout_expiry(int argc, uint32_t arg, ...);
+
+static inline void stm32_poll_process(FAR struct stm32_ethmac_s *priv);
+#ifdef CONFIG_NET_NOINTS
+static void stm32_poll_work(FAR void *arg);
+#endif
+static void stm32_poll_expiry(int argc, uint32_t arg, ...);
/* NuttX callback functions */
static int stm32_ifup(struct net_driver_s *dev);
static int stm32_ifdown(struct net_driver_s *dev);
+static int stm32_ifdown(struct net_driver_s *dev);
+static inline void stm32_txavail_process(FAR struct stm32_ethmac_s *priv);
+#ifdef CONFIG_NET_NOINTS
+static void stm32_txavail_work(FAR void *arg);
+#endif
static int stm32_txavail(struct net_driver_s *dev);
#if defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_ICMPv6)
static int stm32_addmac(struct net_driver_s *dev, FAR const uint8_t *mac);
@@ -1140,7 +1172,7 @@ static int stm32_transmit(FAR struct stm32_ethmac_s *priv)
/* Setup the TX timeout watchdog (perhaps restarting the timer) */
- (void)wd_start(priv->txtimeout, STM32_TXTIMEOUT, stm32_txtimeout, 1, (uint32_t)priv);
+ (void)wd_start(priv->txtimeout, STM32_TXTIMEOUT, stm32_txtimeout_expiry, 1, (uint32_t)priv);
return OK;
}
@@ -1904,25 +1936,25 @@ static void stm32_txdone(FAR struct stm32_ethmac_s *priv)
}
/****************************************************************************
- * Function: stm32_interrupt
+ * Function: stm32_interrupt_process
*
* Description:
- * Hardware interrupt handler
+ * Interrupt processing. This may be performed either within the interrupt
+ * handler or on the worker thread, depending upon the configuration
*
* Parameters:
- * irq - Number of the IRQ that generated the interrupt
- * context - Interrupt register state save info (architecture-specific)
+ * priv - Reference to the driver state structure
*
* Returned Value:
- * OK on success
+ * None
*
* Assumptions:
+ * Ethernet interrupts are disabled
*
****************************************************************************/
-static int stm32_interrupt(int irq, FAR void *context)
+static inline void stm32_interrupt_process(FAR struct stm32_ethmac_s *priv)
{
- register FAR struct stm32_ethmac_s *priv = &g_stm32ethmac[0];
uint32_t dmasr;
/* Get the DMA interrupt status bits (no MAC interrupts are expected) */
@@ -1956,7 +1988,7 @@ static int stm32_interrupt(int irq, FAR void *context)
/* Check if a packet transmission just completed. If so, call
* stm32_txdone(). This may disable further TX interrupts if there
- * are no pending tansmissions.
+ * are no pending transmissions.
*/
if ((dmasr & ETH_DMAINT_TI) != 0)
@@ -1996,34 +2028,133 @@ static int stm32_interrupt(int irq, FAR void *context)
stm32_putreg(ETH_DMAINT_AIS, STM32_ETH_DMASR);
}
#endif
- return OK;
}
/****************************************************************************
- * Function: stm32_txtimeout
+ * Function: stm32_interrupt_work
*
* Description:
- * Our TX watchdog timed out. Called from the timer interrupt handler.
- * The last TX never completed. Reset the hardware and start again.
+ * Perform interrupt related work from the worker thread
*
* Parameters:
- * argc - The number of available arguments
- * arg - The first argument
+ * arg - The argument passed when work_queue() was called.
*
* Returned Value:
- * None
+ * OK on success
*
* Assumptions:
- * Global interrupts are disabled by the watchdog logic.
+ * Ethernet interrupts are disabled
*
****************************************************************************/
-static void stm32_txtimeout(int argc, uint32_t arg, ...)
+#ifdef CONFIG_NET_NOINTS
+static void stm32_interrupt_work(FAR void *arg)
{
FAR struct stm32_ethmac_s *priv = (FAR struct stm32_ethmac_s *)arg;
+ net_lock_t state;
- nlldbg("Timeout!\n");
+ DEBUGASSERT(priv);
+
+ /* Process pending Ethernet interrupts */
+
+ state = net_lock();
+ stm32_interrupt_process(priv);
+ net_unlock(state);
+
+ /* Re-enable Ethernet interrupts at the NVIC */
+
+ up_enable_irq(STM32_IRQ_ETH);
+}
+#endif
+
+/****************************************************************************
+ * Function: stm32_interrupt
+ *
+ * Description:
+ * Hardware interrupt handler
+ *
+ * Parameters:
+ * irq - Number of the IRQ that generated the interrupt
+ * context - Interrupt register state save info (architecture-specific)
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static int stm32_interrupt(int irq, FAR void *context)
+{
+ FAR struct stm32_ethmac_s *priv = &g_stm32ethmac[0];
+
+#ifdef CONFIG_NET_NOINTS
+ uint32_t dmasr;
+
+ /* Get the DMA interrupt status bits (no MAC interrupts are expected) */
+
+ dmasr = stm32_getreg(STM32_ETH_DMASR);
+ if (dmasr != 0)
+ {
+ /* Disable further Ethernet interrupts. Because Ethernet interrupts
+ * are also disabled if the TX timeout event occurs, there can be no
+ * race condition here.
+ */
+
+ up_disable_irq(STM32_IRQ_ETH);
+
+ /* Check if a packet transmission just completed. */
+
+ if ((dmasr & ETH_DMAINT_TI) != 0)
+ {
+ /* If a TX transfer just completed, then cancel the TX timeout so
+ * there will be no race condition between any subsequent timeout
+ * expiration and the deferred interrupt processing.
+ */
+
+ wd_cancel(priv->txtimeout);
+ }
+
+ /* Cancel any pending poll work */
+
+ work_cancel(HPWORK, &priv->work);
+
+ /* Schedule to perform the interrupt processing on the worker thread. */
+
+ work_queue(HPWORK, &priv->work, stm32_interrupt_work, priv, 0);
+ }
+
+#else
+ /* Process the interrupt now */
+
+ stm32_interrupt_process(priv);
+#endif
+ return OK;
+}
+
+/****************************************************************************
+ * Function: stm32_txtimeout_process
+ *
+ * Description:
+ * Process a TX timeout. Called from the either the watchdog timer
+ * expiration logic or from the worker thread, depending upon the
+ * configuration. The timeout means that the last TX never completed.
+ * Reset the hardware and start again.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static inline void stm32_txtimeout_process(FAR struct stm32_ethmac_s *priv)
+{
/* Then reset the hardware. Just take the interface down, then back
* up again.
*/
@@ -2031,16 +2162,48 @@ static void stm32_txtimeout(int argc, uint32_t arg, ...)
stm32_ifdown(&priv->dev);
stm32_ifup(&priv->dev);
- /* Then poll uIP for new XMIT data */
+ /* Then poll for new XMIT data */
stm32_dopoll(priv);
}
/****************************************************************************
- * Function: stm32_polltimer
+ * Function: stm32_txtimeout_work
*
* Description:
- * Periodic timer handler. Called from the timer interrupt handler.
+ * Perform TX timeout related work from the worker thread
+ *
+ * Parameters:
+ * arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ * Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_NOINTS
+static void stm32_txtimeout_work(FAR void *arg)
+{
+ FAR struct stm32_ethmac_s *priv = (FAR struct stm32_ethmac_s *)arg;
+ net_lock_t state;
+
+ /* Process pending Ethernet interrupts */
+
+ state = net_lock();
+ stm32_txtimeout_process(priv);
+ net_unlock(state);
+}
+#endif
+
+/****************************************************************************
+ * Function: stm32_txtimeout_expiry
+ *
+ * Description:
+ * Our TX watchdog timed out. Called from the timer interrupt handler.
+ * The last TX never completed. Reset the hardware and start again.
*
* Parameters:
* argc - The number of available arguments
@@ -2054,10 +2217,59 @@ static void stm32_txtimeout(int argc, uint32_t arg, ...)
*
****************************************************************************/
-static void stm32_polltimer(int argc, uint32_t arg, ...)
+static void stm32_txtimeout_expiry(int argc, uint32_t arg, ...)
{
FAR struct stm32_ethmac_s *priv = (FAR struct stm32_ethmac_s *)arg;
- FAR struct net_driver_s *dev = &priv->dev;
+
+ nlldbg("Timeout!\n");
+
+#ifdef CONFIG_NET_NOINTS
+ /* Disable further Ethernet interrupts. This will prevent some race
+ * conditions with interrupt work. There is still a potential race
+ * condition with interrupt work that is already queued and in progress.
+ *
+ * Interrupts will be re-enabled when stm32_ifup() is called.
+ */
+
+ up_disable_irq(STM32_IRQ_ETH);
+
+ /* Cancel any pending poll or interrupt work. This will have no effect
+ * on work that has already been started.
+ */
+
+ work_cancel(HPWORK, &priv->work);
+
+ /* Schedule to perform the TX timeout processing on the worker thread. */
+
+ work_queue(HPWORK, &priv->work, stm32_txtimeout_work, priv, 0);
+
+#else
+ /* Process the timeout now */
+
+ stm32_txtimeout_process(priv);
+#endif
+}
+
+/****************************************************************************
+ * Function: stm32_poll_process
+ *
+ * Description:
+ * Perform the periodic poll. This may be called either from watchdog
+ * timer logic or from the worker thread, depending upon the configuration.
+ *
+ * Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ *
+ ****************************************************************************/
+
+static inline void stm32_poll_process(FAR struct stm32_ethmac_s *priv)
+{
+ FAR struct net_driver_s *dev = &priv->dev;
/* Check if the next TX descriptor is owned by the Ethernet DMA or CPU. We
* cannot perform the timer poll if we are unable to accept another packet
@@ -2104,7 +2316,87 @@ static void stm32_polltimer(int argc, uint32_t arg, ...)
/* Setup the watchdog poll timer again */
- (void)wd_start(priv->txpoll, STM32_WDDELAY, stm32_polltimer, 1, arg);
+ (void)wd_start(priv->txpoll, STM32_WDDELAY, stm32_poll_expiry, 1, priv);
+}
+
+/****************************************************************************
+ * Function: stm32_poll_work
+ *
+ * Description:
+ * Perform periodic polling from the worker thread
+ *
+ * Parameters:
+ * arg - The argument passed when work_queue() as called.
+ *
+ * Returned Value:
+ * OK on success
+ *
+ * Assumptions:
+ * Ethernet interrupts are disabled
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_NOINTS
+static void stm32_poll_work(FAR void *arg)
+{
+ FAR struct stm32_ethmac_s *priv = (FAR struct stm32_ethmac_s *)arg;
+ net_lock_t state;
+
+ /* Perform the poll */
+
+ state = net_lock();
+ stm32_poll_process(priv);
+ net_unlock(state);
+}
+#endif
+
+/****************************************************************************
+ * Function: stm32_poll_expiry
+ *
+ * Description:
+ * Periodic timer handler. Called from the timer interrupt handler.
+ *
+ * Parameters:
+ * argc - The number of available arguments
+ * arg - The first argument
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Global interrupts are disabled by the watchdog logic.
+ *
+ ****************************************************************************/
+
+static void stm32_poll_expiry(int argc, uint32_t arg, ...)
+{
+ FAR struct stm32_ethmac_s *priv = (FAR struct stm32_ethmac_s *)arg;
+
+#ifdef CONFIG_NET_NOINTS
+ /* Is our single work structure available? It may not be if there are
+ * pending interrupt actions.
+ */
+
+ if (work_available(&priv->work))
+ {
+ /* Schedule to perform the interrupt processing on the worker thread. */
+
+ work_queue(HPWORK, &priv->work, stm32_poll_work, priv, 0);
+ }
+ else
+ {
+ /* No.. Just re-start the watchdog poll timer, missing one polling
+ * cycle.
+ */
+
+ (void)wd_start(priv->txpoll, STM32_WDDELAY, stm32_poll_expiry, 1, (uint32_t)priv);
+ }
+
+#else
+ /* Process the interrupt now */
+
+ stm32_poll_process(priv);
+#endif
}
/****************************************************************************
@@ -2151,7 +2443,7 @@ static int stm32_ifup(struct net_driver_s *dev)
/* Set and activate a timer process */
- (void)wd_start(priv->txpoll, STM32_WDDELAY, stm32_polltimer, 1, (uint32_t)priv);
+ (void)wd_start(priv->txpoll, STM32_WDDELAY, stm32_poll_expiry, 1, (uint32_t)priv);
/* Enable the Ethernet interrupt */
@@ -2210,6 +2502,69 @@ static int stm32_ifdown(struct net_driver_s *dev)
}
/****************************************************************************
+ * Function: stm32_txavail_process
+ *
+ * Description:
+ * Perform an out-of-cycle poll.
+ *
+ * Parameters:
+ * priv - Reference to the NuttX driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called in normal user mode
+ *
+ ****************************************************************************/
+
+static inline void stm32_txavail_process(FAR struct stm32_ethmac_s *priv)
+{
+ nvdbg("ifup: %d\n", priv->ifup);
+
+ /* Ignore the notification if the interface is not yet up */
+
+ if (priv->ifup)
+ {
+ /* Poll uIP for new XMIT data */
+
+ stm32_dopoll(priv);
+ }
+
+}
+
+/****************************************************************************
+ * Function: stm32_txavail_work
+ *
+ * Description:
+ * Perform an out-of-cycle poll on the worker thread.
+ *
+ * Parameters:
+ * arg - Reference to the NuttX driver state structure (cast to void*)
+ *
+ * Returned Value:
+ * None
+ *
+ * Assumptions:
+ * Called on the higher priority worker thread.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_NOINTS
+static void stm32_txavail_work(FAR void *arg)
+{
+ FAR struct stm32_ethmac_s *priv = (FAR struct stm32_ethmac_s *)arg;
+ net_lock_t state;
+
+ /* Perform the poll */
+
+ state = net_lock();
+ stm32_txavail_process(priv);
+ net_unlock(state);
+}
+#endif
+
+/****************************************************************************
* Function: stm32_txavail
*
* Description:
@@ -2231,9 +2586,22 @@ static int stm32_ifdown(struct net_driver_s *dev)
static int stm32_txavail(struct net_driver_s *dev)
{
FAR struct stm32_ethmac_s *priv = (FAR struct stm32_ethmac_s *)dev->d_private;
- irqstate_t flags;
- nllvdbg("ifup: %d\n", priv->ifup);
+#ifdef CONFIG_NET_NOINTS
+ /* Is our single work structure available? It may not be if there are
+ * pending interrupt actions and we will have to ignore the Tx
+ * availability action.
+ */
+
+ if (work_available(&priv->work))
+ {
+ /* Schedule to serialize the poll on the worker thread. */
+
+ work_queue(HPWORK, &priv->work, stm32_txavail_work, priv, 0);
+ }
+
+#else
+ irqstate_t flags;
/* Disable interrupts because this function may be called from interrupt
* level processing.
@@ -2241,16 +2609,12 @@ static int stm32_txavail(struct net_driver_s *dev)
flags = irqsave();
- /* Ignore the notification if the interface is not yet up */
-
- if (priv->ifup)
- {
- /* Poll uIP for new XMIT data */
-
- stm32_dopoll(priv);
- }
+ /* Perform the out-of-cycle poll now */
+ stm32_txavail_process(priv);
irqrestore(flags);
+#endif
+
return OK;
}