| /* |
| FreeRTOS+TCP V2.0.11 |
| Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| |
| Permission is hereby granted, free of charge, to any person obtaining a copy of |
| this software and associated documentation files (the "Software"), to deal in |
| the Software without restriction, including without limitation the rights to |
| use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of |
| the Software, and to permit persons to whom the Software is furnished to do so, |
| subject to the following conditions: |
| |
| The above copyright notice and this permission notice shall be included in all |
| copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS |
| FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR |
| COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
| IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| |
| http://aws.amazon.com/freertos |
| http://www.FreeRTOS.org |
| */ |
| |
| /* Standard includes. */ |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| /* FreeRTOS includes. */ |
| #include "FreeRTOS.h" |
| #include "task.h" |
| #include "queue.h" |
| #include "semphr.h" |
| |
| /* FreeRTOS+TCP includes. */ |
| #include "FreeRTOS_IP.h" |
| #include "FreeRTOS_Sockets.h" |
| #include "FreeRTOS_IP_Private.h" |
| #include "NetworkBufferManagement.h" |
| #include "NetworkInterface.h" |
| |
| /* LPCOpen includes. */ |
| #include "chip.h" |
| #include "lpc_phy.h" |
| |
| /* The size of the stack allocated to the task that handles Rx packets. */ |
| #define nwRX_TASK_STACK_SIZE 140 |
| |
| #ifndef PHY_LS_HIGH_CHECK_TIME_MS |
| /* Check if the LinkSStatus in the PHY is still high after 15 seconds of not |
| receiving packets. */ |
| #define PHY_LS_HIGH_CHECK_TIME_MS 15000 |
| #endif |
| |
| #ifndef PHY_LS_LOW_CHECK_TIME_MS |
| /* Check if the LinkSStatus in the PHY is still low every second. */ |
| #define PHY_LS_LOW_CHECK_TIME_MS 1000 |
| #endif |
| |
| #ifndef configUSE_RMII |
| #define configUSE_RMII 1 |
| #endif |
| |
| #ifndef configNUM_RX_DESCRIPTORS |
| #error please define configNUM_RX_DESCRIPTORS in your FreeRTOSIPConfig.h |
| #endif |
| |
| #ifndef configNUM_TX_DESCRIPTORS |
| #error please define configNUM_TX_DESCRIPTORS in your FreeRTOSIPConfig.h |
| #endif |
| |
| #ifndef NETWORK_IRQHandler |
| #error NETWORK_IRQHandler must be defined to the name of the function that is installed in the interrupt vector table to handle Ethernet interrupts. |
| #endif |
| |
| #if !defined( MAC_FF_HMC ) |
| /* Hash for multicast. */ |
| #define MAC_FF_HMC ( 1UL << 2UL ) |
| #endif |
| |
| #ifndef iptraceEMAC_TASK_STARTING |
| #define iptraceEMAC_TASK_STARTING() do { } while( 0 ) |
| #endif |
| |
| /* Define the bits of .STATUS that indicate a reception error. */ |
| #define nwRX_STATUS_ERROR_BITS \ |
| ( RDES_CE /* CRC Error */ | \ |
| RDES_RE /* Receive Error */ | \ |
| RDES_DE /* Descriptor Error */ | \ |
| RDES_RWT /* Receive Watchdog Timeout */ | \ |
| RDES_LC /* Late Collision */ | \ |
| RDES_OE /* Overflow Error */ | \ |
| RDES_SAF /* Source Address Filter Fail */ | \ |
| RDES_AFM /* Destination Address Filter Fail */ | \ |
| RDES_LE /* Length Error */ ) |
| |
| /* Define the EMAC status bits that should trigger an interrupt. */ |
| #define nwDMA_INTERRUPT_MASK \ |
| ( DMA_IE_TIE /* Transmit interrupt enable */ | \ |
| DMA_IE_TSE /* Transmit stopped enable */ | \ |
| DMA_IE_OVE /* Overflow interrupt enable */ | \ |
| DMA_IE_RIE /* Receive interrupt enable */ | \ |
| DMA_IE_NIE /* Normal interrupt summary enable */ | \ |
| DMA_IE_AIE /* Abnormal interrupt summary enable */ | \ |
| DMA_IE_RUE /* Receive buffer unavailable enable */ | \ |
| DMA_IE_UNE /* Underflow interrupt enable. */ | \ |
| DMA_IE_TJE /* Transmit jabber timeout enable */ | \ |
| DMA_IE_RSE /* Received stopped enable */ | \ |
| DMA_IE_RWE /* Receive watchdog timeout enable */ | \ |
| DMA_IE_FBE )/* Fatal bus error enable */ |
| |
| /* Interrupt events to process. Currently only the RX/TX events are processed |
| although code for other events is included to allow for possible future |
| expansion. */ |
| #define EMAC_IF_RX_EVENT 1UL |
| #define EMAC_IF_TX_EVENT 2UL |
| #define EMAC_IF_ERR_EVENT 4UL |
| #define EMAC_IF_ALL_EVENT ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT ) |
| |
| /* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet |
| driver will filter incoming packets and only pass the stack those packets it |
| considers need processing. */ |
| #if( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 ) |
| #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer |
| #else |
| #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) ) |
| #endif |
| |
| #if( ipconfigZERO_COPY_RX_DRIVER == 0 ) || ( ipconfigZERO_COPY_TX_DRIVER == 0 ) |
| #warning It is adviced to enable both macros |
| #endif |
| |
| #ifndef configPLACE_IN_SECTION_RAM |
| #define configPLACE_IN_SECTION_RAM |
| /* |
| #define configPLACE_IN_SECTION_RAM __attribute__ ((section(".ramfunc"))) |
| */ |
| #endif |
| |
| /*-----------------------------------------------------------*/ |
| |
| /* |
| * Delay function passed into the library. The implementation uses FreeRTOS |
| * calls so the scheduler must be started before the driver can be used. |
| */ |
| static void prvDelay( uint32_t ulMilliSeconds ); |
| |
| /* |
| * Initialises the Tx and Rx descriptors respectively. |
| */ |
| static void prvSetupTxDescriptors( void ); |
| static void prvSetupRxDescriptors( void ); |
| |
| /* |
| * A task that processes received frames. |
| */ |
| static void prvEMACHandlerTask( void *pvParameters ); |
| |
| /* |
| * Sets up the MAC with the results of an auto-negotiation. |
| */ |
| static BaseType_t prvSetLinkSpeed( void ); |
| |
| /* |
| * Generates a CRC for a MAC address that is then used to generate a hash index. |
| */ |
| static uint32_t prvGenerateCRC32( const uint8_t *ucAddress ); |
| |
| /* |
| * Generates a hash index when setting a filter to permit a MAC address. |
| */ |
| static uint32_t prvGetHashIndex( const uint8_t *ucAddress ); |
| |
| /* |
| * Update the hash table to allow a MAC address. |
| */ |
| static void prvAddMACAddress( const uint8_t* ucMacAddress ); |
| |
| /* |
| * Sometimes the DMA will report received data as being longer than the actual |
| * received from length. This function checks the reported length and corrects |
| * if if necessary. |
| */ |
| static void prvRemoveTrailingBytes( NetworkBufferDescriptor_t *pxDescriptor ); |
| |
| /*-----------------------------------------------------------*/ |
| |
| /* Bit map of outstanding ETH interrupt events for processing. Currently only |
| the Rx and Tx interrupt is handled, although code is included for other events |
| to enable future expansion. */ |
| static volatile uint32_t ulISREvents; |
| |
| /* A copy of PHY register 1: 'PHY_REG_01_BMSR' */ |
| static uint32_t ulPHYLinkStatus = 0; |
| |
| /* Tx descriptors and index. */ |
| static ENET_ENHTXDESC_T xDMATxDescriptors[ configNUM_TX_DESCRIPTORS ]; |
| |
| /* ulNextFreeTxDescriptor is declared volatile, because it is accessed from |
| to different tasks. */ |
| static volatile uint32_t ulNextFreeTxDescriptor; |
| static uint32_t ulTxDescriptorToClear; |
| |
| /* Rx descriptors and index. */ |
| static ENET_ENHRXDESC_T xDMARxDescriptors[ configNUM_RX_DESCRIPTORS ]; |
| static uint32_t ulNextRxDescriptorToProcess; |
| |
| /* Must be defined externally - the demo applications define this in main.c. */ |
| extern uint8_t ucMACAddress[ 6 ]; |
| |
| /* The handle of the task that processes Rx packets. The handle is required so |
| the task can be notified when new packets arrive. */ |
| static TaskHandle_t xRxHanderTask = NULL; |
| |
| #if( ipconfigUSE_LLMNR == 1 ) |
| static const uint8_t xLLMNR_MACAddress[] = { '\x01', '\x00', '\x5E', '\x00', '\x00', '\xFC' }; |
| #endif /* ipconfigUSE_LLMNR == 1 */ |
| |
| /* xTXDescriptorSemaphore is a counting semaphore with |
| a maximum count of ETH_TXBUFNB, which is the number of |
| DMA TX descriptors. */ |
| static SemaphoreHandle_t xTXDescriptorSemaphore = NULL; |
| |
| /*-----------------------------------------------------------*/ |
| |
| |
| BaseType_t xNetworkInterfaceInitialise( void ) |
| { |
| BaseType_t xReturn = pdPASS; |
| |
| /* The interrupt will be turned on when a link is established. */ |
| NVIC_DisableIRQ( ETHERNET_IRQn ); |
| |
| /* Disable receive and transmit DMA processes. */ |
| LPC_ETHERNET->DMA_OP_MODE &= ~( DMA_OM_ST | DMA_OM_SR ); |
| |
| /* Disable packet reception. */ |
| LPC_ETHERNET->MAC_CONFIG &= ~( MAC_CFG_RE | MAC_CFG_TE ); |
| |
| /* Call the LPCOpen function to initialise the hardware. */ |
| Chip_ENET_Init( LPC_ETHERNET ); |
| |
| /* Save MAC address. */ |
| Chip_ENET_SetADDR( LPC_ETHERNET, ucMACAddress ); |
| |
| /* Clear all MAC address hash entries. */ |
| LPC_ETHERNET->MAC_HASHTABLE_HIGH = 0; |
| LPC_ETHERNET->MAC_HASHTABLE_LOW = 0; |
| |
| #if( ipconfigUSE_LLMNR == 1 ) |
| { |
| prvAddMACAddress( xLLMNR_MACAddress ); |
| } |
| #endif /* ipconfigUSE_LLMNR == 1 */ |
| |
| /* Promiscuous flag (PR) and Receive All flag (RA) set to zero. The |
| registers MAC_HASHTABLE_[LOW|HIGH] will be loaded to allow certain |
| multi-cast addresses. */ |
| LPC_ETHERNET->MAC_FRAME_FILTER = MAC_FF_HMC; |
| |
| #if( configUSE_RMII == 1 ) |
| { |
| if( lpc_phy_init( pdTRUE, prvDelay ) != SUCCESS ) |
| { |
| xReturn = pdFAIL; |
| } |
| } |
| #else |
| { |
| #warning This path has not been tested. |
| if( lpc_phy_init( pdFALSE, prvDelay ) != SUCCESS ) |
| { |
| xReturn = pdFAIL; |
| } |
| } |
| #endif |
| |
| if( xReturn == pdPASS ) |
| { |
| /* Guard against the task being created more than once and the |
| descriptors being initialised more than once. */ |
| if( xRxHanderTask == NULL ) |
| { |
| xReturn = xTaskCreate( prvEMACHandlerTask, "EMAC", nwRX_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xRxHanderTask ); |
| configASSERT( xReturn ); |
| } |
| |
| if( xTXDescriptorSemaphore == NULL ) |
| { |
| /* Create a counting semaphore, with a value of 'configNUM_TX_DESCRIPTORS' |
| and a maximum of 'configNUM_TX_DESCRIPTORS'. */ |
| xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) configNUM_TX_DESCRIPTORS, ( UBaseType_t ) configNUM_TX_DESCRIPTORS ); |
| configASSERT( xTXDescriptorSemaphore ); |
| } |
| |
| /* Enable MAC interrupts. */ |
| LPC_ETHERNET->DMA_INT_EN = nwDMA_INTERRUPT_MASK; |
| } |
| |
| if( xReturn != pdFAIL ) |
| { |
| /* Auto-negotiate was already started. Wait for it to complete. */ |
| xReturn = prvSetLinkSpeed(); |
| |
| if( xReturn == pdPASS ) |
| { |
| /* Initialise the descriptors. */ |
| prvSetupTxDescriptors(); |
| prvSetupRxDescriptors(); |
| |
| /* Clear all interrupts. */ |
| LPC_ETHERNET->DMA_STAT = DMA_ST_ALL; |
| |
| /* Enable receive and transmit DMA processes. */ |
| LPC_ETHERNET->DMA_OP_MODE |= DMA_OM_ST | DMA_OM_SR; |
| |
| /* Set Receiver / Transmitter Enable. */ |
| LPC_ETHERNET->MAC_CONFIG |= MAC_CFG_RE | MAC_CFG_TE; |
| |
| /* Start receive polling. */ |
| LPC_ETHERNET->DMA_REC_POLL_DEMAND = 1; |
| |
| /* Enable interrupts in the NVIC. */ |
| NVIC_SetPriority( ETHERNET_IRQn, configMAC_INTERRUPT_PRIORITY ); |
| NVIC_EnableIRQ( ETHERNET_IRQn ); |
| } |
| } |
| |
| return xReturn; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| #define niBUFFER_1_PACKET_SIZE 1536 |
| |
| static __attribute__ ((section("._ramAHB32"))) uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ] __attribute__ ( ( aligned( 32 ) ) ); |
| |
| void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] ) |
| { |
| |
| uint8_t *ucRAMBuffer = ucNetworkPackets; |
| uint32_t ul; |
| |
| for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ ) |
| { |
| pxNetworkBuffers[ ul ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING; |
| *( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ul ] ) ); |
| ucRAMBuffer += niBUFFER_1_PACKET_SIZE; |
| } |
| } |
| /*-----------------------------------------------------------*/ |
| |
| configPLACE_IN_SECTION_RAM |
| static void vClearTXBuffers() |
| { |
| uint32_t ulLastDescriptor = ulNextFreeTxDescriptor; |
| size_t uxCount = ( ( size_t ) configNUM_TX_DESCRIPTORS ) - uxSemaphoreGetCount( xTXDescriptorSemaphore ); |
| #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) |
| NetworkBufferDescriptor_t *pxNetworkBuffer; |
| uint8_t *ucPayLoad; |
| #endif |
| |
| /* This function is called after a TX-completion interrupt. |
| It will release each Network Buffer used in xNetworkInterfaceOutput(). |
| 'uxCount' represents the number of descriptors given to DMA for transmission. |
| After sending a packet, the DMA will clear the 'TDES_OWN' bit. */ |
| while( ( uxCount > ( size_t ) 0u ) && ( ( xDMATxDescriptors[ ulTxDescriptorToClear ].CTRLSTAT & TDES_OWN ) == 0 ) ) |
| { |
| if( ( ulTxDescriptorToClear == ulLastDescriptor ) && ( uxCount != ( size_t ) configNUM_TX_DESCRIPTORS ) ) |
| { |
| break; |
| } |
| |
| |
| #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) |
| { |
| ucPayLoad = ( uint8_t * )xDMATxDescriptors[ ulTxDescriptorToClear ].B1ADD; |
| if( ucPayLoad != NULL ) |
| { |
| /* B1ADD points to a pucEthernetBuffer of a Network Buffer descriptor. */ |
| pxNetworkBuffer = pxPacketBuffer_to_NetworkBuffer( ucPayLoad ); |
| |
| configASSERT( pxNetworkBuffer != NULL ); |
| |
| vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ) ; |
| xDMATxDescriptors[ ulTxDescriptorToClear ].B1ADD = ( uint32_t )0u; |
| } |
| } |
| #endif /* ipconfigZERO_COPY_TX_DRIVER */ |
| |
| /* Move onto the next descriptor, wrapping if necessary. */ |
| ulTxDescriptorToClear++; |
| if( ulTxDescriptorToClear >= configNUM_TX_DESCRIPTORS ) |
| { |
| ulTxDescriptorToClear = 0; |
| } |
| |
| uxCount--; |
| /* Tell the counting semaphore that one more TX descriptor is available. */ |
| xSemaphoreGive( xTXDescriptorSemaphore ); |
| } |
| } |
| |
| /*-----------------------------------------------------------*/ |
| |
| configPLACE_IN_SECTION_RAM |
| BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, BaseType_t bReleaseAfterSend ) |
| { |
| BaseType_t xReturn = pdFAIL; |
| const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50 ); |
| |
| /* Attempt to obtain access to a Tx descriptor. */ |
| do |
| { |
| if( xTXDescriptorSemaphore == NULL ) |
| { |
| break; |
| } |
| if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS ) |
| { |
| /* Time-out waiting for a free TX descriptor. */ |
| break; |
| } |
| |
| /* If the descriptor is still owned by the DMA it can't be used. */ |
| if( ( xDMATxDescriptors[ ulNextFreeTxDescriptor ].CTRLSTAT & TDES_OWN ) != 0 ) |
| { |
| /* The semaphore was taken, the TX DMA-descriptor is still not available. |
| Actually that should not occur, the 'TDES_OWN' was already confirmed low in vClearTXBuffers(). */ |
| xSemaphoreGive( xTXDescriptorSemaphore ); |
| } |
| else |
| { |
| #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) |
| { |
| /* bReleaseAfterSend should always be set when using the zero |
| copy driver. */ |
| configASSERT( bReleaseAfterSend != pdFALSE ); |
| |
| /* The DMA's descriptor to point directly to the data in the |
| network buffer descriptor. The data is not copied. */ |
| xDMATxDescriptors[ ulNextFreeTxDescriptor ].B1ADD = ( uint32_t ) pxDescriptor->pucEthernetBuffer; |
| |
| /* The DMA descriptor will 'own' this Network Buffer, |
| until it has been sent. So don't release it now. */ |
| bReleaseAfterSend = false; |
| } |
| #else |
| { |
| /* The data is copied from the network buffer descriptor into |
| the DMA's descriptor. */ |
| memcpy( ( void * ) xDMATxDescriptors[ ulNextFreeTxDescriptor ].B1ADD, ( void * ) pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength ); |
| } |
| #endif |
| |
| xDMATxDescriptors[ ulNextFreeTxDescriptor ].BSIZE = ( uint32_t ) TDES_ENH_BS1( pxDescriptor->xDataLength ); |
| |
| /* This descriptor is given back to the DMA. */ |
| xDMATxDescriptors[ ulNextFreeTxDescriptor ].CTRLSTAT |= TDES_OWN; |
| |
| /* Ensure the DMA is polling Tx descriptors. */ |
| LPC_ETHERNET->DMA_TRANS_POLL_DEMAND = 1; |
| |
| iptraceNETWORK_INTERFACE_TRANSMIT(); |
| |
| /* Move onto the next descriptor, wrapping if necessary. */ |
| ulNextFreeTxDescriptor++; |
| if( ulNextFreeTxDescriptor >= configNUM_TX_DESCRIPTORS ) |
| { |
| ulNextFreeTxDescriptor = 0; |
| } |
| |
| /* The Tx has been initiated. */ |
| xReturn = pdPASS; |
| } |
| } while( 0 ); |
| |
| /* The buffer has been sent so can be released. */ |
| if( bReleaseAfterSend != pdFALSE ) |
| { |
| vReleaseNetworkBufferAndDescriptor( pxDescriptor ); |
| } |
| |
| return xReturn; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| static void prvDelay( uint32_t ulMilliSeconds ) |
| { |
| /* Ensure the scheduler was started before attempting to use the scheduler to |
| create a delay. */ |
| configASSERT( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING ); |
| |
| vTaskDelay( pdMS_TO_TICKS( ulMilliSeconds ) ); |
| } |
| /*-----------------------------------------------------------*/ |
| |
| static void prvSetupTxDescriptors( void ) |
| { |
| BaseType_t x; |
| |
| /* Start with Tx descriptors clear. */ |
| memset( ( void * ) xDMATxDescriptors, 0, sizeof( xDMATxDescriptors ) ); |
| |
| /* Index to the next Tx descriptor to use. */ |
| ulNextFreeTxDescriptor = 0ul; |
| |
| /* Index to the next Tx descriptor to clear ( after transmission ). */ |
| ulTxDescriptorToClear = 0ul; |
| |
| for( x = 0; x < configNUM_TX_DESCRIPTORS; x++ ) |
| { |
| #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) |
| { |
| /* Nothing to do, B1ADD will be set when data is ready to transmit. |
| Currently the memset above will have set it to NULL. */ |
| } |
| #else |
| { |
| /* Allocate a buffer to the Tx descriptor. This is the most basic |
| way of creating a driver as the data is then copied into the |
| buffer. */ |
| xDMATxDescriptors[ x ].B1ADD = ( uint32_t ) pvPortMalloc( ipTOTAL_ETHERNET_FRAME_SIZE ); |
| |
| /* Use an assert to check the allocation as +TCP applications will |
| often not use a malloc() failed hook as the TCP stack will recover |
| from allocation failures. */ |
| configASSERT( xDMATxDescriptors[ x ].B1ADD ); |
| } |
| #endif |
| |
| /* Buffers hold an entire frame so all buffers are both the start and |
| end of a frame. */ |
| /* TDES_ENH_TCH Second Address Chained. */ |
| /* TDES_ENH_CIC(n) Checksum Insertion Control, tried but it does not work for the LPC18xx... */ |
| /* TDES_ENH_FS First Segment. */ |
| /* TDES_ENH_LS Last Segment. */ |
| /* TDES_ENH_IC Interrupt on Completion. */ |
| xDMATxDescriptors[ x ].CTRLSTAT = TDES_ENH_TCH | TDES_ENH_CIC( 3 ) | TDES_ENH_FS | TDES_ENH_LS | TDES_ENH_IC; |
| xDMATxDescriptors[ x ].B2ADD = ( uint32_t ) &xDMATxDescriptors[ x + 1 ]; |
| } |
| |
| xDMATxDescriptors[ configNUM_TX_DESCRIPTORS - 1 ].CTRLSTAT |= TDES_ENH_TER; |
| xDMATxDescriptors[ configNUM_TX_DESCRIPTORS - 1 ].B2ADD = ( uint32_t ) &xDMATxDescriptors[ 0 ]; |
| |
| /* Point the DMA to the base of the descriptor list. */ |
| LPC_ETHERNET->DMA_TRANS_DES_ADDR = ( uint32_t ) xDMATxDescriptors; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| static void prvSetupRxDescriptors( void ) |
| { |
| BaseType_t x; |
| #if( ipconfigZERO_COPY_RX_DRIVER != 0 ) |
| NetworkBufferDescriptor_t *pxNetworkBuffer; |
| #endif |
| |
| /* Index to the next Rx descriptor to use. */ |
| ulNextRxDescriptorToProcess = 0; |
| |
| /* Clear RX descriptor list. */ |
| memset( ( void * ) xDMARxDescriptors, 0, sizeof( xDMARxDescriptors ) ); |
| |
| for( x = 0; x < configNUM_RX_DESCRIPTORS; x++ ) |
| { |
| /* Allocate a buffer of the largest possible frame size as it is not |
| known what size received frames will be. */ |
| |
| #if( ipconfigZERO_COPY_RX_DRIVER != 0 ) |
| { |
| pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, 0 ); |
| |
| /* During start-up there should be enough Network Buffers available, |
| so it is safe to use configASSERT(). |
| In case this assert fails, please check: configNUM_RX_DESCRIPTORS, |
| ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS, and in case BufferAllocation_2.c |
| is included, check the amount of available heap. */ |
| configASSERT( pxNetworkBuffer != NULL ); |
| |
| /* Pass the actual buffer to DMA. */ |
| xDMARxDescriptors[ x ].B1ADD = ( uint32_t ) pxNetworkBuffer->pucEthernetBuffer; |
| } |
| #else |
| { |
| /* All DMA descriptors are populated with permanent memory blocks. |
| Their contents will be copy to Network Buffers. */ |
| xDMARxDescriptors[ x ].B1ADD = ( uint32_t ) pvPortMalloc( ipTOTAL_ETHERNET_FRAME_SIZE ); |
| } |
| #endif /* ipconfigZERO_COPY_RX_DRIVER */ |
| |
| /* Use an assert to check the allocation as +TCP applications will often |
| not use a malloc failed hook as the TCP stack will recover from |
| allocation failures. */ |
| configASSERT( xDMARxDescriptors[ x ].B1ADD ); |
| |
| xDMARxDescriptors[ x ].B2ADD = ( uint32_t ) &( xDMARxDescriptors[ x + 1 ] ); |
| xDMARxDescriptors[ x ].CTRL = ( uint32_t ) RDES_ENH_BS1( ipTOTAL_ETHERNET_FRAME_SIZE ) | RDES_ENH_RCH; |
| |
| /* The descriptor is available for use by the DMA. */ |
| xDMARxDescriptors[ x ].STATUS = RDES_OWN; |
| } |
| |
| /* RDES_ENH_RER Receive End of Ring. */ |
| xDMARxDescriptors[ ( configNUM_RX_DESCRIPTORS - 1 ) ].CTRL |= RDES_ENH_RER; |
| xDMARxDescriptors[ configNUM_RX_DESCRIPTORS - 1 ].B2ADD = ( uint32_t ) &( xDMARxDescriptors[ 0 ] ); |
| |
| /* Point the DMA to the base of the descriptor list. */ |
| LPC_ETHERNET->DMA_REC_DES_ADDR = ( uint32_t ) xDMARxDescriptors; |
| } |
| /*-----------------------------------------------------------*/ |
| configPLACE_IN_SECTION_RAM |
| static void prvRemoveTrailingBytes( NetworkBufferDescriptor_t *pxDescriptor ) |
| { |
| size_t xExpectedLength; |
| IPPacket_t *pxIPPacket; |
| |
| pxIPPacket = ( IPPacket_t * ) pxDescriptor->pucEthernetBuffer; |
| /* Look at the actual length of the packet, translate it to a host-endial notation. */ |
| xExpectedLength = sizeof( EthernetHeader_t ) + ( size_t ) FreeRTOS_htons( pxIPPacket->xIPHeader.usLength ); |
| |
| if( xExpectedLength == ( pxDescriptor->xDataLength + 4 ) ) |
| { |
| pxDescriptor->xDataLength -= 4; |
| } |
| else |
| { |
| if( pxDescriptor->xDataLength > xExpectedLength ) |
| { |
| pxDescriptor->xDataLength = ( size_t ) xExpectedLength; |
| } |
| } |
| } |
| /*-----------------------------------------------------------*/ |
| configPLACE_IN_SECTION_RAM |
| BaseType_t xGetPhyLinkStatus( void ) |
| { |
| BaseType_t xReturn; |
| |
| if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) == 0 ) |
| { |
| xReturn = pdFALSE; |
| } |
| else |
| { |
| xReturn = pdTRUE; |
| } |
| |
| return xReturn; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| uint32_t ulDataAvailable; |
| |
| configPLACE_IN_SECTION_RAM |
| static BaseType_t prvNetworkInterfaceInput() |
| { |
| BaseType_t xResult = pdFALSE; |
| uint32_t ulStatus; |
| eFrameProcessingResult_t eResult; |
| const TickType_t xDescriptorWaitTime = pdMS_TO_TICKS( 250 ); |
| const UBaseType_t uxMinimumBuffersRemaining = 3UL; |
| uint16_t usLength; |
| NetworkBufferDescriptor_t *pxDescriptor; |
| #if( ipconfigZERO_COPY_RX_DRIVER != 0 ) |
| NetworkBufferDescriptor_t *pxNewDescriptor; |
| #endif /* ipconfigZERO_COPY_RX_DRIVER */ |
| #if( ipconfigUSE_LINKED_RX_MESSAGES == 0 ) |
| IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL }; |
| #endif |
| |
| /* Process each descriptor that is not still in use by the DMA. */ |
| ulStatus = xDMARxDescriptors[ ulNextRxDescriptorToProcess ].STATUS; |
| if( ( ulStatus & RDES_OWN ) == 0 ) |
| { |
| /* Check packet for errors */ |
| if( ( ulStatus & nwRX_STATUS_ERROR_BITS ) != 0 ) |
| { |
| /* There is some reception error. */ |
| intCount[ 3 ]++; |
| /* Clear error bits. */ |
| ulStatus &= ~( ( uint32_t )nwRX_STATUS_ERROR_BITS ); |
| } |
| else |
| { |
| xResult++; |
| |
| eResult = ipCONSIDER_FRAME_FOR_PROCESSING( ( const uint8_t * const ) ( xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD ) ); |
| if( eResult == eProcessBuffer ) |
| { |
| if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) == 0 ) |
| { |
| ulPHYLinkStatus |= PHY_LINK_CONNECTED; |
| FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d (message received)\n", ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != 0 ) ); |
| } |
| |
| #if( ipconfigZERO_COPY_RX_DRIVER != 0 ) |
| if( uxGetNumberOfFreeNetworkBuffers() > uxMinimumBuffersRemaining ) |
| { |
| pxNewDescriptor = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, xDescriptorWaitTime ); |
| } |
| else |
| { |
| /* Too risky to allocate a new Network Buffer. */ |
| pxNewDescriptor = NULL; |
| } |
| if( pxNewDescriptor != NULL ) |
| #else |
| if( uxGetNumberOfFreeNetworkBuffers() > uxMinimumBuffersRemaining ) |
| #endif /* ipconfigZERO_COPY_RX_DRIVER */ |
| { |
| #if( ipconfigZERO_COPY_RX_DRIVER != 0 ) |
| const uint8_t *pucBuffer; |
| #endif |
| |
| /* Get the actual length. */ |
| usLength = RDES_FLMSK( ulStatus ); |
| |
| #if( ipconfigZERO_COPY_RX_DRIVER != 0 ) |
| { |
| /* Replace the character buffer 'B1ADD'. */ |
| pucBuffer = ( const uint8_t * const ) ( xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD ); |
| xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD = ( uint32_t ) pxNewDescriptor->pucEthernetBuffer; |
| |
| /* 'B1ADD' contained the address of a 'pucEthernetBuffer' that |
| belongs to a Network Buffer. Find the original Network Buffer. */ |
| pxDescriptor = pxPacketBuffer_to_NetworkBuffer( pucBuffer ); |
| /* This zero-copy driver makes sure that every 'xDMARxDescriptors' contains |
| a reference to a Network Buffer at any time. |
| In case it runs out of Network Buffers, a DMA buffer won't be replaced, |
| and the received messages is dropped. */ |
| configASSERT( pxDescriptor != NULL ); |
| } |
| #else |
| { |
| /* Create a buffer of exactly the required length. */ |
| pxDescriptor = pxGetNetworkBufferWithDescriptor( usLength, xDescriptorWaitTime ); |
| } |
| #endif /* ipconfigZERO_COPY_RX_DRIVER */ |
| |
| if( pxDescriptor != NULL ) |
| { |
| pxDescriptor->xDataLength = ( size_t ) usLength; |
| #if( ipconfigZERO_COPY_RX_DRIVER == 0 ) |
| { |
| /* Copy the data into the allocated buffer. */ |
| memcpy( ( void * ) pxDescriptor->pucEthernetBuffer, ( void * ) xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD, usLength ); |
| } |
| #endif /* ipconfigZERO_COPY_RX_DRIVER */ |
| /* It is possible that more data was copied than |
| actually makes up the frame. If this is the case |
| adjust the length to remove any trailing bytes. */ |
| prvRemoveTrailingBytes( pxDescriptor ); |
| |
| /* Pass the data to the TCP/IP task for processing. */ |
| xRxEvent.pvData = ( void * ) pxDescriptor; |
| if( xSendEventStructToIPTask( &xRxEvent, xDescriptorWaitTime ) == pdFALSE ) |
| { |
| /* Could not send the descriptor into the TCP/IP |
| stack, it must be released. */ |
| vReleaseNetworkBufferAndDescriptor( pxDescriptor ); |
| } |
| else |
| { |
| iptraceNETWORK_INTERFACE_RECEIVE(); |
| |
| /* The data that was available at the top of this |
| loop has been sent, so is no longer available. */ |
| ulDataAvailable = pdFALSE; |
| } |
| } |
| } |
| } |
| else |
| { |
| /* The packet is discarded as uninteresting. */ |
| ulDataAvailable = pdFALSE; |
| } |
| /* Got here because received data was sent to the IP task or the |
| data contained an error and was discarded. Give the descriptor |
| back to the DMA. */ |
| xDMARxDescriptors[ ulNextRxDescriptorToProcess ].STATUS = ulStatus | RDES_OWN; |
| |
| /* Move onto the next descriptor. */ |
| ulNextRxDescriptorToProcess++; |
| if( ulNextRxDescriptorToProcess >= configNUM_RX_DESCRIPTORS ) |
| { |
| ulNextRxDescriptorToProcess = 0; |
| } |
| |
| ulStatus = xDMARxDescriptors[ ulNextRxDescriptorToProcess ].STATUS; |
| } /* if( ( ulStatus & nwRX_STATUS_ERROR_BITS ) != 0 ) */ |
| } /* if( ( ulStatus & RDES_OWN ) == 0 ) */ |
| |
| /* Restart receive polling. */ |
| LPC_ETHERNET->DMA_REC_POLL_DEMAND = 1; |
| |
| return xResult; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| configPLACE_IN_SECTION_RAM |
| void NETWORK_IRQHandler( void ) |
| { |
| BaseType_t xHigherPriorityTaskWoken = pdFALSE; |
| uint32_t ulDMAStatus; |
| const uint32_t ulRxInterruptMask = |
| DMA_ST_RI | /* Receive interrupt */ |
| DMA_ST_RU; /* Receive buffer unavailable */ |
| const uint32_t ulTxInterruptMask = |
| DMA_ST_TI | /* Transmit interrupt */ |
| DMA_ST_TPS; /* Transmit process stopped */ |
| |
| configASSERT( xRxHanderTask ); |
| |
| /* Get pending interrupts. */ |
| ulDMAStatus = LPC_ETHERNET->DMA_STAT; |
| |
| /* RX group interrupt(s). */ |
| if( ( ulDMAStatus & ulRxInterruptMask ) != 0x00 ) |
| { |
| /* Remember that an RX event has happened. */ |
| ulISREvents |= EMAC_IF_RX_EVENT; |
| vTaskNotifyGiveFromISR( xRxHanderTask, &xHigherPriorityTaskWoken ); |
| intCount[ 0 ]++; |
| } |
| |
| /* TX group interrupt(s). */ |
| if( ( ulDMAStatus & ulTxInterruptMask ) != 0x00 ) |
| { |
| /* Remember that a TX event has happened. */ |
| ulISREvents |= EMAC_IF_TX_EVENT; |
| vTaskNotifyGiveFromISR( xRxHanderTask, &xHigherPriorityTaskWoken ); |
| intCount[ 1 ]++; |
| } |
| |
| /* Test for 'Abnormal interrupt summary'. */ |
| if( ( ulDMAStatus & DMA_ST_AIE ) != 0x00 ) |
| { |
| /* The trace macro must be written such that it can be called from |
| an interrupt. */ |
| iptraceETHERNET_RX_EVENT_LOST(); |
| } |
| |
| /* Clear pending interrupts */ |
| LPC_ETHERNET->DMA_STAT = ulDMAStatus; |
| |
| /* Context switch needed? */ |
| portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); |
| } |
| /*-----------------------------------------------------------*/ |
| |
| static BaseType_t prvSetLinkSpeed( void ) |
| { |
| BaseType_t xReturn = pdFAIL; |
| TickType_t xTimeOnEntering; |
| uint32_t ulPhyStatus; |
| const TickType_t xAutoNegotiateDelay = pdMS_TO_TICKS( 5000UL ); |
| |
| /* Ensure polling does not starve lower priority tasks by temporarily |
| setting the priority of this task to that of the idle task. */ |
| vTaskPrioritySet( NULL, tskIDLE_PRIORITY ); |
| |
| xTimeOnEntering = xTaskGetTickCount(); |
| do |
| { |
| ulPhyStatus = lpcPHYStsPoll(); |
| if( ( ulPhyStatus & PHY_LINK_CONNECTED ) != 0x00 ) |
| { |
| /* Set interface speed and duplex. */ |
| if( ( ulPhyStatus & PHY_LINK_SPEED100 ) != 0x00 ) |
| { |
| Chip_ENET_SetSpeed( LPC_ETHERNET, 1 ); |
| } |
| else |
| { |
| Chip_ENET_SetSpeed( LPC_ETHERNET, 0 ); |
| } |
| |
| if( ( ulPhyStatus & PHY_LINK_FULLDUPLX ) != 0x00 ) |
| { |
| Chip_ENET_SetDuplex( LPC_ETHERNET, true ); |
| } |
| else |
| { |
| Chip_ENET_SetDuplex( LPC_ETHERNET, false ); |
| } |
| |
| xReturn = pdPASS; |
| break; |
| } |
| } while( ( xTaskGetTickCount() - xTimeOnEntering ) < xAutoNegotiateDelay ); |
| |
| /* Reset the priority of this task back to its original value. */ |
| vTaskPrioritySet( NULL, ipconfigIP_TASK_PRIORITY ); |
| |
| return xReturn; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| static uint32_t prvGenerateCRC32( const uint8_t *ucAddress ) |
| { |
| unsigned int j; |
| const uint32_t Polynomial = 0xEDB88320; |
| uint32_t crc = ~0ul; |
| const uint8_t *pucCurrent = ( const uint8_t * ) ucAddress; |
| const uint8_t *pucLast = pucCurrent + 6; |
| |
| /* Calculate normal CRC32 */ |
| while( pucCurrent < pucLast ) |
| { |
| crc ^= *( pucCurrent++ ); |
| for( j = 0; j < 8; j++ ) |
| { |
| if( ( crc & 1 ) != 0 ) |
| { |
| crc = (crc >> 1) ^ Polynomial; |
| } |
| else |
| { |
| crc >>= 1; |
| } |
| } |
| } |
| return ~crc; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| static uint32_t prvGetHashIndex( const uint8_t *ucAddress ) |
| { |
| uint32_t ulCrc = prvGenerateCRC32( ucAddress ); |
| uint32_t ulIndex = 0ul; |
| BaseType_t xCount = 6; |
| |
| /* Take the lowest 6 bits of the CRC32 and reverse them */ |
| while( xCount-- ) |
| { |
| ulIndex <<= 1; |
| ulIndex |= ( ulCrc & 1 ); |
| ulCrc >>= 1; |
| } |
| |
| /* This is the has value of 'ucAddress' */ |
| return ulIndex; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| static void prvAddMACAddress( const uint8_t* ucMacAddress ) |
| { |
| BaseType_t xIndex; |
| |
| xIndex = prvGetHashIndex( ucMacAddress ); |
| if( xIndex >= 32 ) |
| { |
| LPC_ETHERNET->MAC_HASHTABLE_HIGH |= ( 1u << ( xIndex - 32 ) ); |
| } |
| else |
| { |
| LPC_ETHERNET->MAC_HASHTABLE_LOW |= ( 1u << xIndex ); |
| } |
| } |
| /*-----------------------------------------------------------*/ |
| |
| configPLACE_IN_SECTION_RAM |
| static void prvEMACHandlerTask( void *pvParameters ) |
| { |
| TimeOut_t xPhyTime; |
| TickType_t xPhyRemTime; |
| UBaseType_t uxLastMinBufferCount = 0; |
| UBaseType_t uxCurrentCount; |
| BaseType_t xResult = 0; |
| uint32_t ulStatus; |
| const TickType_t xBlockTime = pdMS_TO_TICKS( 5000ul ); |
| |
| /* Remove compiler warning about unused parameter. */ |
| ( void ) pvParameters; |
| |
| /* A possibility to set some additional task properties. */ |
| iptraceEMAC_TASK_STARTING(); |
| |
| vTaskSetTimeOutState( &xPhyTime ); |
| xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); |
| |
| for( ;; ) |
| { |
| uxCurrentCount = uxGetMinimumFreeNetworkBuffers(); |
| if( uxLastMinBufferCount != uxCurrentCount ) |
| { |
| /* The logging produced below may be helpful |
| while tuning +TCP: see how many buffers are in use. */ |
| uxLastMinBufferCount = uxCurrentCount; |
| FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n", |
| uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) ); |
| } |
| |
| #if( ipconfigCHECK_IP_QUEUE_SPACE != 0 ) |
| { |
| static UBaseType_t uxLastMinQueueSpace = 0; |
| |
| uxCurrentCount = uxGetMinimumIPQueueSpace(); |
| if( uxLastMinQueueSpace != uxCurrentCount ) |
| { |
| /* The logging produced below may be helpful |
| while tuning +TCP: see how many buffers are in use. */ |
| uxLastMinQueueSpace = uxCurrentCount; |
| FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) ); |
| } |
| } |
| #endif /* ipconfigCHECK_IP_QUEUE_SPACE */ |
| |
| ulTaskNotifyTake( pdTRUE, xBlockTime ); |
| |
| xResult = ( BaseType_t ) 0; |
| |
| if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 ) |
| { |
| /* Code to release TX buffers if zero-copy is used. */ |
| ulISREvents &= ~EMAC_IF_TX_EVENT; |
| { |
| /* Check if DMA packets have been delivered. */ |
| vClearTXBuffers(); |
| } |
| } |
| |
| if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 ) |
| { |
| ulISREvents &= ~EMAC_IF_RX_EVENT; |
| |
| xResult = prvNetworkInterfaceInput(); |
| if( xResult > 0 ) |
| { |
| while( prvNetworkInterfaceInput() > 0 ) |
| { |
| } |
| } |
| } |
| |
| if( xResult > 0 ) |
| { |
| /* A packet was received. No need to check for the PHY status now, |
| but set a timer to check it later on. */ |
| vTaskSetTimeOutState( &xPhyTime ); |
| xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS ); |
| xResult = 0; |
| } |
| else if( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE ) |
| { |
| ulStatus = lpcPHYStsPoll(); |
| |
| if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != ( ulStatus & PHY_LINK_CONNECTED ) ) |
| { |
| ulPHYLinkStatus = ulStatus; |
| FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d (polled PHY)\n", ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != 0 ) ); |
| } |
| |
| vTaskSetTimeOutState( &xPhyTime ); |
| if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != 0 ) |
| { |
| xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS ); |
| } |
| else |
| { |
| xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS ); |
| } |
| } |
| } |
| } |
| /*-----------------------------------------------------------*/ |