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