3 Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5 Permission is hereby granted, free of charge, to any person obtaining a copy of
6 this software and associated documentation files (the "Software"), to deal in
7 the Software without restriction, including without limitation the rights to
8 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 the Software, and to permit persons to whom the Software is furnished to do so,
10 subject to the following conditions:
12 The above copyright notice and this permission notice shall be included in all
13 copies or substantial portions of the Software.
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 http://aws.amazon.com/freertos
23 http://www.FreeRTOS.org
26 /* Standard includes. */
31 /* FreeRTOS includes. */
37 /* FreeRTOS+TCP includes. */
38 #include "FreeRTOS_IP.h"
39 #include "FreeRTOS_Sockets.h"
40 #include "FreeRTOS_IP_Private.h"
41 #include "NetworkBufferManagement.h"
42 #include "NetworkInterface.h"
44 /* LPCOpen includes. */
48 /* The size of the stack allocated to the task that handles Rx packets. */
49 #define nwRX_TASK_STACK_SIZE 140
51 #ifndef PHY_LS_HIGH_CHECK_TIME_MS
52 /* Check if the LinkSStatus in the PHY is still high after 15 seconds of not
54 #define PHY_LS_HIGH_CHECK_TIME_MS 15000
57 #ifndef PHY_LS_LOW_CHECK_TIME_MS
58 /* Check if the LinkSStatus in the PHY is still low every second. */
59 #define PHY_LS_LOW_CHECK_TIME_MS 1000
62 #ifndef configUSE_RMII
63 #define configUSE_RMII 1
66 #ifndef configNUM_RX_DESCRIPTORS
67 #error please define configNUM_RX_DESCRIPTORS in your FreeRTOSIPConfig.h
70 #ifndef configNUM_TX_DESCRIPTORS
71 #error please define configNUM_TX_DESCRIPTORS in your FreeRTOSIPConfig.h
74 #ifndef NETWORK_IRQHandler
75 #error NETWORK_IRQHandler must be defined to the name of the function that is installed in the interrupt vector table to handle Ethernet interrupts.
78 #if !defined( MAC_FF_HMC )
79 /* Hash for multicast. */
80 #define MAC_FF_HMC ( 1UL << 2UL )
83 #ifndef iptraceEMAC_TASK_STARTING
84 #define iptraceEMAC_TASK_STARTING() do { } while( 0 )
87 /* Define the bits of .STATUS that indicate a reception error. */
88 #define nwRX_STATUS_ERROR_BITS \
89 ( RDES_CE /* CRC Error */ | \
90 RDES_RE /* Receive Error */ | \
91 RDES_DE /* Descriptor Error */ | \
92 RDES_RWT /* Receive Watchdog Timeout */ | \
93 RDES_LC /* Late Collision */ | \
94 RDES_OE /* Overflow Error */ | \
95 RDES_SAF /* Source Address Filter Fail */ | \
96 RDES_AFM /* Destination Address Filter Fail */ | \
97 RDES_LE /* Length Error */ )
99 /* Define the EMAC status bits that should trigger an interrupt. */
100 #define nwDMA_INTERRUPT_MASK \
101 ( DMA_IE_TIE /* Transmit interrupt enable */ | \
102 DMA_IE_TSE /* Transmit stopped enable */ | \
103 DMA_IE_OVE /* Overflow interrupt enable */ | \
104 DMA_IE_RIE /* Receive interrupt enable */ | \
105 DMA_IE_NIE /* Normal interrupt summary enable */ | \
106 DMA_IE_AIE /* Abnormal interrupt summary enable */ | \
107 DMA_IE_RUE /* Receive buffer unavailable enable */ | \
108 DMA_IE_UNE /* Underflow interrupt enable. */ | \
109 DMA_IE_TJE /* Transmit jabber timeout enable */ | \
110 DMA_IE_RSE /* Received stopped enable */ | \
111 DMA_IE_RWE /* Receive watchdog timeout enable */ | \
112 DMA_IE_FBE )/* Fatal bus error enable */
114 /* Interrupt events to process. Currently only the RX/TX events are processed
115 although code for other events is included to allow for possible future
117 #define EMAC_IF_RX_EVENT 1UL
118 #define EMAC_IF_TX_EVENT 2UL
119 #define EMAC_IF_ERR_EVENT 4UL
120 #define EMAC_IF_ALL_EVENT ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT )
122 /* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet
123 driver will filter incoming packets and only pass the stack those packets it
124 considers need processing. */
125 #if( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 )
126 #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer
128 #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) )
131 #if( ipconfigZERO_COPY_RX_DRIVER == 0 ) || ( ipconfigZERO_COPY_TX_DRIVER == 0 )
132 #warning It is adviced to enable both macros
135 #ifndef configPLACE_IN_SECTION_RAM
136 #define configPLACE_IN_SECTION_RAM
138 #define configPLACE_IN_SECTION_RAM __attribute__ ((section(".ramfunc")))
142 /*-----------------------------------------------------------*/
145 * Delay function passed into the library. The implementation uses FreeRTOS
146 * calls so the scheduler must be started before the driver can be used.
148 static void prvDelay( uint32_t ulMilliSeconds );
151 * Initialises the Tx and Rx descriptors respectively.
153 static void prvSetupTxDescriptors( void );
154 static void prvSetupRxDescriptors( void );
157 * A task that processes received frames.
159 static void prvEMACHandlerTask( void *pvParameters );
162 * Sets up the MAC with the results of an auto-negotiation.
164 static BaseType_t prvSetLinkSpeed( void );
167 * Generates a CRC for a MAC address that is then used to generate a hash index.
169 static uint32_t prvGenerateCRC32( const uint8_t *ucAddress );
172 * Generates a hash index when setting a filter to permit a MAC address.
174 static uint32_t prvGetHashIndex( const uint8_t *ucAddress );
177 * Update the hash table to allow a MAC address.
179 static void prvAddMACAddress( const uint8_t* ucMacAddress );
182 * Sometimes the DMA will report received data as being longer than the actual
183 * received from length. This function checks the reported length and corrects
186 static void prvRemoveTrailingBytes( NetworkBufferDescriptor_t *pxDescriptor );
188 /*-----------------------------------------------------------*/
190 /* Bit map of outstanding ETH interrupt events for processing. Currently only
191 the Rx and Tx interrupt is handled, although code is included for other events
192 to enable future expansion. */
193 static volatile uint32_t ulISREvents;
195 /* A copy of PHY register 1: 'PHY_REG_01_BMSR' */
196 static uint32_t ulPHYLinkStatus = 0;
198 /* Tx descriptors and index. */
199 static ENET_ENHTXDESC_T xDMATxDescriptors[ configNUM_TX_DESCRIPTORS ];
201 /* ulNextFreeTxDescriptor is declared volatile, because it is accessed from
202 to different tasks. */
203 static volatile uint32_t ulNextFreeTxDescriptor;
204 static uint32_t ulTxDescriptorToClear;
206 /* Rx descriptors and index. */
207 static ENET_ENHRXDESC_T xDMARxDescriptors[ configNUM_RX_DESCRIPTORS ];
208 static uint32_t ulNextRxDescriptorToProcess;
210 /* Must be defined externally - the demo applications define this in main.c. */
211 extern uint8_t ucMACAddress[ 6 ];
213 /* The handle of the task that processes Rx packets. The handle is required so
214 the task can be notified when new packets arrive. */
215 static TaskHandle_t xRxHanderTask = NULL;
217 #if( ipconfigUSE_LLMNR == 1 )
218 static const uint8_t xLLMNR_MACAddress[] = { '\x01', '\x00', '\x5E', '\x00', '\x00', '\xFC' };
219 #endif /* ipconfigUSE_LLMNR == 1 */
221 /* xTXDescriptorSemaphore is a counting semaphore with
222 a maximum count of ETH_TXBUFNB, which is the number of
223 DMA TX descriptors. */
224 static SemaphoreHandle_t xTXDescriptorSemaphore = NULL;
226 /*-----------------------------------------------------------*/
229 BaseType_t xNetworkInterfaceInitialise( void )
231 BaseType_t xReturn = pdPASS;
233 /* The interrupt will be turned on when a link is established. */
234 NVIC_DisableIRQ( ETHERNET_IRQn );
236 /* Disable receive and transmit DMA processes. */
237 LPC_ETHERNET->DMA_OP_MODE &= ~( DMA_OM_ST | DMA_OM_SR );
239 /* Disable packet reception. */
240 LPC_ETHERNET->MAC_CONFIG &= ~( MAC_CFG_RE | MAC_CFG_TE );
242 /* Call the LPCOpen function to initialise the hardware. */
243 Chip_ENET_Init( LPC_ETHERNET );
245 /* Save MAC address. */
246 Chip_ENET_SetADDR( LPC_ETHERNET, ucMACAddress );
248 /* Clear all MAC address hash entries. */
249 LPC_ETHERNET->MAC_HASHTABLE_HIGH = 0;
250 LPC_ETHERNET->MAC_HASHTABLE_LOW = 0;
252 #if( ipconfigUSE_LLMNR == 1 )
254 prvAddMACAddress( xLLMNR_MACAddress );
256 #endif /* ipconfigUSE_LLMNR == 1 */
258 /* Promiscuous flag (PR) and Receive All flag (RA) set to zero. The
259 registers MAC_HASHTABLE_[LOW|HIGH] will be loaded to allow certain
260 multi-cast addresses. */
261 LPC_ETHERNET->MAC_FRAME_FILTER = MAC_FF_HMC;
263 #if( configUSE_RMII == 1 )
265 if( lpc_phy_init( pdTRUE, prvDelay ) != SUCCESS )
272 #warning This path has not been tested.
273 if( lpc_phy_init( pdFALSE, prvDelay ) != SUCCESS )
280 if( xReturn == pdPASS )
282 /* Guard against the task being created more than once and the
283 descriptors being initialised more than once. */
284 if( xRxHanderTask == NULL )
286 xReturn = xTaskCreate( prvEMACHandlerTask, "EMAC", nwRX_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xRxHanderTask );
287 configASSERT( xReturn );
290 if( xTXDescriptorSemaphore == NULL )
292 /* Create a counting semaphore, with a value of 'configNUM_TX_DESCRIPTORS'
293 and a maximum of 'configNUM_TX_DESCRIPTORS'. */
294 xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) configNUM_TX_DESCRIPTORS, ( UBaseType_t ) configNUM_TX_DESCRIPTORS );
295 configASSERT( xTXDescriptorSemaphore );
298 /* Enable MAC interrupts. */
299 LPC_ETHERNET->DMA_INT_EN = nwDMA_INTERRUPT_MASK;
302 if( xReturn != pdFAIL )
304 /* Auto-negotiate was already started. Wait for it to complete. */
305 xReturn = prvSetLinkSpeed();
307 if( xReturn == pdPASS )
309 /* Initialise the descriptors. */
310 prvSetupTxDescriptors();
311 prvSetupRxDescriptors();
313 /* Clear all interrupts. */
314 LPC_ETHERNET->DMA_STAT = DMA_ST_ALL;
316 /* Enable receive and transmit DMA processes. */
317 LPC_ETHERNET->DMA_OP_MODE |= DMA_OM_ST | DMA_OM_SR;
319 /* Set Receiver / Transmitter Enable. */
320 LPC_ETHERNET->MAC_CONFIG |= MAC_CFG_RE | MAC_CFG_TE;
322 /* Start receive polling. */
323 LPC_ETHERNET->DMA_REC_POLL_DEMAND = 1;
325 /* Enable interrupts in the NVIC. */
326 NVIC_SetPriority( ETHERNET_IRQn, configMAC_INTERRUPT_PRIORITY );
327 NVIC_EnableIRQ( ETHERNET_IRQn );
333 /*-----------------------------------------------------------*/
335 #define niBUFFER_1_PACKET_SIZE 1536
337 static __attribute__ ((section("._ramAHB32"))) uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ] __attribute__ ( ( aligned( 32 ) ) );
339 void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
342 uint8_t *ucRAMBuffer = ucNetworkPackets;
345 for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ )
347 pxNetworkBuffers[ ul ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING;
348 *( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ul ] ) );
349 ucRAMBuffer += niBUFFER_1_PACKET_SIZE;
352 /*-----------------------------------------------------------*/
354 configPLACE_IN_SECTION_RAM
355 static void vClearTXBuffers()
357 uint32_t ulLastDescriptor = ulNextFreeTxDescriptor;
358 size_t uxCount = ( ( size_t ) configNUM_TX_DESCRIPTORS ) - uxSemaphoreGetCount( xTXDescriptorSemaphore );
359 #if( ipconfigZERO_COPY_TX_DRIVER != 0 )
360 NetworkBufferDescriptor_t *pxNetworkBuffer;
364 /* This function is called after a TX-completion interrupt.
365 It will release each Network Buffer used in xNetworkInterfaceOutput().
366 'uxCount' represents the number of descriptors given to DMA for transmission.
367 After sending a packet, the DMA will clear the 'TDES_OWN' bit. */
368 while( ( uxCount > ( size_t ) 0u ) && ( ( xDMATxDescriptors[ ulTxDescriptorToClear ].CTRLSTAT & TDES_OWN ) == 0 ) )
370 if( ( ulTxDescriptorToClear == ulLastDescriptor ) && ( uxCount != ( size_t ) configNUM_TX_DESCRIPTORS ) )
376 #if( ipconfigZERO_COPY_TX_DRIVER != 0 )
378 ucPayLoad = ( uint8_t * )xDMATxDescriptors[ ulTxDescriptorToClear ].B1ADD;
379 if( ucPayLoad != NULL )
381 /* B1ADD points to a pucEthernetBuffer of a Network Buffer descriptor. */
382 pxNetworkBuffer = pxPacketBuffer_to_NetworkBuffer( ucPayLoad );
384 configASSERT( pxNetworkBuffer != NULL );
386 vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ) ;
387 xDMATxDescriptors[ ulTxDescriptorToClear ].B1ADD = ( uint32_t )0u;
390 #endif /* ipconfigZERO_COPY_TX_DRIVER */
392 /* Move onto the next descriptor, wrapping if necessary. */
393 ulTxDescriptorToClear++;
394 if( ulTxDescriptorToClear >= configNUM_TX_DESCRIPTORS )
396 ulTxDescriptorToClear = 0;
400 /* Tell the counting semaphore that one more TX descriptor is available. */
401 xSemaphoreGive( xTXDescriptorSemaphore );
405 /*-----------------------------------------------------------*/
407 configPLACE_IN_SECTION_RAM
408 BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, BaseType_t bReleaseAfterSend )
410 BaseType_t xReturn = pdFAIL;
411 const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50 );
413 /* Attempt to obtain access to a Tx descriptor. */
416 if( xTXDescriptorSemaphore == NULL )
420 if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS )
422 /* Time-out waiting for a free TX descriptor. */
426 /* If the descriptor is still owned by the DMA it can't be used. */
427 if( ( xDMATxDescriptors[ ulNextFreeTxDescriptor ].CTRLSTAT & TDES_OWN ) != 0 )
429 /* The semaphore was taken, the TX DMA-descriptor is still not available.
430 Actually that should not occur, the 'TDES_OWN' was already confirmed low in vClearTXBuffers(). */
431 xSemaphoreGive( xTXDescriptorSemaphore );
435 #if( ipconfigZERO_COPY_TX_DRIVER != 0 )
437 /* bReleaseAfterSend should always be set when using the zero
439 configASSERT( bReleaseAfterSend != pdFALSE );
441 /* The DMA's descriptor to point directly to the data in the
442 network buffer descriptor. The data is not copied. */
443 xDMATxDescriptors[ ulNextFreeTxDescriptor ].B1ADD = ( uint32_t ) pxDescriptor->pucEthernetBuffer;
445 /* The DMA descriptor will 'own' this Network Buffer,
446 until it has been sent. So don't release it now. */
447 bReleaseAfterSend = false;
451 /* The data is copied from the network buffer descriptor into
452 the DMA's descriptor. */
453 memcpy( ( void * ) xDMATxDescriptors[ ulNextFreeTxDescriptor ].B1ADD, ( void * ) pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength );
457 xDMATxDescriptors[ ulNextFreeTxDescriptor ].BSIZE = ( uint32_t ) TDES_ENH_BS1( pxDescriptor->xDataLength );
459 /* This descriptor is given back to the DMA. */
460 xDMATxDescriptors[ ulNextFreeTxDescriptor ].CTRLSTAT |= TDES_OWN;
462 /* Ensure the DMA is polling Tx descriptors. */
463 LPC_ETHERNET->DMA_TRANS_POLL_DEMAND = 1;
465 iptraceNETWORK_INTERFACE_TRANSMIT();
467 /* Move onto the next descriptor, wrapping if necessary. */
468 ulNextFreeTxDescriptor++;
469 if( ulNextFreeTxDescriptor >= configNUM_TX_DESCRIPTORS )
471 ulNextFreeTxDescriptor = 0;
474 /* The Tx has been initiated. */
479 /* The buffer has been sent so can be released. */
480 if( bReleaseAfterSend != pdFALSE )
482 vReleaseNetworkBufferAndDescriptor( pxDescriptor );
487 /*-----------------------------------------------------------*/
489 static void prvDelay( uint32_t ulMilliSeconds )
491 /* Ensure the scheduler was started before attempting to use the scheduler to
493 configASSERT( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING );
495 vTaskDelay( pdMS_TO_TICKS( ulMilliSeconds ) );
497 /*-----------------------------------------------------------*/
499 static void prvSetupTxDescriptors( void )
503 /* Start with Tx descriptors clear. */
504 memset( ( void * ) xDMATxDescriptors, 0, sizeof( xDMATxDescriptors ) );
506 /* Index to the next Tx descriptor to use. */
507 ulNextFreeTxDescriptor = 0ul;
509 /* Index to the next Tx descriptor to clear ( after transmission ). */
510 ulTxDescriptorToClear = 0ul;
512 for( x = 0; x < configNUM_TX_DESCRIPTORS; x++ )
514 #if( ipconfigZERO_COPY_TX_DRIVER != 0 )
516 /* Nothing to do, B1ADD will be set when data is ready to transmit.
517 Currently the memset above will have set it to NULL. */
521 /* Allocate a buffer to the Tx descriptor. This is the most basic
522 way of creating a driver as the data is then copied into the
524 xDMATxDescriptors[ x ].B1ADD = ( uint32_t ) pvPortMalloc( ipTOTAL_ETHERNET_FRAME_SIZE );
526 /* Use an assert to check the allocation as +TCP applications will
527 often not use a malloc() failed hook as the TCP stack will recover
528 from allocation failures. */
529 configASSERT( xDMATxDescriptors[ x ].B1ADD );
533 /* Buffers hold an entire frame so all buffers are both the start and
535 /* TDES_ENH_TCH Second Address Chained. */
536 /* TDES_ENH_CIC(n) Checksum Insertion Control, tried but it does not work for the LPC18xx... */
537 /* TDES_ENH_FS First Segment. */
538 /* TDES_ENH_LS Last Segment. */
539 /* TDES_ENH_IC Interrupt on Completion. */
540 xDMATxDescriptors[ x ].CTRLSTAT = TDES_ENH_TCH | TDES_ENH_CIC( 3 ) | TDES_ENH_FS | TDES_ENH_LS | TDES_ENH_IC;
541 xDMATxDescriptors[ x ].B2ADD = ( uint32_t ) &xDMATxDescriptors[ x + 1 ];
544 xDMATxDescriptors[ configNUM_TX_DESCRIPTORS - 1 ].CTRLSTAT |= TDES_ENH_TER;
545 xDMATxDescriptors[ configNUM_TX_DESCRIPTORS - 1 ].B2ADD = ( uint32_t ) &xDMATxDescriptors[ 0 ];
547 /* Point the DMA to the base of the descriptor list. */
548 LPC_ETHERNET->DMA_TRANS_DES_ADDR = ( uint32_t ) xDMATxDescriptors;
550 /*-----------------------------------------------------------*/
552 static void prvSetupRxDescriptors( void )
555 #if( ipconfigZERO_COPY_RX_DRIVER != 0 )
556 NetworkBufferDescriptor_t *pxNetworkBuffer;
559 /* Index to the next Rx descriptor to use. */
560 ulNextRxDescriptorToProcess = 0;
562 /* Clear RX descriptor list. */
563 memset( ( void * ) xDMARxDescriptors, 0, sizeof( xDMARxDescriptors ) );
565 for( x = 0; x < configNUM_RX_DESCRIPTORS; x++ )
567 /* Allocate a buffer of the largest possible frame size as it is not
568 known what size received frames will be. */
570 #if( ipconfigZERO_COPY_RX_DRIVER != 0 )
572 pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, 0 );
574 /* During start-up there should be enough Network Buffers available,
575 so it is safe to use configASSERT().
576 In case this assert fails, please check: configNUM_RX_DESCRIPTORS,
577 ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS, and in case BufferAllocation_2.c
578 is included, check the amount of available heap. */
579 configASSERT( pxNetworkBuffer != NULL );
581 /* Pass the actual buffer to DMA. */
582 xDMARxDescriptors[ x ].B1ADD = ( uint32_t ) pxNetworkBuffer->pucEthernetBuffer;
586 /* All DMA descriptors are populated with permanent memory blocks.
587 Their contents will be copy to Network Buffers. */
588 xDMARxDescriptors[ x ].B1ADD = ( uint32_t ) pvPortMalloc( ipTOTAL_ETHERNET_FRAME_SIZE );
590 #endif /* ipconfigZERO_COPY_RX_DRIVER */
592 /* Use an assert to check the allocation as +TCP applications will often
593 not use a malloc failed hook as the TCP stack will recover from
594 allocation failures. */
595 configASSERT( xDMARxDescriptors[ x ].B1ADD );
597 xDMARxDescriptors[ x ].B2ADD = ( uint32_t ) &( xDMARxDescriptors[ x + 1 ] );
598 xDMARxDescriptors[ x ].CTRL = ( uint32_t ) RDES_ENH_BS1( ipTOTAL_ETHERNET_FRAME_SIZE ) | RDES_ENH_RCH;
600 /* The descriptor is available for use by the DMA. */
601 xDMARxDescriptors[ x ].STATUS = RDES_OWN;
604 /* RDES_ENH_RER Receive End of Ring. */
605 xDMARxDescriptors[ ( configNUM_RX_DESCRIPTORS - 1 ) ].CTRL |= RDES_ENH_RER;
606 xDMARxDescriptors[ configNUM_RX_DESCRIPTORS - 1 ].B2ADD = ( uint32_t ) &( xDMARxDescriptors[ 0 ] );
608 /* Point the DMA to the base of the descriptor list. */
609 LPC_ETHERNET->DMA_REC_DES_ADDR = ( uint32_t ) xDMARxDescriptors;
611 /*-----------------------------------------------------------*/
612 configPLACE_IN_SECTION_RAM
613 static void prvRemoveTrailingBytes( NetworkBufferDescriptor_t *pxDescriptor )
615 size_t xExpectedLength;
616 IPPacket_t *pxIPPacket;
618 pxIPPacket = ( IPPacket_t * ) pxDescriptor->pucEthernetBuffer;
619 /* Look at the actual length of the packet, translate it to a host-endial notation. */
620 xExpectedLength = sizeof( EthernetHeader_t ) + ( size_t ) FreeRTOS_htons( pxIPPacket->xIPHeader.usLength );
622 if( xExpectedLength == ( pxDescriptor->xDataLength + 4 ) )
624 pxDescriptor->xDataLength -= 4;
628 if( pxDescriptor->xDataLength > xExpectedLength )
630 pxDescriptor->xDataLength = ( size_t ) xExpectedLength;
634 /*-----------------------------------------------------------*/
635 configPLACE_IN_SECTION_RAM
636 BaseType_t xGetPhyLinkStatus( void )
640 if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) == 0 )
651 /*-----------------------------------------------------------*/
653 uint32_t ulDataAvailable;
655 configPLACE_IN_SECTION_RAM
656 static BaseType_t prvNetworkInterfaceInput()
658 BaseType_t xResult = pdFALSE;
660 eFrameProcessingResult_t eResult;
661 const TickType_t xDescriptorWaitTime = pdMS_TO_TICKS( 250 );
662 const UBaseType_t uxMinimumBuffersRemaining = 3UL;
664 NetworkBufferDescriptor_t *pxDescriptor;
665 #if( ipconfigZERO_COPY_RX_DRIVER != 0 )
666 NetworkBufferDescriptor_t *pxNewDescriptor;
667 #endif /* ipconfigZERO_COPY_RX_DRIVER */
668 #if( ipconfigUSE_LINKED_RX_MESSAGES == 0 )
669 IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL };
672 /* Process each descriptor that is not still in use by the DMA. */
673 ulStatus = xDMARxDescriptors[ ulNextRxDescriptorToProcess ].STATUS;
674 if( ( ulStatus & RDES_OWN ) == 0 )
676 /* Check packet for errors */
677 if( ( ulStatus & nwRX_STATUS_ERROR_BITS ) != 0 )
679 /* There is some reception error. */
681 /* Clear error bits. */
682 ulStatus &= ~( ( uint32_t )nwRX_STATUS_ERROR_BITS );
688 eResult = ipCONSIDER_FRAME_FOR_PROCESSING( ( const uint8_t * const ) ( xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD ) );
689 if( eResult == eProcessBuffer )
691 if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) == 0 )
693 ulPHYLinkStatus |= PHY_LINK_CONNECTED;
694 FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d (message received)\n", ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != 0 ) );
697 #if( ipconfigZERO_COPY_RX_DRIVER != 0 )
698 if( uxGetNumberOfFreeNetworkBuffers() > uxMinimumBuffersRemaining )
700 pxNewDescriptor = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, xDescriptorWaitTime );
704 /* Too risky to allocate a new Network Buffer. */
705 pxNewDescriptor = NULL;
707 if( pxNewDescriptor != NULL )
709 if( uxGetNumberOfFreeNetworkBuffers() > uxMinimumBuffersRemaining )
710 #endif /* ipconfigZERO_COPY_RX_DRIVER */
712 #if( ipconfigZERO_COPY_RX_DRIVER != 0 )
713 const uint8_t *pucBuffer;
716 /* Get the actual length. */
717 usLength = RDES_FLMSK( ulStatus );
719 #if( ipconfigZERO_COPY_RX_DRIVER != 0 )
721 /* Replace the character buffer 'B1ADD'. */
722 pucBuffer = ( const uint8_t * const ) ( xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD );
723 xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD = ( uint32_t ) pxNewDescriptor->pucEthernetBuffer;
725 /* 'B1ADD' contained the address of a 'pucEthernetBuffer' that
726 belongs to a Network Buffer. Find the original Network Buffer. */
727 pxDescriptor = pxPacketBuffer_to_NetworkBuffer( pucBuffer );
728 /* This zero-copy driver makes sure that every 'xDMARxDescriptors' contains
729 a reference to a Network Buffer at any time.
730 In case it runs out of Network Buffers, a DMA buffer won't be replaced,
731 and the received messages is dropped. */
732 configASSERT( pxDescriptor != NULL );
736 /* Create a buffer of exactly the required length. */
737 pxDescriptor = pxGetNetworkBufferWithDescriptor( usLength, xDescriptorWaitTime );
739 #endif /* ipconfigZERO_COPY_RX_DRIVER */
741 if( pxDescriptor != NULL )
743 pxDescriptor->xDataLength = ( size_t ) usLength;
744 #if( ipconfigZERO_COPY_RX_DRIVER == 0 )
746 /* Copy the data into the allocated buffer. */
747 memcpy( ( void * ) pxDescriptor->pucEthernetBuffer, ( void * ) xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD, usLength );
749 #endif /* ipconfigZERO_COPY_RX_DRIVER */
750 /* It is possible that more data was copied than
751 actually makes up the frame. If this is the case
752 adjust the length to remove any trailing bytes. */
753 prvRemoveTrailingBytes( pxDescriptor );
755 /* Pass the data to the TCP/IP task for processing. */
756 xRxEvent.pvData = ( void * ) pxDescriptor;
757 if( xSendEventStructToIPTask( &xRxEvent, xDescriptorWaitTime ) == pdFALSE )
759 /* Could not send the descriptor into the TCP/IP
760 stack, it must be released. */
761 vReleaseNetworkBufferAndDescriptor( pxDescriptor );
765 iptraceNETWORK_INTERFACE_RECEIVE();
767 /* The data that was available at the top of this
768 loop has been sent, so is no longer available. */
769 ulDataAvailable = pdFALSE;
776 /* The packet is discarded as uninteresting. */
777 ulDataAvailable = pdFALSE;
779 /* Got here because received data was sent to the IP task or the
780 data contained an error and was discarded. Give the descriptor
782 xDMARxDescriptors[ ulNextRxDescriptorToProcess ].STATUS = ulStatus | RDES_OWN;
784 /* Move onto the next descriptor. */
785 ulNextRxDescriptorToProcess++;
786 if( ulNextRxDescriptorToProcess >= configNUM_RX_DESCRIPTORS )
788 ulNextRxDescriptorToProcess = 0;
791 ulStatus = xDMARxDescriptors[ ulNextRxDescriptorToProcess ].STATUS;
792 } /* if( ( ulStatus & nwRX_STATUS_ERROR_BITS ) != 0 ) */
793 } /* if( ( ulStatus & RDES_OWN ) == 0 ) */
795 /* Restart receive polling. */
796 LPC_ETHERNET->DMA_REC_POLL_DEMAND = 1;
800 /*-----------------------------------------------------------*/
802 configPLACE_IN_SECTION_RAM
803 void NETWORK_IRQHandler( void )
805 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
806 uint32_t ulDMAStatus;
807 const uint32_t ulRxInterruptMask =
808 DMA_ST_RI | /* Receive interrupt */
809 DMA_ST_RU; /* Receive buffer unavailable */
810 const uint32_t ulTxInterruptMask =
811 DMA_ST_TI | /* Transmit interrupt */
812 DMA_ST_TPS; /* Transmit process stopped */
814 configASSERT( xRxHanderTask );
816 /* Get pending interrupts. */
817 ulDMAStatus = LPC_ETHERNET->DMA_STAT;
819 /* RX group interrupt(s). */
820 if( ( ulDMAStatus & ulRxInterruptMask ) != 0x00 )
822 /* Remember that an RX event has happened. */
823 ulISREvents |= EMAC_IF_RX_EVENT;
824 vTaskNotifyGiveFromISR( xRxHanderTask, &xHigherPriorityTaskWoken );
828 /* TX group interrupt(s). */
829 if( ( ulDMAStatus & ulTxInterruptMask ) != 0x00 )
831 /* Remember that a TX event has happened. */
832 ulISREvents |= EMAC_IF_TX_EVENT;
833 vTaskNotifyGiveFromISR( xRxHanderTask, &xHigherPriorityTaskWoken );
837 /* Test for 'Abnormal interrupt summary'. */
838 if( ( ulDMAStatus & DMA_ST_AIE ) != 0x00 )
840 /* The trace macro must be written such that it can be called from
842 iptraceETHERNET_RX_EVENT_LOST();
845 /* Clear pending interrupts */
846 LPC_ETHERNET->DMA_STAT = ulDMAStatus;
848 /* Context switch needed? */
849 portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
851 /*-----------------------------------------------------------*/
853 static BaseType_t prvSetLinkSpeed( void )
855 BaseType_t xReturn = pdFAIL;
856 TickType_t xTimeOnEntering;
857 uint32_t ulPhyStatus;
858 const TickType_t xAutoNegotiateDelay = pdMS_TO_TICKS( 5000UL );
860 /* Ensure polling does not starve lower priority tasks by temporarily
861 setting the priority of this task to that of the idle task. */
862 vTaskPrioritySet( NULL, tskIDLE_PRIORITY );
864 xTimeOnEntering = xTaskGetTickCount();
867 ulPhyStatus = lpcPHYStsPoll();
868 if( ( ulPhyStatus & PHY_LINK_CONNECTED ) != 0x00 )
870 /* Set interface speed and duplex. */
871 if( ( ulPhyStatus & PHY_LINK_SPEED100 ) != 0x00 )
873 Chip_ENET_SetSpeed( LPC_ETHERNET, 1 );
877 Chip_ENET_SetSpeed( LPC_ETHERNET, 0 );
880 if( ( ulPhyStatus & PHY_LINK_FULLDUPLX ) != 0x00 )
882 Chip_ENET_SetDuplex( LPC_ETHERNET, true );
886 Chip_ENET_SetDuplex( LPC_ETHERNET, false );
892 } while( ( xTaskGetTickCount() - xTimeOnEntering ) < xAutoNegotiateDelay );
894 /* Reset the priority of this task back to its original value. */
895 vTaskPrioritySet( NULL, ipconfigIP_TASK_PRIORITY );
899 /*-----------------------------------------------------------*/
901 static uint32_t prvGenerateCRC32( const uint8_t *ucAddress )
904 const uint32_t Polynomial = 0xEDB88320;
906 const uint8_t *pucCurrent = ( const uint8_t * ) ucAddress;
907 const uint8_t *pucLast = pucCurrent + 6;
909 /* Calculate normal CRC32 */
910 while( pucCurrent < pucLast )
912 crc ^= *( pucCurrent++ );
913 for( j = 0; j < 8; j++ )
915 if( ( crc & 1 ) != 0 )
917 crc = (crc >> 1) ^ Polynomial;
927 /*-----------------------------------------------------------*/
929 static uint32_t prvGetHashIndex( const uint8_t *ucAddress )
931 uint32_t ulCrc = prvGenerateCRC32( ucAddress );
932 uint32_t ulIndex = 0ul;
933 BaseType_t xCount = 6;
935 /* Take the lowest 6 bits of the CRC32 and reverse them */
939 ulIndex |= ( ulCrc & 1 );
943 /* This is the has value of 'ucAddress' */
946 /*-----------------------------------------------------------*/
948 static void prvAddMACAddress( const uint8_t* ucMacAddress )
952 xIndex = prvGetHashIndex( ucMacAddress );
955 LPC_ETHERNET->MAC_HASHTABLE_HIGH |= ( 1u << ( xIndex - 32 ) );
959 LPC_ETHERNET->MAC_HASHTABLE_LOW |= ( 1u << xIndex );
962 /*-----------------------------------------------------------*/
964 configPLACE_IN_SECTION_RAM
965 static void prvEMACHandlerTask( void *pvParameters )
968 TickType_t xPhyRemTime;
969 UBaseType_t uxLastMinBufferCount = 0;
970 UBaseType_t uxCurrentCount;
971 BaseType_t xResult = 0;
973 const TickType_t xBlockTime = pdMS_TO_TICKS( 5000ul );
975 /* Remove compiler warning about unused parameter. */
976 ( void ) pvParameters;
978 /* A possibility to set some additional task properties. */
979 iptraceEMAC_TASK_STARTING();
981 vTaskSetTimeOutState( &xPhyTime );
982 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );
986 uxCurrentCount = uxGetMinimumFreeNetworkBuffers();
987 if( uxLastMinBufferCount != uxCurrentCount )
989 /* The logging produced below may be helpful
990 while tuning +TCP: see how many buffers are in use. */
991 uxLastMinBufferCount = uxCurrentCount;
992 FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n",
993 uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) );
996 #if( ipconfigCHECK_IP_QUEUE_SPACE != 0 )
998 static UBaseType_t uxLastMinQueueSpace = 0;
1000 uxCurrentCount = uxGetMinimumIPQueueSpace();
1001 if( uxLastMinQueueSpace != uxCurrentCount )
1003 /* The logging produced below may be helpful
1004 while tuning +TCP: see how many buffers are in use. */
1005 uxLastMinQueueSpace = uxCurrentCount;
1006 FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) );
1009 #endif /* ipconfigCHECK_IP_QUEUE_SPACE */
1011 ulTaskNotifyTake( pdTRUE, xBlockTime );
1013 xResult = ( BaseType_t ) 0;
1015 if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 )
1017 /* Code to release TX buffers if zero-copy is used. */
1018 ulISREvents &= ~EMAC_IF_TX_EVENT;
1020 /* Check if DMA packets have been delivered. */
1025 if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 )
1027 ulISREvents &= ~EMAC_IF_RX_EVENT;
1029 xResult = prvNetworkInterfaceInput();
1032 while( prvNetworkInterfaceInput() > 0 )
1040 /* A packet was received. No need to check for the PHY status now,
1041 but set a timer to check it later on. */
1042 vTaskSetTimeOutState( &xPhyTime );
1043 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );
1046 else if( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE )
1048 ulStatus = lpcPHYStsPoll();
1050 if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != ( ulStatus & PHY_LINK_CONNECTED ) )
1052 ulPHYLinkStatus = ulStatus;
1053 FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d (polled PHY)\n", ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != 0 ) );
1056 vTaskSetTimeOutState( &xPhyTime );
1057 if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != 0 )
1059 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );
1063 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );
1068 /*-----------------------------------------------------------*/