diff options
author | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2010-11-14 14:33:47 +0000 |
---|---|---|
committer | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2010-11-14 14:33:47 +0000 |
commit | a41ff25bb73795bcd7d10d9bb3b0b8d16483f78a (patch) | |
tree | 05a169f06f3b92a741edb97c360d4704b21d2621 /nuttx/arch/arm/src | |
parent | 54aebedda1cf30b83aff456a42378cc0a8f2ee6b (diff) | |
download | px4-nuttx-a41ff25bb73795bcd7d10d9bb3b0b8d16483f78a.tar.gz px4-nuttx-a41ff25bb73795bcd7d10d9bb3b0b8d16483f78a.tar.bz2 px4-nuttx-a41ff25bb73795bcd7d10d9bb3b0b8d16483f78a.zip |
Add Rx logic
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@3107 42af7a65-404d-4744-a932-0658087f49c3
Diffstat (limited to 'nuttx/arch/arm/src')
-rwxr-xr-x | nuttx/arch/arm/src/lpc17xx/lpc17_allocateheap.c | 19 | ||||
-rw-r--r-- | nuttx/arch/arm/src/lpc17xx/lpc17_ethernet.c | 306 |
2 files changed, 275 insertions, 50 deletions
diff --git a/nuttx/arch/arm/src/lpc17xx/lpc17_allocateheap.c b/nuttx/arch/arm/src/lpc17xx/lpc17_allocateheap.c index 9cc3ba353..3bba12e85 100755 --- a/nuttx/arch/arm/src/lpc17xx/lpc17_allocateheap.c +++ b/nuttx/arch/arm/src/lpc17xx/lpc17_allocateheap.c @@ -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 diff --git a/nuttx/arch/arm/src/lpc17xx/lpc17_ethernet.c b/nuttx/arch/arm/src/lpc17xx/lpc17_ethernet.c index b20e811fd..c3df8c11f 100644 --- a/nuttx/arch/arm/src/lpc17xx/lpc17_ethernet.c +++ b/nuttx/arch/arm/src/lpc17xx/lpc17_ethernet.c @@ -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 */ @@ -578,6 +588,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 * * Description: @@ -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); + + /* Get the current producer index */ + + 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); - /* Send the packet: address=priv->lp_dev.d_buf, length=priv->lp_dev.d_len */ + /* Enable Tx interrupts */ - /* Restore Ethernet 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_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_receive + * 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 */ |