2 * FreeRTOS+TCP V2.0.1
\r
3 * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
\r
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
\r
6 * this software and associated documentation files (the "Software"), to deal in
\r
7 * the Software without restriction, including without limitation the rights to
\r
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
\r
9 * the Software, and to permit persons to whom the Software is furnished to do so,
\r
10 * subject to the following conditions:
\r
12 * The above copyright notice and this permission notice shall be included in all
\r
13 * copies or substantial portions of the Software.
\r
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
\r
17 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
\r
18 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
\r
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
22 * http://www.FreeRTOS.org
\r
23 * http://aws.amazon.com/freertos
\r
25 * 1 tab == 4 spaces!
\r
28 /* Standard includes. */
\r
33 /* FreeRTOS includes. */
\r
34 #include "FreeRTOS.h"
\r
39 /* FreeRTOS+TCP includes. */
\r
40 #include "FreeRTOS_IP.h"
\r
41 #include "FreeRTOS_Sockets.h"
\r
42 #include "FreeRTOS_IP_Private.h"
\r
43 #include "NetworkBufferManagement.h"
\r
44 #include "NetworkInterface.h"
\r
46 /* LPCOpen includes. */
\r
48 #include "lpc_phy.h"
\r
50 /* The size of the stack allocated to the task that handles Rx packets. */
\r
51 #define nwRX_TASK_STACK_SIZE 140
\r
53 #ifndef PHY_LS_HIGH_CHECK_TIME_MS
\r
54 /* Check if the LinkSStatus in the PHY is still high after 15 seconds of not
\r
55 receiving packets. */
\r
56 #define PHY_LS_HIGH_CHECK_TIME_MS 15000
\r
59 #ifndef PHY_LS_LOW_CHECK_TIME_MS
\r
60 /* Check if the LinkSStatus in the PHY is still low every second. */
\r
61 #define PHY_LS_LOW_CHECK_TIME_MS 1000
\r
64 #ifndef configUSE_RMII
\r
65 #define configUSE_RMII 1
\r
68 #ifndef configNUM_RX_DESCRIPTORS
\r
69 #error please define configNUM_RX_DESCRIPTORS in your FreeRTOSIPConfig.h
\r
72 #ifndef configNUM_TX_DESCRIPTORS
\r
73 #error please define configNUM_TX_DESCRIPTORS in your FreeRTOSIPConfig.h
\r
76 #ifndef NETWORK_IRQHandler
\r
77 #error NETWORK_IRQHandler must be defined to the name of the function that is installed in the interrupt vector table to handle Ethernet interrupts.
\r
80 #if !defined( MAC_FF_HMC )
\r
81 /* Hash for multicast. */
\r
82 #define MAC_FF_HMC ( 1UL << 2UL )
\r
85 #ifndef iptraceEMAC_TASK_STARTING
\r
86 #define iptraceEMAC_TASK_STARTING() do { } while( 0 )
\r
89 /* Define the bits of .STATUS that indicate a reception error. */
\r
90 #define nwRX_STATUS_ERROR_BITS \
\r
91 ( RDES_CE /* CRC Error */ | \
\r
92 RDES_RE /* Receive Error */ | \
\r
93 RDES_DE /* Descriptor Error */ | \
\r
94 RDES_RWT /* Receive Watchdog Timeout */ | \
\r
95 RDES_LC /* Late Collision */ | \
\r
96 RDES_OE /* Overflow Error */ | \
\r
97 RDES_SAF /* Source Address Filter Fail */ | \
\r
98 RDES_AFM /* Destination Address Filter Fail */ | \
\r
99 RDES_LE /* Length Error */ )
\r
101 /* Define the EMAC status bits that should trigger an interrupt. */
\r
102 #define nwDMA_INTERRUPT_MASK \
\r
103 ( DMA_IE_TIE /* Transmit interrupt enable */ | \
\r
104 DMA_IE_TSE /* Transmit stopped enable */ | \
\r
105 DMA_IE_OVE /* Overflow interrupt enable */ | \
\r
106 DMA_IE_RIE /* Receive interrupt enable */ | \
\r
107 DMA_IE_NIE /* Normal interrupt summary enable */ | \
\r
108 DMA_IE_AIE /* Abnormal interrupt summary enable */ | \
\r
109 DMA_IE_RUE /* Receive buffer unavailable enable */ | \
\r
110 DMA_IE_UNE /* Underflow interrupt enable. */ | \
\r
111 DMA_IE_TJE /* Transmit jabber timeout enable */ | \
\r
112 DMA_IE_RSE /* Received stopped enable */ | \
\r
113 DMA_IE_RWE /* Receive watchdog timeout enable */ | \
\r
114 DMA_IE_FBE )/* Fatal bus error enable */
\r
116 /* Interrupt events to process. Currently only the RX/TX events are processed
\r
117 although code for other events is included to allow for possible future
\r
119 #define EMAC_IF_RX_EVENT 1UL
\r
120 #define EMAC_IF_TX_EVENT 2UL
\r
121 #define EMAC_IF_ERR_EVENT 4UL
\r
122 #define EMAC_IF_ALL_EVENT ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT )
\r
124 /* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet
\r
125 driver will filter incoming packets and only pass the stack those packets it
\r
126 considers need processing. */
\r
127 #if( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 )
\r
128 #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer
\r
130 #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) )
\r
133 #if( ipconfigZERO_COPY_RX_DRIVER == 0 ) || ( ipconfigZERO_COPY_TX_DRIVER == 0 )
\r
134 #warning It is adviced to enable both macros
\r
137 #ifndef configPLACE_IN_SECTION_RAM
\r
138 #define configPLACE_IN_SECTION_RAM
\r
140 #define configPLACE_IN_SECTION_RAM __attribute__ ((section(".ramfunc")))
\r
144 /*-----------------------------------------------------------*/
\r
147 * Delay function passed into the library. The implementation uses FreeRTOS
\r
148 * calls so the scheduler must be started before the driver can be used.
\r
150 static void prvDelay( uint32_t ulMilliSeconds );
\r
153 * Initialises the Tx and Rx descriptors respectively.
\r
155 static void prvSetupTxDescriptors( void );
\r
156 static void prvSetupRxDescriptors( void );
\r
159 * A task that processes received frames.
\r
161 static void prvEMACHandlerTask( void *pvParameters );
\r
164 * Sets up the MAC with the results of an auto-negotiation.
\r
166 static BaseType_t prvSetLinkSpeed( void );
\r
169 * Generates a CRC for a MAC address that is then used to generate a hash index.
\r
171 static uint32_t prvGenerateCRC32( const uint8_t *ucAddress );
\r
174 * Generates a hash index when setting a filter to permit a MAC address.
\r
176 static uint32_t prvGetHashIndex( const uint8_t *ucAddress );
\r
179 * Update the hash table to allow a MAC address.
\r
181 static void prvAddMACAddress( const uint8_t* ucMacAddress );
\r
184 * Sometimes the DMA will report received data as being longer than the actual
\r
185 * received from length. This function checks the reported length and corrects
\r
188 static void prvRemoveTrailingBytes( NetworkBufferDescriptor_t *pxDescriptor );
\r
190 /*-----------------------------------------------------------*/
\r
192 /* Bit map of outstanding ETH interrupt events for processing. Currently only
\r
193 the Rx and Tx interrupt is handled, although code is included for other events
\r
194 to enable future expansion. */
\r
195 static volatile uint32_t ulISREvents;
\r
197 /* A copy of PHY register 1: 'PHY_REG_01_BMSR' */
\r
198 static uint32_t ulPHYLinkStatus = 0;
\r
200 /* Tx descriptors and index. */
\r
201 static ENET_ENHTXDESC_T xDMATxDescriptors[ configNUM_TX_DESCRIPTORS ];
\r
203 /* ulNextFreeTxDescriptor is declared volatile, because it is accessed from
\r
204 to different tasks. */
\r
205 static volatile uint32_t ulNextFreeTxDescriptor;
\r
206 static uint32_t ulTxDescriptorToClear;
\r
208 /* Rx descriptors and index. */
\r
209 static ENET_ENHRXDESC_T xDMARxDescriptors[ configNUM_RX_DESCRIPTORS ];
\r
210 static uint32_t ulNextRxDescriptorToProcess;
\r
212 /* Must be defined externally - the demo applications define this in main.c. */
\r
213 extern uint8_t ucMACAddress[ 6 ];
\r
215 /* The handle of the task that processes Rx packets. The handle is required so
\r
216 the task can be notified when new packets arrive. */
\r
217 static TaskHandle_t xRxHanderTask = NULL;
\r
219 #if( ipconfigUSE_LLMNR == 1 )
\r
220 static const uint8_t xLLMNR_MACAddress[] = { '\x01', '\x00', '\x5E', '\x00', '\x00', '\xFC' };
\r
221 #endif /* ipconfigUSE_LLMNR == 1 */
\r
223 /* xTXDescriptorSemaphore is a counting semaphore with
\r
224 a maximum count of ETH_TXBUFNB, which is the number of
\r
225 DMA TX descriptors. */
\r
226 static SemaphoreHandle_t xTXDescriptorSemaphore = NULL;
\r
228 /*-----------------------------------------------------------*/
\r
231 BaseType_t xNetworkInterfaceInitialise( void )
\r
233 BaseType_t xReturn = pdPASS;
\r
234 static BaseType_t xHasInitialised = pdFALSE;
\r
236 if( xHasInitialised == pdFALSE )
\r
238 xHasInitialised = pdTRUE;
\r
240 /* The interrupt will be turned on when a link is established. */
\r
241 NVIC_DisableIRQ( ETHERNET_IRQn );
\r
243 /* Disable receive and transmit DMA processes. */
\r
244 LPC_ETHERNET->DMA_OP_MODE &= ~( DMA_OM_ST | DMA_OM_SR );
\r
246 /* Disable packet reception. */
\r
247 LPC_ETHERNET->MAC_CONFIG &= ~( MAC_CFG_RE | MAC_CFG_TE );
\r
249 /* Call the LPCOpen function to initialise the hardware. */
\r
250 Chip_ENET_Init( LPC_ETHERNET );
\r
252 /* Save MAC address. */
\r
253 Chip_ENET_SetADDR( LPC_ETHERNET, ucMACAddress );
\r
255 /* Clear all MAC address hash entries. */
\r
256 LPC_ETHERNET->MAC_HASHTABLE_HIGH = 0;
\r
257 LPC_ETHERNET->MAC_HASHTABLE_LOW = 0;
\r
259 #if( ipconfigUSE_LLMNR == 1 )
\r
261 prvAddMACAddress( xLLMNR_MACAddress );
\r
263 #endif /* ipconfigUSE_LLMNR == 1 */
\r
265 /* Promiscuous flag (PR) and Receive All flag (RA) set to zero. The
\r
266 registers MAC_HASHTABLE_[LOW|HIGH] will be loaded to allow certain
\r
267 multi-cast addresses. */
\r
268 LPC_ETHERNET->MAC_FRAME_FILTER = MAC_FF_HMC;
\r
270 #if( configUSE_RMII == 1 )
\r
272 if( lpc_phy_init( pdTRUE, prvDelay ) != SUCCESS )
\r
279 #warning This path has not been tested.
\r
280 if( lpc_phy_init( pdFALSE, prvDelay ) != SUCCESS )
\r
287 if( xReturn == pdPASS )
\r
289 if( xTXDescriptorSemaphore == NULL )
\r
291 /* Create a counting semaphore, with a value of 'configNUM_TX_DESCRIPTORS'
\r
292 and a maximum of 'configNUM_TX_DESCRIPTORS'. */
\r
293 xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) configNUM_TX_DESCRIPTORS, ( UBaseType_t ) configNUM_TX_DESCRIPTORS );
\r
294 configASSERT( xTXDescriptorSemaphore );
\r
297 /* Enable MAC interrupts. */
\r
298 LPC_ETHERNET->DMA_INT_EN = nwDMA_INTERRUPT_MASK;
\r
300 /* Auto-negotiate was already started. Wait for it to complete. */
\r
301 xReturn = prvSetLinkSpeed();
\r
303 if( xReturn == pdPASS )
\r
305 /* Initialise the descriptors. */
\r
306 prvSetupTxDescriptors();
\r
307 prvSetupRxDescriptors();
\r
309 /* Clear all interrupts. */
\r
310 LPC_ETHERNET->DMA_STAT = DMA_ST_ALL;
\r
312 /* Enable receive and transmit DMA processes. */
\r
313 LPC_ETHERNET->DMA_OP_MODE |= DMA_OM_ST | DMA_OM_SR;
\r
315 /* Set Receiver / Transmitter Enable. */
\r
316 LPC_ETHERNET->MAC_CONFIG |= MAC_CFG_RE | MAC_CFG_TE;
\r
318 /* Start receive polling. */
\r
319 LPC_ETHERNET->DMA_REC_POLL_DEMAND = 1;
\r
321 /* Enable interrupts in the NVIC. */
\r
322 NVIC_SetPriority( ETHERNET_IRQn, configMAC_INTERRUPT_PRIORITY );
\r
323 NVIC_EnableIRQ( ETHERNET_IRQn );
\r
325 /* Guard against the task being created more than once and the
\r
326 descriptors being initialised more than once. */
\r
327 if( xRxHanderTask == NULL )
\r
329 xReturn = xTaskCreate( prvEMACHandlerTask, "EMAC", nwRX_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xRxHanderTask );
\r
330 configASSERT( xReturn );
\r
335 /* Once prvEMACHandlerTask() has started, the variable
\r
336 'ulPHYLinkStatus' will be updated by that task.
\r
337 The IP-task will keep on calling this function untill
\r
338 it finally returns pdPASS.
\r
339 Only then can the DHCP-procedure start (if configured). */
\r
340 if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != 0 )
\r
351 /*-----------------------------------------------------------*/
\r
353 #define niBUFFER_1_PACKET_SIZE 1536
\r
355 static __attribute__ ((section("._ramAHB32"))) uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ] __attribute__ ( ( aligned( 32 ) ) );
\r
357 void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
\r
360 uint8_t *ucRAMBuffer = ucNetworkPackets;
\r
363 for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ )
\r
365 pxNetworkBuffers[ ul ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING;
\r
366 *( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ul ] ) );
\r
367 ucRAMBuffer += niBUFFER_1_PACKET_SIZE;
\r
370 /*-----------------------------------------------------------*/
\r
372 configPLACE_IN_SECTION_RAM
\r
373 static void vClearTXBuffers()
\r
375 uint32_t ulLastDescriptor = ulNextFreeTxDescriptor;
\r
376 size_t uxCount = ( ( size_t ) configNUM_TX_DESCRIPTORS ) - uxSemaphoreGetCount( xTXDescriptorSemaphore );
\r
377 #if( ipconfigZERO_COPY_TX_DRIVER != 0 )
\r
378 NetworkBufferDescriptor_t *pxNetworkBuffer;
\r
379 uint8_t *ucPayLoad;
\r
382 /* This function is called after a TX-completion interrupt.
\r
383 It will release each Network Buffer used in xNetworkInterfaceOutput().
\r
384 'uxCount' represents the number of descriptors given to DMA for transmission.
\r
385 After sending a packet, the DMA will clear the 'TDES_OWN' bit. */
\r
386 while( ( uxCount > ( size_t ) 0u ) && ( ( xDMATxDescriptors[ ulTxDescriptorToClear ].CTRLSTAT & TDES_OWN ) == 0 ) )
\r
388 if( ( ulTxDescriptorToClear == ulLastDescriptor ) && ( uxCount != ( size_t ) configNUM_TX_DESCRIPTORS ) )
\r
394 #if( ipconfigZERO_COPY_TX_DRIVER != 0 )
\r
396 ucPayLoad = ( uint8_t * )xDMATxDescriptors[ ulTxDescriptorToClear ].B1ADD;
\r
397 if( ucPayLoad != NULL )
\r
399 /* B1ADD points to a pucEthernetBuffer of a Network Buffer descriptor. */
\r
400 pxNetworkBuffer = pxPacketBuffer_to_NetworkBuffer( ucPayLoad );
\r
402 configASSERT( pxNetworkBuffer != NULL );
\r
404 vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ) ;
\r
405 xDMATxDescriptors[ ulTxDescriptorToClear ].B1ADD = ( uint32_t )0u;
\r
408 #endif /* ipconfigZERO_COPY_TX_DRIVER */
\r
410 /* Move onto the next descriptor, wrapping if necessary. */
\r
411 ulTxDescriptorToClear++;
\r
412 if( ulTxDescriptorToClear >= configNUM_TX_DESCRIPTORS )
\r
414 ulTxDescriptorToClear = 0;
\r
418 /* Tell the counting semaphore that one more TX descriptor is available. */
\r
419 xSemaphoreGive( xTXDescriptorSemaphore );
\r
423 /*-----------------------------------------------------------*/
\r
425 configPLACE_IN_SECTION_RAM
\r
426 BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, BaseType_t bReleaseAfterSend )
\r
428 BaseType_t xReturn = pdFAIL;
\r
429 const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50 );
\r
431 /* Attempt to obtain access to a Tx descriptor. */
\r
434 if( xTXDescriptorSemaphore == NULL )
\r
438 if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS )
\r
440 /* Time-out waiting for a free TX descriptor. */
\r
444 /* If the descriptor is still owned by the DMA it can't be used. */
\r
445 if( ( xDMATxDescriptors[ ulNextFreeTxDescriptor ].CTRLSTAT & TDES_OWN ) != 0 )
\r
447 /* The semaphore was taken, the TX DMA-descriptor is still not available.
\r
448 Actually that should not occur, the 'TDES_OWN' was already confirmed low in vClearTXBuffers(). */
\r
449 xSemaphoreGive( xTXDescriptorSemaphore );
\r
453 #if( ipconfigZERO_COPY_TX_DRIVER != 0 )
\r
455 /* bReleaseAfterSend should always be set when using the zero
\r
457 configASSERT( bReleaseAfterSend != pdFALSE );
\r
459 /* The DMA's descriptor to point directly to the data in the
\r
460 network buffer descriptor. The data is not copied. */
\r
461 xDMATxDescriptors[ ulNextFreeTxDescriptor ].B1ADD = ( uint32_t ) pxDescriptor->pucEthernetBuffer;
\r
463 /* The DMA descriptor will 'own' this Network Buffer,
\r
464 until it has been sent. So don't release it now. */
\r
465 bReleaseAfterSend = pdFALSE;
\r
469 /* The data is copied from the network buffer descriptor into
\r
470 the DMA's descriptor. */
\r
471 memcpy( ( void * ) xDMATxDescriptors[ ulNextFreeTxDescriptor ].B1ADD, ( void * ) pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength );
\r
475 xDMATxDescriptors[ ulNextFreeTxDescriptor ].BSIZE = ( uint32_t ) TDES_ENH_BS1( pxDescriptor->xDataLength );
\r
477 /* This descriptor is given back to the DMA. */
\r
478 xDMATxDescriptors[ ulNextFreeTxDescriptor ].CTRLSTAT |= TDES_OWN;
\r
480 /* Ensure the DMA is polling Tx descriptors. */
\r
481 LPC_ETHERNET->DMA_TRANS_POLL_DEMAND = 1;
\r
483 iptraceNETWORK_INTERFACE_TRANSMIT();
\r
485 /* Move onto the next descriptor, wrapping if necessary. */
\r
486 ulNextFreeTxDescriptor++;
\r
487 if( ulNextFreeTxDescriptor >= configNUM_TX_DESCRIPTORS )
\r
489 ulNextFreeTxDescriptor = 0;
\r
492 /* The Tx has been initiated. */
\r
497 /* The buffer has been sent so can be released. */
\r
498 if( bReleaseAfterSend != pdFALSE )
\r
500 vReleaseNetworkBufferAndDescriptor( pxDescriptor );
\r
505 /*-----------------------------------------------------------*/
\r
507 static void prvDelay( uint32_t ulMilliSeconds )
\r
509 /* Ensure the scheduler was started before attempting to use the scheduler to
\r
511 configASSERT( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING );
\r
513 vTaskDelay( pdMS_TO_TICKS( ulMilliSeconds ) );
\r
515 /*-----------------------------------------------------------*/
\r
517 static void prvSetupTxDescriptors( void )
\r
521 /* Start with Tx descriptors clear. */
\r
522 memset( ( void * ) xDMATxDescriptors, 0, sizeof( xDMATxDescriptors ) );
\r
524 /* Index to the next Tx descriptor to use. */
\r
525 ulNextFreeTxDescriptor = 0ul;
\r
527 /* Index to the next Tx descriptor to clear ( after transmission ). */
\r
528 ulTxDescriptorToClear = 0ul;
\r
530 for( x = 0; x < configNUM_TX_DESCRIPTORS; x++ )
\r
532 #if( ipconfigZERO_COPY_TX_DRIVER != 0 )
\r
534 /* Nothing to do, B1ADD will be set when data is ready to transmit.
\r
535 Currently the memset above will have set it to NULL. */
\r
539 /* Allocate a buffer to the Tx descriptor. This is the most basic
\r
540 way of creating a driver as the data is then copied into the
\r
542 xDMATxDescriptors[ x ].B1ADD = ( uint32_t ) pvPortMalloc( ipTOTAL_ETHERNET_FRAME_SIZE );
\r
544 /* Use an assert to check the allocation as +TCP applications will
\r
545 often not use a malloc() failed hook as the TCP stack will recover
\r
546 from allocation failures. */
\r
547 configASSERT( xDMATxDescriptors[ x ].B1ADD );
\r
551 /* Buffers hold an entire frame so all buffers are both the start and
\r
553 /* TDES_ENH_TCH Second Address Chained. */
\r
554 /* TDES_ENH_CIC(n) Checksum Insertion Control, tried but it does not work for the LPC18xx... */
\r
555 /* TDES_ENH_FS First Segment. */
\r
556 /* TDES_ENH_LS Last Segment. */
\r
557 /* TDES_ENH_IC Interrupt on Completion. */
\r
558 xDMATxDescriptors[ x ].CTRLSTAT = TDES_ENH_TCH | TDES_ENH_CIC( 3 ) | TDES_ENH_FS | TDES_ENH_LS | TDES_ENH_IC;
\r
559 xDMATxDescriptors[ x ].B2ADD = ( uint32_t ) &xDMATxDescriptors[ x + 1 ];
\r
562 xDMATxDescriptors[ configNUM_TX_DESCRIPTORS - 1 ].CTRLSTAT |= TDES_ENH_TER;
\r
563 xDMATxDescriptors[ configNUM_TX_DESCRIPTORS - 1 ].B2ADD = ( uint32_t ) &xDMATxDescriptors[ 0 ];
\r
565 /* Point the DMA to the base of the descriptor list. */
\r
566 LPC_ETHERNET->DMA_TRANS_DES_ADDR = ( uint32_t ) xDMATxDescriptors;
\r
568 /*-----------------------------------------------------------*/
\r
570 static void prvSetupRxDescriptors( void )
\r
573 #if( ipconfigZERO_COPY_RX_DRIVER != 0 )
\r
574 NetworkBufferDescriptor_t *pxNetworkBuffer;
\r
577 /* Index to the next Rx descriptor to use. */
\r
578 ulNextRxDescriptorToProcess = 0;
\r
580 /* Clear RX descriptor list. */
\r
581 memset( ( void * ) xDMARxDescriptors, 0, sizeof( xDMARxDescriptors ) );
\r
583 for( x = 0; x < configNUM_RX_DESCRIPTORS; x++ )
\r
585 /* Allocate a buffer of the largest possible frame size as it is not
\r
586 known what size received frames will be. */
\r
588 #if( ipconfigZERO_COPY_RX_DRIVER != 0 )
\r
590 pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, 0 );
\r
592 /* During start-up there should be enough Network Buffers available,
\r
593 so it is safe to use configASSERT().
\r
594 In case this assert fails, please check: configNUM_RX_DESCRIPTORS,
\r
595 ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS, and in case BufferAllocation_2.c
\r
596 is included, check the amount of available heap. */
\r
597 configASSERT( pxNetworkBuffer != NULL );
\r
599 /* Pass the actual buffer to DMA. */
\r
600 xDMARxDescriptors[ x ].B1ADD = ( uint32_t ) pxNetworkBuffer->pucEthernetBuffer;
\r
604 /* All DMA descriptors are populated with permanent memory blocks.
\r
605 Their contents will be copy to Network Buffers. */
\r
606 xDMARxDescriptors[ x ].B1ADD = ( uint32_t ) pvPortMalloc( ipTOTAL_ETHERNET_FRAME_SIZE );
\r
608 #endif /* ipconfigZERO_COPY_RX_DRIVER */
\r
610 /* Use an assert to check the allocation as +TCP applications will often
\r
611 not use a malloc failed hook as the TCP stack will recover from
\r
612 allocation failures. */
\r
613 configASSERT( xDMARxDescriptors[ x ].B1ADD );
\r
615 xDMARxDescriptors[ x ].B2ADD = ( uint32_t ) &( xDMARxDescriptors[ x + 1 ] );
\r
616 xDMARxDescriptors[ x ].CTRL = ( uint32_t ) RDES_ENH_BS1( ipTOTAL_ETHERNET_FRAME_SIZE ) | RDES_ENH_RCH;
\r
618 /* The descriptor is available for use by the DMA. */
\r
619 xDMARxDescriptors[ x ].STATUS = RDES_OWN;
\r
622 /* RDES_ENH_RER Receive End of Ring. */
\r
623 xDMARxDescriptors[ ( configNUM_RX_DESCRIPTORS - 1 ) ].CTRL |= RDES_ENH_RER;
\r
624 xDMARxDescriptors[ configNUM_RX_DESCRIPTORS - 1 ].B2ADD = ( uint32_t ) &( xDMARxDescriptors[ 0 ] );
\r
626 /* Point the DMA to the base of the descriptor list. */
\r
627 LPC_ETHERNET->DMA_REC_DES_ADDR = ( uint32_t ) xDMARxDescriptors;
\r
629 /*-----------------------------------------------------------*/
\r
630 configPLACE_IN_SECTION_RAM
\r
631 static void prvRemoveTrailingBytes( NetworkBufferDescriptor_t *pxDescriptor )
\r
633 size_t xExpectedLength;
\r
634 IPPacket_t *pxIPPacket;
\r
636 pxIPPacket = ( IPPacket_t * ) pxDescriptor->pucEthernetBuffer;
\r
637 /* Look at the actual length of the packet, translate it to a host-endial notation. */
\r
638 xExpectedLength = sizeof( EthernetHeader_t ) + ( size_t ) FreeRTOS_htons( pxIPPacket->xIPHeader.usLength );
\r
640 if( xExpectedLength == ( pxDescriptor->xDataLength + 4 ) )
\r
642 pxDescriptor->xDataLength -= 4;
\r
646 if( pxDescriptor->xDataLength > xExpectedLength )
\r
648 pxDescriptor->xDataLength = ( size_t ) xExpectedLength;
\r
652 /*-----------------------------------------------------------*/
\r
653 configPLACE_IN_SECTION_RAM
\r
654 BaseType_t xGetPhyLinkStatus( void )
\r
656 BaseType_t xReturn;
\r
658 if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) == 0 )
\r
669 /*-----------------------------------------------------------*/
\r
671 configPLACE_IN_SECTION_RAM
\r
672 static BaseType_t prvNetworkInterfaceInput()
\r
674 BaseType_t xResult = pdFALSE;
\r
676 eFrameProcessingResult_t eResult;
\r
677 const TickType_t xDescriptorWaitTime = pdMS_TO_TICKS( 250 );
\r
678 const UBaseType_t uxMinimumBuffersRemaining = 3UL;
\r
680 NetworkBufferDescriptor_t *pxDescriptor;
\r
681 #if( ipconfigZERO_COPY_RX_DRIVER != 0 )
\r
682 NetworkBufferDescriptor_t *pxNewDescriptor;
\r
683 #endif /* ipconfigZERO_COPY_RX_DRIVER */
\r
684 IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL };
\r
686 /* Process each descriptor that is not still in use by the DMA. */
\r
687 ulStatus = xDMARxDescriptors[ ulNextRxDescriptorToProcess ].STATUS;
\r
688 if( ( ulStatus & RDES_OWN ) == 0 )
\r
690 /* Check packet for errors */
\r
691 if( ( ulStatus & nwRX_STATUS_ERROR_BITS ) != 0 )
\r
693 /* There is some reception error. */
\r
694 /* Clear error bits. */
\r
695 ulStatus &= ~( ( uint32_t )nwRX_STATUS_ERROR_BITS );
\r
701 eResult = ipCONSIDER_FRAME_FOR_PROCESSING( ( const uint8_t * const ) ( xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD ) );
\r
702 if( eResult == eProcessBuffer )
\r
704 if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) == 0 )
\r
706 ulPHYLinkStatus |= PHY_LINK_CONNECTED;
\r
707 FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d (message received)\n", ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != 0 ) );
\r
710 #if( ipconfigZERO_COPY_RX_DRIVER != 0 )
\r
711 if( uxGetNumberOfFreeNetworkBuffers() > uxMinimumBuffersRemaining )
\r
713 pxNewDescriptor = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, xDescriptorWaitTime );
\r
717 /* Too risky to allocate a new Network Buffer. */
\r
718 pxNewDescriptor = NULL;
\r
720 if( pxNewDescriptor != NULL )
\r
722 if( uxGetNumberOfFreeNetworkBuffers() > uxMinimumBuffersRemaining )
\r
723 #endif /* ipconfigZERO_COPY_RX_DRIVER */
\r
725 #if( ipconfigZERO_COPY_RX_DRIVER != 0 )
\r
726 const uint8_t *pucBuffer;
\r
729 /* Get the actual length. */
\r
730 usLength = RDES_FLMSK( ulStatus );
\r
732 #if( ipconfigZERO_COPY_RX_DRIVER != 0 )
\r
734 /* Replace the character buffer 'B1ADD'. */
\r
735 pucBuffer = ( const uint8_t * const ) ( xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD );
\r
736 xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD = ( uint32_t ) pxNewDescriptor->pucEthernetBuffer;
\r
738 /* 'B1ADD' contained the address of a 'pucEthernetBuffer' that
\r
739 belongs to a Network Buffer. Find the original Network Buffer. */
\r
740 pxDescriptor = pxPacketBuffer_to_NetworkBuffer( pucBuffer );
\r
741 /* This zero-copy driver makes sure that every 'xDMARxDescriptors' contains
\r
742 a reference to a Network Buffer at any time.
\r
743 In case it runs out of Network Buffers, a DMA buffer won't be replaced,
\r
744 and the received messages is dropped. */
\r
745 configASSERT( pxDescriptor != NULL );
\r
749 /* Create a buffer of exactly the required length. */
\r
750 pxDescriptor = pxGetNetworkBufferWithDescriptor( usLength, xDescriptorWaitTime );
\r
752 #endif /* ipconfigZERO_COPY_RX_DRIVER */
\r
754 if( pxDescriptor != NULL )
\r
756 pxDescriptor->xDataLength = ( size_t ) usLength;
\r
757 #if( ipconfigZERO_COPY_RX_DRIVER == 0 )
\r
759 /* Copy the data into the allocated buffer. */
\r
760 memcpy( ( void * ) pxDescriptor->pucEthernetBuffer, ( void * ) xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD, usLength );
\r
762 #endif /* ipconfigZERO_COPY_RX_DRIVER */
\r
763 /* It is possible that more data was copied than
\r
764 actually makes up the frame. If this is the case
\r
765 adjust the length to remove any trailing bytes. */
\r
766 prvRemoveTrailingBytes( pxDescriptor );
\r
768 /* Pass the data to the TCP/IP task for processing. */
\r
769 xRxEvent.pvData = ( void * ) pxDescriptor;
\r
770 if( xSendEventStructToIPTask( &xRxEvent, xDescriptorWaitTime ) == pdFALSE )
\r
772 /* Could not send the descriptor into the TCP/IP
\r
773 stack, it must be released. */
\r
774 vReleaseNetworkBufferAndDescriptor( pxDescriptor );
\r
778 iptraceNETWORK_INTERFACE_RECEIVE();
\r
783 /* Got here because received data was sent to the IP task or the
\r
784 data contained an error and was discarded. Give the descriptor
\r
785 back to the DMA. */
\r
786 xDMARxDescriptors[ ulNextRxDescriptorToProcess ].STATUS = ulStatus | RDES_OWN;
\r
788 /* Move onto the next descriptor. */
\r
789 ulNextRxDescriptorToProcess++;
\r
790 if( ulNextRxDescriptorToProcess >= configNUM_RX_DESCRIPTORS )
\r
792 ulNextRxDescriptorToProcess = 0;
\r
795 ulStatus = xDMARxDescriptors[ ulNextRxDescriptorToProcess ].STATUS;
\r
796 } /* if( ( ulStatus & nwRX_STATUS_ERROR_BITS ) != 0 ) */
\r
797 } /* if( ( ulStatus & RDES_OWN ) == 0 ) */
\r
799 /* Restart receive polling. */
\r
800 LPC_ETHERNET->DMA_REC_POLL_DEMAND = 1;
\r
804 /*-----------------------------------------------------------*/
\r
806 configPLACE_IN_SECTION_RAM
\r
807 void NETWORK_IRQHandler( void )
\r
809 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
\r
810 uint32_t ulDMAStatus;
\r
811 const uint32_t ulRxInterruptMask =
\r
812 DMA_ST_RI | /* Receive interrupt */
\r
813 DMA_ST_RU; /* Receive buffer unavailable */
\r
814 const uint32_t ulTxInterruptMask =
\r
815 DMA_ST_TI | /* Transmit interrupt */
\r
816 DMA_ST_TPS; /* Transmit process stopped */
\r
818 configASSERT( xRxHanderTask );
\r
820 /* Get pending interrupts. */
\r
821 ulDMAStatus = LPC_ETHERNET->DMA_STAT;
\r
823 /* RX group interrupt(s). */
\r
824 if( ( ulDMAStatus & ulRxInterruptMask ) != 0x00 )
\r
826 /* Remember that an RX event has happened. */
\r
827 ulISREvents |= EMAC_IF_RX_EVENT;
\r
828 vTaskNotifyGiveFromISR( xRxHanderTask, &xHigherPriorityTaskWoken );
\r
831 /* TX group interrupt(s). */
\r
832 if( ( ulDMAStatus & ulTxInterruptMask ) != 0x00 )
\r
834 /* Remember that a TX event has happened. */
\r
835 ulISREvents |= EMAC_IF_TX_EVENT;
\r
836 vTaskNotifyGiveFromISR( xRxHanderTask, &xHigherPriorityTaskWoken );
\r
839 /* Test for 'Abnormal interrupt summary'. */
\r
840 if( ( ulDMAStatus & DMA_ST_AIE ) != 0x00 )
\r
842 /* The trace macro must be written such that it can be called from
\r
844 iptraceETHERNET_RX_EVENT_LOST();
\r
847 /* Clear pending interrupts */
\r
848 LPC_ETHERNET->DMA_STAT = ulDMAStatus;
\r
850 /* Context switch needed? */
\r
851 portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
\r
853 /*-----------------------------------------------------------*/
\r
855 static BaseType_t prvSetLinkSpeed( void )
\r
857 BaseType_t xReturn = pdFAIL;
\r
858 TickType_t xTimeOnEntering;
\r
859 uint32_t ulPhyStatus;
\r
860 const TickType_t xAutoNegotiateDelay = pdMS_TO_TICKS( 5000UL );
\r
862 /* Ensure polling does not starve lower priority tasks by temporarily
\r
863 setting the priority of this task to that of the idle task. */
\r
864 vTaskPrioritySet( NULL, tskIDLE_PRIORITY );
\r
866 xTimeOnEntering = xTaskGetTickCount();
\r
869 ulPhyStatus = lpcPHYStsPoll();
\r
870 if( ( ulPhyStatus & PHY_LINK_CONNECTED ) != 0x00 )
\r
872 /* Set interface speed and duplex. */
\r
873 if( ( ulPhyStatus & PHY_LINK_SPEED100 ) != 0x00 )
\r
875 Chip_ENET_SetSpeed( LPC_ETHERNET, 1 );
\r
879 Chip_ENET_SetSpeed( LPC_ETHERNET, 0 );
\r
882 if( ( ulPhyStatus & PHY_LINK_FULLDUPLX ) != 0x00 )
\r
884 Chip_ENET_SetDuplex( LPC_ETHERNET, pdTRUE );
\r
888 Chip_ENET_SetDuplex( LPC_ETHERNET, pdFALSE );
\r
894 } while( ( xTaskGetTickCount() - xTimeOnEntering ) < xAutoNegotiateDelay );
\r
896 /* Reset the priority of this task back to its original value. */
\r
897 vTaskPrioritySet( NULL, ipconfigIP_TASK_PRIORITY );
\r
901 /*-----------------------------------------------------------*/
\r
903 static uint32_t prvGenerateCRC32( const uint8_t *ucAddress )
\r
906 const uint32_t Polynomial = 0xEDB88320;
\r
907 uint32_t crc = ~0ul;
\r
908 const uint8_t *pucCurrent = ( const uint8_t * ) ucAddress;
\r
909 const uint8_t *pucLast = pucCurrent + 6;
\r
911 /* Calculate normal CRC32 */
\r
912 while( pucCurrent < pucLast )
\r
914 crc ^= *( pucCurrent++ );
\r
915 for( j = 0; j < 8; j++ )
\r
917 if( ( crc & 1 ) != 0 )
\r
919 crc = (crc >> 1) ^ Polynomial;
\r
929 /*-----------------------------------------------------------*/
\r
931 static uint32_t prvGetHashIndex( const uint8_t *ucAddress )
\r
933 uint32_t ulCrc = prvGenerateCRC32( ucAddress );
\r
934 uint32_t ulIndex = 0ul;
\r
935 BaseType_t xCount = 6;
\r
937 /* Take the lowest 6 bits of the CRC32 and reverse them */
\r
941 ulIndex |= ( ulCrc & 1 );
\r
945 /* This is the has value of 'ucAddress' */
\r
948 /*-----------------------------------------------------------*/
\r
950 static void prvAddMACAddress( const uint8_t* ucMacAddress )
\r
954 xIndex = prvGetHashIndex( ucMacAddress );
\r
957 LPC_ETHERNET->MAC_HASHTABLE_HIGH |= ( 1u << ( xIndex - 32 ) );
\r
961 LPC_ETHERNET->MAC_HASHTABLE_LOW |= ( 1u << xIndex );
\r
964 /*-----------------------------------------------------------*/
\r
966 configPLACE_IN_SECTION_RAM
\r
967 static void prvEMACHandlerTask( void *pvParameters )
\r
969 TimeOut_t xPhyTime;
\r
970 TickType_t xPhyRemTime;
\r
971 UBaseType_t uxLastMinBufferCount = 0;
\r
972 UBaseType_t uxCurrentCount;
\r
973 BaseType_t xResult = 0;
\r
975 const TickType_t xBlockTime = pdMS_TO_TICKS( 5000ul );
\r
977 /* Remove compiler warning about unused parameter. */
\r
978 ( void ) pvParameters;
\r
980 /* A possibility to set some additional task properties. */
\r
981 iptraceEMAC_TASK_STARTING();
\r
983 vTaskSetTimeOutState( &xPhyTime );
\r
984 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );
\r
988 uxCurrentCount = uxGetMinimumFreeNetworkBuffers();
\r
989 if( uxLastMinBufferCount != uxCurrentCount )
\r
991 /* The logging produced below may be helpful
\r
992 while tuning +TCP: see how many buffers are in use. */
\r
993 uxLastMinBufferCount = uxCurrentCount;
\r
994 FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n",
\r
995 uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) );
\r
998 #if( ipconfigCHECK_IP_QUEUE_SPACE != 0 )
\r
1000 static UBaseType_t uxLastMinQueueSpace = 0;
\r
1002 uxCurrentCount = uxGetMinimumIPQueueSpace();
\r
1003 if( uxLastMinQueueSpace != uxCurrentCount )
\r
1005 /* The logging produced below may be helpful
\r
1006 while tuning +TCP: see how many buffers are in use. */
\r
1007 uxLastMinQueueSpace = uxCurrentCount;
\r
1008 FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) );
\r
1011 #endif /* ipconfigCHECK_IP_QUEUE_SPACE */
\r
1013 ulTaskNotifyTake( pdTRUE, xBlockTime );
\r
1015 xResult = ( BaseType_t ) 0;
\r
1017 if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 )
\r
1019 /* Code to release TX buffers if zero-copy is used. */
\r
1020 ulISREvents &= ~EMAC_IF_TX_EVENT;
\r
1022 /* Check if DMA packets have been delivered. */
\r
1023 vClearTXBuffers();
\r
1027 if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 )
\r
1029 ulISREvents &= ~EMAC_IF_RX_EVENT;
\r
1031 xResult = prvNetworkInterfaceInput();
\r
1034 while( prvNetworkInterfaceInput() > 0 )
\r
1042 /* A packet was received. No need to check for the PHY status now,
\r
1043 but set a timer to check it later on. */
\r
1044 vTaskSetTimeOutState( &xPhyTime );
\r
1045 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );
\r
1048 else if( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE )
\r
1050 ulStatus = lpcPHYStsPoll();
\r
1052 if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != ( ulStatus & PHY_LINK_CONNECTED ) )
\r
1054 ulPHYLinkStatus = ulStatus;
\r
1055 FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d (polled PHY)\n", ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != 0 ) );
\r
1058 vTaskSetTimeOutState( &xPhyTime );
\r
1059 if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != 0 )
\r
1061 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );
\r
1065 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );
\r
1070 /*-----------------------------------------------------------*/
\r