ENCX24J600: Improved descriptor handling, free packets on rx abort interrupt. From Max Holtzberg

This commit is contained in:
Gregory Nutt 2013-10-11 10:57:58 -06:00
parent 8f7c6e23a6
commit edf984df2d
3 changed files with 200 additions and 111 deletions

View File

@ -5751,3 +5751,6 @@
dropped; This is needed for the ENCX24J600 driver that must make
a decision to return the packet or not: It should not retai
dropped packets. From Max Holtzberg (2013-10-11).
* drivers/net/encx24j600.c and Kconfig: ENCX24J600: Improved descriptor
handling, free packets on rx abort interrupt. From Max Holtzberg
(2013-10-11).

View File

@ -135,9 +135,9 @@ config ENCX24J600_FREQUENCY
---help---
Define to use a different bus frequency
config ENCX24J600_NDESCR
config ENCX24J600_NRXDESCR
int "Descriptor Count"
default 16
default 8
---help---
Defines how many descriptors are preallocated for the
transmission and reception queues.

View File

@ -169,7 +169,8 @@
#define PKTMEM_ALIGNED_BUFSIZE ((CONFIG_NET_BUFSIZE + 1) & ~1)
#define PKTMEM_RX_START (PKTMEM_START + PKTMEM_SIZE / 2) /* Followed by RX buffer */
#define PKTMEM_RX_END (PKTMEM_START + PKTMEM_SIZE - 2) /* RX buffer goes to the end of SRAM */
#define PKTMEM_RX_SIZE (PKTMEM_SIZE - PKTMEM_RX_START)
#define PKTMEM_RX_END (PKTMEM_START + PKTMEM_SIZE) /* RX buffer goes to the end of SRAM */
/* We use preinitialized TX descriptors */
@ -330,6 +331,7 @@ static int enc_uiptxpoll(struct uip_driver_s *dev);
/* Common RX logic */
static struct enc_descr_s *enc_rxgetdescr(FAR struct enc_driver_s *priv);
static void enc_rxldpkt(FAR struct enc_driver_s *priv, struct enc_descr_s *descr);
static void enc_rxrmpkt(FAR struct enc_driver_s *priv, struct enc_descr_s *descr);
static void enc_rxdispatch(FAR struct enc_driver_s *priv);
@ -365,6 +367,7 @@ static int enc_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
static void enc_pwrsave(FAR struct enc_driver_s *priv);
static void enc_setmacaddr(FAR struct enc_driver_s *priv);
static void enc_resetbuffers(FAR struct enc_driver_s *priv);
static int enc_reset(FAR struct enc_driver_s *priv);
/****************************************************************************
@ -1246,7 +1249,7 @@ static void enc_linkstatus(FAR struct enc_driver_s *priv)
* FULDPX (MACON2<0>) to match PHYDPX (ESTAT<10>).
*/
regval = enc_rdphy(priv, ENC_ESTAT);
regval = enc_rdreg(priv, ENC_ESTAT);
if (regval & ESTAT_PHYDPX)
{
@ -1342,6 +1345,38 @@ static void enc_rxldpkt(FAR struct enc_driver_s *priv,
enc_dumppacket("loaded RX packet", priv->dev.d_buf, priv->dev.d_len);
}
/****************************************************************************
* Function: enc_rxgetdescr
*
* Description:
* Check for a free descriptor in the free list. If no free descriptor is
* available a pending descriptor will be freed and returned
*
* Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* A free rx descriptor
*
* Assumptions:
* Interrupts are enabled but the caller holds the uIP lock.
*
****************************************************************************/
static struct enc_descr_s *enc_rxgetdescr(FAR struct enc_driver_s *priv)
{
if (sq_empty(&priv->rxfreedescr))
{
DEBUGASSERT(sq_peek(&priv->rxqueue) != NULL);
/* Packets are held in the enc's SRAM until the space is needed */
enc_rxrmpkt(priv, (struct enc_descr_s*)sq_peek(&priv->rxqueue));
}
return (struct enc_descr_s*)sq_remfirst(&priv->rxfreedescr);
}
/****************************************************************************
* Function: enc_rxrmpkt
*
@ -1372,28 +1407,33 @@ static void enc_rxrmpkt(FAR struct enc_driver_s *priv, struct enc_descr_s *descr
* can be reordered th enc's DMA to free RX space?
*/
if (descr == (struct enc_descr_s*)sq_peek(&priv->rxqueue))
if (descr != NULL)
{
/* @REVISIT wrap around? */
if (descr == (struct enc_descr_s*)sq_peek(&priv->rxqueue))
{
/* Wrap address properly around */
addr = (descr->addr - PKTMEM_RX_START + descr->len - 2 + PKTMEM_RX_SIZE)
% PKTMEM_RX_SIZE + PKTMEM_RX_START;
addr = descr->addr + descr->len - 2;
DEBUGASSERT(addr >= PKTMEM_RX_START && addr < PKTMEM_RX_END);
nllvdbg("set ERXTAIL to %04x\n", addr);
nllvdbg("ERXTAIL %04x\n", addr);
enc_wrreg(priv, ENC_ERXTAIL, addr);
enc_wrreg(priv, ENC_ERXTAIL, addr);
/* Remove packet from RX queue */
/* Remove packet from RX queue */
sq_remfirst(&priv->rxqueue);
sq_remfirst(&priv->rxqueue);
}
else
{
/* Remove packet from RX queue */
sq_rem((sq_entry_t*)descr, &priv->rxqueue);
}
sq_addlast((sq_entry_t*)descr, &priv->rxfreedescr);
}
else
{
/* Remove packet from RX queue */
sq_rem((sq_entry_t*)descr, &priv->rxqueue);
}
sq_addlast((sq_entry_t*)descr, &priv->rxfreedescr);
}
/****************************************************************************
@ -1525,7 +1565,7 @@ static void enc_pktif(FAR struct enc_driver_s *priv)
uint16_t curpkt;
int pktcnt;
DEBUGASSERT(priv->nextpkt >= PKTMEM_RX_START && priv->nextpkt <= PKTMEM_RX_END);
DEBUGASSERT(priv->nextpkt >= PKTMEM_RX_START && priv->nextpkt < PKTMEM_RX_END);
/* Enqueue all pending packets to the RX queue until PKTCNT == 0 or
* no more descriptors are available.
@ -1564,7 +1604,23 @@ static void enc_pktif(FAR struct enc_driver_s *priv)
(uint32_t)rsv[5] << 8 | (uint32_t)rsv[4];
nllvdbg("Receiving packet, nextpkt: %04x pktlen: %d rxstat: %08x pktcnt: %d\n",
priv->nextpkt, pktlen, rxstat, pktcnt);
priv->nextpkt, pktlen, rxstat, pktcnt);
/* We enqueue the packet first and remove it later if its faulty.
* This way we avoid freeing packets that are not processed yet.
*/
descr = enc_rxgetdescr(priv);
/* Set current timestamp */
descr->ts = clock_systimer();
/* Store the start address of the frame without the enc's header */
descr->addr = curpkt + 8;
descr->len = pktlen;
sq_addlast((sq_entry_t*)descr, &priv->rxqueue);
/* Check if the packet was received OK */
@ -1572,6 +1628,10 @@ static void enc_pktif(FAR struct enc_driver_s *priv)
{
nlldbg("ERROR: RXSTAT: %08x\n", rxstat);
/* Discard packet */
enc_rxrmpkt(priv, descr);
#ifdef CONFIG_ENCX24J600_STATS
priv->stats.rxnotok++;
#endif
@ -1582,55 +1642,28 @@ static void enc_pktif(FAR struct enc_driver_s *priv)
else if (pktlen > (CONFIG_NET_BUFSIZE + 4) || pktlen <= (UIP_LLH_LEN + 4))
{
nlldbg("Bad packet size dropped (%d)\n", pktlen);
/* Discard packet */
enc_rxrmpkt(priv, descr);
#ifdef CONFIG_ENCX24J600_STATS
priv->stats.rxpktlen++;
#endif
}
/* Otherwise, enqueue the packet to the RX queue */
else
{
descr = (struct enc_descr_s*)sq_remfirst(&priv->rxfreedescr);
if (descr != NULL)
{
nllvdbg("allocated RX descriptor: %p\n", descr);
/* Set current timestamp */
descr->ts = clock_systimer();
/* Store the start address of the frame without the enc's header */
descr->addr = curpkt + 8;
descr->len = pktlen;
/* Enqueue packet */
sq_addlast((sq_entry_t*)descr, &priv->rxqueue);
}
else
{
nlldbg("no free descriptors\n");
}
}
/* Decrement PKTCNT */
enc_bfs(priv, ENC_ECON1, ECON1_PKTDEC);
/* Try to process the packet */
enc_rxdispatch(priv);
/* Read out again, maybe there has another packet arrived */
pktcnt = (enc_rdreg(priv, ENC_ESTAT) & ESTAT_PKTCNT_MASK) >> ESTAT_PKTCNT_SHIFT;
}
/* Process the RX queue */
enc_rxdispatch(priv);
}
/****************************************************************************
@ -1648,7 +1681,6 @@ static void enc_pktif(FAR struct enc_driver_s *priv)
* filters rejecting a packet. The interrupt should be cleared by software
* once it has been serviced."
*
*
* Parameters:
* priv - Reference to the driver state structure
*
@ -1662,11 +1694,42 @@ static void enc_pktif(FAR struct enc_driver_s *priv)
static void enc_rxabtif(FAR struct enc_driver_s *priv)
{
struct enc_descr_s *descr;
#if 0
/* Free the last received packet from the RX queue */
nlldbg("rx abort\n");
nlldbg("ESTAT: %04x\n", enc_rdreg(priv, ENC_ESTAT));
nlldbg("EIR: %04x\n", enc_rdreg(priv, ENC_EIR));
nlldbg("ERXTAIL: %04x\n", enc_rdreg(priv, ENC_ERXTAIL));
nlldbg("ERXHAED: %04x\n", enc_rdreg(priv, ENC_ERXHEAD));
enc_rxrmpkt(priv, (struct enc_descr_s*)sq_peek(&priv->rxqueue));
descr = (struct enc_descr_s*)sq_peek(&priv->rxqueue);
while (descr != NULL)
{
nlldbg("addr: %04x len: %d\n", descr->addr, descr->len);
descr = (struct enc_descr_s*)sq_next(descr);
}
DEBUGASSERT(false);
#endif
descr = (struct enc_descr_s*)sq_peek(&priv->rxqueue);
if (descr != NULL)
{
enc_rxrmpkt(priv, descr);
nlldbg("pending packet freed\n");
}
else
{
/* If no pending packet blocks the reception, reset all buffers */
enc_resetbuffers(priv);
}
}
/****************************************************************************
@ -1757,6 +1820,28 @@ static void enc_irqworker(FAR void *arg)
enc_bfc(priv, ENC_EIR, EIR_TXIF);
}
/* The receive abort interrupt occurs when the reception of a frame has
* been aborted. A frame being received is aborted when the Head Pointer
* attempts to overrun the Tail Pointer, or when the packet counter has
* reached FFh. In either case, the receive buffer is full and cannot fit
* the incoming frame, so the packet has been dropped. This interrupt does
* not occur when packets are dropped due to the receive filters rejecting
* a packet. The interrupt should be cleared by software once it has been
* serviced.
*
* To enable the receive abort interrupt, set RXABTIE (EIE<1>).
* The corresponding interrupt flag is RXABTIF (EIR<1>).
*/
if ((eir & EIR_RXABTIF) != 0) /* Receive Abort */
{
#ifdef CONFIG_ENCX24J600_STATS
priv->stats.rxerifs++;
#endif
enc_rxabtif(priv);
enc_bfc(priv, ENC_EIR, EIR_RXABTIF); /* Clear the RXABTIF interrupt */
}
/* The received packet pending interrupt occurs when one or more frames
* have been received and are ready for software processing. This flag is
* set when the PKTCNT<7:0> (ESTAT<7:0>) bits are non-zero. This interrupt
@ -1807,28 +1892,6 @@ static void enc_irqworker(FAR void *arg)
enc_bfc(priv, ENC_EIR, EIR_TXABTIF); /* Clear the TXABTIF interrupt */
}
#endif
/* The receive abort interrupt occurs when the reception of a frame has
* been aborted. A frame being received is aborted when the Head Pointer
* attempts to overrun the Tail Pointer, or when the packet counter has
* reached FFh. In either case, the receive buffer is full and cannot fit
* the incoming frame, so the packet has been dropped. This interrupt does
* not occur when packets are dropped due to the receive filters rejecting
* a packet. The interrupt should be cleared by software once it has been
* serviced.
*
* To enable the receive abort interrupt, set RXABTIE (EIE<1>).
* The corresponding interrupt flag is RXABTIF (EIR<1>).
*/
if ((eir & EIR_RXABTIF) != 0) /* Receive Abort */
{
#ifdef CONFIG_ENCX24J600_STATS
priv->stats.rxerifs++;
#endif
enc_rxabtif(priv);
enc_bfc(priv, ENC_EIR, EIR_RXABTIF); /* Clear the RXABTIF interrupt */
}
}
/* Enable GPIO interrupts */
@ -2288,7 +2351,7 @@ static int enc_rxavail(struct uip_driver_s *dev)
if (!sq_empty(&priv->rxqueue))
{
nlldbg("RX queue not empty, trying to dispatch\n");
nllvdbg("RX queue not empty, trying to dispatch\n");
enc_rxdispatch(priv);
}
@ -2524,6 +2587,56 @@ static void enc_setmacaddr(FAR struct enc_driver_s *priv)
}
}
/****************************************************************************
* Function: enc_resetbuffers
*
* Description:
* Initializes the RX/TX queues and configures the enc's RX/TX buffers.
* Called on general reset and on rxabt interrupt.
*
* Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* None
*
* Assumptions:
*
****************************************************************************/
static void enc_resetbuffers(FAR struct enc_driver_s *priv)
{
int i;
/* Initialize receive and transmit buffers */
priv->nextpkt = PKTMEM_RX_START;
enc_wrreg(priv, ENC_ERXST, PKTMEM_RX_START);
/* Program the Tail Pointer, ERXTAIL, to the last even address of the buffer */
enc_wrreg(priv, ENC_ERXTAIL, PKTMEM_RX_END - 2);
sq_init(&priv->txfreedescr);
sq_init(&priv->rxfreedescr);
sq_init(&priv->txqueue);
sq_init(&priv->rxqueue);
/* For transmition we preinitialize the descriptors to aligned NET_BUFFSIZE */
for (i = 0; i < ENC_NTXDESCR; i++)
{
priv->txdescralloc[i].addr = PKTMEM_START + PKTMEM_ALIGNED_BUFSIZE * i;
sq_addlast((sq_entry_t*)&priv->txdescralloc[i], &priv->txfreedescr);
}
/* Receive descriptors addresses are set on reception */
for (i = 0; i < CONFIG_ENCX24J600_NRXDESCR; i++)
{
sq_addlast((sq_entry_t*)&priv->rxdescralloc[i], &priv->rxfreedescr);
}
}
/****************************************************************************
* Function: enc_reset
*
@ -2543,7 +2656,6 @@ static void enc_setmacaddr(FAR struct enc_driver_s *priv)
static int enc_reset(FAR struct enc_driver_s *priv)
{
int i;
int ret;
uint16_t regval;
@ -2585,35 +2697,9 @@ static int enc_reset(FAR struct enc_driver_s *priv)
*/
up_udelay(256);
/* Initialize receive and transmit buffers */
priv->nextpkt = PKTMEM_RX_START;
enc_wrreg(priv, ENC_ERXST, PKTMEM_RX_START);
/* Program the Tail Pointer, ERXTAIL, to the last even address of the buffer */
enc_wrreg(priv, ENC_ERXTAIL, PKTMEM_RX_END);
sq_init(&priv->txfreedescr);
sq_init(&priv->rxfreedescr);
sq_init(&priv->txqueue);
sq_init(&priv->rxqueue);
/* For transmition we preinitialize the descriptors to aligned NET_BUFFSIZE */
for (i = 0; i < ENC_NTXDESCR; i++)
{
priv->txdescralloc[i].addr = PKTMEM_START + PKTMEM_ALIGNED_BUFSIZE * i;
sq_addlast((sq_entry_t*)&priv->txdescralloc[i], &priv->txfreedescr);
}
/* Receive descriptors addresses are set on reception */
for (i = 0; i < CONFIG_ENCX24J600_NRXDESCR; i++)
{
sq_addlast((sq_entry_t*)&priv->rxdescralloc[i], &priv->rxfreedescr);
}
/* Initialize RX/TX buffers */
enc_resetbuffers(priv);
#if 0
/* When restarting auto-negotiation, the ESTAT_PHYLINK gets set but the link