]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/NetworkInterface/ATSAM4E/NetworkInterface.c
Sync FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP with the version in GitHub at (23665258ca...
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-Plus-TCP / portable / NetworkInterface / ATSAM4E / NetworkInterface.c
1 /*\r
2 FreeRTOS+TCP V2.0.11\r
3 Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4 \r
5 Permission is hereby granted, free of charge, to any person obtaining a copy of\r
6 this software and associated documentation files (the "Software"), to deal in\r
7 the Software without restriction, including without limitation the rights to\r
8 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
9 the Software, and to permit persons to whom the Software is furnished to do so,\r
10 subject to the following conditions:\r
11 \r
12 The above copyright notice and this permission notice shall be included in all\r
13 copies or substantial portions of the Software.\r
14 \r
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
17 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
18 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21 \r
22  http://aws.amazon.com/freertos\r
23  http://www.FreeRTOS.org\r
24 */\r
25 \r
26 /* Standard includes. */\r
27 #include <stdint.h>\r
28 #include <stdio.h>\r
29 #include <stdlib.h>\r
30 \r
31 /* FreeRTOS includes. */\r
32 #include "FreeRTOS.h"\r
33 #include "task.h"\r
34 #include "queue.h"\r
35 #include "semphr.h"\r
36 \r
37 /* FreeRTOS+TCP includes. */\r
38 #include "FreeRTOS_IP.h"\r
39 #include "FreeRTOS_Sockets.h"\r
40 #include "FreeRTOS_IP_Private.h"\r
41 #include "NetworkBufferManagement.h"\r
42 #include "NetworkInterface.h"\r
43 \r
44 /* Some files from the Atmel Software Framework */\r
45 /*_RB_ The SAM4E portable layer has three different header files called gmac.h! */\r
46 #include "instance/gmac.h"\r
47 #include <sysclk.h>\r
48 #include <ethernet_phy.h>\r
49 \r
50 #ifndef BMSR_LINK_STATUS\r
51         #define BMSR_LINK_STATUS            0x0004  //!< Link status\r
52 #endif\r
53 \r
54 #ifndef PHY_LS_HIGH_CHECK_TIME_MS\r
55         /* Check if the LinkSStatus in the PHY is still high after 15 seconds of not\r
56         receiving packets. */\r
57         #define PHY_LS_HIGH_CHECK_TIME_MS       15000\r
58 #endif\r
59 \r
60 #ifndef PHY_LS_LOW_CHECK_TIME_MS\r
61         /* Check if the LinkSStatus in the PHY is still low every second. */\r
62         #define PHY_LS_LOW_CHECK_TIME_MS        1000\r
63 #endif\r
64 \r
65 /* Interrupt events to process.  Currently only the Rx event is processed\r
66 although code for other events is included to allow for possible future\r
67 expansion. */\r
68 #define EMAC_IF_RX_EVENT        1UL\r
69 #define EMAC_IF_TX_EVENT        2UL\r
70 #define EMAC_IF_ERR_EVENT       4UL\r
71 #define EMAC_IF_ALL_EVENT       ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT )\r
72 \r
73 #define ETHERNET_CONF_PHY_ADDR  BOARD_GMAC_PHY_ADDR\r
74 \r
75 #define HZ_PER_MHZ                              ( 1000000UL )\r
76 \r
77 #ifndef EMAC_MAX_BLOCK_TIME_MS\r
78         #define EMAC_MAX_BLOCK_TIME_MS  100ul\r
79 #endif\r
80 \r
81 #if !defined( GMAC_USES_TX_CALLBACK ) || ( GMAC_USES_TX_CALLBACK != 1 )\r
82         #error Please define GMAC_USES_TX_CALLBACK as 1\r
83 #endif\r
84 \r
85 /* Default the size of the stack used by the EMAC deferred handler task to 4x\r
86 the size of the stack used by the idle task - but allow this to be overridden in\r
87 FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */\r
88 #ifndef configEMAC_TASK_STACK_SIZE\r
89         #define configEMAC_TASK_STACK_SIZE ( 4 * configMINIMAL_STACK_SIZE )\r
90 #endif\r
91 \r
92 /*-----------------------------------------------------------*/\r
93 \r
94 /*\r
95  * Wait a fixed time for the link status to indicate the network is up.\r
96  */\r
97 static BaseType_t xGMACWaitLS( TickType_t xMaxTime );\r
98 \r
99 #if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 ) && ( ipconfigHAS_TX_CRC_OFFLOADING == 0 )\r
100         void vGMACGenerateChecksum( uint8_t *apBuffer );\r
101 #endif\r
102 \r
103 /*\r
104  * Called from the ASF GMAC driver.\r
105  */\r
106 static void prvRxCallback( uint32_t ulStatus );\r
107 static void prvTxCallback( uint32_t ulStatus, uint8_t *puc_buffer );\r
108 \r
109 /*\r
110  * A deferred interrupt handler task that processes GMAC interrupts.\r
111  */\r
112 static void prvEMACHandlerTask( void *pvParameters );\r
113 \r
114 /*\r
115  * Initialise the ASF GMAC driver.\r
116  */\r
117 static BaseType_t prvGMACInit( void );\r
118 \r
119 /*\r
120  * Try to obtain an Rx packet from the hardware.\r
121  */\r
122 static uint32_t prvEMACRxPoll( void );\r
123 \r
124 /*-----------------------------------------------------------*/\r
125 \r
126 /* Bit map of outstanding ETH interrupt events for processing.  Currently only\r
127 the Rx interrupt is handled, although code is included for other events to\r
128 enable future expansion. */\r
129 static volatile uint32_t ulISREvents;\r
130 \r
131 /* A copy of PHY register 1: 'PHY_REG_01_BMSR' */\r
132 static uint32_t ulPHYLinkStatus = 0;\r
133 static volatile BaseType_t xGMACSwitchRequired;\r
134 \r
135 /* ethernet_phy_addr: the address of the PHY in use.\r
136 Atmel was a bit ambiguous about it so the address will be stored\r
137 in this variable, see ethernet_phy.c */\r
138 extern int ethernet_phy_addr;\r
139 \r
140 /* LLMNR multicast address. */\r
141 static const uint8_t llmnr_mac_address[] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0xFC };\r
142 \r
143 /* The GMAC object as defined by the ASF drivers. */\r
144 static gmac_device_t gs_gmac_dev;\r
145 \r
146 /* MAC address to use. */\r
147 extern const uint8_t ucMACAddress[ 6 ];\r
148 \r
149 /* Holds the handle of the task used as a deferred interrupt processor.  The\r
150 handle is used so direct notifications can be sent to the task for all EMAC/DMA\r
151 related interrupts. */\r
152 TaskHandle_t xEMACTaskHandle = NULL;\r
153 \r
154 static QueueHandle_t xTxBufferQueue;\r
155 int tx_release_count[ 4 ];\r
156 \r
157 /* xTXDescriptorSemaphore is a counting semaphore with\r
158 a maximum count of GMAC_TX_BUFFERS, which is the number of\r
159 DMA TX descriptors. */\r
160 static SemaphoreHandle_t xTXDescriptorSemaphore = NULL;\r
161 \r
162 /*-----------------------------------------------------------*/\r
163 \r
164 /*\r
165  * GMAC interrupt handler.\r
166  */\r
167 void GMAC_Handler(void)\r
168 {\r
169         xGMACSwitchRequired = pdFALSE;\r
170 \r
171         /* gmac_handler() may call prvRxCallback() which may change\r
172         the value of xGMACSwitchRequired. */\r
173         gmac_handler( &gs_gmac_dev );\r
174 \r
175         if( xGMACSwitchRequired != pdFALSE )\r
176         {\r
177                 portEND_SWITCHING_ISR( xGMACSwitchRequired );\r
178         }\r
179 }\r
180 /*-----------------------------------------------------------*/\r
181 \r
182 static void prvRxCallback( uint32_t ulStatus )\r
183 {\r
184         if( ( ( ulStatus & GMAC_RSR_REC ) != 0 ) && ( xEMACTaskHandle != NULL ) )\r
185         {\r
186                 /* let the prvEMACHandlerTask know that there was an RX event. */\r
187                 ulISREvents |= EMAC_IF_RX_EVENT;\r
188                 /* Only an RX interrupt can wakeup prvEMACHandlerTask. */\r
189                 vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xGMACSwitchRequired );\r
190         }\r
191 }\r
192 /*-----------------------------------------------------------*/\r
193 \r
194 static void prvTxCallback( uint32_t ulStatus, uint8_t *puc_buffer )\r
195 {\r
196         if( ( xTxBufferQueue != NULL ) && ( xEMACTaskHandle != NULL ) )\r
197         {\r
198                 /* let the prvEMACHandlerTask know that there was an RX event. */\r
199                 ulISREvents |= EMAC_IF_TX_EVENT;\r
200 \r
201                 vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xGMACSwitchRequired );\r
202                 xQueueSendFromISR( xTxBufferQueue, &puc_buffer, ( BaseType_t * ) &xGMACSwitchRequired );\r
203                 tx_release_count[ 2 ]++;\r
204         }\r
205 }\r
206 /*-----------------------------------------------------------*/\r
207 \r
208 BaseType_t xNetworkInterfaceInitialise( void )\r
209 {\r
210 const TickType_t x5_Seconds = 5000UL;\r
211 \r
212         if( xEMACTaskHandle == NULL )\r
213         {\r
214                 prvGMACInit();\r
215 \r
216                 /* Wait at most 5 seconds for a Link Status in the PHY. */\r
217                 xGMACWaitLS( pdMS_TO_TICKS( x5_Seconds ) );\r
218 \r
219                 /* The handler task is created at the highest possible priority to\r
220                 ensure the interrupt handler can return directly to it. */\r
221                 xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xEMACTaskHandle );\r
222                 configASSERT( xEMACTaskHandle );\r
223         }\r
224 \r
225         if( xTxBufferQueue == NULL )\r
226         {\r
227                 xTxBufferQueue = xQueueCreate( GMAC_TX_BUFFERS, sizeof( void * ) );\r
228                 configASSERT( xTxBufferQueue );\r
229         }\r
230 \r
231         if( xTXDescriptorSemaphore == NULL )\r
232         {\r
233                 xTXDescriptorSemaphore = xSemaphoreCreateCounting( ( UBaseType_t ) GMAC_TX_BUFFERS, ( UBaseType_t ) GMAC_TX_BUFFERS );\r
234                 configASSERT( xTXDescriptorSemaphore );\r
235         }\r
236         /* When returning non-zero, the stack will become active and\r
237     start DHCP (in configured) */\r
238         return ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0;\r
239 }\r
240 /*-----------------------------------------------------------*/\r
241 \r
242 BaseType_t xGetPhyLinkStatus( void )\r
243 {\r
244 BaseType_t xResult;\r
245 \r
246         /* This function returns true if the Link Status in the PHY is high. */\r
247         if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )\r
248         {\r
249                 xResult = pdTRUE;\r
250         }\r
251         else\r
252         {\r
253                 xResult = pdFALSE;\r
254         }\r
255 \r
256         return xResult;\r
257 }\r
258 /*-----------------------------------------------------------*/\r
259 \r
260 BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor, BaseType_t bReleaseAfterSend )\r
261 {\r
262 /* Do not wait too long for a free TX DMA buffer. */\r
263 const TickType_t xBlockTimeTicks = pdMS_TO_TICKS( 50u );\r
264 \r
265         do {\r
266                 if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) == 0 )\r
267                 {\r
268                         /* Do not attempt to send packets as long as the Link Status is low. */\r
269                         break;\r
270                 }\r
271                 if( xTXDescriptorSemaphore == NULL )\r
272                 {\r
273                         /* Semaphore has not been created yet? */\r
274                         break;\r
275                 }\r
276                 if( xSemaphoreTake( xTXDescriptorSemaphore, xBlockTimeTicks ) != pdPASS )\r
277                 {\r
278                         /* Time-out waiting for a free TX descriptor. */\r
279                         tx_release_count[ 3 ]++;\r
280                         break;\r
281                 }\r
282                 #if( ipconfigZERO_COPY_TX_DRIVER != 0 )\r
283                 {\r
284                         /* Confirm that the pxDescriptor may be kept by the driver. */\r
285                         configASSERT( bReleaseAfterSend != pdFALSE );\r
286                 }\r
287                 #endif /* ipconfigZERO_COPY_TX_DRIVER */\r
288 \r
289                 gmac_dev_write( &gs_gmac_dev, (void *)pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength, prvTxCallback );\r
290 \r
291                 #if( ipconfigZERO_COPY_TX_DRIVER != 0 )\r
292                 {\r
293                         /* Confirm that the pxDescriptor may be kept by the driver. */\r
294                         bReleaseAfterSend = pdFALSE;\r
295                 }\r
296                 #endif /* ipconfigZERO_COPY_TX_DRIVER */\r
297                 /* Not interested in a call-back after TX. */\r
298                 iptraceNETWORK_INTERFACE_TRANSMIT();\r
299         } while( 0 );\r
300 \r
301         if( bReleaseAfterSend != pdFALSE )\r
302         {\r
303                 vReleaseNetworkBufferAndDescriptor( pxDescriptor );\r
304         }\r
305         return pdTRUE;\r
306 }\r
307 /*-----------------------------------------------------------*/\r
308 \r
309 static BaseType_t prvGMACInit( void )\r
310 {\r
311 uint32_t ncfgr;\r
312 \r
313         gmac_options_t gmac_option;\r
314 \r
315         memset( &gmac_option, '\0', sizeof( gmac_option ) );\r
316         gmac_option.uc_copy_all_frame = 0;\r
317         gmac_option.uc_no_boardcast = 0;\r
318         memcpy( gmac_option.uc_mac_addr, ucMACAddress, sizeof( gmac_option.uc_mac_addr ) );\r
319 \r
320         gs_gmac_dev.p_hw = GMAC;\r
321         gmac_dev_init( GMAC, &gs_gmac_dev, &gmac_option );\r
322 \r
323         NVIC_SetPriority( GMAC_IRQn, configMAC_INTERRUPT_PRIORITY );\r
324         NVIC_EnableIRQ( GMAC_IRQn );\r
325 \r
326         /* Contact the Ethernet PHY and store it's address in 'ethernet_phy_addr' */\r
327         ethernet_phy_init( GMAC, ETHERNET_CONF_PHY_ADDR, sysclk_get_cpu_hz() );\r
328 \r
329         ethernet_phy_auto_negotiate( GMAC, ethernet_phy_addr );\r
330         ethernet_phy_set_link( GMAC, ethernet_phy_addr, 1 );\r
331 \r
332         /* The GMAC driver will call a hook prvRxCallback(), which\r
333         in turn will wake-up the task by calling vTaskNotifyGiveFromISR() */\r
334         gmac_dev_set_rx_callback( &gs_gmac_dev, prvRxCallback );\r
335         gmac_set_address( GMAC, 1, (uint8_t*)llmnr_mac_address );\r
336 \r
337         ncfgr = GMAC_NCFGR_SPD | GMAC_NCFGR_FD;\r
338 \r
339         GMAC->GMAC_NCFGR = ( GMAC->GMAC_NCFGR & ~( GMAC_NCFGR_SPD | GMAC_NCFGR_FD ) ) | ncfgr;\r
340 \r
341         return 1;\r
342 }\r
343 /*-----------------------------------------------------------*/\r
344 \r
345 static inline unsigned long ulReadMDIO( unsigned /*short*/ usAddress )\r
346 {\r
347 uint32_t ulValue, ulReturn;\r
348 int rc;\r
349 \r
350         gmac_enable_management( GMAC, 1 );\r
351         rc = gmac_phy_read( GMAC, ethernet_phy_addr, usAddress, &ulValue );\r
352         gmac_enable_management( GMAC, 0 );\r
353         if( rc == GMAC_OK )\r
354         {\r
355                 ulReturn = ulValue;\r
356         }\r
357         else\r
358         {\r
359                 ulReturn = 0UL;\r
360         }\r
361 \r
362         return ulReturn;\r
363 }\r
364 /*-----------------------------------------------------------*/\r
365 \r
366 static BaseType_t xGMACWaitLS( TickType_t xMaxTime )\r
367 {\r
368 TickType_t xStartTime = xTaskGetTickCount();\r
369 TickType_t xEndTime;\r
370 BaseType_t xReturn;\r
371 const TickType_t xShortTime = pdMS_TO_TICKS( 100UL );\r
372 \r
373         for( ;; )\r
374         {\r
375                 xEndTime = xTaskGetTickCount();\r
376 \r
377                 if( ( xEndTime - xStartTime ) > xMaxTime )\r
378                 {\r
379                         /* Wated more than xMaxTime, return. */\r
380                         xReturn = pdFALSE;\r
381                         break;\r
382                 }\r
383 \r
384                 /* Check the link status again. */\r
385                 ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR );\r
386 \r
387                 if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )\r
388                 {\r
389                         /* Link is up - return. */\r
390                         xReturn = pdTRUE;\r
391                         break;\r
392                 }\r
393 \r
394                 /* Link is down - wait in the Blocked state for a short while (to allow\r
395                 other tasks to execute) before checking again. */\r
396                 vTaskDelay( xShortTime );\r
397         }\r
398 \r
399         FreeRTOS_printf( ( "xGMACWaitLS: %ld (PHY %d) freq %lu Mz\n",\r
400                 xReturn,\r
401                 ethernet_phy_addr,\r
402                 sysclk_get_cpu_hz() / HZ_PER_MHZ ) );\r
403 \r
404         return xReturn;\r
405 }\r
406 /*-----------------------------------------------------------*/\r
407 \r
408 //#if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 1 ) && ( ipconfigHAS_TX_CRC_OFFLOADING == 0 )\r
409 \r
410         void vGMACGenerateChecksum( uint8_t *apBuffer )\r
411         {\r
412         ProtocolPacket_t *xProtPacket = (ProtocolPacket_t *)apBuffer;\r
413 \r
414                 if ( xProtPacket->xTCPPacket.xEthernetHeader.usFrameType == ipIPv4_FRAME_TYPE )\r
415                 {\r
416                         IPHeader_t *pxIPHeader = &(xProtPacket->xTCPPacket.xIPHeader);\r
417 \r
418                         /* Calculate the IP header checksum. */\r
419                         pxIPHeader->usHeaderChecksum = 0x00;\r
420                         pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER );\r
421                         pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum );\r
422 \r
423                         /* Calculate the TCP checksum for an outgoing packet. */\r
424                         usGenerateProtocolChecksum( ( uint8_t * ) apBuffer, pdTRUE );\r
425                 }\r
426         }\r
427 \r
428 //#endif\r
429 /*-----------------------------------------------------------*/\r
430 \r
431 static uint32_t prvEMACRxPoll( void )\r
432 {\r
433 unsigned char *pucUseBuffer;\r
434 uint32_t ulReceiveCount, ulResult, ulReturnValue = 0;\r
435 static NetworkBufferDescriptor_t *pxNextNetworkBufferDescriptor = NULL;\r
436 const UBaseType_t xMinDescriptorsToLeave = 2UL;\r
437 const TickType_t xBlockTime = pdMS_TO_TICKS( 100UL );\r
438 static IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL };\r
439 \r
440         for( ;; )\r
441         {\r
442                 /* If pxNextNetworkBufferDescriptor was not left pointing at a valid\r
443                 descriptor then allocate one now. */\r
444                 if( ( pxNextNetworkBufferDescriptor == NULL ) && ( uxGetNumberOfFreeNetworkBuffers() > xMinDescriptorsToLeave ) )\r
445                 {\r
446                         pxNextNetworkBufferDescriptor = pxGetNetworkBufferWithDescriptor( ipTOTAL_ETHERNET_FRAME_SIZE, xBlockTime );\r
447                 }\r
448 \r
449                 if( pxNextNetworkBufferDescriptor != NULL )\r
450                 {\r
451                         /* Point pucUseBuffer to the buffer pointed to by the descriptor. */\r
452                         pucUseBuffer = ( unsigned char* ) ( pxNextNetworkBufferDescriptor->pucEthernetBuffer - ipconfigPACKET_FILLER_SIZE );\r
453                 }\r
454                 else\r
455                 {\r
456                         /* As long as pxNextNetworkBufferDescriptor is NULL, the incoming\r
457                         messages will be flushed and ignored. */\r
458                         pucUseBuffer = NULL;\r
459                 }\r
460 \r
461                 /* Read the next packet from the hardware into pucUseBuffer. */\r
462                 ulResult = gmac_dev_read( &gs_gmac_dev, pucUseBuffer, ipTOTAL_ETHERNET_FRAME_SIZE, &ulReceiveCount );\r
463 \r
464                 if( ( ulResult != GMAC_OK ) || ( ulReceiveCount == 0 ) )\r
465                 {\r
466                         /* No data from the hardware. */\r
467                         break;\r
468                 }\r
469 \r
470                 if( pxNextNetworkBufferDescriptor == NULL )\r
471                 {\r
472                         /* Data was read from the hardware, but no descriptor was available\r
473                         for it, so it will be dropped. */\r
474                         iptraceETHERNET_RX_EVENT_LOST();\r
475                         continue;\r
476                 }\r
477 \r
478                 iptraceNETWORK_INTERFACE_RECEIVE();\r
479                 pxNextNetworkBufferDescriptor->xDataLength = ( size_t ) ulReceiveCount;\r
480                 xRxEvent.pvData = ( void * ) pxNextNetworkBufferDescriptor;\r
481 \r
482                 /* Send the descriptor to the IP task for processing. */\r
483                 if( xSendEventStructToIPTask( &xRxEvent, xBlockTime ) != pdTRUE )\r
484                 {\r
485                         /* The buffer could not be sent to the stack so must be released\r
486                         again. */\r
487                         vReleaseNetworkBufferAndDescriptor( pxNextNetworkBufferDescriptor );\r
488                         iptraceETHERNET_RX_EVENT_LOST();\r
489                         FreeRTOS_printf( ( "prvEMACRxPoll: Can not queue return packet!\n" ) );\r
490                 }\r
491 \r
492                 /* Now the buffer has either been passed to the IP-task,\r
493                 or it has been released in the code above. */\r
494                 pxNextNetworkBufferDescriptor = NULL;\r
495                 ulReturnValue++;\r
496         }\r
497 \r
498         return ulReturnValue;\r
499 }\r
500 /*-----------------------------------------------------------*/\r
501 \r
502 static void prvEMACHandlerTask( void *pvParameters )\r
503 {\r
504 TimeOut_t xPhyTime;\r
505 TickType_t xPhyRemTime;\r
506 UBaseType_t uxLastMinBufferCount = 0, uxCount;\r
507 UBaseType_t uxCurrentCount;\r
508 #if( ipconfigCHECK_IP_QUEUE_SPACE != 0 )\r
509         UBaseType_t uxLastMinQueueSpace;\r
510 #endif\r
511 #if( ipconfigZERO_COPY_TX_DRIVER != 0 )\r
512         NetworkBufferDescriptor_t *pxBuffer;\r
513 #endif\r
514 uint8_t *pucBuffer;\r
515 BaseType_t xResult = 0;\r
516 uint32_t xStatus;\r
517 const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( EMAC_MAX_BLOCK_TIME_MS );\r
518 \r
519         /* Remove compiler warnings about unused parameters. */\r
520         ( void ) pvParameters;\r
521 \r
522         configASSERT( xEMACTaskHandle );\r
523 \r
524         vTaskSetTimeOutState( &xPhyTime );\r
525         xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );\r
526 \r
527         for( ;; )\r
528         {\r
529                 uxCurrentCount = uxGetMinimumFreeNetworkBuffers();\r
530                 if( uxLastMinBufferCount != uxCurrentCount )\r
531                 {\r
532                         /* The logging produced below may be helpful\r
533                         while tuning +TCP: see how many buffers are in use. */\r
534                         uxLastMinBufferCount = uxCurrentCount;\r
535                         FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n",\r
536                                 uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) );\r
537                 }\r
538 \r
539                 #if( ipconfigCHECK_IP_QUEUE_SPACE != 0 )\r
540                 {\r
541                         uxCurrentCount = uxGetMinimumIPQueueSpace();\r
542                         if( uxLastMinQueueSpace != uxCurrentCount )\r
543                         {\r
544                                 /* The logging produced below may be helpful\r
545                                 while tuning +TCP: see how many buffers are in use. */\r
546                                 uxLastMinQueueSpace = uxCurrentCount;\r
547                                 FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) );\r
548                         }\r
549                 }\r
550                 #endif /* ipconfigCHECK_IP_QUEUE_SPACE */\r
551 \r
552                 if( ( ulISREvents & EMAC_IF_ALL_EVENT ) == 0 )\r
553                 {\r
554                         /* No events to process now, wait for the next. */\r
555                         ulTaskNotifyTake( pdFALSE, ulMaxBlockTime );\r
556                 }\r
557 \r
558                 if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 )\r
559                 {\r
560                         ulISREvents &= ~EMAC_IF_RX_EVENT;\r
561 \r
562                         /* Wait for the EMAC interrupt to indicate that another packet has been\r
563                         received. */\r
564                         xResult = prvEMACRxPoll();\r
565                 }\r
566 \r
567                 if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 )\r
568                 {\r
569                         /* Future extension: code to release TX buffers if zero-copy is used. */\r
570                         ulISREvents &= ~EMAC_IF_TX_EVENT;\r
571                         while( xQueueReceive( xTxBufferQueue, &pucBuffer, 0 ) != pdFALSE )\r
572                         {\r
573                                 #if( ipconfigZERO_COPY_TX_DRIVER != 0 )\r
574                                 {\r
575                                         pxBuffer = pxPacketBuffer_to_NetworkBuffer( pucBuffer );\r
576                                         if( pxBuffer != NULL )\r
577                                         {\r
578                                                 vReleaseNetworkBufferAndDescriptor( pxBuffer );\r
579                                                 tx_release_count[ 0 ]++;\r
580                                         }\r
581                                         else\r
582                                         {\r
583                                                 tx_release_count[ 1 ]++;\r
584                                         }\r
585                                 }\r
586                                 #else\r
587                                 {\r
588                                         tx_release_count[ 0 ]++;\r
589                                 }\r
590                                 #endif\r
591                                 uxCount = uxQueueMessagesWaiting( ( QueueHandle_t ) xTXDescriptorSemaphore );\r
592                                 if( uxCount < GMAC_TX_BUFFERS )\r
593                                 {\r
594                                         /* Tell the counting semaphore that one more TX descriptor is available. */\r
595                                         xSemaphoreGive( xTXDescriptorSemaphore );\r
596                                 }\r
597                         }\r
598                 }\r
599 \r
600                 if( ( ulISREvents & EMAC_IF_ERR_EVENT ) != 0 )\r
601                 {\r
602                         /* Future extension: logging about errors that occurred. */\r
603                         ulISREvents &= ~EMAC_IF_ERR_EVENT;\r
604                 }\r
605 \r
606                 if( xResult > 0 )\r
607                 {\r
608                         /* A packet was received. No need to check for the PHY status now,\r
609                         but set a timer to check it later on. */\r
610                         vTaskSetTimeOutState( &xPhyTime );\r
611                         xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );\r
612                         xResult = 0;\r
613                 }\r
614                 else if( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE )\r
615                 {\r
616                         /* Check the link status again. */\r
617                         xStatus = ulReadMDIO( PHY_REG_01_BMSR );\r
618 \r
619                         if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != ( xStatus & BMSR_LINK_STATUS ) )\r
620                         {\r
621                                 ulPHYLinkStatus = xStatus;\r
622                                 FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) );\r
623                         }\r
624 \r
625                         vTaskSetTimeOutState( &xPhyTime );\r
626                         if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )\r
627                         {\r
628                                 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );\r
629                         }\r
630                         else\r
631                         {\r
632                                 xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );\r
633                         }\r
634                 }\r
635         }\r
636 }\r
637 /*-----------------------------------------------------------*/\r