xref: /FreeRTOS-Plus-TCP-v4.0.0/source/portable/NetworkInterface/Zynq/NetworkInterface.c (revision c64bef1957a72e35be2f6584dafea00df0ed6f03)
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 "FreeRTOS_ARP.h"
44 #include "NetworkBufferManagement.h"
45 #include "NetworkInterface.h"
46 #include "FreeRTOS_DHCP.h"
47 #include "FreeRTOS_DNS.h"
48 #include "FreeRTOS_Routing.h"
49 
50 /* Xilinx library files. */
51 #include <xemacps.h>
52 #include "Zynq/x_topology.h"
53 #include "Zynq/x_emacpsif.h"
54 #include "Zynq/x_emacpsif_hw.h"
55 
56 /* Provided memory configured as uncached. */
57 #include "uncached_memory.h"
58 
59 #ifndef niEMAC_HANDLER_TASK_PRIORITY
60     /* Define the priority of the task prvEMACHandlerTask(). */
61     #define niEMAC_HANDLER_TASK_PRIORITY    configMAX_PRIORITIES - 1
62 #endif
63 
64 #define niBMSR_LINK_STATUS                  0x0004uL
65 
66 #if defined( PHY_LS_HIGH_CHECK_TIME_MS ) || defined( PHY_LS_LOW_CHECK_TIME_MS )
67     #error please use the new defines with 'ipconfig' prefix
68 #endif
69 
70 #ifndef ipconfigPHY_LS_HIGH_CHECK_TIME_MS
71 
72 /* Check if the LinkStatus in the PHY is still high after 15 seconds of not
73  * receiving packets. */
74     #define ipconfigPHY_LS_HIGH_CHECK_TIME_MS    15000U
75 #endif
76 
77 #ifndef ipconfigPHY_LS_LOW_CHECK_TIME_MS
78     /* Check if the LinkStatus in the PHY is still low every second. */
79     #define ipconfigPHY_LS_LOW_CHECK_TIME_MS    1000U
80 #endif
81 
82 
83 /* The size of each buffer when BufferAllocation_1 is used:
84  * http://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Buffer_Management.html */
85 #define niBUFFER_1_PACKET_SIZE    1536
86 
87 /* Naming and numbering of PHY registers. */
88 #define PHY_REG_01_BMSR           0x01  /* Basic mode status register */
89 
90 #ifndef iptraceEMAC_TASK_STARTING
91     #define iptraceEMAC_TASK_STARTING()    do {} while( ipFALSE_BOOL )
92 #endif
93 
94 /* Default the size of the stack used by the EMAC deferred handler task to 8 times
95  * the size of the stack used by the idle task - but allow this to be overridden in
96  * FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */
97 #ifndef configEMAC_TASK_STACK_SIZE
98     #define configEMAC_TASK_STACK_SIZE    ( 8 * configMINIMAL_STACK_SIZE )
99 #endif
100 
101 #if ( ipconfigNIC_LINKSPEED100 != 1 )
102 
103 /* When the PHY is forces to work with a speed of 100 Mbps
104  * many outgoing packets seem to get dropped.
105  */
106     #if ( ipconfigPORT_SUPPRESS_WARNING == 0 )
107         #warning ipconfigNIC_LINKSPEED100 is btoken. Are you sure?
108     #endif
109 #endif
110 
111 static NetworkInterface_t * pxMyInterfaces[ XPAR_XEMACPS_NUM_INSTANCES ];
112 
113 #if ( ipconfigZERO_COPY_RX_DRIVER == 0 || ipconfigZERO_COPY_TX_DRIVER == 0 )
114     #error Please define both 'ipconfigZERO_COPY_RX_DRIVER' and 'ipconfigZERO_COPY_TX_DRIVER' as 1
115 #endif
116 
117 #if ( ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM == 0 || ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 )
118     #if ( ipconfigPORT_SUPPRESS_WARNING == 0 )
119         #warning Please define both 'ipconfigDRIVER_INCLUDED_RX_IP_CHECKSUM' and 'ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM' as 1
120     #endif
121 #endif
122 
123 #ifndef nicUSE_UNCACHED_MEMORY
124     #define nicUSE_UNCACHED_MEMORY    1
125 #endif
126 
127 /*-----------------------------------------------------------*/
128 
129 /*
130  * Look for the link to be up every few milliseconds until either xMaxTime time
131  * has passed or a link is found.
132  */
133 static BaseType_t prvGMACWaitLS( BaseType_t xEMACIndex,
134                                  TickType_t xMaxTime );
135 
136 /*
137  * A deferred interrupt handler for all MAC/DMA interrupt sources.
138  */
139 static void prvEMACHandlerTask( void * pvParameters );
140 
141 /* FreeRTOS+TCP/multi :
142  * Each network device has 3 access functions:
143  * - initialise the device
144  * - output a network packet
145  * - return the PHY link-status (LS)
146  * They can be defined as static because their addresses will be
147  * stored in struct NetworkInterface_t. */
148 
149 static BaseType_t xZynqNetworkInterfaceInitialise( NetworkInterface_t * pxInterface );
150 
151 static BaseType_t xZynqNetworkInterfaceOutput( NetworkInterface_t * pxInterface,
152                                                NetworkBufferDescriptor_t * const pxBuffer,
153                                                BaseType_t bReleaseAfterSend );
154 
155 static BaseType_t xZynqGetPhyLinkStatus( NetworkInterface_t * pxInterface );
156 
157 NetworkInterface_t * pxZynq_FillInterfaceDescriptor( BaseType_t xEMACIndex,
158                                                      NetworkInterface_t * pxInterface );
159 
160 /*-----------------------------------------------------------*/
161 
162 /* EMAC data/descriptions. */
163 static xemacpsif_s xEMACpsifs[ XPAR_XEMACPS_NUM_INSTANCES ];
164 
165 struct xtopology_t xXTopologies[ XPAR_XEMACPS_NUM_INSTANCES ] =
166 {
167     [ 0 ] =
168         {
169         .emac_baseaddr    = XPAR_PS7_ETHERNET_0_BASEADDR,
170         .emac_type        = xemac_type_emacps,
171         .intc_baseaddr    = 0x0,
172         .intc_emac_intr   = 0x0,
173         .scugic_baseaddr  = XPAR_PS7_SCUGIC_0_BASEADDR,
174         .scugic_emac_intr = 0x36,
175         },
176     #if ( XPAR_XEMACPS_NUM_INSTANCES > 1 )
177         [ 1 ] =
178         {
179         .emac_baseaddr    = XPAR_PS7_ETHERNET_1_BASEADDR,
180         .emac_type        = xemac_type_emacps,
181         .intc_baseaddr    = 0x0,
182         .intc_emac_intr   = 0x0,
183         .scugic_baseaddr  = XPAR_PS7_SCUGIC_0_BASEADDR,
184         .scugic_emac_intr = 0x4D,   /* See "7.2.3 Shared Peripheral Interrupts (SPI)" */
185         },
186     #endif
187 };
188 
189 XEmacPs_Config mac_configs[ XPAR_XEMACPS_NUM_INSTANCES ] =
190 {
191     [ 0 ] =
192         {
193         .DeviceId    = XPAR_PS7_ETHERNET_0_DEVICE_ID, /**< Unique ID  of device, used for 'xEMACIndex' */
194         .BaseAddress = XPAR_PS7_ETHERNET_0_BASEADDR   /**< Physical base address of IPIF registers */
195         },
196     #if ( XPAR_XEMACPS_NUM_INSTANCES > 1 )
197         [ 1 ] =
198         {
199         .DeviceId    = XPAR_PS7_ETHERNET_1_DEVICE_ID, /**< Unique ID  of device */
200         .BaseAddress = XPAR_PS7_ETHERNET_1_BASEADDR   /**< Physical base address of IPIF registers */
201         },
202     #endif
203 };
204 
205 extern int phy_detected[ XPAR_XEMACPS_NUM_INSTANCES ];
206 
207 /* A copy of PHY register 1: 'PHY_REG_01_BMSR' */
208 static uint32_t ulPHYLinkStates[ XPAR_XEMACPS_NUM_INSTANCES ];
209 
210 
211 /* Holds the handle of the task used as a deferred interrupt processor.  The
212  * handle is used so direct notifications can be sent to the task for all EMAC/DMA
213  * related interrupts. */
214 TaskHandle_t xEMACTaskHandles[ XPAR_XEMACPS_NUM_INSTANCES ];
215 
216 /*-----------------------------------------------------------*/
217 
218 /**
219  * @brief Initialise the interface number 'xIndex'
220  * @param xIndex: the index of the interface, between 0
221  *                zero and (XPAR_XEMACPS_NUM_INSTANCES-1)
222  * @note Although the function is declared public, it should
223  *       not be called directly by an application.
224  */
vInitialiseOnIndex(BaseType_t xIndex)225 void vInitialiseOnIndex( BaseType_t xIndex )
226 {
227     if( ( xIndex >= 0 ) && ( xIndex < XPAR_XEMACPS_NUM_INSTANCES ) )
228     {
229         NetworkInterface_t * pxInterface = pxMyInterfaces[ xIndex ];
230 
231         if( pxInterface != NULL )
232         {
233             xZynqNetworkInterfaceInitialise( pxInterface );
234         }
235     }
236 }
237 /*-----------------------------------------------------------*/
238 
xZynqNetworkInterfaceInitialise(NetworkInterface_t * pxInterface)239 static BaseType_t xZynqNetworkInterfaceInitialise( NetworkInterface_t * pxInterface )
240 {
241     uint32_t ulLinkSpeed, ulDMAReg;
242     BaseType_t xStatus, xLinkStatus;
243     XEmacPs * pxEMAC_PS;
244     const TickType_t xWaitLinkDelay = pdMS_TO_TICKS( 7000UL ), xWaitRelinkDelay = pdMS_TO_TICKS( 1000UL );
245     NetworkEndPoint_t * pxEndPoint;
246     BaseType_t xEMACIndex = ( BaseType_t ) pxInterface->pvArgument;
247 
248     configASSERT( xEMACIndex >= 0 );
249     configASSERT( xEMACIndex < XPAR_XEMACPS_NUM_INSTANCES );
250 
251     /* Guard against the init function being called more than once. */
252     if( xEMACTaskHandles[ xEMACIndex ] == NULL )
253     {
254         const char * pcTaskName;
255 
256         pxMyInterfaces[ xEMACIndex ] = pxInterface;
257 
258         pxEMAC_PS = &( xEMACpsifs[ xEMACIndex ].emacps );
259         memset( &xEMACpsifs[ xEMACIndex ], '\0', sizeof( xEMACpsifs[ xEMACIndex ] ) );
260 
261         xStatus = XEmacPs_CfgInitialize( pxEMAC_PS, &( mac_configs[ xEMACIndex ] ), mac_configs[ xEMACIndex ].BaseAddress );
262 
263         if( xStatus != XST_SUCCESS )
264         {
265             FreeRTOS_printf( ( "xEMACInit: EmacPs Configuration Failed....\n" ) );
266         }
267 
268         pxEndPoint = FreeRTOS_FirstEndPoint( pxInterface );
269         configASSERT( pxEndPoint != NULL );
270 
271         /* Initialize the mac and set the MAC address at position 1. */
272         XEmacPs_SetMacAddress( pxEMAC_PS, ( void * ) pxEndPoint->xMACAddress.ucBytes, 1 );
273 
274         #if ( ipconfigUSE_LLMNR == 1 )
275             {
276                 /* Also add LLMNR multicast MAC address. */
277                 #if ( ipconfigUSE_IPv6 == 0 )
278                     {
279                         XEmacPs_SetHash( pxEMAC_PS, ( void * ) xLLMNR_MacAdress.ucBytes );
280                     }
281                 #else
282                     {
283                         NetworkEndPoint_t * pxEndPoint;
284                         NetworkInterface_t * pxInterface = pxMyInterfaces[ xEMACIndex ];
285 
286                         for( pxEndPoint = FreeRTOS_FirstEndPoint( pxInterface );
287                              pxEndPoint != NULL;
288                              pxEndPoint = FreeRTOS_NextEndPoint( pxInterface, pxEndPoint ) )
289                         {
290                             if( pxEndPoint->bits.bIPv6 != pdFALSE_UNSIGNED )
291                             {
292                                 unsigned char ucMACAddress[ 6 ] = { 0x33, 0x33, 0xff, 0, 0, 0 };
293                                 ucMACAddress[ 3 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 13 ];
294                                 ucMACAddress[ 4 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 14 ];
295                                 ucMACAddress[ 5 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 15 ];
296                                 XEmacPs_SetHash( pxEMAC_PS, ( void * ) ucMACAddress );
297                             }
298                         }
299 
300                         XEmacPs_SetHash( pxEMAC_PS, ( void * ) xLLMNR_MacAdressIPv6.ucBytes );
301                     }
302                 #endif /* if ( ipconfigUSE_IPv6 == 0 ) */
303             }
304         #endif /* ipconfigUSE_LLMNR == 1 */
305 
306         #if ( ( ipconfigUSE_MDNS == 1 ) && ( ipconfigUSE_IPv6 != 0 ) )
307             XEmacPs_SetHash( pxEMAC_PS, ( void * ) xMDNS_MacAdress.ucBytes );
308             XEmacPs_SetHash( pxEMAC_PS, ( void * ) xMDNS_MACAdressIPv6.ucBytes );
309         #endif
310 
311         pxEndPoint = FreeRTOS_NextEndPoint( pxInterface, pxEndPoint );
312 
313         if( pxEndPoint != NULL )
314         {
315             /* If there is a second end-point, store the MAC
316              * address at position 4.*/
317             XEmacPs_SetMacAddress( pxEMAC_PS, ( void * ) pxEndPoint->xMACAddress.ucBytes, 4 );
318         }
319 
320         /* MDIO goes via ETH0 only */
321         XEmacPs_SetMdioDivisor( pxEMAC_PS, MDC_DIV_224 );
322         ulLinkSpeed = Phy_Setup( pxEMAC_PS );
323         XEmacPs_SetOperatingSpeed( pxEMAC_PS, ulLinkSpeed );
324 
325         /* Setting the operating speed of the MAC needs a delay. */
326         vTaskDelay( pdMS_TO_TICKS( 25UL ) );
327 
328         ulDMAReg = XEmacPs_ReadReg( pxEMAC_PS->Config.BaseAddress, XEMACPS_DMACR_OFFSET );
329 
330         {
331             uint32_t ulValue = XEmacPs_ReadReg( pxEMAC_PS->Config.BaseAddress, XEMACPS_NWCFG_OFFSET );
332             /* Allow the use of hashed MAC addresses. */
333             ulValue |= XEMACPS_NWCFG_MCASTHASHEN_MASK;
334             /* As 'MCASTHASHEN' doesn't seem to work, use the promiscuous mode so that IPv6 multicast packets are received. */
335             /* Allow promiscuous mode. */
336             ulValue |= XEMACPS_NWCFG_COPYALLEN_MASK;
337             XEmacPs_WriteReg( pxEMAC_PS->Config.BaseAddress, XEMACPS_NWCFG_OFFSET, ulValue );
338         }
339 
340         /* DISC_WHEN_NO_AHB: when set, the GEM DMA will automatically discard receive
341          * packets from the receiver packet buffer memory when no AHB resource is available. */
342         XEmacPs_WriteReg( pxEMAC_PS->Config.BaseAddress, XEMACPS_DMACR_OFFSET,
343                           ulDMAReg | XEMACPS_DMACR_DISC_WHEN_NO_AHB_MASK );
344 
345         setup_isr( &( xEMACpsifs[ xEMACIndex ] ) );
346         init_dma( &( xEMACpsifs[ xEMACIndex ] ) );
347         start_emacps( &( xEMACpsifs[ xEMACIndex ] ) );
348 
349         prvGMACWaitLS( xEMACIndex, xWaitLinkDelay );
350 
351         /* The deferred interrupt handler task is created at the highest
352          * possible priority to ensure the interrupt handler can return directly
353          * to it.  The task's handle is stored in xEMACTaskHandles[] so interrupts can
354          * notify the task when there is something to process. */
355         if( xEMACIndex == 0 )
356         {
357             pcTaskName = "GEM0";
358         }
359         else
360         {
361             pcTaskName = "GEM1";
362         }
363 
364         xTaskCreate( prvEMACHandlerTask, pcTaskName, configEMAC_TASK_STACK_SIZE, ( void * ) xEMACIndex, niEMAC_HANDLER_TASK_PRIORITY, &( xEMACTaskHandles[ xEMACIndex ] ) );
365     }
366     else
367     {
368         /* Initialisation was already performed, just wait for the link. */
369         prvGMACWaitLS( xEMACIndex, xWaitRelinkDelay );
370     }
371 
372     /* Only return pdTRUE when the Link Status of the PHY is high, otherwise the
373      * DHCP process and all other communication will fail. */
374     xLinkStatus = xZynqGetPhyLinkStatus( pxInterface );
375 
376 /* return ( xLinkStatus != pdFALSE ); */
377     return pdTRUE; /* Workaround because network buffers are not freed when xZynqNetworkInterfaceInitialise() did not complete */
378 }
379 /*-----------------------------------------------------------*/
380 
xZynqNetworkInterfaceOutput(NetworkInterface_t * pxInterface,NetworkBufferDescriptor_t * const pxBuffer,BaseType_t bReleaseAfterSend)381 static BaseType_t xZynqNetworkInterfaceOutput( NetworkInterface_t * pxInterface,
382                                                NetworkBufferDescriptor_t * const pxBuffer,
383                                                BaseType_t bReleaseAfterSend )
384 {
385     BaseType_t xEMACIndex = ( BaseType_t ) pxInterface->pvArgument;
386 
387     configASSERT( xEMACIndex >= 0 );
388     configASSERT( xEMACIndex < XPAR_XEMACPS_NUM_INSTANCES );
389 
390     #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM != 0 )
391         {
392             ProtocolPacket_t * pxPacket;
393 
394             /* If the peripheral must calculate the checksum, it wants
395              * the protocol checksum to have a value of zero. */
396             pxPacket = ( ProtocolPacket_t * ) ( pxBuffer->pucEthernetBuffer );
397 
398             #if ( ipconfigUSE_IPv6 != 0 )
399                 ICMPPacket_IPv6_t * pxICMPPacket = ( ICMPPacket_IPv6_t * ) pxBuffer->pucEthernetBuffer;
400 
401                 if( ( pxPacket->xICMPPacket.xEthernetHeader.usFrameType == ipIPv6_FRAME_TYPE ) &&
402                     ( pxICMPPacket->xIPHeader.ucNextHeader == ipPROTOCOL_ICMP_IPv6 ) )
403                 {
404                     /* The EMAC will calculate the checksum of the IP-header.
405                      * It can only calculate protocol checksums of UDP and TCP,
406                      * so for ICMP and other protocols it must be done manually. */
407                     usGenerateProtocolChecksum( pxBuffer->pucEthernetBuffer, pxBuffer->xDataLength, pdTRUE );
408                 }
409             #endif
410 
411             if( ( pxPacket->xICMPPacket.xEthernetHeader.usFrameType == ipIPv4_FRAME_TYPE ) &&
412                 ( pxPacket->xICMPPacket.xIPHeader.ucProtocol == ipPROTOCOL_ICMP ) )
413             {
414                 /* The EMAC will calculate the checksum of the IP-header.
415                  * It can only calculate protocol checksums of UDP and TCP,
416                  * so for ICMP and other protocols it must be done manually. */
417                 usGenerateProtocolChecksum( pxBuffer->pucEthernetBuffer, pxBuffer->xDataLength, pdTRUE );
418             }
419         }
420     #endif /* ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM */
421 
422     if( ( ulPHYLinkStates[ xEMACIndex ] & niBMSR_LINK_STATUS ) != 0UL )
423     {
424         iptraceNETWORK_INTERFACE_TRANSMIT();
425 
426         /* emacps_send_message() will take ownership of pxBuffer, and
427          * make sure it will get release when bReleaseAfterSend is pdTRUE. */
428         emacps_send_message( &( xEMACpsifs[ xEMACIndex ] ), pxBuffer, bReleaseAfterSend );
429     }
430     else if( bReleaseAfterSend != pdFALSE )
431     {
432         /* No link. */
433         vReleaseNetworkBufferAndDescriptor( pxBuffer );
434     }
435 
436     return pdTRUE;
437 }
438 /*-----------------------------------------------------------*/
439 
ulReadMDIO(BaseType_t xEMACIndex,unsigned ulRegister)440 static inline unsigned long ulReadMDIO( BaseType_t xEMACIndex,
441                                         unsigned ulRegister )
442 {
443     uint16_t usValue;
444 
445     /* Always ETH0 because both PHYs are connected to ETH0 MDIO */
446     XEmacPs_PhyRead( &( xEMACpsifs[ 0 ].emacps ), phy_detected[ xEMACIndex ], ulRegister, &usValue );
447     return usValue;
448 }
449 /*-----------------------------------------------------------*/
450 
prvGMACWaitLS(BaseType_t xEMACIndex,TickType_t xMaxTime)451 static BaseType_t prvGMACWaitLS( BaseType_t xEMACIndex,
452                                  TickType_t xMaxTime )
453 {
454     TickType_t xStartTime, xEndTime;
455     const TickType_t xShortDelay = pdMS_TO_TICKS( 20UL );
456     BaseType_t xReturn;
457 
458     xStartTime = xTaskGetTickCount();
459 
460     for( ; ; )
461     {
462         xEndTime = xTaskGetTickCount();
463 
464         if( xEndTime - xStartTime > xMaxTime )
465         {
466             xReturn = pdFALSE;
467             break;
468         }
469 
470         ulPHYLinkStates[ xEMACIndex ] = ulReadMDIO( xEMACIndex, PHY_REG_01_BMSR );
471 
472         if( ( ulPHYLinkStates[ xEMACIndex ] & niBMSR_LINK_STATUS ) != 0U )
473         {
474             xReturn = pdTRUE;
475             break;
476         }
477 
478         vTaskDelay( xShortDelay );
479     }
480 
481     return xReturn;
482 }
483 /*-----------------------------------------------------------*/
484 
485 #if ( nicUSE_UNCACHED_MEMORY == 0 )
vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS])486     void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
487     {
488         static uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ] __attribute__( ( aligned( 32 ) ) );
489         uint8_t * ucRAMBuffer = ucNetworkPackets;
490         uint32_t ul;
491 
492         for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ )
493         {
494             pxNetworkBuffers[ ul ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING;
495             *( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ul ] ) );
496             ucRAMBuffer += niBUFFER_1_PACKET_SIZE;
497         }
498     }
499 #else /* if ( nicUSE_UNCACHED_MEMORY == 0 ) */
vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS])500     void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
501     {
502         static uint8_t * pucNetworkPackets = NULL;
503 
504         if( pucNetworkPackets == NULL )
505         {
506             pucNetworkPackets = pucGetUncachedMemory( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE );
507 
508             if( pucNetworkPackets != NULL )
509             {
510                 uint8_t * ucRAMBuffer = pucNetworkPackets;
511                 uint32_t ul;
512 
513                 for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ )
514                 {
515                     pxNetworkBuffers[ ul ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING;
516                     *( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ul ] ) );
517                     ucRAMBuffer += niBUFFER_1_PACKET_SIZE;
518                 }
519             }
520         }
521     }
522 #endif /* ( nicUSE_UNCACHED_MEMORY == 0 ) */
523 /*-----------------------------------------------------------*/
524 
xZynqGetPhyLinkStatus(NetworkInterface_t * pxInterface)525 static BaseType_t xZynqGetPhyLinkStatus( NetworkInterface_t * pxInterface )
526 {
527     BaseType_t xReturn;
528     BaseType_t xEMACIndex = ( BaseType_t ) pxInterface->pvArgument;
529 
530     if( ( ulPHYLinkStates[ xEMACIndex ] & niBMSR_LINK_STATUS ) == 0U )
531     {
532         xReturn = pdFALSE;
533     }
534     else
535     {
536         xReturn = pdTRUE;
537     }
538 
539     return xReturn;
540 }
541 /*-----------------------------------------------------------*/
542 
543 #if defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 )
544 
545 /* Do not call the following function directly. It is there for downward compatibility.
546  * The function FreeRTOS_IPInit() will call it to initialice the interface and end-point
547  * objects.  See the description in FreeRTOS_Routing.h. */
pxFillInterfaceDescriptor(BaseType_t xEMACIndex,NetworkInterface_t * pxInterface)548     NetworkInterface_t * pxFillInterfaceDescriptor( BaseType_t xEMACIndex,
549                                                     NetworkInterface_t * pxInterface )
550     {
551         pxZynq_FillInterfaceDescriptor( xEMACIndex, pxInterface );
552     }
553 
554 #endif
555 /*-----------------------------------------------------------*/
556 
pxZynq_FillInterfaceDescriptor(BaseType_t xEMACIndex,NetworkInterface_t * pxInterface)557 NetworkInterface_t * pxZynq_FillInterfaceDescriptor( BaseType_t xEMACIndex,
558                                                      NetworkInterface_t * pxInterface )
559 {
560     static char pcNames[ XPAR_XEMACPS_NUM_INSTANCES ][ 8 ];
561 
562     configASSERT( xEMACIndex >= 0 );
563     configASSERT( xEMACIndex < XPAR_XEMACPS_NUM_INSTANCES );
564 
565 /* This function pxZynq_FillInterfaceDescriptor() adds a network-interface.
566  * Make sure that the object pointed to by 'pxInterface'
567  * is declared static or global, and that it will remain to exist. */
568 
569     snprintf( pcNames[ xEMACIndex ], sizeof( pcNames[ xEMACIndex ] ), "eth%ld", xEMACIndex );
570 
571     memset( pxInterface, '\0', sizeof( *pxInterface ) );
572     pxInterface->pcName = pcNames[ xEMACIndex ];     /* Just for logging, debugging. */
573     pxInterface->pvArgument = ( void * ) xEMACIndex; /* Has only meaning for the driver functions. */
574     pxInterface->pfInitialise = xZynqNetworkInterfaceInitialise;
575     pxInterface->pfOutput = xZynqNetworkInterfaceOutput;
576     pxInterface->pfGetPhyLinkStatus = xZynqGetPhyLinkStatus;
577 
578     FreeRTOS_AddNetworkInterface( pxInterface );
579 
580     return pxInterface;
581 }
582 /*-----------------------------------------------------------*/
583 
prvEMACHandlerTask(void * pvParameters)584 static void prvEMACHandlerTask( void * pvParameters )
585 {
586     TimeOut_t xPhyTime;
587     TickType_t xPhyRemTime;
588     BaseType_t xResult = 0;
589     uint32_t xStatus;
590     const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL );
591     BaseType_t xEMACIndex = ( BaseType_t ) pvParameters;
592     xemacpsif_s * pxEMAC_PS;
593 
594     configASSERT( xEMACIndex >= 0 );
595     configASSERT( xEMACIndex < XPAR_XEMACPS_NUM_INSTANCES );
596 
597     pxEMAC_PS = &( xEMACpsifs[ xEMACIndex ] );
598 
599     /* Remove compiler warnings about unused parameters. */
600     ( void ) pvParameters;
601 
602     /* A possibility to set some additional task properties like calling
603      * portTASK_USES_FLOATING_POINT() */
604     iptraceEMAC_TASK_STARTING();
605 
606     vTaskSetTimeOutState( &xPhyTime );
607     xPhyRemTime = pdMS_TO_TICKS( ipconfigPHY_LS_LOW_CHECK_TIME_MS );
608     FreeRTOS_printf( ( "prvEMACHandlerTask[ %ld ] started running\n", xEMACIndex ) );
609 
610     for( ; ; )
611     {
612         #if ( ipconfigHAS_PRINTF != 0 )
613             {
614                 /* Call a function that monitors resources: the amount of free network
615                  * buffers and the amount of free space on the heap.  See FreeRTOS_IP.c
616                  * for more detailed comments. */
617                 vPrintResourceStats();
618             }
619         #endif /* ( ipconfigHAS_PRINTF != 0 ) */
620 
621         if( ( pxEMAC_PS->isr_events & EMAC_IF_ALL_EVENT ) == 0 )
622         {
623             /* No events to process now, wait for the next. */
624             ulTaskNotifyTake( pdFALSE, ulMaxBlockTime );
625         }
626 
627         if( ( pxEMAC_PS->isr_events & EMAC_IF_RX_EVENT ) != 0 )
628         {
629             pxEMAC_PS->isr_events &= ~EMAC_IF_RX_EVENT;
630             xResult = emacps_check_rx( pxEMAC_PS, pxMyInterfaces[ xEMACIndex ] );
631         }
632 
633         if( ( pxEMAC_PS->isr_events & EMAC_IF_TX_EVENT ) != 0 )
634         {
635             pxEMAC_PS->isr_events &= ~EMAC_IF_TX_EVENT;
636             emacps_check_tx( pxEMAC_PS );
637         }
638 
639         if( ( pxEMAC_PS->isr_events & EMAC_IF_ERR_EVENT ) != 0 )
640         {
641             pxEMAC_PS->isr_events &= ~EMAC_IF_ERR_EVENT;
642             emacps_check_errors( pxEMAC_PS );
643         }
644 
645         if( xResult > 0 )
646         {
647             /* A packet was received. No need to check for the PHY status now,
648              * but set a timer to check it later on. */
649             vTaskSetTimeOutState( &xPhyTime );
650             xPhyRemTime = pdMS_TO_TICKS( ipconfigPHY_LS_HIGH_CHECK_TIME_MS );
651             xResult = 0;
652             ulPHYLinkStates[ xEMACIndex ] |= niBMSR_LINK_STATUS;
653         }
654         else if( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE )
655         {
656             xStatus = ulReadMDIO( xEMACIndex, PHY_REG_01_BMSR );
657 
658             if( ( ulPHYLinkStates[ xEMACIndex ] & niBMSR_LINK_STATUS ) != ( xStatus & niBMSR_LINK_STATUS ) )
659             {
660                 ulPHYLinkStates[ xEMACIndex ] = xStatus;
661                 FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStates[ xEMACIndex ] & niBMSR_LINK_STATUS ) != 0 ) );
662             }
663 
664             vTaskSetTimeOutState( &xPhyTime );
665 
666             if( ( ulPHYLinkStates[ xEMACIndex ] & niBMSR_LINK_STATUS ) != 0 )
667             {
668                 xPhyRemTime = pdMS_TO_TICKS( ipconfigPHY_LS_HIGH_CHECK_TIME_MS );
669             }
670             else
671             {
672                 xPhyRemTime = pdMS_TO_TICKS( ipconfigPHY_LS_LOW_CHECK_TIME_MS );
673             }
674         }
675     }
676 }
677 /*-----------------------------------------------------------*/
678