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