ENC28J60: All work is now performed on worker thread, not the in interrupt handler

git-svn-id: https://nuttx.svn.sourceforge.net/svnroot/nuttx/trunk@5159 7fd9a85b-ad96-42d3-883c-3090e2eb8679
This commit is contained in:
patacongo 2012-09-15 20:51:42 +00:00
parent fd63baf0fa
commit 3deb02acb8
3 changed files with 173 additions and 64 deletions

View File

@ -201,7 +201,7 @@ CONFIG_ARCH_STACKDUMP=y
CONFIG_DRAM_START=0x20000000 CONFIG_DRAM_START=0x20000000
CONFIG_DRAM_SIZE=65536 CONFIG_DRAM_SIZE=65536
CONFIG_ARCH_HAVE_INTERRUPTSTACK=y CONFIG_ARCH_HAVE_INTERRUPTSTACK=y
# CONFIG_ARCH_INTERRUPTSTACK is not set CONFIG_ARCH_INTERRUPTSTACK=0
# #
# Boot options # Boot options
@ -430,7 +430,7 @@ CONFIG_USBMSC_REMOVABLE=y
# Networking Support # Networking Support
# #
CONFIG_NET=y CONFIG_NET=y
# CONFIG_NET_NOINTS is not set CONFIG_NET_NOINTS=y
# CONFIG_NET_MULTIBUFFER is not set # CONFIG_NET_MULTIBUFFER is not set
# CONFIG_NET_IPv6 is not set # CONFIG_NET_IPv6 is not set
CONFIG_NSOCKET_DESCRIPTORS=16 CONFIG_NSOCKET_DESCRIPTORS=16

View File

