]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/Zynq/NetworkInterface.c
Added +TCP code to main repo.
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-Plus-TCP / portable / NetworkInterface / Zynq / NetworkInterface.c
1 /*\r
2  * FreeRTOS+TCP Labs Build 200417 (C) 2016 Real Time Engineers ltd.\r
3  * Authors include Hein Tibosch and Richard Barry\r
4  *\r
5  *******************************************************************************\r
6  ***** NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ***\r
7  ***                                                                         ***\r
8  ***                                                                         ***\r
9  ***   FREERTOS+TCP IS STILL IN THE LAB (mainly because the FTP and HTTP     ***\r
10  ***   demos have a dependency on FreeRTOS+FAT, which is only in the Labs    ***\r
11  ***   download):                                                            ***\r
12  ***                                                                         ***\r
13  ***   FreeRTOS+TCP is functional and has been used in commercial products   ***\r
14  ***   for some time.  Be aware however that we are still refining its       ***\r
15  ***   design, the source code does not yet quite conform to the strict      ***\r
16  ***   coding and style standards mandated by Real Time Engineers ltd., and  ***\r
17  ***   the documentation and testing is not necessarily complete.            ***\r
18  ***                                                                         ***\r
19  ***   PLEASE REPORT EXPERIENCES USING THE SUPPORT RESOURCES FOUND ON THE    ***\r
20  ***   URL: http://www.FreeRTOS.org/contact  Active early adopters may, at   ***\r
21  ***   the sole discretion of Real Time Engineers Ltd., be offered versions  ***\r
22  ***   under a license other than that described below.                      ***\r
23  ***                                                                         ***\r
24  ***                                                                         ***\r
25  ***** NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ***\r
26  *******************************************************************************\r
27  *\r
28  * FreeRTOS+TCP can be used under two different free open source licenses.  The\r
29  * license that applies is dependent on the processor on which FreeRTOS+TCP is\r
30  * executed, as follows:\r
31  *\r
32  * If FreeRTOS+TCP is executed on one of the processors listed under the Special\r
33  * License Arrangements heading of the FreeRTOS+TCP license information web\r
34  * page, then it can be used under the terms of the FreeRTOS Open Source\r
35  * License.  If FreeRTOS+TCP is used on any other processor, then it can be used\r
36  * under the terms of the GNU General Public License V2.  Links to the relevant\r
37  * licenses follow:\r
38  *\r
39  * The FreeRTOS+TCP License Information Page: http://www.FreeRTOS.org/tcp_license\r
40  * The FreeRTOS Open Source License: http://www.FreeRTOS.org/license\r
41  * The GNU General Public License Version 2: http://www.FreeRTOS.org/gpl-2.0.txt\r
42  *\r
43  * FreeRTOS+TCP is distributed in the hope that it will be useful.  You cannot\r
44  * use FreeRTOS+TCP unless you agree that you use the software 'as is'.\r
45  * FreeRTOS+TCP is provided WITHOUT ANY WARRANTY; without even the implied\r
46  * warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR\r
47  * PURPOSE. Real Time Engineers Ltd. disclaims all conditions and terms, be they\r
48  * implied, expressed, or statutory.\r
49  *\r
50  * 1 tab == 4 spaces!\r
51  *\r
52  * http://www.FreeRTOS.org\r
53  * http://www.FreeRTOS.org/plus\r
54  * http://www.FreeRTOS.org/labs\r
55  *\r
56  */\r
57 \r
58 /* Standard includes. */\r
59 #include <stdint.h>\r
60 #include <stdio.h>\r
61 #include <stdlib.h>\r
62 \r
63 /* FreeRTOS includes. */\r
64 #include "FreeRTOS.h"\r
65 #include "task.h"\r
66 #include "queue.h"\r
67 #include "semphr.h"\r
68 \r
69 /* FreeRTOS+TCP includes. */\r
70 #include "FreeRTOS_IP.h"\r
71 #include "FreeRTOS_Sockets.h"\r
72 #include "FreeRTOS_IP_Private.h"\r
73 #include "NetworkBufferManagement.h"\r
74 #include "NetworkInterface.h"\r
75 \r
76 /* Xilinx library files. */\r
77 #include <xemacps.h>\r
78 #include "Zynq/x_topology.h"\r
79 #include "Zynq/x_emacpsif.h"\r
80 #include "Zynq/x_emacpsif_hw.h"\r
81 \r
82 /* Provided memory configured as uncached. */\r
83 #include "uncached_memory.h"\r
84 \r
85 #ifndef BMSR_LINK_STATUS\r
86         #define BMSR_LINK_STATUS            0x0004UL\r
87 #endif\r
88 \r
89 #ifndef PHY_LS_HIGH_CHECK_TIME_MS\r
90         /* Check if the LinkSStatus in the PHY is still high after 15 seconds of not\r
91         receiving packets. */\r
92         #define PHY_LS_HIGH_CHECK_TIME_MS       15000\r
93 #endif\r
94 \r
95 #ifndef PHY_LS_LOW_CHECK_TIME_MS\r
96         /* Check if the LinkSStatus in the PHY is still low every second. */\r
97         #define PHY_LS_LOW_CHECK_TIME_MS        1000\r
98 #endif\r
99 \r
100 /* The size of each buffer when BufferAllocation_1 is used:\r
101 http://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Buffer_Management.html */\r
102 #define niBUFFER_1_PACKET_SIZE          1536\r
103 \r
104 /* Naming and numbering of PHY registers. */\r
105 #define PHY_REG_01_BMSR                 0x01    /* Basic mode status register */\r
106 \r
107 #ifndef iptraceEMAC_TASK_STARTING\r
108         #define iptraceEMAC_TASK_STARTING()     do { } while( 0 )\r
109 #endif\r
110 \r
111 /* Default the size of the stack used by the EMAC deferred handler task to twice\r
112 the size of the stack used by the idle task - but allow this to be overridden in\r
113 FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */\r
114 #ifndef configEMAC_TASK_STACK_SIZE\r
115         #define configEMAC_TASK_STACK_SIZE ( 2 * configMINIMAL_STACK_SIZE )\r
116 #endif\r
117 \r
118 /*-----------------------------------------------------------*/\r
119 \r
120 /*\r
121  * Look for the link to be up every few milliseconds until either xMaxTime time\r
122  * has passed or a link is found.\r
123  */\r
124 static BaseType_t prvGMACWaitLS( TickType_t xMaxTime );\r
125 \r
126 /*\r
127  * A deferred interrupt handler for all MAC/DMA interrupt sources.\r
128  */\r
129 static void prvEMACHandlerTask( void *pvParameters );\r
130 \r
131 /*-----------------------------------------------------------*/\r
132 \r
133 /* EMAC data/descriptions. */\r
134 static xemacpsif_s xEMACpsif;\r
135 struct xtopology_t xXTopology =\r
136 {\r
137         .emac_baseaddr = XPAR_PS7_ETHERNET_0_BASEADDR,\r
138         .emac_type = xemac_type_emacps,\r
139         .intc_baseaddr = 0x0,\r
140         .intc_emac_intr = 0x0,\r
141         .scugic_baseaddr = XPAR_PS7_SCUGIC_0_BASEADDR,\r
142         .scugic_emac_intr = 0x36,\r
143 };\r
144 \r
145 XEmacPs_Config mac_config =\r
146 {\r
147         .DeviceId = XPAR_PS7_ETHERNET_0_DEVICE_ID,      /**< Unique ID  of device */\r
148         .BaseAddress = XPAR_PS7_ETHERNET_0_BASEADDR /**< Physical base address of IPIF registers */\r
149 };\r
150 \r
151 extern int phy_detected;\r
152 \r
153 /* A copy of PHY register 1: 'PHY_REG_01_BMSR' */\r
154 static uint32_t ulPHYLinkStatus = 0;\r
155 \r
156 #if( ipconfigUSE_LLMNR == 1 )\r
157         static const uint8_t xLLMNR_MACAddress[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC };\r
158 #endif\r
159 \r
160 /* ucMACAddress as it appears in main.c */\r
161 extern const uint8_t ucMACAddress[ 6 ];\r
162 \r
163 /* Holds the handle of the task used as a deferred interrupt processor.  The\r
164 handle is used so direct notifications can be sent to the task for all EMAC/DMA\r
165 related interrupts. */\r
166 TaskHandle_t xEMACTaskHandle = NULL;\r
167 \r
168 /*-----------------------------------------------------------*/\r
169 \r
170 BaseType_t xNetworkInterfaceInitialise( void )\r
171 {\r
172 uint32_t ulLinkSpeed, ulDMAReg;\r
173 BaseType_t xStatus, xLinkStatus;\r
174 XEmacPs *pxEMAC_PS;\r
175 const TickType_t xWaitLinkDelay = pdMS_TO_TICKS( 7000UL ), xWaitRelinkDelay = pdMS_TO_TICKS( 1000UL );\r
176 \r
177         /* Guard against the init function being called more than once. */\r
178         if( xEMACTaskHandle == NULL )\r
179         {\r
180                 pxEMAC_PS = &( xEMACpsif.emacps );\r
181                 memset( &xEMACpsif, '\0', sizeof( xEMACpsif ) );\r
182 \r
183                 xStatus = XEmacPs_CfgInitialize( pxEMAC_PS, &mac_config, mac_config.BaseAddress);\r
184                 if( xStatus != XST_SUCCESS )\r
185                 {\r
186                         FreeRTOS_printf( ( "xEMACInit: EmacPs Configuration Failed....\n" ) );\r
187                 }\r
188 \r
189                 /* Initialize the mac and set the MAC address. */\r
190                 XEmacPs_SetMacAddress( pxEMAC_PS, ( void * ) ucMACAddress, 1 );\r
191 \r
192                 #if( ipconfigUSE_LLMNR == 1 )\r
193                 {\r
194                         /* Also add LLMNR multicast MAC address. */\r
195                         XEmacPs_SetMacAddress( pxEMAC_PS, ( void * )xLLMNR_MACAddress, 2 );\r
196                 }\r
197                 #endif  /* ipconfigUSE_LLMNR == 1 */\r
198 \r
199                 XEmacPs_SetMdioDivisor( pxEMAC_PS, MDC_DIV_224 );\r
200                 ulLinkSpeed = Phy_Setup( pxEMAC_PS );\r
201                 XEmacPs_SetOperatingSpeed( pxEMAC_PS, ulLinkSpeed);\r
202 \r
203                 /* Setting the operating speed of the MAC needs a delay. */\r
204                 vTaskDelay( pdMS_TO_TICKS( 25UL ) );\r
205 \r
206                 ulDMAReg = XEmacPs_ReadReg( pxEMAC_PS->Config.BaseAddress, XEMACPS_DMACR_OFFSET);\r
207 \r
208                 /* DISC_WHEN_NO_AHB: when set, the GEM DMA will automatically discard receive\r
209                 packets from the receiver packet buffer memory when no AHB resource is available. */\r
210                 XEmacPs_WriteReg( pxEMAC_PS->Config.BaseAddress, XEMACPS_DMACR_OFFSET,\r
211                         ulDMAReg | XEMACPS_DMACR_DISC_WHEN_NO_AHB_MASK);\r
212 \r
213                 setup_isr( &xEMACpsif );\r
214                 init_dma( &xEMACpsif );\r
215                 start_emacps( &xEMACpsif );\r
216 \r
217                 prvGMACWaitLS( xWaitLinkDelay );\r
218 \r
219                 /* The deferred interrupt handler task is created at the highest\r
220                 possible priority to ensure the interrupt handler can return directly\r
221                 to it.  The task's handle is stored in xEMACTaskHandle so interrupts can\r
222                 notify the task when there is something to process. */\r
223                 xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xEMACTaskHandle );\r
224         }\r
225         else\r
226         {\r
227                 /* Initialisation was already performed, just wait for the link. */\r
228                 prvGMACWaitLS( xWaitRelinkDelay );\r
229         }\r
230 \r
231         /* Only return pdTRUE when the Link Status of the PHY is high, otherwise the\r
232         DHCP process and all other communication will fail. */\r
233         xLinkStatus = xGetPhyLinkStatus();\r
234 \r
235         return ( xLinkStatus != pdFALSE );\r
236 }\r
237 /*-----------------------------------------------------------*/\r
238 \r
239 BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxBuffer, BaseType_t bReleaseAfterSend )\r
240 {\r
241         if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )\r
242         {\r
243                 iptraceNETWORK_INTERFACE_TRANSMIT();\r
244                 emacps_send_message( &xEMACpsif, pxBuffer, bReleaseAfterSend );\r
245         }\r
246         else if( bReleaseAfterSend != pdFALSE )\r
247         {\r
248                 /* No link. */\r
249                 vReleaseNetworkBufferAndDescriptor( pxBuffer );\r
250         }\r
251 \r
252         return pdTRUE;\r
253 }\r
254 /*-----------------------------------------------------------*/\r
255 \r
256 static inline unsigned long ulReadMDIO( unsigned ulRegister )\r
257 {\r
258 uint16_t usValue;\r
259 \r
260         XEmacPs_PhyRead( &( xEMACpsif.emacps ), phy_detected, ulRegister, &usValue );\r
261         return usValue;\r
262 }\r
263 /*-----------------------------------------------------------*/\r
264 \r
265 static BaseType_t prvGMACWaitLS( TickType_t xMaxTime )\r
266 {\r
267 TickType_t xStartTime, xEndTime;\r
268 const TickType_t xShortDelay = pdMS_TO_TICKS( 20UL );\r
269 BaseType_t xReturn;\r
270 \r
271         xStartTime = xTaskGetTickCount();\r
272 \r
273         for( ;; )\r
274         {\r
275                 xEndTime = xTaskGetTickCount();\r
276 \r
277                 if( xEndTime - xStartTime > xMaxTime )\r
278                 {\r
279                         xReturn = pdFALSE;\r
280                         break;\r
281                 }\r
282                 ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR );\r
283 \r
284                 if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )\r
285                 {\r
286                         xReturn = pdTRUE;\r
287                         break;\r
288                 }\r
289 \r
290                 vTaskDelay( xShortDelay );\r
291         }\r
292 \r
293         return xReturn;\r
294 }\r
295 /*-----------------------------------------------------------*/\r
296 \r
297 void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )\r
298 {\r
299 static uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ] __attribute__ ( ( aligned( 32 ) ) );\r
300 uint8_t *ucRAMBuffer = ucNetworkPackets;\r
301 uint32_t ul;\r
302 \r
303         for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ )\r
304         {\r
305                 pxNetworkBuffers[ ul ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING;\r
306                 *( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ul ] ) );\r
307                 ucRAMBuffer += niBUFFER_1_PACKET_SIZE;\r
308         }\r
309 }\r
310 /*-----------------------------------------------------------*/\r
311 \r
312 BaseType_t xGetPhyLinkStatus( void )\r
313 {\r
314 BaseType_t xReturn;\r
315 \r
316         if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) == 0 )\r
317         {\r
318                 xReturn = pdFALSE;\r
319         }\r
320         else\r
321         {\r
322                 xReturn = pdTRUE;\r
323         }\r
324 \r
325         return xReturn;\r
326 }\r
327 /*-----------------------------------------------------------*/\r
328 \r
329 static void prvEMACHandlerTask( void *pvParameters )\r
330 {\r
331 TimeOut_t xPhyTime;\r
332 TickType_t xPhyRemTime;\r
333 UBaseType_t uxLastMinBufferCount = 0;\r
334 UBaseType_t uxCurrentCount;\r
335 BaseType_t xResult = 0;\r
336 uint32_t xStatus;\r
337 const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( 100UL );\r
338 \r
339         /* Remove compiler warnings about unused parameters. */\r
340         ( void ) pvParameters;\r
341 \r
342         /* A possibility to set some additional task properties like calling\r
343         portTASK_USES_FLOATING_POINT() */\r
344         iptraceEMAC_TASK_STARTING();\r
345 \r
346         vTaskSetTimeOutState( &xPhyTime );\r
347         xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );\r
348 \r
349         for( ;; )\r
350         {\r
351                 uxCurrentCount = uxGetMinimumFreeNetworkBuffers();\r
352                 if( uxLastMinBufferCount != uxCurrentCount )\r
353                 {\r
354                         /* The logging produced below may be helpful\r
355                         while tuning +TCP: see how many buffers are in use. */\r
356                         uxLastMinBufferCount = uxCurrentCount;\r
357                         FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n",\r
358                                 uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) );\r
359                 }\r
360 \r
361                 #if( ipconfigCHECK_IP_QUEUE_SPACE != 0 )\r
362                 {\r
363                 static UBaseType_t uxLastMinQueueSpace = 0;\r
364 \r
365                         uxCurrentCount = uxGetMinimumIPQueueSpace();\r
366                         if( uxLastMinQueueSpace != uxCurrentCount )\r
367                         {\r
368                                 /* The logging produced below may be helpful\r
369                                 while tuning +TCP: see how many buffers are in use. */\r
370                                 uxLastMinQueueSpace = uxCurrentCount;\r
371                                 FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) );\r
372                         }\r
373                 }\r
374                 #endif /* ipconfigCHECK_IP_QUEUE_SPACE */\r
375 \r
376                 if( ( xEMACpsif.isr_events & EMAC_IF_ALL_EVENT ) == 0 )\r
377                 {\r
378                         /* No events to process now, wait for the next. */\r
379                         ulTaskNotifyTake( pdFALSE, ulMaxBlockTime );\r
380                 }\r
381 \r
382                 if( ( xEMACpsif.isr_events & EMAC_IF_RX_EVENT ) != 0 )\r
383                 {\r
384                         xEMACpsif.isr_events &= ~EMAC_IF_RX_EVENT;\r
385                         xResult = emacps_check_rx( &xEMACpsif );\r
386                 }\r
387 \r
388                 if( ( xEMACpsif.isr_events & EMAC_IF_TX_EVENT ) != 0 )\r
389                 {\r
390                         xEMACpsif.isr_events &= ~EMAC_IF_TX_EVENT;\r
391                         emacps_check_tx( &xEMACpsif );\r
392                 }\r
393 \r
394                 if( ( xEMACpsif.isr_events & EMAC_IF_ERR_EVENT ) != 0 )\r
395                 {\r
396                         xEMACpsif.isr_events &= ~EMAC_IF_ERR_EVENT;\r
397                         emacps_check_errors( &xEMACpsif );\r
398                 }\r
399 \r
400                 if( xResult > 0 )\r
401                 {\r
402                         /* A packet was received. No need to check for the PHY status now,\r
403                         but set a timer to check it later on. */\r
404                         vTaskSetTimeOutState( &xPhyTime );\r
405                         xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );\r
406                         xResult = 0;\r
407                 }\r
408                 else if( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE )\r
409                 {\r
410                         xStatus = ulReadMDIO( PHY_REG_01_BMSR );\r
411 \r
412                         if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != ( xStatus & BMSR_LINK_STATUS ) )\r
413                         {\r
414                                 ulPHYLinkStatus = xStatus;\r
415                                 FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) );\r
416                         }\r
417 \r
418                         vTaskSetTimeOutState( &xPhyTime );\r
419                         if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )\r
420                         {\r
421                                 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );\r
422                         }\r
423                         else\r
424                         {\r
425                                 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );\r
426                         }\r
427                 }\r
428         }\r
429 }\r
430 /*-----------------------------------------------------------*/\r