]> git.sur5r.net Git - freertos/blob - Demo/CORTEX_LPC1768_GCC_Rowley/webserver/emac.c
Formatting only.
[freertos] / Demo / CORTEX_LPC1768_GCC_Rowley / webserver / emac.c
1 /*\r
2         FreeRTOS V5.4.1 - Copyright (C) 2009 Real Time Engineers Ltd.\r
3 \r
4         This file is part of the FreeRTOS distribution.\r
5 \r
6         FreeRTOS is free software; you can redistribute it and/or modify it     under\r
7         the terms of the GNU General Public License (version 2) as published by the\r
8         Free Software Foundation and modified by the FreeRTOS exception.\r
9         **NOTE** The exception to the GPL is included to allow you to distribute a\r
10         combined work that includes FreeRTOS without being obliged to provide the\r
11         source code for proprietary components outside of the FreeRTOS kernel.\r
12         Alternative commercial license and support terms are also available upon\r
13         request.  See the licensing section of http://www.FreeRTOS.org for full\r
14         license details.\r
15 \r
16         FreeRTOS is distributed in the hope that it will be useful,     but WITHOUT\r
17         ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r
18         FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for\r
19         more details.\r
20 \r
21         You should have received a copy of the GNU General Public License along\r
22         with FreeRTOS; if not, write to the Free Software Foundation, Inc., 59\r
23         Temple Place, Suite 330, Boston, MA  02111-1307  USA.\r
24 \r
25 \r
26         ***************************************************************************\r
27         *                                                                         *\r
28         * Looking for a quick start?  Then check out the FreeRTOS eBook!          *\r
29         * See http://www.FreeRTOS.org/Documentation for details                   *\r
30         *                                                                         *\r
31         ***************************************************************************\r
32 \r
33         1 tab == 4 spaces!\r
34 \r
35         Please ensure to read the configuration and relevant port sections of the\r
36         online documentation.\r
37 \r
38         http://www.FreeRTOS.org - Documentation, latest information, license and\r
39         contact details.\r
40 \r
41         http://www.SafeRTOS.com - A version that is certified for use in safety\r
42         critical systems.\r
43 \r
44         http://www.OpenRTOS.com - Commercial support, development, porting,\r
45         licensing and training services.\r
46 */\r
47 \r
48 /* Originally adapted from file written by Andreas Dannenberg.  Supplied with permission. */\r
49 \r
50 /* Kernel includes. */\r
51 #include "FreeRTOS.h"\r
52 #include "task.h"\r
53 #include "semphr.h"\r
54 \r
55 /* Hardware specific includes. */\r
56 #include "EthDev_LPC17xx.h"\r
57 \r
58 /* Time to wait between each inspection of the link status. */\r
59 #define emacWAIT_FOR_LINK_TO_ESTABLISH ( 500 / portTICK_RATE_MS )\r
60 \r
61 /* Short delay used in several places during the initialisation process. */\r
62 #define emacSHORT_DELAY                            ( 2 )\r
63 \r
64 /* Hardware specific bit definitions. */\r
65 #define emacLINK_ESTABLISHED            ( 0x0001 )\r
66 #define emacFULL_DUPLEX_ENABLED         ( 0x0004 )\r
67 #define emac10BASE_T_MODE                       ( 0x0002 )\r
68 #define emacPINSEL2_VALUE                       ( 0x50150105 )\r
69 \r
70 /* If no buffers are available, then wait this long before looking again.... */\r
71 #define emacBUFFER_WAIT_DELAY   ( 3 / portTICK_RATE_MS )\r
72 \r
73 /* ...and don't look more than this many times. */\r
74 #define emacBUFFER_WAIT_ATTEMPTS        ( 30 )\r
75 \r
76 /* Index to the Tx descriptor that is always used first for every Tx.  The second\r
77 descriptor is then used to re-send in order to speed up the uIP Tx process. */\r
78 #define emacTX_DESC_INDEX                       ( 0 )\r
79 \r
80 /*-----------------------------------------------------------*/\r
81 \r
82 /*\r
83  * Configure both the Rx and Tx descriptors during the init process.\r
84  */\r
85 static void prvInitDescriptors( void );\r
86 \r
87 /*\r
88  * Setup the IO and peripherals required for Ethernet communication.\r
89  */\r
90 static void prvSetupEMACHardware( void );\r
91 \r
92 /*\r
93  * Control the auto negotiate process.\r
94  */\r
95 static void prvConfigurePHY( void );\r
96 \r
97 /*\r
98  * Wait for a link to be established, then setup the PHY according to the link\r
99  * parameters.\r
100  */\r
101 static long prvSetupLinkStatus( void );\r
102 \r
103 /*\r
104  * Search the pool of buffers to find one that is free.  If a buffer is found\r
105  * mark it as in use before returning its address.\r
106  */\r
107 static unsigned char *prvGetNextBuffer( void );\r
108 \r
109 /*\r
110  * Return an allocated buffer to the pool of free buffers.\r
111  */\r
112 static void prvReturnBuffer( unsigned char *pucBuffer );\r
113 \r
114 /*\r
115  * Send lValue to the lPhyReg within the PHY.\r
116  */\r
117 static long prvWritePHY( long lPhyReg, long lValue );\r
118 \r
119 /*\r
120  * Read a value from ucPhyReg within the PHY.  *plStatus will be set to\r
121  * pdFALSE if there is an error.\r
122  */\r
123 static unsigned short prvReadPHY( unsigned char ucPhyReg, long *plStatus );\r
124 \r
125 /*-----------------------------------------------------------*/\r
126 \r
127 /* The semaphore used to wake the uIP task when data arrives. */\r
128 extern xSemaphoreHandle xEMACSemaphore;\r
129 \r
130 /* Each ucBufferInUse index corresponds to a position in the pool of buffers.\r
131 If the index contains a 1 then the buffer within pool is in use, if it\r
132 contains a 0 then the buffer is free. */\r
133 static unsigned char ucBufferInUse[ ETH_NUM_BUFFERS ] = { pdFALSE };\r
134 \r
135 /* The uip_buffer is not a fixed array, but instead gets pointed to the buffers\r
136 allocated within this file. */\r
137 unsigned char * uip_buf;\r
138 \r
139 /* Store the length of the data being sent so the data can be sent twice.  The\r
140 value will be set back to 0 once the data has been sent twice. */\r
141 static unsigned short usSendLen = 0;\r
142 \r
143 /*-----------------------------------------------------------*/\r
144 \r
145 long lEMACInit( void )\r
146 {\r
147 long lReturn = pdPASS;\r
148 unsigned long ulID1, ulID2;\r
149 \r
150         /* Reset peripherals, configure port pins and registers. */\r
151         prvSetupEMACHardware();\r
152 \r
153         /* Check the PHY part number is as expected. */\r
154         ulID1 = prvReadPHY( PHY_REG_IDR1, &lReturn );\r
155         ulID2 = prvReadPHY( PHY_REG_IDR2, &lReturn );\r
156         if( ( (ulID1 << 16UL ) | ( ulID2 & 0xFFF0UL ) ) == DP83848C_ID )\r
157         {\r
158                 /* Set the Ethernet MAC Address registers */\r
159                 EMAC->SA0 = ( configMAC_ADDR0 << 8 ) | configMAC_ADDR1;\r
160                 EMAC->SA1 = ( configMAC_ADDR2 << 8 ) | configMAC_ADDR3;\r
161                 EMAC->SA2 = ( configMAC_ADDR4 << 8 ) | configMAC_ADDR5;\r
162 \r
163                 /* Initialize Tx and Rx DMA Descriptors */\r
164                 prvInitDescriptors();\r
165 \r
166                 /* Receive broadcast and perfect match packets */\r
167                 EMAC->RxFilterCtrl = RFC_UCAST_EN | RFC_BCAST_EN | RFC_PERFECT_EN;\r
168 \r
169                 /* Setup the PHY. */\r
170                 prvConfigurePHY();\r
171         }\r
172         else\r
173         {\r
174                 lReturn = pdFAIL;\r
175         }\r
176 \r
177         /* Check the link status. */\r
178         if( lReturn == pdPASS )\r
179         {\r
180                 lReturn = prvSetupLinkStatus();\r
181         }\r
182 \r
183         if( lReturn == pdPASS )\r
184         {\r
185                 /* Initialise uip_buf to ensure it points somewhere valid. */\r
186                 uip_buf = prvGetNextBuffer();\r
187 \r
188                 /* Reset all interrupts */\r
189                 EMAC->IntClear = ( INT_RX_OVERRUN | INT_RX_ERR | INT_RX_FIN | INT_RX_DONE | INT_TX_UNDERRUN | INT_TX_ERR | INT_TX_FIN | INT_TX_DONE | INT_SOFT_INT | INT_WAKEUP );\r
190 \r
191                 /* Enable receive and transmit mode of MAC Ethernet core */\r
192                 EMAC->Command |= ( CR_RX_EN | CR_TX_EN );\r
193                 EMAC->MAC1 |= MAC1_REC_EN;\r
194         }\r
195 \r
196         return lReturn;\r
197 }\r
198 /*-----------------------------------------------------------*/\r
199 \r
200 static unsigned char *prvGetNextBuffer( void )\r
201 {\r
202 long x;\r
203 unsigned char *pucReturn = NULL;\r
204 unsigned long ulAttempts = 0;\r
205 \r
206         while( pucReturn == NULL )\r
207         {\r
208                 /* Look through the buffers to find one that is not in use by\r
209                 anything else. */\r
210                 for( x = 0; x < ETH_NUM_BUFFERS; x++ )\r
211                 {\r
212                         if( ucBufferInUse[ x ] == pdFALSE )\r
213                         {\r
214                                 ucBufferInUse[ x ] = pdTRUE;\r
215                                 pucReturn = ( unsigned char * ) ETH_BUF( x );\r
216                                 break;\r
217                         }\r
218                 }\r
219 \r
220                 /* Was a buffer found? */\r
221                 if( pucReturn == NULL )\r
222                 {\r
223                         ulAttempts++;\r
224 \r
225                         if( ulAttempts >= emacBUFFER_WAIT_ATTEMPTS )\r
226                         {\r
227                                 break;\r
228                         }\r
229 \r
230                         /* Wait then look again. */\r
231                         vTaskDelay( emacBUFFER_WAIT_DELAY );\r
232                 }\r
233         }\r
234 \r
235         return pucReturn;\r
236 }\r
237 /*-----------------------------------------------------------*/\r
238 \r
239 static void prvInitDescriptors( void )\r
240 {\r
241 long x, lNextBuffer = 0;\r
242 \r
243         for( x = 0; x < NUM_RX_FRAG; x++ )\r
244         {\r
245                 /* Allocate the next Ethernet buffer to this descriptor. */\r
246                 RX_DESC_PACKET( x ) = ETH_BUF( lNextBuffer );\r
247                 RX_DESC_CTRL( x ) = RCTRL_INT | ( ETH_FRAG_SIZE - 1 );\r
248                 RX_STAT_INFO( x ) = 0;\r
249                 RX_STAT_HASHCRC( x ) = 0;\r
250 \r
251                 /* The Ethernet buffer is now in use. */\r
252                 ucBufferInUse[ lNextBuffer ] = pdTRUE;\r
253                 lNextBuffer++;\r
254         }\r
255 \r
256         /* Set EMAC Receive Descriptor Registers. */\r
257         EMAC->RxDescriptor = RX_DESC_BASE;\r
258         EMAC->RxStatus = RX_STAT_BASE;\r
259         EMAC->RxDescriptorNumber = NUM_RX_FRAG - 1;\r
260 \r
261         /* Rx Descriptors Point to 0 */\r
262         EMAC->RxConsumeIndex = 0;\r
263 \r
264         /* A buffer is not allocated to the Tx descriptors until they are actually\r
265         used. */\r
266         for( x = 0; x < NUM_TX_FRAG; x++ )\r
267         {\r
268                 TX_DESC_PACKET( x ) = ( unsigned long ) NULL;\r
269                 TX_DESC_CTRL( x ) = 0;\r
270                 TX_STAT_INFO( x ) = 0;\r
271         }\r
272 \r
273         /* Set EMAC Transmit Descriptor Registers. */\r
274         EMAC->TxDescriptor = TX_DESC_BASE;\r
275         EMAC->TxStatus = TX_STAT_BASE;\r
276         EMAC->TxDescriptorNumber = NUM_TX_FRAG - 1;\r
277 \r
278         /* Tx Descriptors Point to 0 */\r
279         EMAC->TxProduceIndex = 0;\r
280 }\r
281 /*-----------------------------------------------------------*/\r
282 \r
283 static void prvSetupEMACHardware( void )\r
284 {\r
285 unsigned short us;\r
286 long x, lDummy;\r
287 \r
288         /* Enable P1 Ethernet Pins. */\r
289         PINCON->PINSEL2 = emacPINSEL2_VALUE;\r
290         PINCON->PINSEL3 = ( PINCON->PINSEL3 & ~0x0000000F ) | 0x00000005;\r
291 \r
292         /* Power Up the EMAC controller. */\r
293         SC->PCONP |= PCONP_PCENET;\r
294         vTaskDelay( emacSHORT_DELAY );\r
295 \r
296         /* Reset all EMAC internal modules. */\r
297         EMAC->MAC1 = MAC1_RES_TX | MAC1_RES_MCS_TX | MAC1_RES_RX | MAC1_RES_MCS_RX | MAC1_SIM_RES | MAC1_SOFT_RES;\r
298         EMAC->Command = CR_REG_RES | CR_TX_RES | CR_RX_RES | CR_PASS_RUNT_FRM;\r
299 \r
300         /* A short delay after reset. */\r
301         vTaskDelay( emacSHORT_DELAY );\r
302 \r
303         /* Initialize MAC control registers. */\r
304         EMAC->MAC1 = MAC1_PASS_ALL;\r
305         EMAC->MAC2 = MAC2_CRC_EN | MAC2_PAD_EN;\r
306         EMAC->MAXF = ETH_MAX_FLEN;\r
307         EMAC->CLRT = CLRT_DEF;\r
308         EMAC->IPGR = IPGR_DEF;\r
309 \r
310         /* Enable Reduced MII interface. */\r
311         EMAC->Command = CR_RMII | CR_PASS_RUNT_FRM;\r
312 \r
313         /* Reset Reduced MII Logic. */\r
314         EMAC->SUPP = SUPP_RES_RMII;\r
315         vTaskDelay( emacSHORT_DELAY );\r
316         EMAC->SUPP = 0;\r
317 \r
318         /* Put the PHY in reset mode */\r
319         prvWritePHY( PHY_REG_BMCR, MCFG_RES_MII );\r
320         prvWritePHY( PHY_REG_BMCR, MCFG_RES_MII );\r
321 \r
322         /* Wait for hardware reset to end. */\r
323         for( x = 0; x < 100; x++ )\r
324         {\r
325                 vTaskDelay( emacSHORT_DELAY * 5 );\r
326                 us = prvReadPHY( PHY_REG_BMCR, &lDummy );\r
327                 if( !( us & MCFG_RES_MII ) )\r
328                 {\r
329                         /* Reset complete */\r
330                         break;\r
331                 }\r
332         }\r
333 }\r
334 /*-----------------------------------------------------------*/\r
335 \r
336 static void prvConfigurePHY( void )\r
337 {\r
338 unsigned short us;\r
339 long x, lDummy;\r
340 \r
341         /* Auto negotiate the configuration. */\r
342         if( prvWritePHY( PHY_REG_BMCR, PHY_AUTO_NEG ) )\r
343         {\r
344                 vTaskDelay( emacSHORT_DELAY * 5 );\r
345 \r
346                 for( x = 0; x < 10; x++ )\r
347                 {\r
348                         us = prvReadPHY( PHY_REG_BMSR, &lDummy );\r
349 \r
350                         if( us & PHY_AUTO_NEG_COMPLETE )\r
351                         {\r
352                                 break;\r
353                         }\r
354 \r
355                         vTaskDelay( emacWAIT_FOR_LINK_TO_ESTABLISH );\r
356                 }\r
357         }\r
358 }\r
359 /*-----------------------------------------------------------*/\r
360 \r
361 static long prvSetupLinkStatus( void )\r
362 {\r
363 long lReturn = pdFAIL, x;\r
364 unsigned short usLinkStatus;\r
365 \r
366         /* Wait with timeout for the link to be established. */\r
367         for( x = 0; x < 10; x++ )\r
368         {\r
369                 usLinkStatus = prvReadPHY( PHY_REG_STS, &lReturn );\r
370                 if( usLinkStatus & emacLINK_ESTABLISHED )\r
371                 {\r
372                         /* Link is established. */\r
373                         lReturn = pdPASS;\r
374                         break;\r
375                 }\r
376 \r
377         vTaskDelay( emacWAIT_FOR_LINK_TO_ESTABLISH );\r
378         }\r
379 \r
380         if( lReturn == pdPASS )\r
381         {\r
382                 /* Configure Full/Half Duplex mode. */\r
383                 if( usLinkStatus & emacFULL_DUPLEX_ENABLED )\r
384                 {\r
385                         /* Full duplex is enabled. */\r
386                         EMAC->MAC2 |= MAC2_FULL_DUP;\r
387                         EMAC->Command |= CR_FULL_DUP;\r
388                         EMAC->IPGT = IPGT_FULL_DUP;\r
389                 }\r
390                 else\r
391                 {\r
392                         /* Half duplex mode. */\r
393                         EMAC->IPGT = IPGT_HALF_DUP;\r
394                 }\r
395 \r
396                 /* Configure 100MBit/10MBit mode. */\r
397                 if( usLinkStatus & emac10BASE_T_MODE )\r
398                 {\r
399                         /* 10MBit mode. */\r
400                         EMAC->SUPP = 0;\r
401                 }\r
402                 else\r
403                 {\r
404                         /* 100MBit mode. */\r
405                         EMAC->SUPP = SUPP_SPEED;\r
406                 }\r
407         }\r
408 \r
409         return lReturn;\r
410 }\r
411 /*-----------------------------------------------------------*/\r
412 \r
413 static void prvReturnBuffer( unsigned char *pucBuffer )\r
414 {\r
415 unsigned long ul;\r
416 \r
417         /* Return a buffer to the pool of free buffers. */\r
418         for( ul = 0; ul < ETH_NUM_BUFFERS; ul++ )\r
419         {\r
420                 if( ETH_BUF( ul ) == ( unsigned long ) pucBuffer )\r
421                 {\r
422                         ucBufferInUse[ ul ] = pdFALSE;\r
423                         break;\r
424                 }\r
425         }\r
426 }\r
427 /*-----------------------------------------------------------*/\r
428 \r
429 unsigned long ulGetEMACRxData( void )\r
430 {\r
431 unsigned long ulLen = 0;\r
432 long lIndex;\r
433 \r
434         if( EMAC->RxProduceIndex != EMAC->RxConsumeIndex )\r
435         {\r
436                 /* Mark the current buffer as free as uip_buf is going to be set to\r
437                 the buffer that contains the received data. */\r
438                 prvReturnBuffer( uip_buf );\r
439 \r
440                 ulLen = ( RX_STAT_INFO( EMAC->RxConsumeIndex ) & RINFO_SIZE ) - 3;\r
441                 uip_buf = ( unsigned char * ) RX_DESC_PACKET( EMAC->RxConsumeIndex );\r
442 \r
443                 /* Allocate a new buffer to the descriptor. */\r
444         RX_DESC_PACKET( EMAC->RxConsumeIndex ) = ( unsigned long ) prvGetNextBuffer();\r
445 \r
446                 /* Move the consume index onto the next position, ensuring it wraps to\r
447                 the beginning at the appropriate place. */\r
448                 lIndex = EMAC->RxConsumeIndex;\r
449 \r
450                 lIndex++;\r
451                 if( lIndex >= NUM_RX_FRAG )\r
452                 {\r
453                         lIndex = 0;\r
454                 }\r
455 \r
456                 EMAC->RxConsumeIndex = lIndex;\r
457         }\r
458 \r
459         return ulLen;\r
460 }\r
461 /*-----------------------------------------------------------*/\r
462 \r
463 void vSendEMACTxData( unsigned short usTxDataLen )\r
464 {\r
465 unsigned long ulAttempts = 0UL;\r
466 \r
467         /* Check to see if the Tx descriptor is free, indicated by its buffer being\r
468         NULL. */\r
469         while( TX_DESC_PACKET( emacTX_DESC_INDEX ) != ( unsigned long ) NULL )\r
470         {\r
471                 /* Wait for the Tx descriptor to become available. */\r
472                 vTaskDelay( emacBUFFER_WAIT_DELAY );\r
473 \r
474                 ulAttempts++;\r
475                 if( ulAttempts > emacBUFFER_WAIT_ATTEMPTS )\r
476                 {\r
477                         /* Something has gone wrong as the Tx descriptor is still in use.\r
478                         Clear it down manually, the data it was sending will probably be\r
479                         lost. */\r
480                         prvReturnBuffer( ( unsigned char * ) TX_DESC_PACKET( emacTX_DESC_INDEX ) );\r
481                         break;\r
482                 }\r
483         }\r
484 \r
485         /* Setup the Tx descriptor for transmission.  Remember the length of the\r
486         data being sent so the second descriptor can be used to send it again from\r
487         within the ISR. */\r
488         usSendLen = usTxDataLen;\r
489         TX_DESC_PACKET( emacTX_DESC_INDEX ) = ( unsigned long ) uip_buf;\r
490         TX_DESC_CTRL( emacTX_DESC_INDEX ) = ( usTxDataLen | TCTRL_LAST | TCTRL_INT );\r
491         EMAC->TxProduceIndex = ( emacTX_DESC_INDEX + 1 );\r
492 \r
493         /* uip_buf is being sent by the Tx descriptor.  Allocate a new buffer. */\r
494         uip_buf = prvGetNextBuffer();\r
495 }\r
496 /*-----------------------------------------------------------*/\r
497 \r
498 static long prvWritePHY( long lPhyReg, long lValue )\r
499 {\r
500 const long lMaxTime = 10;\r
501 long x;\r
502 \r
503         EMAC->MADR = DP83848C_DEF_ADR | lPhyReg;\r
504         EMAC->MWTD = lValue;\r
505 \r
506         x = 0;\r
507         for( x = 0; x < lMaxTime; x++ )\r
508         {\r
509                 if( ( EMAC->MIND & MIND_BUSY ) == 0 )\r
510                 {\r
511                         /* Operation has finished. */\r
512                         break;\r
513                 }\r
514 \r
515                 vTaskDelay( emacSHORT_DELAY );\r
516         }\r
517 \r
518         if( x < lMaxTime )\r
519         {\r
520                 return pdPASS;\r
521         }\r
522         else\r
523         {\r
524                 return pdFAIL;\r
525         }\r
526 }\r
527 /*-----------------------------------------------------------*/\r
528 \r
529 static unsigned short prvReadPHY( unsigned char ucPhyReg, long *plStatus )\r
530 {\r
531 long x;\r
532 const long lMaxTime = 10;\r
533 \r
534         EMAC->MADR = DP83848C_DEF_ADR | ucPhyReg;\r
535         EMAC->MCMD = MCMD_READ;\r
536 \r
537         for( x = 0; x < lMaxTime; x++ )\r
538         {\r
539                 /* Operation has finished. */\r
540                 if( ( EMAC->MIND & MIND_BUSY ) == 0 )\r
541                 {\r
542                         break;\r
543                 }\r
544 \r
545                 vTaskDelay( emacSHORT_DELAY );\r
546         }\r
547 \r
548         EMAC->MCMD = 0;\r
549 \r
550         if( x >= lMaxTime )\r
551         {\r
552                 *plStatus = pdFAIL;\r
553         }\r
554 \r
555         return( EMAC->MRDD );\r
556 }\r
557 /*-----------------------------------------------------------*/\r
558 \r
559 void vEMAC_ISR( void )\r
560 {\r
561 unsigned long ulStatus;\r
562 long lHigherPriorityTaskWoken = pdFALSE;\r
563 \r
564         ulStatus = EMAC->IntStatus;\r
565 \r
566         /* Clear the interrupt. */\r
567         EMAC->IntClear = ulStatus;\r
568 \r
569         if( ulStatus & INT_RX_DONE )\r
570         {\r
571                 /* Ensure the uIP task is not blocked as data has arrived. */\r
572                 xSemaphoreGiveFromISR( xEMACSemaphore, &lHigherPriorityTaskWoken );\r
573         }\r
574 \r
575         if( ulStatus & INT_TX_DONE )\r
576         {\r
577                 if( usSendLen > 0 )\r
578                 {\r
579                         /* Send the data again, using the second descriptor.  As there are\r
580                         only two descriptors the index is set back to 0. */\r
581                         TX_DESC_PACKET( ( emacTX_DESC_INDEX + 1 ) ) = TX_DESC_PACKET( emacTX_DESC_INDEX );\r
582                         TX_DESC_CTRL( ( emacTX_DESC_INDEX + 1 ) ) = ( usSendLen | TCTRL_LAST | TCTRL_INT );\r
583                         EMAC->TxProduceIndex = ( emacTX_DESC_INDEX );\r
584 \r
585                         /* This is the second Tx so set usSendLen to 0 to indicate that the\r
586                         Tx descriptors will be free again. */\r
587                         usSendLen = 0UL;\r
588                 }\r
589                 else\r
590                 {\r
591                         /* The Tx buffer is no longer required. */\r
592                         prvReturnBuffer( ( unsigned char * ) TX_DESC_PACKET( emacTX_DESC_INDEX ) );\r
593             TX_DESC_PACKET( emacTX_DESC_INDEX ) = ( unsigned long ) NULL;\r
594                 }\r
595         }\r
596 \r
597         portEND_SWITCHING_ISR( lHigherPriorityTaskWoken );\r
598 }\r