1 /*
2 * FreeRTOS+TCP V3.1.0
3 * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 *
5 * SPDX-License-Identifier: MIT
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy of
8 * this software and associated documentation files (the "Software"), to deal in
9 * the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11 * the Software, and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in all
15 * copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * http://aws.amazon.com/freertos
25 * http://www.FreeRTOS.org
26 */
27
28 /* Standard includes. */
29 #include <stdint.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32
33 /* FreeRTOS includes. */
34 #include "FreeRTOS.h"
35 #include "task.h"
36 #include "queue.h"
37 #include "semphr.h"
38
39 /* FreeRTOS+TCP includes. */
40 #include "FreeRTOS_IP.h"
41 #include "FreeRTOS_Sockets.h"
42 #include "FreeRTOS_IP_Private.h"
43 #include "NetworkBufferManagement.h"
44 #include "NetworkInterface.h"
45
46 /* LPCOpen includes. */
47 #include "chip.h"
48 #include "lpc_phy.h"
49
50 /* The size of the stack allocated to the task that handles Rx packets. */
51 #define nwRX_TASK_STACK_SIZE 140
52
53 #ifndef PHY_LS_HIGH_CHECK_TIME_MS
54
55 /* Check if the LinkSStatus in the PHY is still high after 15 seconds of not
56 * receiving packets. */
57 #define PHY_LS_HIGH_CHECK_TIME_MS 15000
58 #endif
59
60 #ifndef PHY_LS_LOW_CHECK_TIME_MS
61 /* Check if the LinkSStatus in the PHY is still low every second. */
62 #define PHY_LS_LOW_CHECK_TIME_MS 1000
63 #endif
64
65 #ifndef configUSE_RMII
66 #define configUSE_RMII 1
67 #endif
68
69 #ifndef configNUM_RX_DESCRIPTORS
70 #error please define configNUM_RX_DESCRIPTORS in your FreeRTOSIPConfig.h
71 #endif
72
73 #ifndef configNUM_TX_DESCRIPTORS
74 #error please define configNUM_TX_DESCRIPTORS in your FreeRTOSIPConfig.h
75 #endif
76
77 #ifndef NETWORK_IRQHandler
78 #error NETWORK_IRQHandler must be defined to the name of the function that is installed in the interrupt vector table to handle Ethernet interrupts.
79 #endif
80
81 #if !defined( MAC_FF_HMC )
82 /* Hash for multicast. */
83 #define MAC_FF_HMC ( 1UL << 2UL )
84 #endif
85
86 #ifndef iptraceEMAC_TASK_STARTING
87 #define iptraceEMAC_TASK_STARTING() do {} while( ipFALSE_BOOL )
88 #endif
89
90 /* Define the bits of .STATUS that indicate a reception error. */
91 #define nwRX_STATUS_ERROR_BITS \
92 ( RDES_CE /* CRC Error */ | \
93 RDES_RE /* Receive Error */ | \
94 RDES_DE /* Descriptor Error */ | \
95 RDES_RWT /* Receive Watchdog Timeout */ | \
96 RDES_LC /* Late Collision */ | \
97 RDES_OE /* Overflow Error */ | \
98 RDES_SAF /* Source Address Filter Fail */ | \
99 RDES_AFM /* Destination Address Filter Fail */ | \
100 RDES_LE /* Length Error */ )
101
102 /* Define the EMAC status bits that should trigger an interrupt. */
103 #define nwDMA_INTERRUPT_MASK \
104 ( DMA_IE_TIE /* Transmit interrupt enable */ | \
105 DMA_IE_TSE /* Transmit stopped enable */ | \
106 DMA_IE_OVE /* Overflow interrupt enable */ | \
107 DMA_IE_RIE /* Receive interrupt enable */ | \
108 DMA_IE_NIE /* Normal interrupt summary enable */ | \
109 DMA_IE_AIE /* Abnormal interrupt summary enable */ | \
110 DMA_IE_RUE /* Receive buffer unavailable enable */ | \
111 DMA_IE_UNE /* Underflow interrupt enable. */ | \
112 DMA_IE_TJE /* Transmit jabber timeout enable */ | \
113 DMA_IE_RSE /* Received stopped enable */ | \
114 DMA_IE_RWE /* Receive watchdog timeout enable */ | \
115 DMA_IE_FBE ) /* Fatal bus error enable */
116
117 /* Interrupt events to process. Currently only the RX/TX events are processed
118 * although code for other events is included to allow for possible future
119 * expansion. */
120 #define EMAC_IF_RX_EVENT 1UL
121 #define EMAC_IF_TX_EVENT 2UL
122 #define EMAC_IF_ERR_EVENT 4UL
123 #define EMAC_IF_ALL_EVENT ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT )
124
125 /* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet
126 * driver will filter incoming packets and only pass the stack those packets it
127 * considers need processing. */
128 #if ( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 )
129 #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer
130 #else
131 #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) )
132 #endif
133
134 #if ( ipconfigZERO_COPY_RX_DRIVER == 0 ) || ( ipconfigZERO_COPY_TX_DRIVER == 0 )
135 #warning It is adviced to enable both macros
136 #endif
137
138 #ifndef configPLACE_IN_SECTION_RAM
139 #define configPLACE_IN_SECTION_RAM
140
141 /*
142 #define configPLACE_IN_SECTION_RAM __attribute__ ((section(".ramfunc")))
143 */
144 #endif
145
146 /*-----------------------------------------------------------*/
147
148 /*
149 * Delay function passed into the library. The implementation uses FreeRTOS
150 * calls so the scheduler must be started before the driver can be used.
151 */
152 static void prvDelay( uint32_t ulMilliSeconds );
153
154 /*
155 * Initialises the Tx and Rx descriptors respectively.
156 */
157 static void prvSetupTxDescriptors( void );
158 static void prvSetupRxDescriptors( void );
159
160 /*
161 * A task that processes received frames.
162 */
163 static void prvEMACHandlerTask( void * pvParameters );
164
165 /*
166 * Sets up the MAC with the results of an auto-negotiation.
167 */
168 static BaseType_t prvSetLinkSpeed( void );
169
170 /*
171 * Generates a CRC for a MAC address that is then used to generate a hash index.
172 */
173 static uint32_t prvGenerateCRC32( const uint8_t * ucAddress );
174
175 /*
176 * Generates a hash index when setting a filter to permit a MAC address.
177 */
178 static uint32_t prvGetHashIndex( const uint8_t * ucAddress );
179
180 /*
181 * Update the hash table to allow a MAC address.
182 */
183 static void prvAddMACAddress( const uint8_t * ucMacAddress );
184
185 /*
186 * Sometimes the DMA will report received data as being longer than the actual
187 * received from length. This function checks the reported length and corrects
188 * if if necessary.
189 */
190 static void prvRemoveTrailingBytes( NetworkBufferDescriptor_t * pxDescriptor );
191
192 /*-----------------------------------------------------------*/
193
194 /* Bit map of outstanding ETH interrupt events for processing. Currently only
195 * the Rx and Tx interrupt is handled, although code is included for other events
196 * to enable future expansion. */
197 static volatile uint32_t ulISREvents;
198
199 /* A copy of PHY register 1: 'PHY_REG_01_BMSR' */
200 static uint32_t ulPHYLinkStatus = 0;
201
202 /* Tx descriptors and index. */
203 static ENET_ENHTXDESC_T xDMATxDescriptors[ configNUM_TX_DESCRIPTORS ];
204
205 /* ulNextFreeTxDescriptor is declared volatile, because it is accessed from
206 * to different tasks. */
207 static volatile uint32_t ulNextFreeTxDescriptor;
208 static uint32_t ulTxDescriptorToClear;
209
210 /* Rx descriptors and index. */
211 static ENET_ENHRXDESC_T xDMARxDescriptors[ configNUM_RX_DESCRIPTORS ];
212 static uint32_t ulNextRxDescriptorToProcess;
213
214 /* The handle of the task that processes Rx packets. The handle is required so
215 * the task can be notified when new packets arrive. */
216 static TaskHandle_t xRxHanderTask = NULL;
217
218 #if ( ipconfigUSE_LLMNR == 1 )
219 static const uint8_t xLLMNR_MACAddress[] = { '\x01', '\x00', '\x5E', '\x00', '\x00', '\xFC' };
220 #endif /* ipconfigUSE_LLMNR == 1 */
221
222 /* xTXDescriptorSemaphore is a counting semaphore with
223 * a maximum count of ETH_TXBUFNB, which is the number of
224 * DMA TX descriptors. */
225 static SemaphoreHandle_t xTXDescriptorSemaphore = NULL;
226
227 /*-----------------------------------------------------------*/
228
229
xNetworkInterfaceInitialise(void)230 BaseType_t xNetworkInterfaceInitialise( void )
231 {
232 BaseType_t xReturn = pdPASS;
233
234 /* The interrupt will be turned on when a link is established. */
235 NVIC_DisableIRQ( ETHERNET_IRQn );
236
237 /* Disable receive and transmit DMA processes. */
238 LPC_ETHERNET->DMA_OP_MODE &= ~( DMA_OM_ST | DMA_OM_SR );
239
240 /* Disable packet reception. */
241 LPC_ETHERNET->MAC_CONFIG &= ~( MAC_CFG_RE | MAC_CFG_TE );
242
243 /* Call the LPCOpen function to initialise the hardware. */
244 Chip_ENET_Init( LPC_ETHERNET );
245
246 /* Save MAC address. */
247 Chip_ENET_SetADDR( LPC_ETHERNET, ipLOCAL_MAC_ADDRESS );
248
249 /* Clear all MAC address hash entries. */
250 LPC_ETHERNET->MAC_HASHTABLE_HIGH = 0;
251 LPC_ETHERNET->MAC_HASHTABLE_LOW = 0;
252
253 #if ( ipconfigUSE_LLMNR == 1 )
254 {
255 prvAddMACAddress( xLLMNR_MACAddress );
256 }
257 #endif /* ipconfigUSE_LLMNR == 1 */
258
259 /* Promiscuous flag (PR) and Receive All flag (RA) set to zero. The
260 * registers MAC_HASHTABLE_[LOW|HIGH] will be loaded to allow certain
261 * multi-cast addresses. */
262 LPC_ETHERNET->MAC_FRAME_FILTER = MAC_FF_HMC;
263
264 #if ( configUSE_RMII == 1 )
265 {
266 if( lpc_phy_init( pdTRUE, prvDelay ) != SUCCESS )
267 {
268 xReturn = pdFAIL;
269 }
270 }
271 #else
272 {
273 #warning This path has not been tested.
274
275 if( lpc_phy_init( pdFALSE, prvDelay ) != SUCCESS )
276 {
277 xReturn = pdFAIL;
278 }
279 }
280 #endif /* if ( configUSE_RMII == 1 ) */
281
282 if( xReturn == pdPASS )
283 {
284 /* Guard against the task being created more than once and the
285 * descriptors being initialised more than once. */
286 if( xRxHanderTask == NULL )
287 {
288 xReturn = xTaskCreate( prvEMACHandlerTask, "EMAC", nwRX_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xRxHanderTask );
289 configASSERT( xReturn != NULL );
290 }
291
292 if( xTXDescriptorSemaphore == NULL )
293 {
294 /* Create a counting semaphore, with a value of 'configNUM_TX_DESCRIPTORS'
295 * and a maximum of 'configNUM_TX_DESCRIPTORS'. */
296 xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) configNUM_TX_DESCRIPTORS, ( UBaseType_t ) configNUM_TX_DESCRIPTORS );
297 configASSERT( xTXDescriptorSemaphore != NULL );
298 }
299
300 /* Enable MAC interrupts. */
301 LPC_ETHERNET->DMA_INT_EN = nwDMA_INTERRUPT_MASK;
302 }
303
304 if( xReturn != pdFAIL )
305 {
306 /* Auto-negotiate was already started. Wait for it to complete. */
307 xReturn = prvSetLinkSpeed();
308
309 if( xReturn == pdPASS )
310 {
311 /* Initialise the descriptors. */
312 prvSetupTxDescriptors();
313 prvSetupRxDescriptors();
314
315 /* Clear all interrupts. */
316 LPC_ETHERNET->DMA_STAT = DMA_ST_ALL;
317
318 /* Enable receive and transmit DMA processes. */
319 LPC_ETHERNET->DMA_OP_MODE |= DMA_OM_ST | DMA_OM_SR;
320
321 /* Set Receiver / Transmitter Enable. */
322 LPC_ETHERNET->MAC_CONFIG |= MAC_CFG_RE | MAC_CFG_TE;
323
324 /* Start receive polling. */
325 LPC_ETHERNET->DMA_REC_POLL_DEMAND = 1;
326
327 /* Enable interrupts in the NVIC. */
328 NVIC_SetPriority( ETHERNET_IRQn, configMAC_INTERRUPT_PRIORITY );
329 NVIC_EnableIRQ( ETHERNET_IRQn );
330 }
331 }
332
333 return xReturn;
334 }
335 /*-----------------------------------------------------------*/
336
337 #define niBUFFER_1_PACKET_SIZE 1536
338
339 static __attribute__( ( section( "._ramAHB32" ) ) ) uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ] __attribute__( ( aligned( 32 ) ) );
340
vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS])341 void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
342 {
343 uint8_t * ucRAMBuffer = ucNetworkPackets;
344 uint32_t ul;
345
346 for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ )
347 {
348 pxNetworkBuffers[ ul ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING;
349 *( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ul ] ) );
350 ucRAMBuffer += niBUFFER_1_PACKET_SIZE;
351 }
352 }
353 /*-----------------------------------------------------------*/
354
355 configPLACE_IN_SECTION_RAM
vClearTXBuffers()356 static void vClearTXBuffers()
357 {
358 uint32_t ulLastDescriptor = ulNextFreeTxDescriptor;
359 size_t uxCount = ( ( size_t ) configNUM_TX_DESCRIPTORS ) - uxSemaphoreGetCount( xTXDescriptorSemaphore );
360
361 #if ( ipconfigZERO_COPY_TX_DRIVER != 0 )
362 NetworkBufferDescriptor_t * pxNetworkBuffer;
363 uint8_t * ucPayLoad;
364 #endif
365
366 /* This function is called after a TX-completion interrupt.
367 * It will release each Network Buffer used in xNetworkInterfaceOutput().
368 * 'uxCount' represents the number of descriptors given to DMA for transmission.
369 * After sending a packet, the DMA will clear the 'TDES_OWN' bit. */
370 while( ( uxCount > ( size_t ) 0u ) && ( ( xDMATxDescriptors[ ulTxDescriptorToClear ].CTRLSTAT & TDES_OWN ) == 0 ) )
371 {
372 if( ( ulTxDescriptorToClear == ulLastDescriptor ) && ( uxCount != ( size_t ) configNUM_TX_DESCRIPTORS ) )
373 {
374 break;
375 }
376
377 #if ( ipconfigZERO_COPY_TX_DRIVER != 0 )
378 {
379 ucPayLoad = ( uint8_t * ) xDMATxDescriptors[ ulTxDescriptorToClear ].B1ADD;
380
381 if( ucPayLoad != NULL )
382 {
383 /* B1ADD points to a pucEthernetBuffer of a Network Buffer descriptor. */
384 pxNetworkBuffer = pxPacketBuffer_to_NetworkBuffer( ucPayLoad );
385
386 configASSERT( pxNetworkBuffer != NULL );
387
388 vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
389 xDMATxDescriptors[ ulTxDescriptorToClear ].B1ADD = ( uint32_t ) 0u;
390 }
391 }
392 #endif /* ipconfigZERO_COPY_TX_DRIVER */
393
394 /* Move onto the next descriptor, wrapping if necessary. */
395 ulTxDescriptorToClear++;
396
397 if( ulTxDescriptorToClear >= configNUM_TX_DESCRIPTORS )
398 {
399 ulTxDescriptorToClear = 0;
400 }
401
402 uxCount--;
403 /* Tell the counting semaphore that one more TX descriptor is available. */
404 xSemaphoreGive( xTXDescriptorSemaphore );
405 }
406 }
407
408 /*-----------------------------------------------------------*/
409
410 configPLACE_IN_SECTION_RAM
xNetworkInterfaceOutput(NetworkBufferDescriptor_t * const pxDescriptor,BaseType_t bReleaseAfterSend)411 BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor,
412 BaseType_t bReleaseAfterSend )
413 {
414 BaseType_t xReturn = pdFAIL;
415 const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50 );
416
417 /* Attempt to obtain access to a Tx descriptor. */
418 do
419 {
420 if( xTXDescriptorSemaphore == NULL )
421 {
422 break;
423 }
424
425 if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS )
426 {
427 /* Time-out waiting for a free TX descriptor. */
428 break;
429 }
430
431 /* If the descriptor is still owned by the DMA it can't be used. */
432 if( ( xDMATxDescriptors[ ulNextFreeTxDescriptor ].CTRLSTAT & TDES_OWN ) != 0 )
433 {
434 /* The semaphore was taken, the TX DMA-descriptor is still not available.
435 * Actually that should not occur, the 'TDES_OWN' was already confirmed low in vClearTXBuffers(). */
436 xSemaphoreGive( xTXDescriptorSemaphore );
437 }
438 else
439 {
440 #if ( ipconfigZERO_COPY_TX_DRIVER != 0 )
441 {
442 /* bReleaseAfterSend should always be set when using the zero
443 * copy driver. */
444 configASSERT( bReleaseAfterSend != pdFALSE );
445
446 /* The DMA's descriptor to point directly to the data in the
447 * network buffer descriptor. The data is not copied. */
448 xDMATxDescriptors[ ulNextFreeTxDescriptor ].B1ADD = ( uint32_t ) pxDescriptor->pucEthernetBuffer;
449
450 /* The DMA descriptor will 'own' this Network Buffer,
451 * until it has been sent. So don't release it now. */
452 bReleaseAfterSend = false;
453 }
454 #else /* if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) */
455 {
456 /* The data is copied from the network buffer descriptor into
457 * the DMA's descriptor. */
458 memcpy( ( void * ) xDMATxDescriptors[ ulNextFreeTxDescriptor ].B1ADD, ( void * ) pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength );
459 }
460 #endif /* if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) */
461
462 xDMATxDescriptors[ ulNextFreeTxDescriptor ].BSIZE = ( uint32_t ) TDES_ENH_BS1( pxDescriptor->xDataLength );
463
464 /* This descriptor is given back to the DMA. */
465 xDMATxDescriptors[ ulNextFreeTxDescriptor ].CTRLSTAT |= TDES_OWN;
466
467 /* Ensure the DMA is polling Tx descriptors. */
468 LPC_ETHERNET->DMA_TRANS_POLL_DEMAND = 1;
469
470 iptraceNETWORK_INTERFACE_TRANSMIT();
471
472 /* Move onto the next descriptor, wrapping if necessary. */
473 ulNextFreeTxDescriptor++;
474
475 if( ulNextFreeTxDescriptor >= configNUM_TX_DESCRIPTORS )
476 {
477 ulNextFreeTxDescriptor = 0;
478 }
479
480 /* The Tx has been initiated. */
481 xReturn = pdPASS;
482 }
483 } while( 0 );
484
485 /* The buffer has been sent so can be released. */
486 if( bReleaseAfterSend != pdFALSE )
487 {
488 vReleaseNetworkBufferAndDescriptor( pxDescriptor );
489 }
490
491 return xReturn;
492 }
493 /*-----------------------------------------------------------*/
494
prvDelay(uint32_t ulMilliSeconds)495 static void prvDelay( uint32_t ulMilliSeconds )
496 {
497 /* Ensure the scheduler was started before attempting to use the scheduler to
498 * create a delay. */
499 configASSERT( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING );
500
501 vTaskDelay( pdMS_TO_TICKS( ulMilliSeconds ) );
502 }
503 /*-----------------------------------------------------------*/
504
prvSetupTxDescriptors(void)505 static void prvSetupTxDescriptors( void )
506 {
507 BaseType_t x;
508
509 /* Start with Tx descriptors clear. */
510 memset( ( void * ) xDMATxDescriptors, 0, sizeof( xDMATxDescriptors ) );
511
512 /* Index to the next Tx descriptor to use. */
513 ulNextFreeTxDescriptor = 0ul;
514
515 /* Index to the next Tx descriptor to clear ( after transmission ). */
516 ulTxDescriptorToClear = 0ul;
517
518 for( x = 0; x < configNUM_TX_DESCRIPTORS; x++ )
519 {
520 #if ( ipconfigZERO_COPY_TX_DRIVER != 0 )
521 {
522 /* Nothing to do, B1ADD will be set when data is ready to transmit.
523 * Currently the memset above will have set it to NULL. */
524 }
525 #else
526 {
527 /* Allocate a buffer to the Tx descriptor. This is the most basic
528 * way of creating a driver as the data is then copied into the
529 * buffer. */
530 xDMATxDescriptors[ x ].B1ADD = ( uint32_t ) pvPortMalloc( ipTOTAL_ETHERNET_FRAME_SIZE );
531
532 /* Use an assert to check the allocation as +TCP applications will
533 * often not use a malloc() failed hook as the TCP stack will recover
534 * from allocation failures. */
535 configASSERT( xDMATxDescriptors[ x ].B1ADD != 0U );
536 }
537 #endif /* if ( ipconfigZERO_COPY_TX_DRIVER != 0 ) */
538
539 /* Buffers hold an entire frame so all buffers are both the start and
540 * end of a frame. */
541 /* TDES_ENH_TCH Second Address Chained. */
542 /* TDES_ENH_CIC(n) Checksum Insertion Control, tried but it does not work for the LPC18xx... */
543 /* TDES_ENH_FS First Segment. */
544 /* TDES_ENH_LS Last Segment. */
545 /* TDES_ENH_IC Interrupt on Completion. */
546 xDMATxDescriptors[ x ].CTRLSTAT = TDES_ENH_TCH | TDES_ENH_CIC( 3 ) | TDES_ENH_FS | TDES_ENH_LS | TDES_ENH_IC;
547 xDMATxDescriptors[ x ].B2ADD = ( uint32_t ) &xDMATxDescriptors[ x + 1 ];
548 }
549
550 xDMATxDescriptors[ configNUM_TX_DESCRIPTORS - 1 ].CTRLSTAT |= TDES_ENH_TER;
551 xDMATxDescriptors[ configNUM_TX_DESCRIPTORS - 1 ].B2ADD = ( uint32_t ) &xDMATxDescriptors[ 0 ];
552
553 /* Point the DMA to the base of the descriptor list. */
554 LPC_ETHERNET->DMA_TRANS_DES_ADDR = ( uint32_t ) xDMATxDescriptors;
555 }
556 /*-----------------------------------------------------------*/
557
prvSetupRxDescriptors(void)558 static void prvSetupRxDescriptors( void )
559 {
560 BaseType_t x;
561
562 #if ( ipconfigZERO_COPY_RX_DRIVER != 0 )
563 NetworkBufferDescriptor_t * pxNetworkBuffer;
564 #endif
565
566 /* Index to the next Rx descriptor to use. */
567 ulNextRxDescriptorToProcess = 0;
568
569 /* Clear RX descriptor list. */
570 memset( ( void * ) xDMARxDescriptors, 0, sizeof( xDMARxDescriptors ) );
571
572 for( x = 0; x < configNUM_RX_DESCRIPTORS; x++ )
573 {
574 /* Allocate a buffer of the largest possible frame size as it is not
575 * known what size received frames will be. */
576
577 #if ( ipconfigZERO_COPY_RX_DRIVER != 0 )
578 {
579 pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, 0 );
580
581 /* During start-up there should be enough Network Buffers available,
582 * so it is safe to use configASSERT().
583 * In case this assert fails, please check: configNUM_RX_DESCRIPTORS,
584 * ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS, and in case BufferAllocation_2.c
585 * is included, check the amount of available heap. */
586 configASSERT( pxNetworkBuffer != NULL );
587
588 /* Pass the actual buffer to DMA. */
589 xDMARxDescriptors[ x ].B1ADD = ( uint32_t ) pxNetworkBuffer->pucEthernetBuffer;
590 }
591 #else
592 {
593 /* All DMA descriptors are populated with permanent memory blocks.
594 * Their contents will be copy to Network Buffers. */
595 xDMARxDescriptors[ x ].B1ADD = ( uint32_t ) pvPortMalloc( ipTOTAL_ETHERNET_FRAME_SIZE );
596 }
597 #endif /* ipconfigZERO_COPY_RX_DRIVER */
598
599 /* Use an assert to check the allocation as +TCP applications will often
600 * not use a malloc failed hook as the TCP stack will recover from
601 * allocation failures. */
602 configASSERT( xDMARxDescriptors[ x ].B1ADD != 0U );
603
604 xDMARxDescriptors[ x ].B2ADD = ( uint32_t ) &( xDMARxDescriptors[ x + 1 ] );
605 xDMARxDescriptors[ x ].CTRL = ( uint32_t ) RDES_ENH_BS1( ipTOTAL_ETHERNET_FRAME_SIZE ) | RDES_ENH_RCH;
606
607 /* The descriptor is available for use by the DMA. */
608 xDMARxDescriptors[ x ].STATUS = RDES_OWN;
609 }
610
611 /* RDES_ENH_RER Receive End of Ring. */
612 xDMARxDescriptors[ ( configNUM_RX_DESCRIPTORS - 1 ) ].CTRL |= RDES_ENH_RER;
613 xDMARxDescriptors[ configNUM_RX_DESCRIPTORS - 1 ].B2ADD = ( uint32_t ) &( xDMARxDescriptors[ 0 ] );
614
615 /* Point the DMA to the base of the descriptor list. */
616 LPC_ETHERNET->DMA_REC_DES_ADDR = ( uint32_t ) xDMARxDescriptors;
617 }
618 /*-----------------------------------------------------------*/
619 configPLACE_IN_SECTION_RAM
prvRemoveTrailingBytes(NetworkBufferDescriptor_t * pxDescriptor)620 static void prvRemoveTrailingBytes( NetworkBufferDescriptor_t * pxDescriptor )
621 {
622 size_t xExpectedLength;
623 IPPacket_t * pxIPPacket;
624
625 pxIPPacket = ( IPPacket_t * ) pxDescriptor->pucEthernetBuffer;
626 /* Look at the actual length of the packet, translate it to a host-endian notation. */
627 xExpectedLength = sizeof( EthernetHeader_t ) + ( size_t ) FreeRTOS_htons( pxIPPacket->xIPHeader.usLength );
628
629 if( xExpectedLength == ( pxDescriptor->xDataLength + 4 ) )
630 {
631 pxDescriptor->xDataLength -= 4;
632 }
633 else
634 {
635 if( pxDescriptor->xDataLength > xExpectedLength )
636 {
637 pxDescriptor->xDataLength = ( size_t ) xExpectedLength;
638 }
639 }
640 }
641 /*-----------------------------------------------------------*/
642 configPLACE_IN_SECTION_RAM
xGetPhyLinkStatus(void)643 BaseType_t xGetPhyLinkStatus( void )
644 {
645 BaseType_t xReturn;
646
647 if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) == 0 )
648 {
649 xReturn = pdFALSE;
650 }
651 else
652 {
653 xReturn = pdTRUE;
654 }
655
656 return xReturn;
657 }
658 /*-----------------------------------------------------------*/
659
660 uint32_t ulDataAvailable;
661
662 configPLACE_IN_SECTION_RAM
prvNetworkInterfaceInput()663 static BaseType_t prvNetworkInterfaceInput()
664 {
665 BaseType_t xResult = pdFALSE;
666 uint32_t ulStatus;
667 eFrameProcessingResult_t eResult;
668 const TickType_t xDescriptorWaitTime = pdMS_TO_TICKS( 250 );
669 const UBaseType_t uxMinimumBuffersRemaining = 3UL;
670 uint16_t usLength;
671 NetworkBufferDescriptor_t * pxDescriptor;
672
673 #if ( ipconfigZERO_COPY_RX_DRIVER != 0 )
674 NetworkBufferDescriptor_t * pxNewDescriptor;
675 #endif /* ipconfigZERO_COPY_RX_DRIVER */
676 #if ( ipconfigUSE_LINKED_RX_MESSAGES == 0 )
677 IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL };
678 #endif
679
680 /* Process each descriptor that is not still in use by the DMA. */
681 ulStatus = xDMARxDescriptors[ ulNextRxDescriptorToProcess ].STATUS;
682
683 if( ( ulStatus & RDES_OWN ) == 0 )
684 {
685 /* Check packet for errors */
686 if( ( ulStatus & nwRX_STATUS_ERROR_BITS ) != 0 )
687 {
688 /* There is some reception error. */
689 intCount[ 3 ]++;
690 /* Clear error bits. */
691 ulStatus &= ~( ( uint32_t ) nwRX_STATUS_ERROR_BITS );
692 }
693 else
694 {
695 xResult++;
696
697 eResult = ipCONSIDER_FRAME_FOR_PROCESSING( ( const uint8_t * const ) ( xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD ) );
698
699 if( eResult == eProcessBuffer )
700 {
701 if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) == 0 )
702 {
703 ulPHYLinkStatus |= PHY_LINK_CONNECTED;
704 FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d (message received)\n", ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != 0 ) );
705 }
706
707 #if ( ipconfigZERO_COPY_RX_DRIVER != 0 )
708 if( uxGetNumberOfFreeNetworkBuffers() > uxMinimumBuffersRemaining )
709 {
710 pxNewDescriptor = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, xDescriptorWaitTime );
711 }
712 else
713 {
714 /* Too risky to allocate a new Network Buffer. */
715 pxNewDescriptor = NULL;
716 }
717
718 if( pxNewDescriptor != NULL )
719 #else /* if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) */
720 if( uxGetNumberOfFreeNetworkBuffers() > uxMinimumBuffersRemaining )
721 #endif /* ipconfigZERO_COPY_RX_DRIVER */
722 {
723 #if ( ipconfigZERO_COPY_RX_DRIVER != 0 )
724 const uint8_t * pucBuffer;
725 #endif
726
727 /* Get the actual length. */
728 usLength = RDES_FLMSK( ulStatus );
729
730 #if ( ipconfigZERO_COPY_RX_DRIVER != 0 )
731 {
732 /* Replace the character buffer 'B1ADD'. */
733 pucBuffer = ( const uint8_t * const ) ( xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD );
734 xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD = ( uint32_t ) pxNewDescriptor->pucEthernetBuffer;
735
736 /* 'B1ADD' contained the address of a 'pucEthernetBuffer' that
737 * belongs to a Network Buffer. Find the original Network Buffer. */
738 pxDescriptor = pxPacketBuffer_to_NetworkBuffer( pucBuffer );
739
740 /* This zero-copy driver makes sure that every 'xDMARxDescriptors' contains
741 * a reference to a Network Buffer at any time.
742 * In case it runs out of Network Buffers, a DMA buffer won't be replaced,
743 * and the received messages is dropped. */
744 configASSERT( pxDescriptor != NULL );
745 }
746 #else /* if ( ipconfigZERO_COPY_RX_DRIVER != 0 ) */
747 {
748 /* Create a buffer of exactly the required length. */
749 pxDescriptor = pxGetNetworkBufferWithDescriptor( usLength, xDescriptorWaitTime );
750 }
751 #endif /* ipconfigZERO_COPY_RX_DRIVER */
752
753 if( pxDescriptor != NULL )
754 {
755 pxDescriptor->xDataLength = ( size_t ) usLength;
756 #if ( ipconfigZERO_COPY_RX_DRIVER == 0 )
757 {
758 /* Copy the data into the allocated buffer. */
759 memcpy( ( void * ) pxDescriptor->pucEthernetBuffer, ( void * ) xDMARxDescriptors[ ulNextRxDescriptorToProcess ].B1ADD, usLength );
760 }
761 #endif /* ipconfigZERO_COPY_RX_DRIVER */
762
763 /* It is possible that more data was copied than
764 * actually makes up the frame. If this is the case
765 * adjust the length to remove any trailing bytes. */
766 prvRemoveTrailingBytes( pxDescriptor );
767
768 /* Pass the data to the TCP/IP task for processing. */
769 xRxEvent.pvData = ( void * ) pxDescriptor;
770
771 if( xSendEventStructToIPTask( &xRxEvent, xDescriptorWaitTime ) == pdFALSE )
772 {
773 /* Could not send the descriptor into the TCP/IP
774 * stack, it must be released. */
775 vReleaseNetworkBufferAndDescriptor( pxDescriptor );
776 }
777 else
778 {
779 iptraceNETWORK_INTERFACE_RECEIVE();
780
781 /* The data that was available at the top of this
782 * loop has been sent, so is no longer available. */
783 ulDataAvailable = pdFALSE;
784 }
785 }
786 }
787 }
788 else
789 {
790 /* The packet is discarded as uninteresting. */
791 ulDataAvailable = pdFALSE;
792 }
793
794 /* Got here because received data was sent to the IP task or the
795 * data contained an error and was discarded. Give the descriptor
796 * back to the DMA. */
797 xDMARxDescriptors[ ulNextRxDescriptorToProcess ].STATUS = ulStatus | RDES_OWN;
798
799 /* Move onto the next descriptor. */
800 ulNextRxDescriptorToProcess++;
801
802 if( ulNextRxDescriptorToProcess >= configNUM_RX_DESCRIPTORS )
803 {
804 ulNextRxDescriptorToProcess = 0;
805 }
806
807 ulStatus = xDMARxDescriptors[ ulNextRxDescriptorToProcess ].STATUS;
808 } /* if( ( ulStatus & nwRX_STATUS_ERROR_BITS ) != 0 ) */
809 } /* if( ( ulStatus & RDES_OWN ) == 0 ) */
810
811 /* Restart receive polling. */
812 LPC_ETHERNET->DMA_REC_POLL_DEMAND = 1;
813
814 return xResult;
815 }
816 /*-----------------------------------------------------------*/
817
818 configPLACE_IN_SECTION_RAM
NETWORK_IRQHandler(void)819 void NETWORK_IRQHandler( void )
820 {
821 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
822 uint32_t ulDMAStatus;
823 const uint32_t ulRxInterruptMask =
824 DMA_ST_RI | /* Receive interrupt */
825 DMA_ST_RU; /* Receive buffer unavailable */
826 const uint32_t ulTxInterruptMask =
827 DMA_ST_TI | /* Transmit interrupt */
828 DMA_ST_TPS; /* Transmit process stopped */
829
830 configASSERT( xRxHanderTask );
831
832 /* Get pending interrupts. */
833 ulDMAStatus = LPC_ETHERNET->DMA_STAT;
834
835 /* RX group interrupt(s). */
836 if( ( ulDMAStatus & ulRxInterruptMask ) != 0x00 )
837 {
838 /* Remember that an RX event has happened. */
839 ulISREvents |= EMAC_IF_RX_EVENT;
840 vTaskNotifyGiveFromISR( xRxHanderTask, &xHigherPriorityTaskWoken );
841 intCount[ 0 ]++;
842 }
843
844 /* TX group interrupt(s). */
845 if( ( ulDMAStatus & ulTxInterruptMask ) != 0x00 )
846 {
847 /* Remember that a TX event has happened. */
848 ulISREvents |= EMAC_IF_TX_EVENT;
849 vTaskNotifyGiveFromISR( xRxHanderTask, &xHigherPriorityTaskWoken );
850 intCount[ 1 ]++;
851 }
852
853 /* Test for 'Abnormal interrupt summary'. */
854 if( ( ulDMAStatus & DMA_ST_AIE ) != 0x00 )
855 {
856 /* The trace macro must be written such that it can be called from
857 * an interrupt. */
858 iptraceETHERNET_RX_EVENT_LOST();
859 }
860
861 /* Clear pending interrupts */
862 LPC_ETHERNET->DMA_STAT = ulDMAStatus;
863
864 /* Context switch needed? */
865 portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
866 }
867 /*-----------------------------------------------------------*/
868
prvSetLinkSpeed(void)869 static BaseType_t prvSetLinkSpeed( void )
870 {
871 BaseType_t xReturn = pdFAIL;
872 TickType_t xTimeOnEntering;
873 uint32_t ulPhyStatus;
874 const TickType_t xAutoNegotiateDelay = pdMS_TO_TICKS( 5000UL );
875
876 /* Ensure polling does not starve lower priority tasks by temporarily
877 * setting the priority of this task to that of the idle task. */
878 vTaskPrioritySet( NULL, tskIDLE_PRIORITY );
879
880 xTimeOnEntering = xTaskGetTickCount();
881
882 do
883 {
884 ulPhyStatus = lpcPHYStsPoll();
885
886 if( ( ulPhyStatus & PHY_LINK_CONNECTED ) != 0x00 )
887 {
888 /* Set interface speed and duplex. */
889 if( ( ulPhyStatus & PHY_LINK_SPEED100 ) != 0x00 )
890 {
891 Chip_ENET_SetSpeed( LPC_ETHERNET, 1 );
892 }
893 else
894 {
895 Chip_ENET_SetSpeed( LPC_ETHERNET, 0 );
896 }
897
898 if( ( ulPhyStatus & PHY_LINK_FULLDUPLX ) != 0x00 )
899 {
900 Chip_ENET_SetDuplex( LPC_ETHERNET, true );
901 }
902 else
903 {
904 Chip_ENET_SetDuplex( LPC_ETHERNET, false );
905 }
906
907 xReturn = pdPASS;
908 break;
909 }
910 } while( ( xTaskGetTickCount() - xTimeOnEntering ) < xAutoNegotiateDelay );
911
912 /* Reset the priority of this task back to its original value. */
913 vTaskPrioritySet( NULL, ipconfigIP_TASK_PRIORITY );
914
915 return xReturn;
916 }
917 /*-----------------------------------------------------------*/
918
prvGenerateCRC32(const uint8_t * ucAddress)919 static uint32_t prvGenerateCRC32( const uint8_t * ucAddress )
920 {
921 unsigned int j;
922 const uint32_t Polynomial = 0xEDB88320;
923 uint32_t crc = ~0ul;
924 const uint8_t * pucCurrent = ( const uint8_t * ) ucAddress;
925 const uint8_t * pucLast = pucCurrent + 6;
926
927 /* Calculate normal CRC32 */
928 while( pucCurrent < pucLast )
929 {
930 crc ^= *( pucCurrent++ );
931
932 for( j = 0; j < 8; j++ )
933 {
934 if( ( crc & 1 ) != 0 )
935 {
936 crc = ( crc >> 1 ) ^ Polynomial;
937 }
938 else
939 {
940 crc >>= 1;
941 }
942 }
943 }
944
945 return ~crc;
946 }
947 /*-----------------------------------------------------------*/
948
prvGetHashIndex(const uint8_t * ucAddress)949 static uint32_t prvGetHashIndex( const uint8_t * ucAddress )
950 {
951 uint32_t ulCrc = prvGenerateCRC32( ucAddress );
952 uint32_t ulIndex = 0ul;
953 BaseType_t xCount = 6;
954
955 /* Take the lowest 6 bits of the CRC32 and reverse them */
956 while( xCount-- )
957 {
958 ulIndex <<= 1;
959 ulIndex |= ( ulCrc & 1 );
960 ulCrc >>= 1;
961 }
962
963 /* This is the has value of 'ucAddress' */
964 return ulIndex;
965 }
966 /*-----------------------------------------------------------*/
967
prvAddMACAddress(const uint8_t * ucMacAddress)968 static void prvAddMACAddress( const uint8_t * ucMacAddress )
969 {
970 BaseType_t xIndex;
971
972 xIndex = prvGetHashIndex( ucMacAddress );
973
974 if( xIndex >= 32 )
975 {
976 LPC_ETHERNET->MAC_HASHTABLE_HIGH |= ( 1u << ( xIndex - 32 ) );
977 }
978 else
979 {
980 LPC_ETHERNET->MAC_HASHTABLE_LOW |= ( 1u << xIndex );
981 }
982 }
983 /*-----------------------------------------------------------*/
984
985 configPLACE_IN_SECTION_RAM
prvEMACHandlerTask(void * pvParameters)986 static void prvEMACHandlerTask( void * pvParameters )
987 {
988 TimeOut_t xPhyTime;
989 TickType_t xPhyRemTime;
990 UBaseType_t uxLastMinBufferCount = 0;
991 UBaseType_t uxCurrentCount;
992 BaseType_t xResult = 0;
993 uint32_t ulStatus;
994 const TickType_t xBlockTime = pdMS_TO_TICKS( 5000ul );
995
996 /* Remove compiler warning about unused parameter. */
997 ( void ) pvParameters;
998
999 /* A possibility to set some additional task properties. */
1000 iptraceEMAC_TASK_STARTING();
1001
1002 vTaskSetTimeOutState( &xPhyTime );
1003 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );
1004
1005 for( ; ; )
1006 {
1007 uxCurrentCount = uxGetMinimumFreeNetworkBuffers();
1008
1009 if( uxLastMinBufferCount != uxCurrentCount )
1010 {
1011 /* The logging produced below may be helpful
1012 * while tuning +TCP: see how many buffers are in use. */
1013 uxLastMinBufferCount = uxCurrentCount;
1014 FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n",
1015 uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) );
1016 }
1017
1018 #if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 )
1019 {
1020 static UBaseType_t uxLastMinQueueSpace = 0;
1021
1022 uxCurrentCount = uxGetMinimumIPQueueSpace();
1023
1024 if( uxLastMinQueueSpace != uxCurrentCount )
1025 {
1026 /* The logging produced below may be helpful
1027 * while tuning +TCP: see how many buffers are in use. */
1028 uxLastMinQueueSpace = uxCurrentCount;
1029 FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) );
1030 }
1031 }
1032 #endif /* ipconfigCHECK_IP_QUEUE_SPACE */
1033
1034 ulTaskNotifyTake( pdTRUE, xBlockTime );
1035
1036 xResult = ( BaseType_t ) 0;
1037
1038 if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 )
1039 {
1040 /* Code to release TX buffers if zero-copy is used. */
1041 ulISREvents &= ~EMAC_IF_TX_EVENT;
1042 {
1043 /* Check if DMA packets have been delivered. */
1044 vClearTXBuffers();
1045 }
1046 }
1047
1048 if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 )
1049 {
1050 ulISREvents &= ~EMAC_IF_RX_EVENT;
1051
1052 xResult = prvNetworkInterfaceInput();
1053
1054 if( xResult > 0 )
1055 {
1056 while( prvNetworkInterfaceInput() > 0 )
1057 {
1058 }
1059 }
1060 }
1061
1062 if( xResult > 0 )
1063 {
1064 /* A packet was received. No need to check for the PHY status now,
1065 * but set a timer to check it later on. */
1066 vTaskSetTimeOutState( &xPhyTime );
1067 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );
1068 xResult = 0;
1069 }
1070 else if( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE )
1071 {
1072 ulStatus = lpcPHYStsPoll();
1073
1074 if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != ( ulStatus & PHY_LINK_CONNECTED ) )
1075 {
1076 ulPHYLinkStatus = ulStatus;
1077 FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d (polled PHY)\n", ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != 0 ) );
1078 }
1079
1080 vTaskSetTimeOutState( &xPhyTime );
1081
1082 if( ( ulPHYLinkStatus & PHY_LINK_CONNECTED ) != 0 )
1083 {
1084 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );
1085 }
1086 else
1087 {
1088 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );
1089 }
1090 }
1091 }
1092 }
1093 /*-----------------------------------------------------------*/
1094