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
29 /* This driver is made to work with Atmel START's ASF4 GMAC driver.
30 * The START generated GMAC initialization code should be commented out,
31 * since this driver will take care of initializing the GMAC peripheral itself.
32 *
33 * Optimal performance is obtained with:
34 * - CRC offloading enabled for both RX and TX
35 * - "Copy all frames" set to zero / off
36 */
37
38 /* Atmel ASF includes */
39 #include "hal_mac_async.h"
40 #include "hpl_gmac_config.h"
41 /* Include MAC initialization function here: */
42 #include "driver_init.h"
43
44 /* FreeRTOS includes */
45 #include "FreeRTOS.h"
46 #include "task.h"
47
48 /* FreeRTOS+TCP includes */
49 #include "FreeRTOS_IP.h"
50 #include "FreeRTOS_IP_Private.h"
51 #include "NetworkBufferManagement.h"
52 #include "phyHandling.h"
53
54
55
56 /***********************************************/
57 /* Configuration variables */
58 /***********************************************/
59
60 /* Check for optimal performance parameters */
61 #if ( CONF_GMAC_NCFGR_RXCOEN == 0 )
62 #if ( ipconfigPORT_SUPPRESS_WARNING == 0 )
63 #warning This driver works best with RX CRC offloading enabled.
64 #endif
65 #endif
66
67 #if ( CONF_GMAC_DCFGR_TXCOEN == 0 )
68 #if ( ipconfigPORT_SUPPRESS_WARNING == 0 )
69 #warning This driver works best with TX CRC offloading enabled.
70 #endif
71 #endif
72
73 #if ( CONF_GMAC_NCFGR_CAF != 0 )
74 #if ( ipconfigPORT_SUPPRESS_WARNING == 0 )
75 #warning This driver includes GMAC hardware frame filtering for better performance.
76 #endif
77 #endif
78
79
80 /* Make sure someone takes care of the CRC calculation */
81 #if ( ( CONF_GMAC_NCFGR_RXCOEN == 0 ) && ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 1 ) )
82 #error Receive CRC offloading should be enabled.
83 #endif
84 #if ( ( CONF_GMAC_DCFGR_TXCOEN == 0 ) && ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 ) )
85 #error Transmit CRC offloading should be enabled.
86 #endif
87
88 /* Setup LLMNR specific multicast address. */
89 #if ( defined( ipconfigUSE_LLMNR ) && ( ipconfigUSE_LLMNR == 1 ) )
90 static uint8_t ucLLMNR_MAC_address[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC };
91 #endif
92
93 /* Receive task refresh time */
94 #define RECEIVE_BLOCK_TIME_MS 100
95
96 /***********************************************/
97 /* FreeRTOS variables */
98 /***********************************************/
99
100 /* Copied from FreeRTOS_IP.c. Used for ICMP CRC calculation */
101 #define ipCORRECT_CRC 0xffffU
102
103 /* Also copied from FreeRTOS_IP.c */
104
105 /** @brief If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet
106 * driver will filter incoming packets and only pass the stack those packets it
107 * considers need processing. In this case ipCONSIDER_FRAME_FOR_PROCESSING() can
108 * be #-defined away. If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 0
109 * then the Ethernet driver will pass all received packets to the stack, and the
110 * stack must do the filtering itself. In this case ipCONSIDER_FRAME_FOR_PROCESSING
111 * needs to call eConsiderFrameForProcessing.
112 */
113 #if ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0
114 #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eConsiderFrameForProcessing( ( pucEthernetBuffer ) )
115 #else
116 #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer ) eProcessBuffer
117 #endif
118
119 /* Ethernet buffers for BufferAllocation_1.c scheme.
120 * Set ipUSE_STATIC_ALLOCATION to 1 if using BufferAllocation_1.c,
121 * otherwise to 0, to save RAM. From Iperf testing, there is no point in using
122 * static allocation with a non zero-copy driver.
123 */
124 #define ipUSE_STATIC_ALLOCATION 0
125 #if ( defined( ipUSE_STATIC_ALLOCATION ) && ( ipUSE_STATIC_ALLOCATION == 1 ) )
126
127 /* 1536 bytes is more than needed, 1524 would be enough.
128 * But 1536 is a multiple of 32, which gives a great alignment for cached memories. */
129 #define NETWORK_BUFFER_SIZE 1536
130 static uint8_t ucBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ][ NETWORK_BUFFER_SIZE ];
131 #endif /* ( defined( ipUSE_STATIC_ALLOCATION ) && ( ipUSE_STATIC_ALLOCATION == 1 )) */
132
133
134 /* Holds the handle of the task used as a deferred interrupt processor. The
135 * handle is used so direct notifications can be sent to the task for all EMAC/DMA
136 * related interrupts. */
137 TaskHandle_t xEMACTaskHandle = NULL;
138
139 /* The PING response queue */
140 #if ( defined( ipconfigSUPPORT_OUTGOING_PINGS ) && ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) )
141 QueueHandle_t xPingReplyQueue = NULL;
142 #endif
143
144 /* GMAC HW Init */
145 void vGMACInit( void );
146
147 /* GMAC interrupt callbacks. */
148 void xRxCallback( void );
149 static void prvEMACDeferredInterruptHandlerTask( void * pvParameters );
150
151 /***********************************************/
152 /* GMAC variables */
153 /***********************************************/
154
155 /* The Ethernet MAC instance created by ASF4 */
156 extern struct mac_async_descriptor ETH_MAC;
157
158 static void prvGMACInit( void );
159
160 /* Enable/Disable MDC and MDIO ports for PHY register management. */
161 static inline void prvGMACEnablePHYManagementPort( bool enable );
162
163 /* GMAC registers configuration functions. */
164 static inline void prvGMACEnable100Mbps( bool enable );
165 static inline void prvGMACEnableFullDuplex( bool enable );
166 static inline void prvGMACClearMulticastHashTable();
167 static inline void prvGMACEnableMulticastHashTable( bool enable );
168 static inline void prvGMACEnableUnicastHashTable( bool enable );
169
170 /***********************************************/
171 /* PHY variables */
172 /***********************************************/
173
174 /* All PHY handling code has now been separated from the NetworkInterface.c,
175 * see "../Common/phyHandling.c". */
176 static EthernetPhy_t xPhyObject;
177
178 /* PHY link preferences. */
179 /* Set both speed and Duplex to AUTO, or give them BOTH manual values. */
180 const PhyProperties_t xPHYProperties =
181 {
182 .ucSpeed = PHY_SPEED_AUTO,
183 .ucDuplex = PHY_DUPLEX_AUTO,
184 .ucMDI_X = PHY_MDIX_AUTO,
185 };
186
187 static void prvPHYLinkReset( void );
188 static void prvPHYInit( void );
189 static inline BaseType_t xATSAM5x_PHYGetLinkStatus( NetworkInterface_t * );
190
191 /* PHY read and write functions. */
192 static BaseType_t xPHYRead( BaseType_t xAddress,
193 BaseType_t xRegister,
194 uint32_t * pulValue );
195 static BaseType_t xPHYWrite( BaseType_t xAddress,
196 BaseType_t xRegister,
197 uint32_t pulValue );
198
199 /* Pointer to the interface object of this NIC */
200 static NetworkInterface_t * pxMyInterface = NULL;
201
202 /*********************************************************************/
203 /* FreeRTOS+TCP functions */
204 /*********************************************************************/
205
206 /*-----------------------------------------------------------*/
207 /* Function to initialise the network interface */
208 BaseType_t xATSAM5x_NetworkInterfaceInitialise( NetworkInterface_t * pxInterface );
209
210 BaseType_t xATSAM5x_NetworkInterfaceOutput( NetworkInterface_t * pxInterface,
211 NetworkBufferDescriptor_t * const pxDescriptor,
212 BaseType_t xReleaseAfterSend );
213
pxATSAM5x_FillInterfaceDescriptor(BaseType_t xEMACIndex,NetworkInterface_t * pxInterface)214 NetworkInterface_t * pxATSAM5x_FillInterfaceDescriptor( BaseType_t xEMACIndex,
215 NetworkInterface_t * pxInterface )
216 {
217 static char pcName[ 17 ];
218
219 /* This function pxATSAM5x_FillInterfaceDescriptor() adds a network-interface.
220 * Make sure that the object pointed to by 'pxInterface'
221 * is declared static or global, and that it will remain to exist. */
222
223 snprintf( pcName, sizeof( pcName ), "eth%u", ( unsigned ) xEMACIndex );
224
225 memset( pxInterface, '\0', sizeof( *pxInterface ) );
226 pxInterface->pcName = pcName; /* Just for logging, debugging. */
227 pxInterface->pvArgument = ( void * ) xEMACIndex; /* Has only meaning for the driver functions. */
228 pxInterface->pfInitialise = xATSAM5x_NetworkInterfaceInitialise;
229 pxInterface->pfOutput = xATSAM5x_NetworkInterfaceOutput;
230 pxInterface->pfGetPhyLinkStatus = xATSAM5x_PHYGetLinkStatus;
231
232 FreeRTOS_AddNetworkInterface( pxInterface );
233
234 return pxInterface;
235 }
236
xATSAM5x_NetworkInterfaceInitialise(NetworkInterface_t * pxInterface)237 BaseType_t xATSAM5x_NetworkInterfaceInitialise( NetworkInterface_t * pxInterface )
238 {
239 /*
240 * Perform the hardware specific network initialization here. Typically
241 * that will involve using the Ethernet driver library to initialize the
242 * Ethernet (or other network) hardware, initialize DMA descriptors, and
243 * perform a PHY auto-negotiation to obtain a network link. */
244
245 if( xEMACTaskHandle == NULL )
246 {
247 pxMyInterface = pxInterface;
248
249 /* Initialize MAC and PHY */
250 prvGMACInit();
251 prvPHYInit();
252
253 /* (Re)set PHY link */
254 prvPHYLinkReset();
255
256 /* Initialize PING capability */
257 #if ( defined( ipconfigSUPPORT_OUTGOING_PINGS ) && ( ipconfigSUPPORT_OUTGOING_PINGS == 1 ) )
258 xPingReplyQueue = xQueueCreate( ipconfigPING_QUEUE_SIZE, sizeof( uint16_t ) );
259 #endif
260
261 /* Create event handler task */
262 xTaskCreate( prvEMACDeferredInterruptHandlerTask, /* Function that implements the task. */
263 "EMACInt", /* Text name for the task. */
264 256, /* Stack size in words, not bytes. */
265 ( void * ) 1, /* Parameter passed into the task. */
266 configMAX_PRIORITIES - 1, /* Priority at which the task is created. */
267 &xEMACTaskHandle ); /* Used to pass out the created task's handle. */
268
269 configASSERT( xEMACTaskHandle );
270 }
271
272 return xATSAM5x_PHYGetLinkStatus( NULL );
273 }
274
275
prvEMACDeferredInterruptHandlerTask(void * pvParameters)276 static void prvEMACDeferredInterruptHandlerTask( void * pvParameters )
277 {
278 NetworkBufferDescriptor_t * pxBufferDescriptor;
279 size_t xBytesReceived = 0, xBytesRead = 0;
280
281 uint16_t xICMPChecksumResult = ipCORRECT_CRC;
282 const IPPacket_t * pxIPPacket;
283
284
285 /* Used to indicate that xSendEventStructToIPTask() is being called because
286 * of an Ethernet receive event. */
287 IPStackEvent_t xRxEvent;
288
289 for( ; ; )
290 {
291 BaseType_t xRelease = pdFALSE;
292
293 /* Wait for the Ethernet MAC interrupt to indicate that another packet
294 * has been received. The task notification is used in a similar way to a
295 * counting semaphore to count Rx events, but is a lot more efficient than
296 * a semaphore. */
297 ulTaskNotifyTake( pdFALSE, pdMS_TO_TICKS( RECEIVE_BLOCK_TIME_MS ) );
298
299 /* See how much data was received. Here it is assumed ReceiveSize() is
300 * a peripheral driver function that returns the number of bytes in the
301 * received Ethernet frame. */
302 xBytesReceived = mac_async_read_len( Ð_MAC );
303
304 if( xBytesReceived > 0 )
305 {
306 /* Allocate a network buffer descriptor that points to a buffer
307 * large enough to hold the received frame. As this is the simple
308 * rather than efficient example the received data will just be copied
309 * into this buffer. */
310 pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( xBytesReceived, 0 );
311
312 if( pxBufferDescriptor != NULL )
313 {
314 /* pxBufferDescriptor->pucEthernetBuffer now points to an Ethernet
315 * buffer large enough to hold the received data. Copy the
316 * received data into pcNetworkBuffer->pucEthernetBuffer. Here it
317 * is assumed ReceiveData() is a peripheral driver function that
318 * copies the received data into a buffer passed in as the function's
319 * parameter. Remember! While is is a simple robust technique -
320 * it is not efficient. An example that uses a zero copy technique
321 * is provided further down this page. */
322 xBytesRead = mac_async_read( Ð_MAC, pxBufferDescriptor->pucEthernetBuffer, xBytesReceived );
323 pxBufferDescriptor->xDataLength = xBytesRead;
324 pxBufferDescriptor->pxInterface = pxMyInterface;
325 pxBufferDescriptor->pxEndPoint = FreeRTOS_MatchingEndpoint( pxMyInterface, pxBufferDescriptor->pucEthernetBuffer );
326
327 if( pxBufferDescriptor->pxEndPoint == NULL )
328 {
329 /* Couldn't find a proper endpoint for the incoming packet, drop it. */
330 FreeRTOS_printf( ( "NetworkInterface: can not find a proper endpoint\n" ) );
331 xRelease = pdTRUE;
332 }
333 else
334 {
335 #if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 1 )
336 {
337 /* the Atmel SAM GMAC peripheral does not support hardware CRC offloading for ICMP packets.
338 * It must therefore be implemented in software. */
339 pxIPPacket = ipCAST_CONST_PTR_TO_CONST_TYPE_PTR( IPPacket_t, pxBufferDescriptor->pucEthernetBuffer );
340
341 if( pxIPPacket->xIPHeader.ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP )
342 {
343 xICMPChecksumResult = usGenerateProtocolChecksum( pxBufferDescriptor->pucEthernetBuffer, pxBufferDescriptor->xDataLength, pdFALSE );
344 }
345 else
346 {
347 xICMPChecksumResult = ipCORRECT_CRC; /* Reset the result value in case this is not an ICMP packet. */
348 }
349 }
350 #endif /* if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 1 ) */
351
352 /* See if the data contained in the received Ethernet frame needs
353 * to be processed. NOTE! It is preferable to do this in
354 * the interrupt service routine itself, which would remove the need
355 * to unblock this task for packets that don't need processing. */
356 if( ( ipCONSIDER_FRAME_FOR_PROCESSING( pxBufferDescriptor->pucEthernetBuffer ) == eProcessBuffer ) &&
357 ( xICMPChecksumResult == ipCORRECT_CRC ) )
358 {
359 /* The event about to be sent to the TCP/IP is an Rx event. */
360 xRxEvent.eEventType = eNetworkRxEvent;
361
362 /* pvData is used to point to the network buffer descriptor that
363 * now references the received data. */
364 xRxEvent.pvData = ( void * ) pxBufferDescriptor;
365
366 /* Send the data to the TCP/IP stack. */
367 if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE )
368 {
369 /* The buffer could not be sent to the IP task so the buffer
370 * must be released. */
371 xRelease = pdTRUE;
372
373 /* Make a call to the standard trace macro to log the
374 * occurrence. */
375 iptraceETHERNET_RX_EVENT_LOST();
376 }
377 else
378 {
379 /* The message was successfully sent to the TCP/IP stack.
380 * Call the standard trace macro to log the occurrence. */
381 iptraceNETWORK_INTERFACE_RECEIVE();
382 }
383 }
384 else
385 {
386 /* The Ethernet frame can be dropped, but the Ethernet buffer
387 * must be released. */
388 xRelease = pdTRUE;
389 }
390 }
391
392 /* Release the descriptor in case it can not be delivered. */
393 if( xRelease == pdTRUE )
394 {
395 /* The buffer could not be sent to the stack so must be released
396 * again. */
397 vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor );
398 iptraceETHERNET_RX_EVENT_LOST();
399 FreeRTOS_printf( ( "prvEMACDeferredInterruptHandlerTask: Can not queue RX packet!\n" ) );
400 }
401 }
402 else
403 {
404 /* The event was lost because a network buffer was not available.
405 * Call the standard trace macro to log the occurrence. */
406 iptraceETHERNET_RX_EVENT_LOST();
407 }
408 }
409
410 prvGMACEnablePHYManagementPort( true );
411
412 if( xPhyCheckLinkStatus( &xPhyObject, xBytesReceived ) )
413 {
414 prvPHYLinkReset();
415 }
416
417 prvGMACEnablePHYManagementPort( false );
418 }
419 }
420
xATSAM5x_NetworkInterfaceOutput(NetworkInterface_t * pxInterface,NetworkBufferDescriptor_t * const pxDescriptor,BaseType_t xReleaseAfterSend)421 BaseType_t xATSAM5x_NetworkInterfaceOutput( NetworkInterface_t * pxInterface,
422 NetworkBufferDescriptor_t * const pxDescriptor,
423 BaseType_t xReleaseAfterSend )
424 {
425 /* Simple network interfaces (as opposed to more efficient zero copy network
426 * interfaces) just use Ethernet peripheral driver library functions to copy
427 * data from the FreeRTOS+TCP buffer into the peripheral driver's own buffer.
428 * This example assumes SendData() is a peripheral driver library function that
429 * takes a pointer to the start of the data to be sent and the length of the
430 * data to be sent as two separate parameters. The start of the data is located
431 * by pxDescriptor->pucEthernetBuffer. The length of the data is located
432 * by pxDescriptor->xDataLength. */
433
434 /* As there is only a single instance of the EMAC, there is only one pxInterface object. */
435 ( void ) pxInterface;
436
437 if( xATSAM5x_PHYGetLinkStatus( NULL ) )
438 {
439 #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 )
440 {
441 /* the Atmel SAM GMAC peripheral does not support hardware CRC offloading for ICMP packets.
442 * It must therefore be implemented in software. */
443 const IPPacket_t * pxIPPacket = ipCAST_CONST_PTR_TO_CONST_TYPE_PTR( IPPacket_t, pxDescriptor->pucEthernetBuffer );
444
445 if( pxIPPacket->xIPHeader.ucProtocol == ( uint8_t ) ipPROTOCOL_ICMP )
446 {
447 ( void ) usGenerateProtocolChecksum( pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength, pdTRUE );
448 }
449 }
450 #endif /* if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 ) */
451
452 mac_async_write( Ð_MAC, pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength );
453
454 /* Call the standard trace macro to log the send event. */
455 iptraceNETWORK_INTERFACE_TRANSMIT();
456 }
457
458 if( xReleaseAfterSend != pdFALSE )
459 {
460 /* It is assumed SendData() copies the data out of the FreeRTOS+TCP Ethernet
461 * buffer. The Ethernet buffer is therefore no longer needed, and must be
462 * freed for re-use. */
463 vReleaseNetworkBufferAndDescriptor( pxDescriptor );
464 }
465
466 return pdTRUE;
467 }
468
xRxCallback(void)469 void xRxCallback( void )
470 {
471 vTaskNotifyGiveFromISR( xEMACTaskHandle, 0 );
472 }
473
474 #if ( defined( ipUSE_STATIC_ALLOCATION ) && ( ipUSE_STATIC_ALLOCATION == 1 ) )
475
476 /* Next provide the vNetworkInterfaceAllocateRAMToBuffers() function, which
477 * simply fills in the pucEthernetBuffer member of each descriptor. */
vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS])478 void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
479 {
480 BaseType_t x;
481
482 for( x = 0; x < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; x++ )
483 {
484 /* pucEthernetBuffer is set to point ipBUFFER_PADDING bytes in from the
485 * beginning of the allocated buffer. */
486 pxNetworkBuffers[ x ].pucEthernetBuffer = &( ucBuffers[ x ][ ipBUFFER_PADDING ] );
487
488 /* The following line is also required, but will not be required in
489 * future versions. */
490 *( ( uint32_t * ) &ucBuffers[ x ][ 0 ] ) = ( uint32_t ) &( pxNetworkBuffers[ x ] );
491 }
492 }
493 #endif /* ( defined( ipUSE_STATIC_ALLOCATION ) && ( ipUSE_STATIC_ALLOCATION == 1 )) */
494
495
496 /*********************************************************************/
497 /* GMAC functions */
498 /*********************************************************************/
499
500 /* Initializes the GMAC peripheral. This function is based on ASF4 GMAC initialization
501 * and uses the Atmel START- generated code, typically located in "driver_init.h".
502 * Make sure the initialization function is not called twice, e.g. comment out the call in "driver_init.c".
503 * It is compatible with modifications made in Atmel START afterwards because the
504 * configuration is saved in "hpl_gmac_config.h". */
prvGMACInit()505 static void prvGMACInit()
506 {
507 NetworkEndPoint_t * pxEndPointIter;
508
509 /* Call MAC initialization function here: */
510 vGMACInit();
511 prvGMACEnablePHYManagementPort( false );
512 mac_async_disable_irq( Ð_MAC );
513
514 /* Clear the MAC address hash table and enable multicast and unicast
515 * MAC address hash table. */
516 prvGMACClearMulticastHashTable();
517 prvGMACEnableUnicastHashTable( true );
518 prvGMACEnableMulticastHashTable( true );
519
520 /* Enable traffic for LLMNR, if defined. */
521 #if ( defined( ipconfigUSE_LLMNR ) && ( ipconfigUSE_LLMNR == 1 ) )
522 {
523 mac_async_set_filter_ex( Ð_MAC, ucLLMNR_MAC_address );
524 }
525 #endif
526
527
528 #if ( ipconfigUSE_IPv6 != 0 )
529 {
530 /* Allow all nodes IPv6 multicast MAC */
531 uint8_t ucMACAddressAllNodes[ ipMAC_ADDRESS_LENGTH_BYTES ] = { 0x33, 0x33, 0, 0, 0, 1 };
532 mac_async_set_filter_ex( Ð_MAC, ucMACAddressAllNodes );
533
534 #if ( ipconfigUSE_LLMNR == 1 )
535 {
536 uint8_t ucMACAddressLLMNRIPv6[ ipMAC_ADDRESS_LENGTH_BYTES ];
537 /* Avoid warning */
538 memcpy( ucMACAddressLLMNRIPv6, xLLMNR_MacAdressIPv6.ucBytes, ipMAC_ADDRESS_LENGTH_BYTES );
539 mac_async_set_filter_ex( Ð_MAC, ucMACAddressLLMNRIPv6 );
540 }
541 #endif /* ipconfigUSE_LLMNR */
542 }
543 #endif /* ipconfigUSE_IPv6 */
544
545 for( pxEndPointIter = FreeRTOS_FirstEndPoint( pxMyInterface );
546 pxEndPointIter != NULL;
547 pxEndPointIter = FreeRTOS_NextEndPoint( pxMyInterface, pxEndPointIter ) )
548 {
549 #if ( ipconfigUSE_IPv6 != 0 )
550 {
551 if( pxEndPointIter->bits.bIPv6 != pdFALSE_UNSIGNED )
552 {
553 /* Allow traffic from IPv6 solicited-node multicast MAC address for
554 * each endpoint */
555 uint8_t ucMACAddress[ 6 ] = { 0x33, 0x33, 0xff, 0, 0, 0 };
556
557 ucMACAddress[ 3 ] = pxEndPointIter->ipv6_settings.xIPAddress.ucBytes[ 13 ];
558 ucMACAddress[ 4 ] = pxEndPointIter->ipv6_settings.xIPAddress.ucBytes[ 14 ];
559 ucMACAddress[ 5 ] = pxEndPointIter->ipv6_settings.xIPAddress.ucBytes[ 15 ];
560 mac_async_set_filter_ex( Ð_MAC, ucMACAddress );
561 }
562 }
563 #endif /* ipconfigUSE_IPv6 */
564
565 /* Allow endpoint MAC */
566 mac_async_set_filter_ex( Ð_MAC, pxEndPointIter->xMACAddress.ucBytes );
567 }
568
569 /* Set GMAC interrupt priority to be compatible with FreeRTOS API */
570 NVIC_SetPriority( GMAC_IRQn, configMAX_SYSCALL_INTERRUPT_PRIORITY >> ( 8 - configPRIO_BITS ) );
571
572 /* Register callback(s). Currently only RX callback is implemented, but TX callback can be added the same way. */
573 mac_async_register_callback( Ð_MAC, MAC_ASYNC_RECEIVE_CB, ( FUNC_PTR ) xRxCallback );
574
575 /* Start the GMAC. */
576 mac_async_enable( Ð_MAC );
577 mac_async_enable_irq( Ð_MAC );
578 }
579
prvGMACEnablePHYManagementPort(bool enable)580 static inline void prvGMACEnablePHYManagementPort( bool enable )
581 {
582 if( enable )
583 {
584 ( ( Gmac * ) ETH_MAC.dev.hw )->NCR.reg |= GMAC_NCR_MPE;
585 }
586 else
587 {
588 ( ( Gmac * ) ETH_MAC.dev.hw )->NCR.reg &= ~GMAC_NCR_MPE;
589 }
590 }
591
prvGMACEnable100Mbps(bool enable)592 static inline void prvGMACEnable100Mbps( bool enable )
593 {
594 if( enable )
595 {
596 ( ( Gmac * ) ETH_MAC.dev.hw )->NCFGR.reg |= GMAC_NCFGR_SPD;
597 }
598 else
599 {
600 ( ( Gmac * ) ETH_MAC.dev.hw )->NCFGR.reg &= ~GMAC_NCFGR_SPD;
601 }
602 }
603
prvGMACEnableFullDuplex(bool enable)604 static inline void prvGMACEnableFullDuplex( bool enable )
605 {
606 if( enable )
607 {
608 ( ( Gmac * ) ETH_MAC.dev.hw )->NCFGR.reg |= GMAC_NCFGR_FD;
609 }
610 else
611 {
612 ( ( Gmac * ) ETH_MAC.dev.hw )->NCFGR.reg &= ~GMAC_NCFGR_FD;
613 }
614 }
615
prvGMACClearMulticastHashTable()616 static inline void prvGMACClearMulticastHashTable()
617 {
618 /* First clear Hash Register Bottom and then Top */
619 ( ( Gmac * ) ETH_MAC.dev.hw )->HRB.reg = 0;
620 ( ( Gmac * ) ETH_MAC.dev.hw )->HRT.reg = 0;
621 }
622
prvGMACEnableMulticastHashTable(bool enable)623 static inline void prvGMACEnableMulticastHashTable( bool enable )
624 {
625 if( enable )
626 {
627 ( ( Gmac * ) ETH_MAC.dev.hw )->NCFGR.reg |= GMAC_NCFGR_MTIHEN;
628 }
629 else
630 {
631 ( ( Gmac * ) ETH_MAC.dev.hw )->NCFGR.reg &= ~GMAC_NCFGR_MTIHEN;
632 }
633 }
634
prvGMACEnableUnicastHashTable(bool enable)635 static inline void prvGMACEnableUnicastHashTable( bool enable )
636 {
637 if( enable )
638 {
639 ( ( Gmac * ) ETH_MAC.dev.hw )->NCFGR.reg |= GMAC_NCFGR_UNIHEN;
640 }
641 else
642 {
643 ( ( Gmac * ) ETH_MAC.dev.hw )->NCFGR.reg &= ~GMAC_NCFGR_UNIHEN;
644 }
645 }
646
647
648 /*********************************************************************/
649 /* PHY functions */
650 /*********************************************************************/
651
652 /* Initializes the PHY hardware. Based on ASF4 generated code. */
prvPHYInit()653 static void prvPHYInit()
654 {
655 prvGMACEnablePHYManagementPort( true );
656
657 vPhyInitialise( &xPhyObject, &xPHYRead, &xPHYWrite );
658 xPhyDiscover( &xPhyObject );
659 xPhyConfigure( &xPhyObject, &xPHYProperties );
660
661 prvGMACEnablePHYManagementPort( false );
662 }
663
664 /* Start a new link negotiation on the PHY and wait until link is up. */
prvPHYLinkReset()665 static void prvPHYLinkReset()
666 {
667 /* Restart an auto-negotiation */
668 prvGMACEnablePHYManagementPort( true );
669
670 if( ( xPHYProperties.ucDuplex == PHY_DUPLEX_AUTO ) && ( xPHYProperties.ucSpeed == PHY_SPEED_AUTO ) && ( xPHYProperties.ucMDI_X == PHY_MDIX_AUTO ) )
671 {
672 /* Auto-negotiation */
673 xPhyStartAutoNegotiation( &xPhyObject, xPhyGetMask( &xPhyObject ) );
674
675 /* Update the MAC with the auto-negotiation result parameters. */
676 prvGMACEnableFullDuplex( xPhyObject.xPhyProperties.ucDuplex == PHY_DUPLEX_FULL );
677 prvGMACEnable100Mbps( xPhyObject.xPhyProperties.ucSpeed == PHY_SPEED_100 );
678 }
679 else
680 {
681 /* Fixed values */
682 xPhyObject.xPhyPreferences.ucDuplex = xPHYProperties.ucDuplex;
683 xPhyObject.xPhyPreferences.ucSpeed = xPHYProperties.ucSpeed;
684 xPhyObject.xPhyPreferences.ucMDI_X = xPHYProperties.ucMDI_X;
685 xPhyFixedValue( &xPhyObject, xPhyGetMask( &xPhyObject ) );
686
687 /* Update the MAC with the auto-negotiation result parameters. */
688 prvGMACEnableFullDuplex( xPHYProperties.ucDuplex == PHY_DUPLEX_FULL );
689 prvGMACEnable100Mbps( xPHYProperties.ucSpeed == PHY_SPEED_100 );
690 }
691
692 prvGMACEnablePHYManagementPort( false );
693 }
694
xPHYRead(BaseType_t xAddress,BaseType_t xRegister,uint32_t * pulValue)695 static BaseType_t xPHYRead( BaseType_t xAddress,
696 BaseType_t xRegister,
697 uint32_t * pulValue )
698 {
699 prvGMACEnablePHYManagementPort( true );
700 BaseType_t readStatus = mac_async_read_phy_reg( Ð_MAC, xAddress, xRegister, ( ( uint16_t * ) pulValue ) );
701 prvGMACEnablePHYManagementPort( false );
702 return readStatus;
703 }
704
xPHYWrite(BaseType_t xAddress,BaseType_t xRegister,uint32_t pulValue)705 static BaseType_t xPHYWrite( BaseType_t xAddress,
706 BaseType_t xRegister,
707 uint32_t pulValue )
708 {
709 prvGMACEnablePHYManagementPort( true );
710 BaseType_t writeStatus = mac_async_write_phy_reg( Ð_MAC, xAddress, xRegister, pulValue );
711 prvGMACEnablePHYManagementPort( false );
712 return writeStatus;
713 }
714
xATSAM5x_PHYGetLinkStatus(NetworkInterface_t * pxInterface)715 static inline BaseType_t xATSAM5x_PHYGetLinkStatus( NetworkInterface_t * pxInterface )
716 {
717 ( void ) pxInterface;
718 return( xPhyObject.ulLinkStatusMask != 0 );
719 }
720