Add Rx logic

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@3107 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2010-11-14 14:33:47 +00:00
parent 22d47d2263
commit 5c508e5f15
2 changed files with 275 additions and 50 deletions

View File

@ -129,14 +129,23 @@ void up_addregion(void)
{
/* Banks 0 and 1 are each 16Kb. If both are present, they occupy a
* contiguous 32Kb memory region.
*
* If Ethernet is enabled, it will take all of bank 0 for packet
* buffering and descriptor tables.
*/
#ifdef LPC17_HAVE_BANK0
# ifdef LPC17_HAVE_BANK1
mm_addregion((FAR void*)LPC17_SRAM_BANK0, 32*1024);
# else
mm_addregion((FAR void*)LPC17_SRAM_BANK0, 16*1024);
# endif
# if defined(CONFIG_NET) && defined(CONFIG_LPC17_ETHERNET) && defined(LPC17_NETHCONTROLLERS)
# ifdef LPC17_HAVE_BANK1
mm_addregion((FAR void*)LPC17_SRAM_BANK1, 16*1024);
# endif
# else
# ifdef LPC17_HAVE_BANK1
mm_addregion((FAR void*)LPC17_SRAM_BANK0, 32*1024);
# else
mm_addregion((FAR void*)LPC17_SRAM_BANK0, 16*1024);
# endif
# endif
#endif
}
#endif

View File