@ -110,7 +110,7 @@
/* We need to have the work queue to handle SPI interrupts */ /* We need to have the work queue to handle SPI interrupts */
#if !defined(CONFIG_SCHED_WORKQUEUE) && !defined(CONFIG_SPI_OWNBUS) #ifndef CONFIG_SCHED_WORKQUEUE
# error "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)" # error "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)"
#endif #endif
@ -122,6 +122,12 @@
# define enc_dumppacket(m,a,n) # define enc_dumppacket(m,a,n)
#endif #endif
/* The ENC28J60 will not do interrupt level processing */
#ifndef CONFIG_NET_NOINTS
# warrning "CONFIG_NET_NOINTS should be set"
#endif
/* Timing *******************************************************************/ /* Timing *******************************************************************/
/* TX poll deley = 1 seconds. CLK_TCK is the number of clock ticks per second */ /* TX poll deley = 1 seconds. CLK_TCK is the number of clock ticks per second */
@ -201,9 +207,9 @@ struct enc_driver_s
* interrupt handler. * interrupt handler.
*/ */
#ifndef CONFIG_SPI_OWNBUS struct work_s irqwork; /* Interrupt continuation work queue support */
struct work_s work; /* Work queue support */ struct work_s towork; /* Tx timeout work queue support */
#endif struct work_s pollwork; /* Poll timeout work queue support */
/* This is the contained SPI driver intstance */ /* This is the contained SPI driver intstance */
@ -279,15 +285,17 @@ static void enc_txif(FAR struct enc_driver_s *priv);
static void enc_txerif(FAR struct enc_driver_s *priv); static void enc_txerif(FAR struct enc_driver_s *priv);
static void enc_txerif(FAR struct enc_driver_s *priv); static void enc_txerif(FAR struct enc_driver_s *priv);
static void enc_rxerif(FAR struct enc_driver_s *priv); static void enc_rxerif(FAR struct enc_driver_s *priv);
static void enc_rxdispath(FAR struct enc_driver_s *priv); static void enc_rxdispatch(FAR struct enc_driver_s *priv);
static void enc_pktif(FAR struct enc_driver_s *priv); static void enc_pktif(FAR struct enc_driver_s *priv);
static void enc_worker(FAR void *arg); static void enc_irqworker(FAR void *arg);
static int enc_interrupt(int irq, FAR void *context); static int enc_interrupt(int irq, FAR void *context);
/* Watchdog timer expirations */ /* Watchdog timer expirations */
static void enc_polltimer(int argc, uint32_t arg, ...); static void enc_toworker(FAR void *arg);
static void enc_txtimeout(int argc, uint32_t arg, ...); static void enc_txtimeout(int argc, uint32_t arg, ...);
static void enc_pollworker(FAR void *arg);
static void enc_polltimer(int argc, uint32_t arg, ...);
/* NuttX callback functions */ /* NuttX callback functions */
@ -1026,6 +1034,7 @@ static int enc_transmit(FAR struct enc_driver_s *priv)
* OK on success; a negated errno on failure * OK on success; a negated errno on failure
* *
* Assumptions: * Assumptions:
* Interrupts are enabled but the caller holds the uIP lock.
* *
****************************************************************************/ ****************************************************************************/
@ -1095,6 +1104,7 @@ static void enc_linkstatus(FAR struct enc_driver_s *priv)
* None * None
* *
* Assumptions: * Assumptions:
* Interrupts are enabled but the caller holds the uIP lock.
* *
****************************************************************************/ ****************************************************************************/
@ -1195,7 +1205,7 @@ static void enc_rxerif(FAR struct enc_driver_s *priv)
} }
/**************************************************************************** /****************************************************************************
* Function: enc_rxdispath * Function: enc_rxdispatch
* *
* Description: * Description:
* Give the newly received packet to uIP. * Give the newly received packet to uIP.
@ -1207,10 +1217,11 @@ static void enc_rxerif(FAR struct enc_driver_s *priv)
* None * None
* *
* Assumptions: * Assumptions:
* Interrupts are enabled but the caller holds the uIP lock.
* *
****************************************************************************/ ****************************************************************************/
static void enc_rxdispath(FAR struct enc_driver_s *priv) static void enc_rxdispatch(FAR struct enc_driver_s *priv)
{ {
/* We only accept IP packets of the configured type and ARP packets */ /* We only accept IP packets of the configured type and ARP packets */
@ -1267,6 +1278,7 @@ static void enc_rxdispath(FAR struct enc_driver_s *priv)
* None * None
* *
* Assumptions: * Assumptions:
* Interrupts are enabled but the caller holds the uIP lock.
* *
****************************************************************************/ ****************************************************************************/
@ -1344,7 +1356,7 @@ static void enc_pktif(FAR struct enc_driver_s *priv)
/* Dispatch the packet to uIP */ /* Dispatch the packet to uIP */
enc_rxdispath(priv); enc_rxdispatch(priv);
} }
/* Move the RX read pointer to the start of the next received packet. /* Move the RX read pointer to the start of the next received packet.
@ -1360,7 +1372,7 @@ static void enc_pktif(FAR struct enc_driver_s *priv)
} }
/**************************************************************************** /****************************************************************************
* Function: enc_worker * Function: enc_irqworker
* *
* Description: * Description:
* Perform interrupt handling logic outside of the interrupt handler (on * Perform interrupt handling logic outside of the interrupt handler (on
@ -1376,13 +1388,18 @@ static void enc_pktif(FAR struct enc_driver_s *priv)
* *
****************************************************************************/ ****************************************************************************/
static void enc_worker(FAR void *arg) static void enc_irqworker(FAR void *arg)
{ {
FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)arg; FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)arg;
uip_lock_t lock;
uint8_t eir; uint8_t eir;
DEBUGASSERT(priv); DEBUGASSERT(priv);
/* Get exclusive access to uIP. */
lock = uip_lock();
/* Disable further interrupts by clearing the global interrupt enable bit. /* Disable further interrupts by clearing the global interrupt enable bit.
* "After an interrupt occurs, the host controller should clear the global * "After an interrupt occurs, the host controller should clear the global
* enable bit for the interrupt pin before servicing the interrupt. Clearing * enable bit for the interrupt pin before servicing the interrupt. Clearing
@ -1560,15 +1577,17 @@ static void enc_worker(FAR void *arg)
} }
} }
/* Release lock on uIP */
uip_unlock(lock);
/* Enable Ethernet interrupts */ /* Enable Ethernet interrupts */
enc_bfsgreg(priv, ENC_EIE, EIE_INTIE); enc_bfsgreg(priv, ENC_EIE, EIE_INTIE);
/* Enable GPIO interrupts if they were disbled in enc_interrupt */ /* Enable GPIO interrupts */
#ifndef CONFIG_SPI_OWNBUS
priv->lower->enable(priv->lower); priv->lower->enable(priv->lower);
#endif
} }
/**************************************************************************** /****************************************************************************
@ -1592,15 +1611,6 @@ static int enc_interrupt(int irq, FAR void *context)
{ {
register FAR struct enc_driver_s *priv = &g_enc28j60[0]; register FAR struct enc_driver_s *priv = &g_enc28j60[0];
#ifdef CONFIG_SPI_OWNBUS
/* In very simple environments, we own the SPI and can do data transfers
* from the interrupt handler. That is actually a very bad idea in any
* case because it keeps interrupts disabled for a long time.
*/
enc_worker((FAR void*)priv);
return OK;
#else
/* In complex environments, we cannot do SPI transfers from the interrupt /* In complex environments, we cannot do SPI transfers from the interrupt
* handler because semaphores are probably used to lock the SPI bus. In * handler because semaphores are probably used to lock the SPI bus. In
* this case, we will defer processing to the worker thread. This is also * this case, we will defer processing to the worker thread. This is also
@ -1608,16 +1618,69 @@ static int enc_interrupt(int irq, FAR void *context)
* a good thing to do in any event. * a good thing to do in any event.
*/ */
DEBUGASSERT(work_available(&priv->work)); DEBUGASSERT(work_available(&priv->irqwork));
/* Notice that further GPIO interrupts are disabled until the work is /* Notice that further GPIO interrupts are disabled until the work is
* actually performed. This is to prevent overrun of the worker thread. * actually performed. This is to prevent overrun of the worker thread.
* Interrupts are re-enabled in enc_worker() when the work is completed. * Interrupts are re-enabled in enc_irqworker() when the work is completed.
*/ */
priv->lower->disable(priv->lower); priv->lower->disable(priv->lower);
return work_queue(HPWORK, &priv->work, enc_worker, (FAR void *)priv, 0); return work_queue(HPWORK, &priv->irqwork, enc_irqworker, (FAR void *)priv, 0);
}
/****************************************************************************
* Function: enc_toworker
*
* Description:
* Our TX watchdog timed out. This is the worker thread continuation of
* the watchdog timer interrupt. Reset the hardware and start again.
*
* Parameters:
* arg - The reference to the driver structure (case to void*)
*
* Returned Value:
* None
*
* Assumptions:
*
****************************************************************************/
static void enc_toworker(FAR void *arg)
{
FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)arg;
uip_lock_t lock;
int ret;
nlldbg("Tx timeout\n");
DEBUGASSERT(priv);
/* Get exclusive access to uIP. */
lock = uip_lock();
/* Increment statistics and dump debug info */
#ifdef CONFIG_ENC28J60_STATS
priv->stats.txtimeouts++;
#endif #endif
/* Then reset the hardware: Take the interface down, then bring it
* back up
*/
ret = enc_ifdown(&priv->dev);
DEBUGASSERT(ret == OK);
ret = enc_ifup(&priv->dev);
DEBUGASSERT(ret == OK);
/* Then poll uIP for new XMIT data */
(void)uip_poll(&priv->dev, enc_uiptxpoll);
/* Release lock on uIP */
uip_unlock(lock);
} }
/**************************************************************************** /****************************************************************************
@ -1625,7 +1688,7 @@ static int enc_interrupt(int irq, FAR void *context)
* *
* Description: * Description:
* Our TX watchdog timed out. Called from the timer interrupt handler. * Our TX watchdog timed out. Called from the timer interrupt handler.
* The last TX never completed. Reset the hardware and start again. * The last TX never completed. Perform work on the worker thread.
* *
* Parameters: * Parameters:
* argc - The number of available arguments * argc - The number of available arguments
@ -1643,25 +1706,74 @@ static void enc_txtimeout(int argc, uint32_t arg, ...)
FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)arg; FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)arg;
int ret; int ret;
/* Increment statistics and dump debug info */ /* In complex environments, we cannot do SPI transfers from the timout
* handler because semaphores are probably used to lock the SPI bus. In
nlldbg("Tx timeout\n"); * this case, we will defer processing to the worker thread. This is also
#ifdef CONFIG_ENC28J60_STATS * much kinder in the use of system resources and is, therefore, probably
priv->stats.txtimeouts++; * a good thing to do in any event.
#endif
/* Then reset the hardware: Take the interface down, then bring it
* back up
*/ */
ret = enc_ifdown(&priv->dev); DEBUGASSERT(priv && work_available(&priv->towork));
DEBUGASSERT(ret == OK);
ret = enc_ifup(&priv->dev);
DEBUGASSERT(ret == OK);
/* Then poll uIP for new XMIT data */ /* Notice that Tx timeout watchdog is not active so further Tx timeouts
* can occur until we restart the Tx timeout watchdog.
*/
(void)uip_poll(&priv->dev, enc_uiptxpoll); ret = work_queue(HPWORK, &priv->towork, enc_toworker, (FAR void *)priv, 0);
DEBUGASSERT(ret == OK);
}
/****************************************************************************
* Function: enc_pollworker
*
* Description:
* Periodic timer handler continuation.
*
* Parameters:
* argc - The number of available arguments
* arg - The first argument
*
* Returned Value:
* None
*
* Assumptions:
*
****************************************************************************/
static void enc_pollworker(FAR void *arg)
{
FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)arg;
uip_lock_t lock;
DEBUGASSERT(priv);
/* Get exclusive access to uIP. */
lock = uip_lock();
/* Verify that the hardware is ready to send another packet. The driver
* start a transmission process by setting ECON1.TXRTS. When the packet is
* finished transmitting or is aborted due to an error/cancellation, the
* ECON1.TXRTS bit will be cleared.
*/
if ((enc_rdgreg(priv, ENC_ECON1) & ECON1_TXRTS) == 0)
{
/* Yes.. update TCP timing states and poll uIP for new XMIT data. Hmmm..
* looks like a bug here to me. Does this mean if there is a transmit
* in progress, we will missing TCP time state updates?
*/
(void)uip_timer(&priv->dev, enc_uiptxpoll, ENC_POLLHSEC);
}
/* Release lock on uIP */
uip_unlock(lock);
/* Setup the watchdog poll timer again */
(void)wd_start(priv->txpoll, ENC_WDDELAY, enc_polltimer, 1, arg);
} }
/**************************************************************************** /****************************************************************************
@ -1684,26 +1796,23 @@ static void enc_txtimeout(int argc, uint32_t arg, ...)
static void enc_polltimer(int argc, uint32_t arg, ...) static void enc_polltimer(int argc, uint32_t arg, ...)
{ {
FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)arg; FAR struct enc_driver_s *priv = (FAR struct enc_driver_s *)arg;
int ret;
/* Verify that the hardware is ready to send another packet. The driver /* In complex environments, we cannot do SPI transfers from the timout
* start a transmission process by setting ECON1.TXRTS. When the packet is * handler because semaphores are probably used to lock the SPI bus. In
* finished transmitting or is aborted due to an error/cancellation, the * this case, we will defer processing to the worker thread. This is also
* ECON1.TXRTS bit will be cleared. * much kinder in the use of system resources and is, therefore, probably
* a good thing to do in any event.
*/ */
if ((enc_rdgreg(priv, ENC_ECON1) & ECON1_TXRTS) == 0) DEBUGASSERT(priv && work_available(&priv->pollwork));
{
/* Yes.. update TCP timing states and poll uIP for new XMIT data. Hmmm..
* looks like a bug here to me. Does this mean if there is a transmit
* in progress, we will missing TCP time state updates?
*/
(void)uip_timer(&priv->dev, enc_uiptxpoll, ENC_POLLHSEC); /* Notice that poll watchdog is not active so further poll timeouts can
} * occur until we restart the poll timeout watchdog.
*/
/* Setup the watchdog poll timer again */ ret = work_queue(HPWORK, &priv->pollwork, enc_pollworker, (FAR void *)priv, 0);
DEBUGASSERT(ret == OK);
(void)wd_start(priv->txpoll, ENC_WDDELAY, enc_polltimer, 1, arg);
} }
/**************************************************************************** /****************************************************************************

View File

@ -34,7 +34,7 @@ config NET_NOINTS
bool "Not interrupt driven" bool "Not interrupt driven"
default n default n
---help--- ---help---
NET_NOINT indicates that uIP not called from the interrupt level. NET_NOINT indicates that uIP is not called from the interrupt level.
If NET_NOINTS is defined, critical sections will be managed with semaphores; If NET_NOINTS is defined, critical sections will be managed with semaphores;
Otherwise, it assumed that uIP will be called from interrupt level handling Otherwise, it assumed that uIP will be called from interrupt level handling
and critical sections will be managed by enabling and disabling interrupts. and critical sections will be managed by enabling and disabling interrupts.