]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-UDP/portable/NetworkInterface/LPC18xx/Using_LPCOpen_Library/lpc18xx_43xx_EMAC_LPCOpen.c
8575e703bb367bdab0a9c1c944ec98ee01a79b5d
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-Plus-UDP / portable / NetworkInterface / LPC18xx / Using_LPCOpen_Library / lpc18xx_43xx_EMAC_LPCOpen.c
1 /*\r
2  * FreeRTOS+UDP V1.0.1 (C) 2013 Real Time Engineers ltd.\r
3  * All rights reserved\r
4  *\r
5  * This file is part of the FreeRTOS+UDP distribution.  The FreeRTOS+UDP license\r
6  * terms are different to the FreeRTOS license terms.\r
7  *\r
8  * FreeRTOS+UDP uses a dual license model that allows the software to be used \r
9  * under a standard GPL open source license, or a commercial license.  The \r
10  * standard GPL license (unlike the modified GPL license under which FreeRTOS \r
11  * itself is distributed) requires that all software statically linked with \r
12  * FreeRTOS+UDP is also distributed under the same GPL V2 license terms.  \r
13  * Details of both license options follow:\r
14  *\r
15  * - Open source licensing -\r
16  * FreeRTOS+UDP is a free download and may be used, modified, evaluated and\r
17  * distributed without charge provided the user adheres to version two of the\r
18  * GNU General Public License (GPL) and does not remove the copyright notice or\r
19  * this text.  The GPL V2 text is available on the gnu.org web site, and on the\r
20  * following URL: http://www.FreeRTOS.org/gpl-2.0.txt.\r
21  *\r
22  * - Commercial licensing -\r
23  * Businesses and individuals that for commercial or other reasons cannot comply\r
24  * with the terms of the GPL V2 license must obtain a commercial license before \r
25  * incorporating FreeRTOS+UDP into proprietary software for distribution in any \r
26  * form.  Commercial licenses can be purchased from http://shop.freertos.org/udp \r
27  * and do not require any source files to be changed.\r
28  *\r
29  * FreeRTOS+UDP is distributed in the hope that it will be useful.  You cannot\r
30  * use FreeRTOS+UDP unless you agree that you use the software 'as is'.\r
31  * FreeRTOS+UDP is provided WITHOUT ANY WARRANTY; without even the implied\r
32  * warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR\r
33  * PURPOSE. Real Time Engineers Ltd. disclaims all conditions and terms, be they\r
34  * implied, expressed, or statutory.\r
35  *\r
36  * 1 tab == 4 spaces!\r
37  *\r
38  * http://www.FreeRTOS.org\r
39  * http://www.FreeRTOS.org/udp\r
40  *\r
41  */\r
42  \r
43 /* FreeRTOS includes. */\r
44 #include "FreeRTOS.h"\r
45 #include "task.h"\r
46 #include "semphr.h"\r
47 \r
48 /* FreeRTOS+UDP includes. */\r
49 #include "FreeRTOS_UDP_IP.h"\r
50 #include "FreeRTOS_IP_Private.h"\r
51 #include "NetworkBufferManagement.h"\r
52 \r
53 /* Library includes. */\r
54 #include "board.h"\r
55 \r
56 /* Descriptors that reference received buffers are expected to have both the\r
57 first and last frame bits set because buffers are dimensioned to hold complete\r
58 Ethernet frames. */\r
59 #define emacEXPECTED_RX_STATUS_MASK     ( RDES_LS | RDES_FS )\r
60 \r
61 /*-----------------------------------------------------------*/\r
62 \r
63 /*\r
64  * Set the Rx and Tx descriptors into their expected initial state.\r
65  */\r
66 static void prvResetRxDescriptors( void );\r
67 static void prvResetTxDescriptors( void );\r
68 \r
69 /*\r
70  * Returns the length of the data pointed to by the next Rx descriptor.\r
71  */\r
72 static uint32_t prvReceivedDataLength( void );\r
73 \r
74 \r
75 /*-----------------------------------------------------------*/\r
76 \r
77 /* Rx and Tx descriptors and data array. */\r
78 static volatile IP_ENET_001_ENHRXDESC_T xRXDescriptors[ configNUM_RX_ETHERNET_DMA_DESCRIPTORS ];\r
79 static volatile IP_ENET_001_ENHTXDESC_T xTxDescriptors[ configNUM_TX_ETHERNET_DMA_DESCRIPTORS ];\r
80 \r
81 /* Indexes into the Rx and Tx descriptor arrays. */\r
82 static unsigned int xRxDescriptorIndex = 0;\r
83 static unsigned int xTxDescriptorIndex = 0;\r
84 \r
85 /*-----------------------------------------------------------*/\r
86 \r
87 portBASE_TYPE xEMACInit( uint8_t ucMACAddress[ 6 ] )\r
88 {\r
89 portBASE_TYPE xReturn;\r
90 uint32_t ulPHYStatus;\r
91 \r
92         /* Configure the hardware. */\r
93         Chip_ENET_Init( LPC_ETHERNET );\r
94 \r
95         if( lpc_phy_init( pdTRUE, vTaskDelay ) == SUCCESS )\r
96         {\r
97                 /* The MAC address is passed in as the function parameter. */\r
98                 Chip_ENET_SetADDR( LPC_ETHERNET, ucMACAddress );\r
99 \r
100                 /* Wait for autonegotiation to complete. */\r
101                 do\r
102                 {\r
103                         vTaskDelay( 100 );\r
104                         ulPHYStatus = lpcPHYStsPoll();\r
105                 } while( ( ulPHYStatus & PHY_LINK_CONNECTED ) == 0x00 );\r
106 \r
107                 /* Configure the hardware as per the negotiated link. */\r
108                 if( ( ulPHYStatus & PHY_LINK_FULLDUPLX ) == PHY_LINK_FULLDUPLX )\r
109                 {\r
110                         IP_ENET_SetDuplex( LPC_ETHERNET, pdTRUE );\r
111                 }\r
112                 else\r
113                 {\r
114                         IP_ENET_SetDuplex( LPC_ETHERNET, pdFALSE );\r
115                 }\r
116 \r
117                 if( ( ulPHYStatus & PHY_LINK_SPEED100 ) == PHY_LINK_SPEED100 )\r
118                 {\r
119                         IP_ENET_SetSpeed( LPC_ETHERNET, pdTRUE );\r
120                 }\r
121                 else\r
122                 {\r
123                         IP_ENET_SetSpeed( LPC_ETHERNET, pdFALSE );\r
124                 }\r
125 \r
126                 /* Set descriptors to their initial state. */\r
127                 prvResetRxDescriptors();\r
128                 prvResetTxDescriptors();\r
129 \r
130                 /* Enable RX and TX. */\r
131                 Chip_ENET_TXEnable( LPC_ETHERNET );\r
132                 Chip_ENET_RXEnable( LPC_ETHERNET );\r
133 \r
134                 /* Enable the interrupt and set its priority as configured.  THIS\r
135                 DRIVER REQUIRES configMAC_INTERRUPT_PRIORITY TO BE DEFINED, PREFERABLY\r
136                 IN FreeRTOSConfig.h. */\r
137                 NVIC_SetPriority( ETHERNET_IRQn, configMAC_INTERRUPT_PRIORITY );\r
138                 NVIC_EnableIRQ( ETHERNET_IRQn );\r
139 \r
140                 /* Enable interrupts. */\r
141                 LPC_ETHERNET->DMA_INT_EN =  DMA_IE_NIE | DMA_IE_RIE;\r
142 \r
143                 xReturn = pdPASS;\r
144         }\r
145         else\r
146         {\r
147                 xReturn = pdFAIL;\r
148         }\r
149 \r
150         return xReturn;\r
151 }\r
152 /*-----------------------------------------------------------*/\r
153 \r
154 portBASE_TYPE xEMACIsTxDescriptorAvailable( void )\r
155 {\r
156 portBASE_TYPE xReturn;\r
157 \r
158         if( ( xTxDescriptors[ xTxDescriptorIndex ].CTRLSTAT & RDES_OWN ) == 0 )\r
159         {\r
160                 xReturn = pdPASS;\r
161         }\r
162         else\r
163         {\r
164                 xReturn = pdFAIL;\r
165         }\r
166 \r
167         return xReturn;\r
168 }\r
169 /*-----------------------------------------------------------*/\r
170 \r
171 void vEMACAssignBufferToDescriptor( uint8_t * pucBuffer )\r
172 {\r
173         /* The old packet is now finished with and can be freed. */\r
174         vEthernetBufferRelease( ( void * ) xTxDescriptors[ xTxDescriptorIndex ].B1ADD );\r
175 \r
176         /* Assign the new packet to the descriptor. */\r
177         xTxDescriptors[ xTxDescriptorIndex ].B1ADD = ( uint32_t ) pucBuffer;\r
178 }\r
179 /*-----------------------------------------------------------*/\r
180 \r
181 void vEMACStartNextTransmission( uint32_t ulLength )\r
182 {\r
183         xTxDescriptors[ xTxDescriptorIndex ].BSIZE = ulLength;\r
184         xTxDescriptors[ xTxDescriptorIndex ].CTRLSTAT |= RDES_OWN;\r
185 \r
186         /* Wake Up the DMA if it's in Suspended Mode. */\r
187         LPC_ETHERNET->DMA_TRANS_POLL_DEMAND = 1;\r
188         xTxDescriptorIndex++;\r
189 \r
190         if( xTxDescriptorIndex == configNUM_TX_ETHERNET_DMA_DESCRIPTORS )\r
191         {\r
192                 xTxDescriptorIndex = 0;\r
193         }\r
194 }\r
195 /*-----------------------------------------------------------*/\r
196 \r
197 static uint32_t prvReceivedDataLength( void )\r
198 {\r
199 unsigned short RxLen = 0;\r
200 \r
201         RxLen = ( xRXDescriptors[ xRxDescriptorIndex ].STATUS >> 16 ) & 0x03FFF;\r
202         return RxLen;\r
203 }\r
204 /*-----------------------------------------------------------*/\r
205 \r
206 void vEMACReturnRxDescriptor( void )\r
207 {\r
208         xRXDescriptors[ xRxDescriptorIndex ].STATUS = RDES_OWN;\r
209         xRxDescriptorIndex++;\r
210 \r
211         if( xRxDescriptorIndex == configNUM_RX_ETHERNET_DMA_DESCRIPTORS )\r
212         {\r
213                 xRxDescriptorIndex = 0;\r
214         }\r
215 }\r
216 /*-----------------------------------------------------------*/\r
217 \r
218 portBASE_TYPE xEMACRxDataAvailable( void )\r
219 {\r
220 portBASE_TYPE xReturn;\r
221 \r
222         if( ( xRXDescriptors[ xRxDescriptorIndex ].STATUS & RDES_OWN ) == 0 )\r
223         {\r
224                 xReturn = pdPASS;\r
225         }\r
226         else\r
227         {\r
228                 xReturn = pdFAIL;\r
229         }\r
230 \r
231         return xReturn;\r
232 }\r
233 /*-----------------------------------------------------------*/\r
234 \r
235 void vEMACSwapEmptyBufferForRxedData( xNetworkBufferDescriptor_t *pxNetworkBuffer )\r
236 {\r
237 uint8_t *pucTemp;\r
238 \r
239         /* Swap the buffer in the network buffer with the buffer used by the DMA.\r
240         This allows the data to be passed out without having to perform any copies. */\r
241         pucTemp = ( uint8_t * ) xRXDescriptors[ xRxDescriptorIndex ].B1ADD;\r
242         xRXDescriptors[ xRxDescriptorIndex ].B1ADD = ( uint32_t ) pxNetworkBuffer->pucEthernetBuffer;\r
243         pxNetworkBuffer->pucEthernetBuffer = pucTemp;\r
244 \r
245         /* Only supports frames coming in single buffers.  If this frame is split\r
246         across multiple buffers then reject it (and if the frame is needed increase\r
247         the ipconfigNETWORK_MTU setting). */\r
248         if( ( xRXDescriptors[ xRxDescriptorIndex ].STATUS & emacEXPECTED_RX_STATUS_MASK ) != emacEXPECTED_RX_STATUS_MASK )\r
249         {\r
250                 pxNetworkBuffer->xDataLength = 0;\r
251         }\r
252         else\r
253         {\r
254                 pxNetworkBuffer->xDataLength = ( size_t ) prvReceivedDataLength() - ( ipETHERNET_CRC_BYTES - 1U );;\r
255         }\r
256 }\r
257 /*-----------------------------------------------------------*/\r
258 \r
259 static void prvResetRxDescriptors( void )\r
260 {\r
261 uint32_t x;\r
262 size_t xBufferSize = ipTOTAL_ETHERNET_FRAME_SIZE;\r
263 \r
264         for( x = 0; x < configNUM_RX_ETHERNET_DMA_DESCRIPTORS; x++ )\r
265         {\r
266                 /* Obtain the buffer first, as the size of the buffer might be changed\r
267                 within the pucEthernetBufferGet() call. */\r
268                 xRXDescriptors[ x ].B1ADD  = ( uint32_t ) pucEthernetBufferGet( &xBufferSize );\r
269                 xRXDescriptors[ x ].STATUS = RDES_OWN;\r
270                 xRXDescriptors[ x ].CTRL  = xBufferSize;\r
271                 xRXDescriptors[ x ].B2ADD = ( uint32_t ) &xRXDescriptors[ x + 1 ];\r
272                 \r
273                 configASSERT( ( ( ( uint32_t ) xRXDescriptors[x].B1ADD ) & 0x07 ) == 0 );\r
274         }\r
275 \r
276         /* Last Descriptor */\r
277         xRXDescriptors[ configNUM_RX_ETHERNET_DMA_DESCRIPTORS - 1 ].CTRL |= RDES_ENH_RER;\r
278 \r
279         xRxDescriptorIndex = 0;\r
280 \r
281         /* Set Starting address of RX Descriptor list */\r
282         LPC_ETHERNET->DMA_REC_DES_ADDR = ( uint32_t ) xRXDescriptors;\r
283 }\r
284 /*-----------------------------------------------------------*/\r
285 \r
286 static void prvResetTxDescriptors( void )\r
287 {\r
288 /* Initialize Transmit Descriptor and Status array. */\r
289 uint32_t x;\r
290 \r
291         for( x = 0; x < configNUM_TX_ETHERNET_DMA_DESCRIPTORS; x++ )\r
292         {\r
293                 xTxDescriptors[ x ].CTRLSTAT = TDES_ENH_FS | TDES_ENH_LS;\r
294                 xTxDescriptors[ x ].BSIZE  = 0;\r
295                 xTxDescriptors[ x ].B2ADD = ( uint32_t ) &xTxDescriptors[ x + 1 ];\r
296 \r
297                 /* Packet is assigned when a Tx is initiated. */\r
298                 xTxDescriptors[ x ].B1ADD   = ( uint32_t )NULL;\r
299         }\r
300 \r
301         /* Last Descriptor? */\r
302         xTxDescriptors[ configNUM_TX_ETHERNET_DMA_DESCRIPTORS-1 ].CTRLSTAT |= TDES_ENH_TER;\r
303 \r
304         /* Set Starting address of TX Descriptor list */\r
305         LPC_ETHERNET->DMA_TRANS_DES_ADDR = ( uint32_t ) xTxDescriptors;\r
306 }\r
307 /*-----------------------------------------------------------*/\r
308 \r
309 void ETH_IRQHandler( void )\r
310 {\r
311 uint32_t ulInterruptCause;\r
312 extern xSemaphoreHandle xEMACRxEventSemaphore;\r
313 \r
314         configASSERT( xEMACRxEventSemaphore );\r
315 \r
316         ulInterruptCause = LPC_ETHERNET->DMA_STAT ;\r
317 \r
318         /* Clear the interrupt. */\r
319         LPC_ETHERNET->DMA_STAT |= ( DMA_ST_NIS | DMA_ST_RI );\r
320 \r
321         /* Clear fatal error conditions.  NOTE:  The driver does not clear all\r
322         errors, only those actually experienced.  For future reference, range\r
323         errors are not actually errors so can be ignored. */\r
324         if( ( ulInterruptCause & DMA_ST_FBI ) != 0U )\r
325         {\r
326                 LPC_ETHERNET->DMA_STAT |= DMA_ST_FBI;\r
327         }\r
328 \r
329         /* Unblock the deferred interrupt handler task if the event was an Rx. */\r
330         if( ( ulInterruptCause & DMA_IE_RIE ) != 0UL )\r
331         {\r
332                 xSemaphoreGiveFromISR( xEMACRxEventSemaphore, NULL );\r
333         }\r
334 \r
335         /* ulInterruptCause is used for convenience here.  A context switch is\r
336         wanted, but coding portEND_SWITCHING_ISR( 1 ) would likely result in a\r
337         compiler warning. */\r
338         portEND_SWITCHING_ISR( ulInterruptCause );\r
339 }\r
340 \r