@ -108,6 +108,11 @@
#define LPC17_TXTIMEOUT (60*CLK_TCK)
/* Interrupts */
#define ETH_RXINTS (ETH_INT_RXOVR | ETH_INT_RXERR | ETH_INT_RXFIN | ETH_INT_RXDONE)
#define ETH_TXINTS (ETH_INT_TXUNR | ETH_INT_TXERR | ETH_INT_TXFIN | ETH_INT_TXDONE)
/* This is a helper pointer for accessing the contents of the Ethernet header */
#define BUF ((struct uip_eth_hdr *)priv->lp_dev.d_buf)
@ -240,7 +245,6 @@
#define LPC17_EMACRAM_BASE LPC17_SRAM_BANK0
#define LPC17_EMACRAM_SIZE LPC17_BANK0_SIZE
#warning "Need to exclude bank0 from the heap"
/* Descriptor table memory organization. Descriptor tables are packed at
* the end of AHB SRAM, Bank 0. The beginning of bank 0 is reserved for
@ -308,6 +312,8 @@ struct lpc17_statistics_s
uint32_t rx_pktsize; /* Number of dropped, too small or too big */
uint32_t tx_packets; /* Number of Tx packets queued */
uint32_t tx_pending; /* Number of Tx packets that had to wait for a TxDesc */
uint32_t tx_unpend; /* Number of pending Tx packets that were sent */
uint32_t tx_finished; /* Tx finished interrupts */
uint32_t tx_done; /* Tx done interrupts */
uint32_t tx_underrun; /* Number of Tx underrun error interrupts */
@ -336,9 +342,11 @@ struct lpc17_driver_s
bool lp_ifup; /* true:ifup false:ifdown */
bool lp_mode; /* speed/duplex */
bool lp_txpending; /* There is a pending Tx in lp_dev */
#ifdef LPC17_HAVE_PHY
uint8_t lp_phyaddr; /* PHY device address */
#endif
uint32_t lp_inten; /* Shadow copy of INTEN register */
WDOG_ID lp_txpoll; /* TX poll timer */
WDOG_ID lp_txtimeout; /* TX timeout timer */
@ -390,14 +398,16 @@ static void lpc17_putreg(uint32_t val, uint32_t addr);
/* Common TX logic */
static int lpc17_transmit(FAR struct lpc17_driver_s *priv);
static bool lpc17_txdesc(struct lpc17_driver_s *priv);
static int lpc17_transmit(struct lpc17_driver_s *priv);
static int lpc17_uiptxpoll(struct uip_driver_s *dev);
/* Interrupt handling */
static void lpc17_receive(FAR struct lpc17_driver_s *priv);
static void lpc17_txdone(FAR struct lpc17_driver_s *priv);
static int lpc17_interrupt(int irq, FAR void *context);
static void lpc17_response(struct lpc17_driver_s *priv);
static void lpc17_rxdone(struct lpc17_driver_s *priv);
static void lpc17_txdone(struct lpc17_driver_s *priv);
static int lpc17_interrupt(int irq, void *context);
/* Watchdog timer expirations */
@ -410,8 +420,8 @@ static int lpc17_ifup(struct uip_driver_s *dev);
static int lpc17_ifdown(struct uip_driver_s *dev);
static int lpc17_txavail(struct uip_driver_s *dev);
#ifdef CONFIG_NET_IGMP
static int lpc17_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
static int lpc17_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
static int lpc17_addmac(struct uip_driver_s *dev, const uint8_t *mac);
static int lpc17_rmmac(struct uip_driver_s *dev, const uint8_t *mac);
#endif
/* Initialization functions */
@ -577,6 +587,48 @@ static void lpc17_putreg(uint32_t val, uint32_t addr)
}
#endif
/****************************************************************************
* Function: lpc17_txdesc
*
* Description:
* Check if a free TX descriptor is available.
*
* Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* OK on success; a negated errno on failure
*
* Assumptions:
* May or may not be called from an interrupt handler. In either case,
* global interrupts are disabled, either explicitly or indirectly through
* interrupt handling logic.
*
****************************************************************************/
static bool lpc17_txdesc(struct lpc17_driver_s *priv)
{
unsigned int prodidx;
unsigned int considx;
/* Get the next producer index */
prodidx = lpc17_getreg(LPC17_ETH_TXPRODIDX) & ETH_TXPRODIDX_MASK;
if (++prodidx >= CONFIG_ETH_NTXDESC)
{
/* Wrap back to index zero */
prodidx = 0;
}
/* If the next producer index would overrun the consumer index, then there
* are not available descriptors.
*/
considx = lpc17_getreg(LPC17_ETH_TXCONSIDX) & ETH_TXCONSIDX_MASK;
return prodidx != considx ? OK : -EAGAIN;
}
/****************************************************************************
* Function: lpc17_transmit
*
@ -591,23 +643,70 @@ static void lpc17_putreg(uint32_t val, uint32_t addr)
* OK on success; a negated errno on failure
*
* Assumptions:
* May or may not be called from an interrupt handler. In either case,
* global interrupts are disabled, either explicitly or indirectly through
* interrupt handling logic.
*
****************************************************************************/
static int lpc17_transmit(FAR struct lpc17_driver_s *priv)
static int lpc17_transmit(struct lpc17_driver_s *priv)
{
uint32_t *txdesc;
void *txbuffer;
unsigned int prodidx;
/* Verify that the hardware is ready to send another packet. If we get
* here, then we are committed to sending a packet; Higher level logic
* must have assured that there is not transmission in progress.
*/
DEBUGASSERT(lpc17_txdesc(priv) == OK);
/* Increment statistics */
/* Disable Ethernet interrupts */
EMAC_STAT(priv, tx_packets);
/* Send the packet: address=priv->lp_dev.d_buf, length=priv->lp_dev.d_len */
/* Get the current producer index */
/* Restore Ethernet interrupts */
prodidx = lpc17_getreg(LPC17_ETH_TXPRODIDX) & ETH_TXPRODIDX_MASK;
/* Get the packet address from the descriptor and set the descriptor control
* fields.
*/
txdesc = (uint32_t*)(LPC17_TXDESC_BASE + (prodidx << 3));
txbuffer = (void*)*txdesc++;
*txdesc = TXDESC_CONTROL_INT | TXDESC_CONTROL_LAST | TXDESC_CONTROL_CRC |
(priv->lp_dev.d_len - 1);
/* Copy the packet data into the Tx buffer assignd to this. It should fit;
* because each packet buffer is MTU size and breaking up larger TCP messages
* is handled by higher level logic. The hardware does, however, support
* breaking up larger messages into many fragments, however, that capability
* is not exploited here.
*
* The would be a great performance improvement: Remove the buffer from
* the lp_dev structure and replac it a pointer directly into the EMAC
* DMA memory. This could eliminate the following, costly memcpy.
*/
DEBUGASSERT(priv->lp_dev.d_len <= LPC17_MAXPACKET_SIZE);
memcpy((void*)txbuffer, priv->lp_dev.d_buf, priv->lp_dev.d_len);
/* Bump the producer index, making the packet available for transmission. */
if (++prodidx >= CONFIG_ETH_NTXDESC)
{
/* Wrap back to index zero */
prodidx = 0;
}
lpc17_putreg(prodidx, LPC17_ETH_TXPRODIDX);
/* Enable Tx interrupts */
priv->lp_inten |= ETH_TXINTS;
lpc17_putreg(priv->lp_inten, LPC17_ETH_INTEN);
/* Setup the TX timeout watchdog (perhaps restarting the timer) */
@ -633,12 +732,16 @@ static int lpc17_transmit(FAR struct lpc17_driver_s *priv)
* OK on success; a negated errno on failure
*
* Assumptions:
* May or may not be called from an interrupt handler. In either case,
* global interrupts are disabled, either explicitly or indirectly through
* interrupt handling logic.
*
****************************************************************************/
static int lpc17_uiptxpoll(struct uip_driver_s *dev)
{
FAR struct lpc17_driver_s *priv = (FAR struct lpc17_driver_s *)dev->d_private;
struct lpc17_driver_s *priv = (struct lpc17_driver_s *)dev->d_private;
int ret = OK;
/* If the polling resulted in data that should be sent out on the network,
* the field d_len is set to a value > 0.
@ -646,23 +749,79 @@ static int lpc17_uiptxpoll(struct uip_driver_s *dev)
if (priv->lp_dev.d_len > 0)
{
/* Send this packet. In this context, we know that there is space for
* at least one more packet in the descriptor list.
*/
uip_arp_out(&priv->lp_dev);
lpc17_transmit(priv);
/* Check if there is room in the device to hold another packet. If not,
* return a non-zero value to terminate the poll.
* return any non-zero value to terminate the poll.
*/
ret = lpc17_txdesc(priv);
}
/* If zero is returned, the polling will continue until all connections have
* been examined.
*/
return 0;
return ret;
}
/****************************************************************************
* Function: lpc17_receive
* Function: lpc17_response
*
* Description:
* While processing an RxDone event, higher logic decides to send a packet,
* possibly a response to the incoming packet (but probably not, in reality).
* However, since the Rx and Tx operations are decoupled, there is no
* guarantee that there will be a Tx descriptor available at that time.
* This function will perform that check and, if no Tx descriptor is
* available, this function will (1) stop incoming Rx processing (bad), and
* (2) hold the outgoing packet in a pending state until the next Tx
* interrupt occurs.
*
* Parameters:
* priv - Reference to the driver state structure
*
* Returned Value:
* None
*
* Assumptions:
* Global interrupts are disabled by interrupt handling logic.
*
****************************************************************************/
static void lpc17_response(struct lpc17_driver_s *priv)
{
int ret;
/* Check if there is room in the device to hold another packet. */
ret = lpc17_txdesc(priv);
if (ret == OK)
{
/* Yes.. queue the packet now. */
lpc17_transmit(priv);
}
else
{
/* No.. mark the Tx as pending and halt further Tx interrupts */
DEBUGASSERT((priv->lp_inten & ETH_INT_TXDONE) != 0);
priv->lp_txpending = true;
priv->lp_inten &= ~ETH_RXINTS;
lpc17_putreg(priv->lp_inten, LPC17_ETH_INTEN);
EMAC_STAT(priv, tx_pending);
}
}
/****************************************************************************
* Function: lpc17_rxdone
*
* Description:
* An interrupt was received indicating the availability of a new RX packet
@ -674,17 +833,22 @@ static int lpc17_uiptxpoll(struct uip_driver_s *dev)
* None
*
* Assumptions:
* Global interrupts are disabled by interrupt handling logic.
*
****************************************************************************/
static void lpc17_receive(FAR struct lpc17_driver_s *priv)
static void lpc17_rxdone(struct lpc17_driver_s *priv)
{
do
{
/* Check for errors and update statistics */
/* Update statistics */
EMAC_STAT(priv, rx_packets);
/* Check if the packet is a valid size for the uIP buffer configuration */
EMAC_STAT(priv, rx_pktsize);
/* Copy the data data from the hardware to priv->lp_dev.d_buf. Set
* amount of data in priv->lp_dev.d_len
*/
@ -697,6 +861,9 @@ static void lpc17_receive(FAR struct lpc17_driver_s *priv)
if (BUF->type == HTONS(UIP_ETHTYPE_IP))
#endif
{
/* Handle the incoming Rx packet */
EMAC_STAT(priv, rx_ip);
uip_arp_ipin();
uip_input(&priv->lp_dev);
@ -707,11 +874,12 @@ static void lpc17_receive(FAR struct lpc17_driver_s *priv)
if (priv->lp_dev.d_len > 0)
{
uip_arp_out(&priv->lp_dev);
lpc17_transmit(priv);
lpc17_response(priv);
}
}
else if (BUF->type == htons(UIP_ETHTYPE_ARP))
{
EMAC_STAT(priv, rx_arp);
uip_arp_arpin(&priv->lp_dev);
/* If the above function invocation resulted in data that should be
@ -720,9 +888,15 @@ static void lpc17_receive(FAR struct lpc17_driver_s *priv)
if (priv->lp_dev.d_len > 0)
{
lpc17_transmit(priv);
lpc17_response(priv);
}
}
else
{
/* Unrecognized... drop it. */
EMAC_STAT(priv, rx_dropped);
}
}
while (1); /* While there are more packets to be processed */
}
@ -740,20 +914,52 @@ static void lpc17_receive(FAR struct lpc17_driver_s *priv)
* None
*
* Assumptions:
* Global interrupts are disabled by interrupt handling logic.
*
****************************************************************************/
static void lpc17_txdone(FAR struct lpc17_driver_s *priv)
static void lpc17_txdone(struct lpc17_driver_s *priv)
{
/* Check for errors and update statistics */
/* If no further xmits are pending, then cancel the TX timeout */
/* Cancel the pending Tx timeout */
wd_cancel(priv->lp_txtimeout);
/* Then poll uIP for new XMIT data */
/* Disable further Tx interrupts. Tx interrupts may be re-enabled again
* depending upon the result of the poll.
*/
(void)uip_poll(&priv->lp_dev, lpc17_uiptxpoll);
priv->lp_inten &= ~ETH_TXINTS;
lpc17_putreg(priv->lp_inten, LPC17_ETH_INTEN);
/* Verify that the hardware is ready to send another packet. Since a Tx
* just completed, this must be the case.
*/
DEBUGASSERT(lpc17_txdesc(priv) == OK);
/* Check if there is a pending Tx transfer that was scheduled by Rx handling
* while the Tx logic was busy. If so, processing that pending Tx now.
*/
if (priv->lp_txpending)
{
/* Clear the pending condition, send the packet, and restore Rx interrupts */
priv->lp_txpending = false;
EMAC_STAT(priv, tx_unpend);
lpc17_transmit(priv);
priv->lp_inten |= ETH_RXINTS;
lpc17_putreg(priv->lp_inten, LPC17_ETH_INTEN);
}
/* Otherwise poll uIP for new XMIT data */
else
{
(void)uip_poll(&priv->lp_dev, lpc17_uiptxpoll);
}
}
/****************************************************************************
@ -773,9 +979,9 @@ static void lpc17_txdone(FAR struct lpc17_driver_s *priv)
*
****************************************************************************/
static int lpc17_interrupt(int irq, FAR void *context)
static int lpc17_interrupt(int irq, void *context)
{
register FAR struct lpc17_driver_s *priv = &g_ethdrvr[0];
register struct lpc17_driver_s *priv = &g_ethdrvr[0];
uint32_t status;
/* Get the interrupt status (zero means no interrupts pending). */
@ -798,7 +1004,7 @@ static int lpc17_interrupt(int irq, FAR void *context)
EMAC_STAT(priv, rx_errors);
}
/* Check if we received an incoming packet, if so, call lpc17_receive() */
/* Check if we received an incoming packet, if so, call lpc17_rxdone() */
if ((status & ETH_INT_RXFIN) != 0)
{
@ -811,7 +1017,7 @@ static int lpc17_interrupt(int irq, FAR void *context)
{
lpc17_putreg(ETH_INT_RXDONE, LPC17_ETH_INTCLR);
EMAC_STAT(priv, rx_done);
lpc17_receive(priv);
lpc17_rxdone(priv);
}
/* Check for Tx errors */
@ -849,7 +1055,6 @@ static int lpc17_interrupt(int irq, FAR void *context)
if ((status & ETH_INT_WKUP) != 0)
{
lpc17_putreg(ETH_INT_WKUP, LPC17_ETH_INTCLR);
INTCLEAR = EMAC_INT_WOL;
EMAC_STAT(priv, wol);
# warning "Missing logic"
}
@ -875,15 +1080,18 @@ static int lpc17_interrupt(int irq, FAR void *context)
* None
*
* Assumptions:
* Global interrupts are disabled by the watchdog logic.
*
****************************************************************************/
static void lpc17_txtimeout(int argc, uint32_t arg, ...)
{
FAR struct lpc17_driver_s *priv = (FAR struct lpc17_driver_s *)arg;
struct lpc17_driver_s *priv = (struct lpc17_driver_s *)arg;
/* Increment statistics and dump debug info */
EMAC_STAT(priv, tx_timeouts);
/* Then reset the hardware */
/* Then poll uIP for new XMIT data */
@ -905,12 +1113,13 @@ static void lpc17_txtimeout(int argc, uint32_t arg, ...)
* None
*
* Assumptions:
* Global interrupts are disabled by the watchdog logic.
*
****************************************************************************/
static void lpc17_polltimer(int argc, uint32_t arg, ...)
{
FAR struct lpc17_driver_s *priv = (FAR struct lpc17_driver_s *)arg;
struct lpc17_driver_s *priv = (struct lpc17_driver_s *)arg;
/* Check if there is room in the send another TX packet. We cannot perform
* the TX poll if he are unable to accept another packet for transmission.
@ -947,7 +1156,7 @@ static void lpc17_polltimer(int argc, uint32_t arg, ...)
static int lpc17_ifup(struct uip_driver_s *dev)
{
FAR struct lpc17_driver_s *priv = (FAR struct lpc17_driver_s *)dev->d_private;
struct lpc17_driver_s *priv = (struct lpc17_driver_s *)dev->d_private;
uint32_t regval;
int ret;
@ -1044,13 +1253,17 @@ static int lpc17_ifup(struct uip_driver_s *dev)
lpc17_putreg(0xffffffff, LPC17_ETH_RXFLWOLCLR);
lpc17_putreg(ETH_RXFLCTRL_RXFILEN, LPC17_ETH_RXRLCTRL);
priv->lp_inten = ETH_INT_WKUP;
lpc17_putreg(ETH_INT_WKUP, LPC17_ETH_INTEN);
#else
/* Otherwise, enable all interrupts except SOFTINT and WoL */
/* Otherwise, enable all Rx interrupts. Tx interrupts, SOFTINT and WoL are
* excluded. Tx interrupts will not be enabled until there is data to be
* sent.
*/
lpc17_putreg((ETH_INT_RXOVR | ETH_INT_RXERR | ETH_INT_RXFIN | ETH_INT_RXDONE |
ETH_INT_TXUNR | ETH_INT_TXERR | ETH_INT_TXFIN | ETH_INT_TXDONE),
LPC17_ETH_INTEN);
priv->lp_inten = ETH_RXINTS;
lpc17_putreg(ETH_RXINTS, LPC17_ETH_INTEN);
#endif
/* Set and activate a timer process */
@ -1086,7 +1299,7 @@ static int lpc17_ifup(struct uip_driver_s *dev)
static int lpc17_ifdown(struct uip_driver_s *dev)
{
FAR struct lpc17_driver_s *priv = (FAR struct lpc17_driver_s *)dev->d_private;
struct lpc17_driver_s *priv = (struct lpc17_driver_s *)dev->d_private;
irqstate_t flags;
/* Disable the Ethernet interrupt */
@ -1099,7 +1312,7 @@ static int lpc17_ifdown(struct uip_driver_s *dev)
wd_cancel(priv->lp_txpoll);
wd_cancel(priv->lp_txtimeout);
/* Reset the device */
/* Reset the device and mark it as down. */
lpc17_ethreset(priv);
priv->lp_ifup = false;
@ -1128,16 +1341,19 @@ static int lpc17_ifdown(struct uip_driver_s *dev)
static int lpc17_txavail(struct uip_driver_s *dev)
{
FAR struct lpc17_driver_s *priv = (FAR struct lpc17_driver_s *)dev->d_private;
struct lpc17_driver_s *priv = (struct lpc17_driver_s *)dev->d_private;
irqstate_t flags;
/* Disable interrupts because this function may be called from interrupt
* level processing.
*/
flags = irqsave();
/* Ignore the notification if the interface is not yet up */
if (priv->lp_ifup)
{
/* Check if there is room in the hardware to hold another outgoing packet. */
/* If so, then poll uIP for new XMIT data */
@ -1168,9 +1384,9 @@ static int lpc17_txavail(struct uip_driver_s *dev)
****************************************************************************/
#ifdef CONFIG_NET_IGMP
static int lpc17_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
static int lpc17_addmac(struct uip_driver_s *dev, const uint8_t *mac)
{
FAR struct lpc17_driver_s *priv = (FAR struct lpc17_driver_s *)dev->d_private;
struct lpc17_driver_s *priv = (struct lpc17_driver_s *)dev->d_private;
/* Add the MAC address to the hardware multicast routing table */
@ -1197,9 +1413,9 @@ static int lpc17_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
****************************************************************************/
#ifdef CONFIG_NET_IGMP
static int lpc17_rmmac(struct uip_driver_s *dev, FAR const uint8_t *mac)
static int lpc17_rmmac(struct uip_driver_s *dev, const uint8_t *mac)
{
FAR struct lpc17_driver_s *priv = (FAR struct lpc17_driver_s *)dev->d_private;
struct lpc17_driver_s *priv = (struct lpc17_driver_s *)dev->d_private;
/* Add the MAC address to the hardware multicast routing table */