diff options
author | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2011-12-11 15:04:39 +0000 |
---|---|---|
committer | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2011-12-11 15:04:39 +0000 |
commit | e20aebd6db67f544e58cd13905eb5d9c6f78d370 (patch) | |
tree | 48e97fa83d1aef30f358f4e36502a9bc8086a1ab | |
parent | 095bd43f40c84e8787f1d1063dbdd203ee82fbb6 (diff) | |
download | nuttx-e20aebd6db67f544e58cd13905eb5d9c6f78d370.tar.gz nuttx-e20aebd6db67f544e58cd13905eb5d9c6f78d370.tar.bz2 nuttx-e20aebd6db67f544e58cd13905eb5d9c6f78d370.zip |
Add STM32 Ethernet packet reception logic
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@4160 42af7a65-404d-4744-a932-0658087f49c3
-rwxr-xr-x | nuttx/arch/arm/src/stm32/stm32_eth.c | 452 | ||||
-rwxr-xr-x | nuttx/configs/stm3240g-eval/nsh/defconfig | 4 | ||||
-rwxr-xr-x | nuttx/configs/stm3240g-eval/ostest/defconfig | 4 |
3 files changed, 385 insertions, 75 deletions
diff --git a/nuttx/arch/arm/src/stm32/stm32_eth.c b/nuttx/arch/arm/src/stm32/stm32_eth.c index 1e9f6933b..6f1f4a944 100755 --- a/nuttx/arch/arm/src/stm32/stm32_eth.c +++ b/nuttx/arch/arm/src/stm32/stm32_eth.c @@ -46,6 +46,7 @@ #include <string.h> #include <debug.h> #include <wdog.h> +#include <queue.h> #include <errno.h> #include <nuttx/irq.h> @@ -139,19 +140,26 @@ #undef CONFIG_STM32_ETH_ENHANCEDDESC #undef CONFIG_STM32_ETH_HWCHECKSUM -/* Ethernet buffer sizes and numbers */ +/* Ethernet buffer sizes, nubmer of buffers, and number of descriptors */ -#ifndef CONFIG_STM32_ETH_RXBUFSIZE -# define CONFIG_STM32_ETH_RXBUFSIZE CONFIG_NET_BUFSIZE +#ifndef CONFIG_NET_MULTIBUFFER +# error "CONFIG_NET_MULTIBUFFER is required" #endif -#ifndef CONFIG_STM32_ETH_TXBUFSIZE -# define CONFIG_STM32_ETH_TXBUFSIZE CONFIG_NET_BUFSIZE + +#ifndef CONFIG_STM32_ETH_BUFSIZE +# define CONFIG_STM32_ETH_BUFSIZE CONFIG_NET_BUFSIZE +#endif +#ifndef CONFIG_STM32_ETH_NRXDESC +# define CONFIG_STM32_ETH_NRXDESC 8 #endif -#ifndef CONFIG_STM32_ETH_RXNBUFFERS -# define CONFIG_STM32_ETH_RXNBUFFERS 20 +#ifndef CONFIG_STM32_ETH_NTXDESC +# define CONFIG_STM32_ETH_NTXDESC 4 #endif -#ifndef CONFIG_STM32_ETH_TXNBUFFERS -# define CONFIG_STM32_ETH_TXNBUFFERS 5 + +#if CONFIG_STM32_ETH_NTXDESC > 2 +# define STM32_ETH_NFREEBUFFERS CONFIG_STM32_ETH_NTXDESC +#else +# define STM32_ETH_NFREEBUFFERS 2 #endif /* Clocking *****************************************************************/ @@ -463,48 +471,39 @@ * Private Types ****************************************************************************/ -struct eth_rxframe_info_s -{ - volatile struct stm2_ethdesc_s *rxfirst; /* First Segment Rx Desc */ - volatile struct stm2_ethdesc_s *rxlast; /* Last Segment Rx Desc */ - volatile uint32_t segcount; /* Segment count */ -}; - /* The stm32_ethmac_s encapsulates all state information for a single hardware * interface */ struct stm32_ethmac_s { - uint8_t ifup : 1; /* true:ifup false:ifdown */ - uint8_t mbps100 : 1; /* 100MBps operation (vs 10 MBps) */ - uint8_t fduplex : 1; /* Full (vs. half) duplex */ - WDOG_ID txpoll; /* TX poll timer */ - WDOG_ID txtimeout; /* TX timeout timer */ + uint8_t ifup : 1; /* true:ifup false:ifdown */ + uint8_t mbps100 : 1; /* 100MBps operation (vs 10 MBps) */ + uint8_t fduplex : 1; /* Full (vs. half) duplex */ + WDOG_ID txpoll; /* TX poll timer */ + WDOG_ID txtimeout; /* TX timeout timer */ /* This holds the information visible to uIP/NuttX */ - struct uip_driver_s dev; /* Interface understood by uIP */ + struct uip_driver_s dev; /* Interface understood by uIP */ /* Used to track transmit and receive descriptors */ - volatile struct eth_txdesc_s *txdesc; - volatile struct eth_rxdesc_s *rxdesc; - - /* Frame info */ - - struct eth_rxframe_info_s rxframe; - volatile struct eth_rxframe_info_s *rxframeinfo; + struct eth_txdesc_s *txdesc; /* Next TX descriptor */ + struct eth_rxdesc_s *rxdesc; /* Next RX descriptor */ + struct eth_rxdesc_s *rxfirst; /* First RX descriptor of the segment */ + uint32_t segcount; /* Segment count */ + sq_queue_t freeb; /* The free buffer list */ /* Descriptor allocations */ - struct eth_rxdesc_s rxtable[CONFIG_STM32_ETH_RXNBUFFERS]; - struct eth_txdesc_s txtable[CONFIG_STM32_ETH_TXNBUFFERS]; + struct eth_rxdesc_s rxtable[CONFIG_STM32_ETH_NRXDESC]; + struct eth_txdesc_s txtable[CONFIG_STM32_ETH_NTXDESC]; /* Buffer allocations */ - uint8_t rxbuffer[CONFIG_STM32_ETH_RXNBUFFERS*CONFIG_STM32_ETH_RXBUFSIZE]; - uint8_t txbuffer[CONFIG_STM32_ETH_TXNBUFFERS*CONFIG_STM32_ETH_TXBUFSIZE]; + uint8_t rxbuffer[CONFIG_STM32_ETH_NRXDESC*CONFIG_STM32_ETH_BUFSIZE]; + uint8_t alloc[STM32_ETH_NFREEBUFFERS*CONFIG_STM32_ETH_BUFSIZE]; }; /**************************************************************************** @@ -516,6 +515,12 @@ static struct stm32_ethmac_s g_stm32ethmac[STM32_NETHERNET]; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ +/* Free buffer management */ + +static void stm32_initbuffer(FAR struct stm32_ethmac_s *priv); +static inline uint8_t *stm32_allocbuffer(FAR struct stm32_ethmac_s *priv); +static inline void stm32_freebuffer(FAR struct stm32_ethmac_s *priv, uint8_t *buffer); +static inline bool stm32_isfreebuffer(FAR struct stm32_ethmac_s *priv); /* Common TX logic */ @@ -524,6 +529,9 @@ static int stm32_uiptxpoll(struct uip_driver_s *dev); /* Interrupt handling */ +static void stm32_freesegment(FAR struct stm32_ethmac_s *priv, + FAR struct eth_rxdesc_s *rxfirst, int nsegments); +static int stm32_recvframe(FAR struct stm32_ethmac_s *priv); static void stm32_receive(FAR struct stm32_ethmac_s *priv); static void stm32_txdone(FAR struct stm32_ethmac_s *priv); static int stm32_interrupt(int irq, FAR void *context); @@ -567,6 +575,111 @@ static int stm32_ethconfig(FAR struct stm32_ethmac_s *priv); ****************************************************************************/ /**************************************************************************** + * Function: stm32_initbuffer + * + * Description: + * Initialize the free buffer list. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Called during early driver initialization before Ethernet interrupts + * are enabled. + * + ****************************************************************************/ + +static void stm32_initbuffer(FAR struct stm32_ethmac_s *priv) +{ + uint8_t *buffer; + int i; + + sq_init(&priv->freeb); + + for (i = 0, buffer = priv->alloc; + i < STM32_ETH_NFREEBUFFERS; + i++, buffer += CONFIG_STM32_ETH_BUFSIZE) + { + sq_addlast((FAR sq_entry_t *)buffer, &priv->freeb); + } +} + +/**************************************************************************** + * Function: stm32_allocbuffer + * + * Description: + * Allocate one buffer from the free buffer list. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * Pointer to the allocated buffer on success; NULL 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 inline uint8_t *stm32_allocbuffer(FAR struct stm32_ethmac_s *priv) +{ + return (uint8_t *)sq_remfirst(&priv->freeb); +} + +/**************************************************************************** + * Function: stm32_freebuffer + * + * Description: + * Return a buffer to the free buffer list. + * + * Parameters: + * priv - Reference to the driver state structure + * buffer - A pointer to the buffer to be freed + * + * Returned Value: + * None + * + * 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 inline void stm32_freebuffer(FAR struct stm32_ethmac_s *priv, uint8_t *buffer) +{ + sq_addlast((FAR sq_entry_t *)buffer, &priv->freeb); +} + +/**************************************************************************** + * Function: stm32_isfreebuffer + * + * Description: + * Return TRUE if the free buffer list is not empty. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * True if there are one or more buffers in the free buffer list; + * false if the free buffer list is empty + * + * Assumptions: + * None. + * + ****************************************************************************/ + +static inline bool stm32_isfreebuffer(FAR struct stm32_ethmac_s *priv) +{ + return !sq_empty(&priv->freeb); +} + +/**************************************************************************** * Function: stm32_transmit * * Description: @@ -661,6 +774,197 @@ static int stm32_uiptxpoll(struct uip_driver_s *dev) } /**************************************************************************** + * Function: stm32_freesegment + * + * Description: + * The function is called when a frame is received using the DMA receive + * interrupt. It scans the RX descriptors to the the received frame. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * None + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +static void stm32_freesegment(FAR struct stm32_ethmac_s *priv, + FAR struct eth_rxdesc_s *rxfirst, int nsegments) +{ + struct eth_rxdesc_s *rxdesc; + int i; + + /* Set OWN bit in RX descriptors. This gives the buffers back to DMA */ + + rxdesc = rxfirst; + for (i = 0; i < nsegments; i++) + { + rxdesc->rdes0 = ETH_RDES0_OWN; + rxdesc = (struct eth_rxdesc_s *)rxdesc->rdes3; + } + + /* Reset the segment managment logic */ + + priv->rxfirst = NULL; + priv->segcount = 0; + + /* Check if the RX Buffer unavailable flag is set */ + + if ((getreg32(STM32_ETH_DMASR) & ETH_DMAINT_RBUI) != 0) + { + /* Clear RBUS Ethernet DMA flag */ + + putreg32(ETH_DMAINT_RBUI, STM32_ETH_DMASR); + + /* Resume DMA reception */ + + putreg32(0, STM32_ETH_DMARPDR); + } +} + +/**************************************************************************** + * Function: stm32_recvframe + * + * Description: + * The function is called when a frame is received using the DMA receive + * interrupt. It scans the RX descriptors of the received frame. + * + * NOTE: This function will silently discard any packets containing errors. + * + * Parameters: + * priv - Reference to the driver state structure + * + * Returned Value: + * OK if a packet was successfully returned; -EAGAIN if there are no + * further packets available + * + * Assumptions: + * Global interrupts are disabled by interrupt handling logic. + * + ****************************************************************************/ + +static int stm32_recvframe(FAR struct stm32_ethmac_s *priv) +{ + struct eth_rxdesc_s *rxdesc; + struct eth_rxdesc_s *rxfirst; + uint8_t *buffer; + int i; + + /* Check if there are free buffers. We cannot receive new frames in this + * design unless there is at least one free buffer. + */ + + if (!stm32_isfreebuffer(priv)) + { + nlldbg("No free buffers\n"); + return -ENOMEM; + } + + /* Scan descriptors owned by the CPU */ + + rxdesc = priv->rxdesc; + for (i = 0; + (rxdesc->rdes0 & ETH_RDES0_OWN) == 0 && i < CONFIG_STM32_ETH_NRXDESC; + i++) + { + /* Check if this is the first segment in the frame */ + + if ((rxdesc->rdes0 & ETH_RDES0_FS) != 0 && + (rxdesc->rdes0 & ETH_RDES0_LS) == 0) + { + priv->rxfirst = rxdesc; + priv->segcount = 1; + } + + /* Check if this is an intermediate segment in the frame */ + + else if (((rxdesc->rdes0 & ETH_RDES0_LS) == 0)&& + ((rxdesc->rdes0 & ETH_RDES0_FS) == 0)) + { + priv->segcount++; + } + + /* Otherwise, it is the last segment in the frame */ + + else + { + priv->segcount++; + + /* Check if the there is only one segment in the frame */ + + if (priv->segcount == 1) + { + rxfirst = rxdesc; + } + else + { + rxfirst = priv->rxfirst; + } + + /* Check if any errors are reported in the frame */ + + if ((rxdesc->rdes0 & ETH_RDES0_ES) == 0) + { + struct uip_driver_s *dev = &priv->dev; + + /* Get the Frame Length of the received packet: substruct 4 + * bytes of the CRC + */ + + dev->d_len = ((rxdesc->rdes0 & ETH_RDES0_FL_MASK) >> ETH_RDES0_FL_SHIFT) - 4; + + /* Get a buffer from the free list. We don't even check if + * this is successful because we already assure the the free + * list is not empty above. + */ + + buffer = stm32_allocbuffer(priv); + + /* Take the buffer from the RX descriptor of the first free + * segment, put it into the uIP device structure, then replace + * the buffer in the RX descriptor with the newly allocated + * buffer. + */ + + dev->d_buf = (uint8_t*)rxfirst->rdes2; + rxfirst->rdes2 = (uint32_t)buffer; + + /* Return success, remebering where we should re-start scanning + * and resetting the segment scanning logic + */ + + priv->rxdesc = (struct eth_rxdesc_s*)rxdesc->rdes3; + stm32_freesegment(priv, rxfirst, priv->segcount); + return OK; + } + else + { + /* Drop the frame that contains the errors, reset the segment + * scanning logic, and continue scanning with the next frame. + */ + + nlldbg("DROPPED: RX descriptor errors: %08x\n", rxdesc->rdes0); + stm32_freesegment(priv, rxfirst, priv->segcount); + } + } + + /* Try the next descriptor */ + + rxdesc = (struct eth_rxdesc_s*)rxdesc->rdes3; + } + + /* We get here after all of the descriptors have been scanned. Remember + * where we left off. + */ + + priv->rxdesc = rxdesc; + return -EAGAIN; +} + +/**************************************************************************** * Function: stm32_receive * * Description: @@ -679,16 +983,23 @@ static int stm32_uiptxpoll(struct uip_driver_s *dev) static void stm32_receive(FAR struct stm32_ethmac_s *priv) { - do - { - /* Check for errors and update statistics */ + struct uip_driver_s *dev = &priv->dev; - /* Check if the packet is a valid size for the uIP buffer configuration */ + /* Loop while while stm32_recvframe() successfully retrieves valid + * Ethernet frames. + */ - /* Copy the data data from the hardware to priv->dev.d_buf. Set - * amount of data in priv->dev.d_len + while (stm32_recvframe(priv) == OK) + { + /* Check if the packet is a valid size for the uIP buffer configuration + * (this should not happen) */ + if (dev->d_len > CONFIG_NET_BUFSIZE) + { + nlldbg("DROPPED: Too big: %d\n", dev->d_len); + } + /* We only accept IP packets of the configured type and ARP packets */ #ifdef CONFIG_NET_IPv6 @@ -724,7 +1035,6 @@ static void stm32_receive(FAR struct stm32_ethmac_s *priv) } } } - while (false); /* While there are more packets to be processed */ } /**************************************************************************** @@ -1151,7 +1461,7 @@ static void stm32_txdescinit(FAR struct stm32_ethmac_s *priv) /* Initialize each TX descriptor */ - for (i = 0; i < CONFIG_STM32_ETH_TXNBUFFERS; i++) + for (i = 0; i < CONFIG_STM32_ETH_NTXDESC; i++) { txdesc = &priv->txtable[i]; @@ -1159,13 +1469,21 @@ static void stm32_txdescinit(FAR struct stm32_ethmac_s *priv) txdesc->tdes0 = ETH_TDES0_TCH; - /* Set Buffer1 address pointer */ +#ifdef CHECKSUM_BY_HARDWARE + /* Enable the checksum insertion for the Tx frames */ + + txdesc->tdes0 |= ETH_TDES0_CIC_ALL; +#endif + + /* Clear Buffer1 address pointer (buffers will be assigned as they + * are used) + */ - txdesc->tdes2 = (uint32_t)(&priv->txbuffer[i*CONFIG_STM32_ETH_TXBUFSIZE]); + txdesc->tdes2 = 0;; /* Initialize the next descriptor with the Next Descriptor Polling Enable */ - if( i < (CONFIG_STM32_ETH_TXNBUFFERS-1)) + if( i < (CONFIG_STM32_ETH_NTXDESC-1)) { /* Set next descriptor address register with next descriptor base * address @@ -1182,6 +1500,10 @@ static void stm32_txdescinit(FAR struct stm32_ethmac_s *priv) txdesc->tdes3 = (uint32_t)priv->txtable; } } + + /* Set Transmit Desciptor List Address Register */ + + putreg32((uint32_t)priv->txtable, STM32_ETH_DMATDLAR); } /**************************************************************************** @@ -1211,7 +1533,7 @@ static void stm32_rxdescinit(FAR struct stm32_ethmac_s *priv) /* Initialize each TX descriptor */ - for (i = 0; i < CONFIG_STM32_ETH_RXNBUFFERS; i++) + for (i = 0; i < CONFIG_STM32_ETH_NRXDESC; i++) { rxdesc = &priv->rxtable[i]; @@ -1219,17 +1541,19 @@ static void stm32_rxdescinit(FAR struct stm32_ethmac_s *priv) rxdesc->rdes0 = ETH_RDES0_OWN; - /* Set Buffer1 size and Second Address Chained bit */ + /* Set Buffer1 size and Second Address Chained bit and enabled DMA + * RX desc receive interrupt + */ - rxdesc->rdes1 = ETH_RDES1_RCH | (uint32_t)CONFIG_STM32_ETH_RXBUFSIZE; + rxdesc->rdes1 = ETH_RDES1_RCH | (uint32_t)CONFIG_STM32_ETH_BUFSIZE; /* Set Buffer1 address pointer */ - rxdesc->rdes2 = (uint32_t)&priv->rxbuffer[i*CONFIG_STM32_ETH_RXBUFSIZE]; + rxdesc->rdes2 = (uint32_t)&priv->rxbuffer[i*CONFIG_STM32_ETH_BUFSIZE]; /* Initialize the next descriptor with the Next Descriptor Polling Enable */ - if( i < (CONFIG_STM32_ETH_RXNBUFFERS-1)) + if( i < (CONFIG_STM32_ETH_NRXDESC-1)) { /* Set next descriptor address register with next descriptor base * address @@ -1250,8 +1574,6 @@ static void stm32_rxdescinit(FAR struct stm32_ethmac_s *priv) /* Set Receive Descriptor List Address Register */ putreg32((uint32_t)priv->rxtable, STM32_ETH_DMARDLAR); - - priv->rxframeinfo = &priv->rxframe; } /**************************************************************************** @@ -1776,31 +2098,7 @@ static int stm32_macconfig(FAR struct stm32_ethmac_s *priv) static int stm32_macenable(FAR struct stm32_ethmac_s *priv) { uint32_t regval; - int i; - - /* Enable Ethernet Rx interrrupt */ - for (i = 0; i < CONFIG_STM32_ETH_RXNBUFFERS; i++) - { - /* Enable the DMA Rx Desc receive interrupt */ - - priv->rxtable[i].rdes1 &= ~ETH_RDES1_DIC; - } - -#ifdef CHECKSUM_BY_HARDWARE - /* Enable the checksum insertion for the Tx frames */ - - for (i = 0; i < CONFIG_STM32_ETH_TXNBUFFERS; i++) - { - /* Set the selected DMA Tx desc checksum insertion control */ - - priv->txtable[i].tdes0 |= ETH_TDES0_CIC_ALL; - } -#endif - - /* Enable RX and TX */ -#warning "Missing Logic" - /* Enable transmit state machine of the MAC for transmission on the MII */ regval = getreg32(STM32_ETH_MACCR); @@ -1901,6 +2199,10 @@ static int stm32_ethconfig(FAR struct stm32_ethmac_s *priv) return ret; } + /* Initialize the free buffer list */ + + stm32_initbuffer(priv); + /* Initialize Tx Descriptors list: Chain Mode */ stm32_txdescinit(priv); diff --git a/nuttx/configs/stm3240g-eval/nsh/defconfig b/nuttx/configs/stm3240g-eval/nsh/defconfig index ce69e0295..0a852582b 100755 --- a/nuttx/configs/stm3240g-eval/nsh/defconfig +++ b/nuttx/configs/stm3240g-eval/nsh/defconfig @@ -686,6 +686,8 @@ CONFIG_MMCSD_HAVECARDDETECT=n # TCP/IP and UDP support via uIP # # CONFIG_NET - Enable or disable all network features +# CONFIG_NET_NOINTS - uIP not called from interrupt level. +# CONFIG_NET_MULTIBUFFER - Use multiple input/output buffers (probably no) # CONFIG_NET_IPv6 - Build in support for IPv6 # CONFIG_NSOCKET_DESCRIPTORS - Maximum number of socket descriptors per task/thread. # CONFIG_NET_SOCKOPTS - Enable or disable support for socket options @@ -710,6 +712,8 @@ CONFIG_MMCSD_HAVECARDDETECT=n # CONFIG_NET_FWCACHE_SIZE - number of packets to remember when looking for duplicates # CONFIG_NET=n +CONFIG_NET_NOINTS=n +CONFIG_NET_MULTIBUFFER=y CONFIG_NET_IPv6=n CONFIG_NSOCKET_DESCRIPTORS=10 CONFIG_NET_SOCKOPTS=y diff --git a/nuttx/configs/stm3240g-eval/ostest/defconfig b/nuttx/configs/stm3240g-eval/ostest/defconfig index cc91d8a53..e77e97ab9 100755 --- a/nuttx/configs/stm3240g-eval/ostest/defconfig +++ b/nuttx/configs/stm3240g-eval/ostest/defconfig @@ -650,6 +650,8 @@ CONFIG_MMCSD_HAVECARDDETECT=n # # TCP/IP and UDP support via uIP # CONFIG_NET - Enable or disable all network features +# CONFIG_NET_NOINTS - uIP not called from interrupt level. +# CONFIG_NET_MULTIBUFFER - Use multiple input/output buffers (probably no) # CONFIG_NET_IPv6 - Build in support for IPv6 # CONFIG_NSOCKET_DESCRIPTORS - Maximum number of socket descriptors per task/thread. # CONFIG_NET_SOCKOPTS - Enable or disable support for socket options @@ -674,6 +676,8 @@ CONFIG_MMCSD_HAVECARDDETECT=n # CONFIG_NET_FWCACHE_SIZE - number of packets to remember when looking for duplicates # CONFIG_NET=n +CONFIG_NET_NOINTS=n +CONFIG_NET_MULTIBUFFER=y CONFIG_NET_IPv6=n CONFIG_NSOCKET_DESCRIPTORS=0 CONFIG_NET_SOCKOPTS=y |