1 /*
2 * FreeRTOS+TCP <DEVELOPMENT BRANCH>
3 * Copyright (C) 2023 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 * https://www.FreeRTOS.org
25 * https://github.com/FreeRTOS
26 */
27
28 /**
29 * @file NetworkInterface.c
30 * @brief Implements the Network Interface driver for the NXP MIMXRT1060 board.
31 */
32 /* FreeRTOS includes. */
33 #include "FreeRTOS.h"
34 #include "task.h"
35 #include "queue.h"
36 #include "semphr.h"
37
38 #include "fsl_device_registers.h"
39 #include "fsl_debug_console.h"
40 #include "board.h"
41
42 #include "fsl_phy.h"
43
44 #include "fsl_phyksz8081.h"
45 #include "fsl_enet_mdio.h"
46 #include "fsl_enet.h"
47
48 /* FreeRTOS+TCP includes. */
49 #include "FreeRTOS_IP.h"
50 #include "FreeRTOS_Routing.h"
51 #include "FreeRTOS_ARP.h"
52 #include "NetworkInterface.h"
53
54 #if ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES != 1
55 #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer
56 #else
57 #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) )
58 #endif
59
60 /* MDIO operations. */
61 #define EXAMPLE_MDIO_OPS enet_ops
62
63 /* PHY operations. */
64 #define EXAMPLE_PHY_OPS phyksz8081_ops
65
66 /* ENET clock frequency. */
67 #define EXAMPLE_CLOCK_FREQ CLOCK_GetFreq( kCLOCK_IpgClk )
68
69
70 /*******************************************************************************
71 * Definitions
72 ******************************************************************************/
73 #define ENET_RING_NUM ( 1 )
74
75 /* The length or RX buffer. */
76 #ifndef ENET_RXBUFF_SIZE
77 #define ENET_RXBUFF_SIZE ( ENET_FRAME_MAX_FRAMELEN )
78 #endif
79
80 /* ENET IRQ priority. Used in FreeRTOS. */
81 /* Interrupt priorities. */
82 #ifdef __CA7_REV
83 #ifndef ENET_PRIORITY
84 #define ENET_PRIORITY ( 21U )
85 #endif
86 #ifndef ENET_1588_PRIORITY
87 #define ENET_1588_PRIORITY ( 20U )
88 #endif
89 #else
90 #ifndef ENET_PRIORITY
91 #define ENET_PRIORITY ( 6U )
92 #endif
93 #ifndef ENET_1588_PRIORITY
94 #define ENET_1588_PRIORITY ( 5U )
95 #endif
96 #endif /* ifdef __CA7_REV */
97
98 /* The number of ENET buffers needed to receive frame of maximum length. */
99 #define MAX_BUFFERS_PER_FRAME \
100 ( ( ENET_FRAME_MAX_FRAMELEN / ENET_RXBUFF_SIZE ) + ( ( ENET_FRAME_MAX_FRAMELEN % ENET_RXBUFF_SIZE == 0 ) ? 0 : 1 ) )
101
102 /* The length or TX buffer. */
103 #ifndef ENET_TXBUFF_SIZE
104 #define ENET_TXBUFF_SIZE ( ENET_FRAME_MAX_FRAMELEN )
105 #endif
106
107 /* The number of buffer descriptors in ENET RX ring. */
108 #ifndef ENET_RXBD_NUM
109 #define ENET_RXBD_NUM ( 5 )
110 #endif
111
112 /* Ring should be able to receive at least 1 frame with maximum length. */
113 #if ENET_RXBD_NUM < MAX_BUFFERS_PER_FRAME
114 #error "ENET_RXBD_NUM < MAX_BUFFERS_PER_FRAME"
115 #endif
116
117 /* The number of RX buffers. ENET_RXBD_NUM is always held by ENET driver. */
118 #ifndef ENET_RXBUFF_NUM
119 #define ENET_RXBUFF_NUM ( ENET_RXBD_NUM * 2 )
120 #endif
121
122 /* At least ENET_RXBD_NUM number of buffers is always held by ENET driver
123 * for RX. */
124 #if ENET_RXBUFF_NUM < ( ENET_RXBD_NUM + MAX_BUFFERS_PER_FRAME )
125 #error "ENET_RXBUFF_NUM < (ENET_RXBD_NUM + MAX_BUFFERS_PER_FRAME)"
126 #endif
127
128 /* The number of buffer descriptors in ENET TX ring. */
129 #ifndef ENET_TXBD_NUM
130 #define ENET_TXBD_NUM ( 3 )
131 #endif
132
133 /* Set the timeout values such that the total timeout adds up to 4000ms. */
134 #define MAX_AUTONEG_FAILURE_COUNT ( 40 )
135 #define SINGLE_ITERATION_TIMEOUT ( 100 )
136
137 #if defined( FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL ) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL
138 #if defined( FSL_FEATURE_L2CACHE_LINESIZE_BYTE ) && \
139 ( ( !defined( FSL_SDK_DISBLE_L2CACHE_PRESENT ) ) || ( FSL_SDK_DISBLE_L2CACHE_PRESENT == 0 ) )
140 #if defined( FSL_FEATURE_L1DCACHE_LINESIZE_BYTE )
141 #define FSL_CACHE_LINESIZE_MAX MAX( FSL_FEATURE_L1DCACHE_LINESIZE_BYTE, FSL_FEATURE_L2CACHE_LINESIZE_BYTE )
142 #define FSL_ENET_BUFF_ALIGNMENT MAX( ENET_BUFF_ALIGNMENT, FSL_CACHE_LINESIZE_MAX )
143 #else
144 #define FSL_ENET_BUFF_ALIGNMENT MAX( ENET_BUFF_ALIGNMENT, FSL_FEATURE_L2CACHE_LINESIZE_BYTE )
145 #endif
146 #elif defined( FSL_FEATURE_L1DCACHE_LINESIZE_BYTE )
147 #define FSL_ENET_BUFF_ALIGNMENT MAX( ENET_BUFF_ALIGNMENT, FSL_FEATURE_L1DCACHE_LINESIZE_BYTE )
148 #else
149 #define FSL_ENET_BUFF_ALIGNMENT ENET_BUFF_ALIGNMENT
150 #endif /* if defined( FSL_FEATURE_L2CACHE_LINESIZE_BYTE ) && ( ( !defined( FSL_SDK_DISBLE_L2CACHE_PRESENT ) ) || ( FSL_SDK_DISBLE_L2CACHE_PRESENT == 0 ) ) */
151 #else /* if defined( FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL ) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */
152 #define FSL_ENET_BUFF_ALIGNMENT ENET_BUFF_ALIGNMENT
153 #endif /* if defined( FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL ) && FSL_SDK_ENABLE_DRIVER_CACHE_CONTROL */
154
155 /* A bigger value is chosen so that the previous notification values are
156 * not interfering with the driver ready notifications. */
157 #define DRIVER_READY ( 0x40 )
158 #define DRIVER_FATAL ( DRIVER_READY << 1 )
159
160 #if defined( ENET_ENHANCEDBUFFERDESCRIPTOR_MODE )
161 #error "ENET_ENHANCEDBUFFERDESCRIPTOR_MODE is not supported by this driver"
162 #endif
163
164 typedef uint8_t rx_buffer_t[ SDK_SIZEALIGN( ENET_RXBUFF_SIZE, FSL_ENET_BUFF_ALIGNMENT ) ];
165 typedef uint8_t tx_buffer_t[ SDK_SIZEALIGN( ENET_TXBUFF_SIZE, FSL_ENET_BUFF_ALIGNMENT ) ];
166
167 /**
168 * @brief Used to wrap received data a buffer to be passed into the stack.
169 */
170 typedef struct rx_pbuf_wrapper
171 {
172 void * buffer; /*!< Original buffer wrapped. */
173 volatile bool buffer_used; /*!< Wrapped buffer is used by ENET or FreeRTOS+TCP. */
174 } rx_pbuf_wrapper_t;
175
176 /**
177 * Helper structure to hold private data used to operate your Ethernet interface.
178 */
179 struct ethernetif
180 {
181 ENET_Type * base;
182 enet_handle_t handle;
183 enet_rx_bd_struct_t * RxBuffDescrip;
184 enet_tx_bd_struct_t * TxBuffDescrip;
185 rx_buffer_t * RxDataBuff;
186 tx_buffer_t * TxDataBuff;
187 rx_pbuf_wrapper_t RxPbufs[ ENET_RXBUFF_NUM ];
188 };
189
190 typedef enum xEMAC_STATE
191 {
192 xEMAC_SetupPHY,
193 xEMAC_WaitPHY,
194 xEMAC_Init,
195 xEMAC_Ready,
196 } EMACState_t;
197
198 static EMACState_t eEMACState = xEMAC_SetupPHY;
199
200 static mdio_handle_t mdioHandle = { .ops = &EXAMPLE_MDIO_OPS };
201
202 static phy_handle_t phyHandle = { .phyAddr = 0x00, .mdioHandle = &mdioHandle, .ops = &EXAMPLE_PHY_OPS };
203
204 /**
205 * The task-handle for deferred interrupt handler task that processes
206 * incoming packets.
207 */
208 static TaskHandle_t receiveTaskHandle = NULL;
209
210 static struct ethernetif EthernetInterface1;
211 static struct ethernetif * ethernetifLocal = &EthernetInterface1;
212
213 static bool bGlobalLinkStatus = false;
214
215 static NetworkInterface_t * pxMyInterface = NULL;
216
217 /*-----------------------------------------------------------*/
218
219 AT_NONCACHEABLE_SECTION_ALIGN( static enet_rx_bd_struct_t rxBuffDescrip_0[ ENET_RXBD_NUM ], FSL_ENET_BUFF_ALIGNMENT );
220 AT_NONCACHEABLE_SECTION_ALIGN( static enet_tx_bd_struct_t txBuffDescrip_0[ ENET_TXBD_NUM ], FSL_ENET_BUFF_ALIGNMENT );
221 SDK_ALIGN( static rx_buffer_t rxDataBuff_0[ ENET_RXBUFF_NUM ], FSL_ENET_BUFF_ALIGNMENT );
222 SDK_ALIGN( static tx_buffer_t txDataBuff_0[ ENET_TXBD_NUM ], FSL_ENET_BUFF_ALIGNMENT );
223
224 /*-----------------------------------------------------------*/
225 /*-----------------------------------------------------------*/
226 /*------------ PHY configuration parameters. ----------------*/
227 static phy_config_t xConfig =
228 {
229 .autoNeg = pdTRUE, /* Allow auto-negotiation. */
230 .duplex = kPHY_FullDuplex, /* Use full duplex mode. In case
231 * auto-negotiation is turned on,
232 * this is not used. */
233 .phyAddr = BOARD_ENET0_PHY_ADDRESS, /* The PHY address. */
234 .speed = kPHY_Speed100M, /* Use 100 Mbps configuration (maximum possible
235 * for this PHY). In case auto-negotiation is
236 * turned on, this is not used. */
237 .enableEEE = pdFALSE /* Disable the energy efficient PHY. */
238 };
239
240 /*-----------------------------------------------------------*/
241 /*-----------------------------------------------------------*/
242
243 static void prvEMACHandlerTask( void * pvParameters );
244
245 static void ethernet_callback( ENET_Type * base,
246 enet_handle_t * handle,
247 enet_event_t event,
248 enet_frame_info_t * frameInfo,
249 void * userData );
250
251 static void prvProcessFrame( int length );
252
253 static status_t xSetupPHY( phy_config_t * pxConfig );
254
255 static status_t xWaitPHY( phy_config_t xConfig );
256
257 static status_t xEMACInit( phy_speed_t speed,
258 phy_duplex_t duplex );
259
260 static BaseType_t prvNXP1060_NetworkInterfaceInitialise( NetworkInterface_t * pxInterface );
261
262 static BaseType_t prvNXP1060_NetworkInterfaceOutput( NetworkInterface_t * pxInterface,
263 NetworkBufferDescriptor_t * const pxNetworkBuffer,
264 BaseType_t xReleaseAfterSend );
265
266 static BaseType_t prvNXP1060_GetPhyLinkStatus( NetworkInterface_t * pxInterface );
267
268 NetworkInterface_t * pxNXP1060_FillInterfaceDescriptor( BaseType_t xEMACIndex,
269 NetworkInterface_t * pxInterface );
270 /*-----------------------------------------------------------*/
271
272 #if ( ipconfigCOMPATIBLE_WITH_SINGLE != 0 )
273
274 /* Do not call the following function directly. It is there for downward compatibility.
275 * The function FreeRTOS_IPInit() will call it to initialice the interface and end-point
276 * objects. See the description in FreeRTOS_Routing.h. */
pxFillInterfaceDescriptor(BaseType_t xEMACIndex,NetworkInterface_t * pxInterface)277 NetworkInterface_t * pxFillInterfaceDescriptor( BaseType_t xEMACIndex,
278 NetworkInterface_t * pxInterface )
279 {
280 pxNXP1060_FillInterfaceDescriptor( xEMACIndex, pxInterface );
281 }
282
283 #endif /* ( ipconfigCOMPATIBLE_WITH_SINGLE != 0 ) */
284 /*-----------------------------------------------------------*/
285
pxNXP1060_FillInterfaceDescriptor(BaseType_t xEMACIndex,NetworkInterface_t * pxInterface)286 NetworkInterface_t * pxNXP1060_FillInterfaceDescriptor( BaseType_t xEMACIndex,
287 NetworkInterface_t * pxInterface )
288 {
289 static char pcName[ 10 ];
290
291 /* This function pxNXP1060_FillInterfaceDescriptor() adds a network-interface.
292 * Make sure that the object pointed to by 'pxInterface'
293 * is declared static or global, and that it will remain to exist. */
294
295 snprintf( pcName, sizeof( pcName ), "NXP1060%ld", xEMACIndex );
296
297 memset( pxInterface, '\0', sizeof( *pxInterface ) );
298 pxInterface->pcName = pcName; /* Just for logging, debugging. */
299 pxInterface->pvArgument = ( void * ) xEMACIndex; /* Has only meaning for the driver functions. */
300 pxInterface->pfInitialise = prvNXP1060_NetworkInterfaceInitialise;
301 pxInterface->pfOutput = prvNXP1060_NetworkInterfaceOutput;
302 pxInterface->pfGetPhyLinkStatus = prvNXP1060_GetPhyLinkStatus;
303
304 FreeRTOS_AddNetworkInterface( pxInterface );
305 pxMyInterface = pxInterface;
306
307 return pxInterface;
308 }
309 /*-----------------------------------------------------------*/
310
prvNXP1060_NetworkInterfaceInitialise(NetworkInterface_t * pxInterface)311 static BaseType_t prvNXP1060_NetworkInterfaceInitialise( NetworkInterface_t * pxInterface )
312 {
313 status_t xStatus;
314 BaseType_t xResult = pdFAIL;
315 phy_speed_t speed;
316 phy_duplex_t duplex;
317 BaseType_t xTaskCreated;
318 static BaseType_t xFirstCall = pdTRUE;
319
320 configASSERT( FSL_FEATURE_ENET_QUEUE == 1 );
321
322 switch( eEMACState )
323 {
324 case xEMAC_SetupPHY:
325 xStatus = xSetupPHY( &xConfig );
326
327 if( xStatus != kStatus_Success )
328 {
329 break;
330 }
331 else
332 {
333 eEMACState = xEMAC_WaitPHY;
334 }
335
336 /* Fall through. */
337 case xEMAC_WaitPHY:
338 FreeRTOS_printf( ( "Configuration successful. Waiting for link to go up"
339 " and auto-negotiation to complete." ) );
340
341 xStatus = xWaitPHY( xConfig );
342
343 if( xStatus == kStatus_Success )
344 {
345 xStatus = PHY_GetLinkSpeedDuplex( &phyHandle, &speed, &duplex );
346 }
347
348 if( xStatus != kStatus_Success )
349 {
350 break;
351 }
352 else
353 {
354 eEMACState = xEMAC_Init;
355 }
356
357 /* Fall through. */
358 case xEMAC_Init:
359 xStatus = xEMACInit( speed, duplex );
360
361 if( ( xFirstCall == pdTRUE ) || ( receiveTaskHandle == NULL ) )
362 {
363 if( xStatus == kStatus_Success )
364 {
365 /* The link is now up. */
366 bGlobalLinkStatus = true;
367
368 /* The handler task is created at the highest possible priority to
369 * ensure the interrupt handler can return directly to it. */
370 xTaskCreated = xTaskCreate( prvEMACHandlerTask,
371 "EMAC-Handler",
372 configMINIMAL_STACK_SIZE * 3,
373 NULL,
374 configMAX_PRIORITIES - 1,
375 &receiveTaskHandle );
376
377 if( ( receiveTaskHandle == NULL ) || ( xTaskCreated != pdPASS ) )
378 {
379 FreeRTOS_printf( ( "Failed to create the handler task." ) );
380 break;
381 }
382
383 /* Enable the interrupt and set its priority to the minimum
384 * interrupt priority. */
385 NVIC_SetPriority( ENET_IRQn, ENET_PRIORITY );
386 NVIC_EnableIRQ( ENET_IRQn );
387
388 eEMACState = xEMAC_Ready;
389
390 /* After this, the task should not be created. */
391 xFirstCall = pdFALSE;
392 }
393 else
394 {
395 break;
396 }
397 }
398 else
399 {
400 eEMACState = xEMAC_Ready;
401 }
402
403 /* Fall through. */
404 case xEMAC_Ready:
405 FreeRTOS_printf( ( "Driver ready for use." ) );
406
407 /* Kick the task once the driver is ready. */
408 if( receiveTaskHandle != NULL )
409 {
410 xTaskNotify( receiveTaskHandle, DRIVER_READY, eSetValueWithOverwrite );
411 }
412
413 xResult = pdPASS;
414
415 break;
416 }
417
418 return xResult;
419 }
420 /*-----------------------------------------------------------*/
421
prvNXP1060_NetworkInterfaceOutput(NetworkInterface_t * pxInterface,NetworkBufferDescriptor_t * const pxNetworkBuffer,BaseType_t xReleaseAfterSend)422 static BaseType_t prvNXP1060_NetworkInterfaceOutput( NetworkInterface_t * pxInterface,
423 NetworkBufferDescriptor_t * const pxNetworkBuffer,
424 BaseType_t xReleaseAfterSend )
425 {
426 status_t result;
427 BaseType_t xReturn = pdFAIL;
428
429 /* Avoid warning about unused parameter. */
430 ( void ) pxInterface;
431
432 do
433 {
434 if( xCheckLoopback( pxNetworkBuffer, xReleaseAfterSend ) != 0 )
435 {
436 /* The packet has been sent back to the IP-task.
437 * The IP-task will further handle it.
438 * Do not release the descriptor. */
439 xReleaseAfterSend = pdFALSE;
440 break;
441 }
442
443 if( bGlobalLinkStatus == true )
444 {
445 /* ENET_SendFrame copies the data before sending it. Therefore, the network buffer can
446 * be released without worrying about the buffer memory being used by the ENET_SendFrame
447 * function. */
448 result = ENET_SendFrame( ethernetifLocal->base,
449 ðernetifLocal->handle,
450 pxNetworkBuffer->pucEthernetBuffer,
451 pxNetworkBuffer->xDataLength,
452 0,
453 false,
454 NULL );
455
456 switch( result )
457 {
458 case kStatus_ENET_TxFrameBusy:
459 FreeRTOS_printf( ( "Failed to send the frame - driver busy!" ) );
460 break;
461
462 case kStatus_Success:
463 iptraceNETWORK_INTERFACE_TRANSMIT();
464 xReturn = pdPASS;
465 break;
466 }
467 }
468 } while( ipFALSE_BOOL );
469
470 if( xReleaseAfterSend == pdTRUE )
471 {
472 vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
473 }
474
475 return xReturn;
476 }
477 /*-----------------------------------------------------------*/
478
prvNXP1060_GetPhyLinkStatus(NetworkInterface_t * pxInterface)479 static BaseType_t prvNXP1060_GetPhyLinkStatus( NetworkInterface_t * pxInterface )
480 {
481 BaseType_t xReturn = pdFALSE;
482
483 /* Avoid warning about unused parameter. */
484 ( void ) pxInterface;
485
486 if( bGlobalLinkStatus == true )
487 {
488 xReturn = pdTRUE;
489 }
490
491 return xReturn;
492 }
493 /*-----------------------------------------------------------*/
494
prvEMACHandlerTask(void * parameter)495 static void prvEMACHandlerTask( void * parameter )
496 {
497 bool bLinkUp = false;
498 status_t readStatus;
499
500 /* Wait for the driver to finish starting. */
501 ( void ) ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
502
503 while( pdTRUE )
504 {
505 if( ulTaskNotifyTake( pdTRUE, pdMS_TO_TICKS( 500 ) ) == pdFALSE )
506 {
507 /* No RX packets for a bit so check for a link. */
508 const IPStackEvent_t xNetworkEventDown = { .eEventType = eNetworkDownEvent, .pvData = NULL };
509
510 do
511 {
512 readStatus = PHY_GetLinkStatus( &phyHandle, &bLinkUp );
513
514 if( readStatus == kStatus_Success )
515 {
516 if( bLinkUp == pdFALSE )
517 {
518 /* The link is down. */
519 bGlobalLinkStatus = false;
520 /* We need to setup the PHY again. */
521 eEMACState = xEMAC_WaitPHY;
522
523 FreeRTOS_printf( ( "Link down!" ) );
524
525 xSendEventStructToIPTask( &xNetworkEventDown, 0U );
526
527 /* Wait for the driver to finish initialization. */
528 uint32_t ulNotificationValue;
529
530 do
531 {
532 ulNotificationValue = ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
533 } while( !( ulNotificationValue & DRIVER_READY ) );
534 }
535 else
536 {
537 /* The link is still up. */
538 bGlobalLinkStatus = true;
539 }
540 }
541 } while( bGlobalLinkStatus == false );
542 }
543 else
544 {
545 BaseType_t receiving = pdTRUE;
546
547 /* A packet was received, the link must be up. */
548 bGlobalLinkStatus = true;
549
550 while( receiving == pdTRUE )
551 {
552 uint32_t length;
553 const status_t status = ENET_GetRxFrameSize( &( ethernetifLocal->handle ), &length, 0 );
554
555 switch( status )
556 {
557 case kStatus_Success: /* there is a frame. process it */
558
559 if( length )
560 {
561 prvProcessFrame( length );
562 }
563
564 break;
565
566 case kStatus_ENET_RxFrameEmpty: /* Received an empty frame. Ignore it */
567 receiving = pdFALSE;
568 break;
569
570 case kStatus_ENET_RxFrameError: /* Received an error frame. Read & drop it */
571 PRINTF( "RX Receive Error\n" );
572 ENET_ReadFrame( ethernetifLocal->base, &( ethernetifLocal->handle ), NULL, 0, 0, NULL );
573 /* Not sure if a trace is required. The MAC had an error and needed to dump bytes */
574 break;
575
576 default:
577 PRINTF( "RX Receive default" );
578 break;
579 }
580 }
581 }
582 }
583 }
584 /*-----------------------------------------------------------*/
585
586 /**
587 * @brief Callback for ENET interrupts. We have only enabled the Ethernet receive interrupts
588 * in the case of this driver.
589 */
ethernet_callback(ENET_Type * base,enet_handle_t * handle,enet_event_t event,enet_frame_info_t * frameInfo,void * userData)590 static void ethernet_callback( ENET_Type * base,
591 enet_handle_t * handle,
592 enet_event_t event,
593 enet_frame_info_t * frameInfo,
594 void * userData )
595 {
596 BaseType_t needsToYield = pdFALSE;
597
598 ( void ) base;
599 ( void ) handle;
600 ( void ) frameInfo;
601 ( void ) userData;
602
603 switch( event )
604 {
605 case kENET_RxEvent:
606 vTaskNotifyGiveFromISR( receiveTaskHandle, &needsToYield );
607 portEND_SWITCHING_ISR( needsToYield );
608 break;
609
610 default:
611 FreeRTOS_printf( ( "Unknown interrupt callback %u!", event ) );
612 break;
613 }
614 }
615 /*-----------------------------------------------------------*/
616
617 /**
618 * @brief This function verifies that the incoming frame needs processing.
619 * If the frame is deemed to be appropriate, then the frame is sent to the
620 * TCP stack for further processing.
621 * @param[in] length: The length of the incoming frame. This length should be read
622 * using a call to ENET_GetRxFrameSize.
623 */
prvProcessFrame(int length)624 static void prvProcessFrame( int length )
625 {
626 NetworkBufferDescriptor_t * pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( length, 0 );
627 BaseType_t xRelease = pdFALSE;
628
629 if( pxBufferDescriptor != NULL )
630 {
631 ENET_ReadFrame( ethernetifLocal->base, &( ethernetifLocal->handle ), pxBufferDescriptor->pucEthernetBuffer, length, 0, NULL );
632 pxBufferDescriptor->xDataLength = length;
633 pxBufferDescriptor->pxInterface = pxMyInterface;
634 pxBufferDescriptor->pxEndPoint = FreeRTOS_MatchingEndpoint( pxMyInterface, pxBufferDescriptor->pucEthernetBuffer );
635
636 if( pxBufferDescriptor->pxEndPoint == NULL )
637 {
638 /* Endpoint not found, drop the packet. */
639 xRelease = pdTRUE;
640 }
641 else
642 {
643 if( ipCONSIDER_FRAME_FOR_PROCESSING( pxBufferDescriptor->pucEthernetBuffer ) == eProcessBuffer )
644 {
645 IPStackEvent_t xRxEvent;
646 xRxEvent.eEventType = eNetworkRxEvent;
647 xRxEvent.pvData = ( void * ) pxBufferDescriptor;
648
649 if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE )
650 {
651 xRelease = pdTRUE;
652 iptraceETHERNET_RX_EVENT_LOST();
653 FreeRTOS_printf( ( "RX Event Lost\n" ) );
654 }
655 }
656 else
657 {
658 #if ( ( ipconfigHAS_DEBUG_PRINTF == 1 ) && defined( FreeRTOS_debug_printf ) )
659 const EthernetHeader_t * pxEthernetHeader;
660 char ucSource[ 18 ];
661 char ucDestination[ 18 ];
662
663 pxEthernetHeader = ( ( const EthernetHeader_t * ) pxBufferDescriptor->pucEthernetBuffer );
664
665 FreeRTOS_EUI48_ntop( pxEthernetHeader->xSourceAddress.ucBytes, ucSource, 'A', ':' );
666 FreeRTOS_EUI48_ntop( pxEthernetHeader->xDestinationAddress.ucBytes, ucDestination, 'A', ':' );
667
668 FreeRTOS_debug_printf( ( "Invalid target MAC: dropping frame from: %s to: %s", ucSource, ucDestination ) );
669 #endif /* if ( ( ipconfigHAS_DEBUG_PRINTF == 1 ) && defined( FreeRTOS_debug_printf ) ) */
670 xRelease = pdTRUE;
671 /* Not sure if a trace is required. The stack did not want this message */
672 }
673 }
674
675 if( xRelease != pdFALSE )
676 {
677 vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor );
678 }
679 }
680 else
681 {
682 #if ( ( ipconfigHAS_DEBUG_PRINTF == 1 ) && defined( FreeRTOS_debug_printf ) )
683 FreeRTOS_debug_printf( ( "No Buffer Available: dropping incoming frame!!" ) );
684 #endif
685 ENET_ReadFrame( ENET, &( ethernetifLocal->handle ), NULL, length, 0, NULL );
686
687 /* No buffer available to receive this message */
688 iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER();
689 }
690 }
691 /*-----------------------------------------------------------*/
692
693 /**
694 * @brief This function is used to setup the PHY in auto-negotiation mode.
695 *
696 * @param[out] pxConfig: the configuration parameters.
697 *
698 * @return kStatus_Success if the PHY was initialized; error code otherwise.
699 */
xSetupPHY(phy_config_t * pxConfig)700 static status_t xSetupPHY( phy_config_t * pxConfig )
701 {
702 status_t xStatus;
703
704 /* Set the clock frequency. MDIO handle is pointed to by the PHY handle. */
705 mdioHandle.resource.csrClock_Hz = EXAMPLE_CLOCK_FREQ;
706 mdioHandle.resource.base = ( void * ) ENET_BASE;
707
708 FreeRTOS_printf( ( "Starting PHY initialization." ) );
709
710 xStatus = PHY_Init( &phyHandle, pxConfig );
711
712 if( xStatus != kStatus_Success )
713 {
714 FreeRTOS_printf( ( "Failed to initialize the PHY." ) );
715 }
716
717 return xStatus;
718 }
719 /*-----------------------------------------------------------*/
720
721 /**
722 * @brief This function is used wait on the auto-negotiation completion.
723 *
724 * @param[in] xConfig: the configuration parameters.
725 *
726 * @return kStatus_Success if the PHY was initialized; error code otherwise.
727 */
xWaitPHY(phy_config_t xConfig)728 static status_t xWaitPHY( phy_config_t xConfig )
729 {
730 status_t xStatus;
731 bool bLinkUp;
732 bool bAutoNegotiationComplete;
733 uint8_t ucCounter = 0;
734
735 do
736 {
737 xStatus = PHY_GetLinkStatus( &phyHandle, &bLinkUp );
738
739 if( bLinkUp == true )
740 {
741 break;
742 }
743
744 /* Try for only a limited number of times. */
745 if( ucCounter++ > MAX_AUTONEG_FAILURE_COUNT )
746 {
747 break;
748 }
749
750 vTaskDelay( pdMS_TO_TICKS( SINGLE_ITERATION_TIMEOUT ) );
751 }
752 while( xStatus == kStatus_Success );
753
754 if( bLinkUp == false )
755 {
756 FreeRTOS_printf( ( "Failed to get the link up." ) );
757 xStatus = kStatus_Fail;
758 }
759 else
760 {
761 FreeRTOS_printf( ( "Link up." ) );
762 }
763
764 if( ( xStatus == kStatus_Success ) &&
765 ( bLinkUp == true ) &&
766 ( xConfig.autoNeg == true ) )
767 {
768 /* Reset the counter for next use. */
769 ucCounter = 0;
770
771 FreeRTOS_printf( ( "Waiting for auto-negotiation to complete." ) );
772
773 do
774 {
775 xStatus = PHY_GetAutoNegotiationStatus( &phyHandle, &bAutoNegotiationComplete );
776
777 if( bAutoNegotiationComplete == true )
778 {
779 break;
780 }
781
782 /* Try for only a limited number of times. */
783 if( ucCounter++ > MAX_AUTONEG_FAILURE_COUNT )
784 {
785 break;
786 }
787
788 vTaskDelay( pdMS_TO_TICKS( SINGLE_ITERATION_TIMEOUT ) );
789 }
790 while( xStatus == kStatus_Success );
791
792 if( bAutoNegotiationComplete == false )
793 {
794 FreeRTOS_printf( ( "Failed to complete auto-negotiation." ) );
795 xStatus = kStatus_Fail;
796 }
797 else
798 {
799 /* Success in auto-negotiation and the link is up. */
800 FreeRTOS_printf( ( "Auto-negotiation complete." ) );
801 }
802 }
803
804 return xStatus;
805 }
806 /*-----------------------------------------------------------*/
807
808 /**
809 * @brief This function is used to initialize the ENET module. It initializes the network buffers
810 * and buffer descriptors.
811 *
812 * @param[in] speed: The speed of communication (either set by auto-negotiation or the default
813 * value).
814 * @param[in] duplex: The nature of the channel. This must be set to kPHY_FullDuplex by
815 * auto-negotiation.
816 *
817 * @return kStatus_Success if the ENET module was initialized; error code otherwise.
818 */
xEMACInit(phy_speed_t speed,phy_duplex_t duplex)819 static status_t xEMACInit( phy_speed_t speed,
820 phy_duplex_t duplex )
821 {
822 enet_config_t config;
823 uint32_t sysClock;
824 enet_buffer_config_t buffCfg[ ENET_RING_NUM ];
825 status_t xStatus;
826 uint32_t instance;
827 static ENET_Type * const enetBases[] = ENET_BASE_PTRS;
828 static const IRQn_Type enetTxIrqId[] = ENET_Transmit_IRQS;
829 /*! @brief Pointers to enet receive IRQ number for each instance. */
830 static const IRQn_Type enetRxIrqId[] = ENET_Receive_IRQS;
831 int i;
832 NetworkEndPoint_t * pxEndPoint;
833
834 ethernetifLocal->RxBuffDescrip = &( rxBuffDescrip_0[ 0 ] );
835 ethernetifLocal->TxBuffDescrip = &( txBuffDescrip_0[ 0 ] );
836 ethernetifLocal->RxDataBuff = &( rxDataBuff_0[ 0 ] );
837 ethernetifLocal->TxDataBuff = &( txDataBuff_0[ 0 ] );
838 ethernetifLocal->base = ( void * ) ENET_BASE;
839
840 /* prepare the buffer configuration. */
841 buffCfg[ 0 ].rxBdNumber = ENET_RXBD_NUM; /* Number of RX buffer descriptors. */
842 buffCfg[ 0 ].txBdNumber = ENET_TXBD_NUM; /* Transmit buffer descriptor number. */
843 buffCfg[ 0 ].rxBuffSizeAlign = sizeof( rx_buffer_t ); /* Aligned receive data buffer size. */
844 buffCfg[ 0 ].txBuffSizeAlign = sizeof( tx_buffer_t ); /* Aligned transmit data buffer size. */
845 buffCfg[ 0 ].rxBdStartAddrAlign =
846 &( rxBuffDescrip_0[ 0 ] ); /* Aligned receive buffer descriptor start address. */
847 buffCfg[ 0 ].txBdStartAddrAlign =
848 &( txBuffDescrip_0[ 0 ] ); /* Aligned transmit buffer descriptor start address. */
849 buffCfg[ 0 ].rxBufferAlign =
850 &( rxDataBuff_0[ 0 ][ 0 ] ); /* Receive data buffer start address. */
851 buffCfg[ 0 ].txBufferAlign = &( txDataBuff_0[ 0 ][ 0 ] ); /* Transmit data buffer start address. */
852 buffCfg[ 0 ].txFrameInfo = NULL; /* Transmit frame information start address. Set only if using zero-copy transmit. */
853 buffCfg[ 0 ].rxMaintainEnable = true; /* Receive buffer cache maintain. */
854 buffCfg[ 0 ].txMaintainEnable = true; /* Transmit buffer cache maintain. */
855
856 sysClock = phyHandle.mdioHandle->resource.csrClock_Hz;
857
858 ENET_GetDefaultConfig( &config );
859
860 config.ringNum = ENET_RING_NUM;
861 config.rxBuffAlloc = NULL;
862 config.rxBuffFree = NULL;
863 config.userData = ethernetifLocal;
864 config.miiSpeed = ( enet_mii_speed_t ) speed;
865 config.miiDuplex = ( enet_mii_duplex_t ) duplex;
866
867 /* Only get interrupt for incoming messages. */
868 config.interrupt = kENET_RxFrameInterrupt;
869 config.callback = ethernet_callback;
870
871 for( instance = 0; instance < ARRAY_SIZE( enetBases ); instance++ )
872 {
873 if( enetBases[ instance ] == ethernetifLocal->base )
874 {
875 NVIC_SetPriority( enetRxIrqId[ instance ], ENET_PRIORITY );
876 NVIC_SetPriority( enetTxIrqId[ instance ], ENET_PRIORITY );
877 break;
878 }
879 }
880
881 configASSERT( instance != ARRAY_SIZE( enetBases ) );
882
883 if( instance == ARRAY_SIZE( enetBases ) )
884 {
885 xStatus = kStatus_Fail;
886 }
887 else
888 {
889 pxEndPoint = FreeRTOS_FirstEndPoint( pxMyInterface );
890 configASSERT( pxEndPoint != NULL );
891
892 for( i = 0; i < ENET_RXBUFF_NUM; i++ )
893 {
894 ethernetifLocal->RxPbufs[ i ].buffer = &( ethernetifLocal->RxDataBuff[ i ][ 0 ] );
895 ethernetifLocal->RxPbufs[ i ].buffer_used = false;
896 }
897
898 /* Initialize the ENET module. */
899 xStatus = ENET_Init( ethernetifLocal->base,
900 ðernetifLocal->handle,
901 &config,
902 &buffCfg[ 0 ],
903 pxEndPoint->xMACAddress.ucBytes,
904 sysClock );
905
906 #if ( ipconfigUSE_LLMNR == 1 )
907 ENET_AddMulticastGroup( ethernetifLocal->base, ( uint8_t * ) xLLMNR_MacAdress.ucBytes );
908 #endif /* ipconfigUSE_LLMNR */
909
910 #if ( ipconfigUSE_IPv6 != 0 )
911 #if ( ipconfigUSE_LLMNR == 1 )
912 ENET_AddMulticastGroup( ethernetifLocal->base, ( uint8_t * ) xLLMNR_MacAdressIPv6.ucBytes );
913 #endif /* ipconfigUSE_LLMNR */
914
915 for( pxEndPoint = FreeRTOS_FirstEndPoint( pxMyInterface );
916 pxEndPoint != NULL;
917 pxEndPoint = FreeRTOS_NextEndPoint( pxMyInterface, pxEndPoint ) )
918 {
919 if( pxEndPoint->bits.bIPv6 != pdFALSE_UNSIGNED )
920 {
921 /* Allow traffic from IPv6 solicited-node multicast MAC address for
922 * each endpoint */
923 uint8_t ucMACAddress[ 6 ] = { 0x33, 0x33, 0xff, 0, 0, 0 };
924
925 ucMACAddress[ 3 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 13 ];
926 ucMACAddress[ 4 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 14 ];
927 ucMACAddress[ 5 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 15 ];
928 ENET_AddMulticastGroup( ethernetifLocal->base, ucMACAddress );
929 }
930 }
931 #endif /* ( ipconfigUSE_IPv6 != 0 ) */
932
933 if( xStatus == kStatus_Success )
934 {
935 FreeRTOS_printf( ( "ENET initialized." ) );
936 ENET_ActiveRead( ethernetifLocal->base );
937 }
938 else
939 {
940 FreeRTOS_printf( ( "Failed to initialize ENET." ) );
941 }
942 }
943
944 return xStatus;
945 }
946 /*-----------------------------------------------------------*/
947