xref: /FreeRTOS-Plus-TCP-v4.0.0/source/portable/NetworkInterface/LPC54018/NetworkInterface.c (revision 1ab6eb88857cf1011bf1f8349b43a1c34c7ced0f)
1 /*
2  * FreeRTOS+TCP <DEVELOPMENT BRANCH>
3  * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy of
8  * this software and associated documentation files (the "Software"), to deal in
9  * the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11  * the Software, and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * http://aws.amazon.com/freertos
25  * http://www.FreeRTOS.org
26  */
27 
28 /* FreeRTOS includes. */
29 #include "LPC54018.h"
30 #include "FreeRTOS.h"
31 #include "list.h"
32 
33 #include <stdbool.h>
34 
35 /* FreeRTOS+TCP includes. */
36 #include "FreeRTOS_IP.h"
37 
38 #include "FreeRTOS_IP_Private.h"
39 #include "NetworkBufferManagement.h"
40 
41 #include "fsl_enet.h"
42 #include "fsl_phy.h"
43 
44 #include "fsl_enet_mdio.h"
45 #include "fsl_phylan8720a.h"
46 #include "fsl_debug_console.h"
47 
48 #define PHY_ADDRESS         ( 0x00U )
49 /* MDIO operations. */
50 #define EXAMPLE_MDIO_OPS    lpc_enet_ops
51 /* PHY operations. */
52 #define EXAMPLE_PHY_OPS     phylan8720a_ops
53 #define ENET_RXBD_NUM       ( 4 )
54 #define ENET_TXBD_NUM       ( 4 )
55 #define ENET_RXBUFF_SIZE    ( ENET_FRAME_MAX_FRAMELEN )
56 #define ENET_BuffSizeAlign( n )    ENET_ALIGN( n, ENET_BUFF_ALIGNMENT )
57 #define ENET_ALIGN( x, align )     ( ( unsigned int ) ( ( x ) + ( ( align ) - 1 ) ) & ( unsigned int ) ( ~( unsigned int ) ( ( align ) - 1 ) ) )
58 
59 #if defined( __GNUC__ )
60     #ifndef __ALIGN_END
61         #define __ALIGN_END    __attribute__( ( aligned( ENET_BUFF_ALIGNMENT ) ) )
62     #endif
63     #ifndef __ALIGN_BEGIN
64         #define __ALIGN_BEGIN
65     #endif
66 #else
67     #ifndef __ALIGN_END
68         #define __ALIGN_END
69     #endif
70     #ifndef __ALIGN_BEGIN
71         #if defined( __CC_ARM ) || defined( __ARMCC_VERSION )
72             #define __ALIGN_BEGIN    __attribute__( ( aligned( ENET_BUFF_ALIGNMENT ) ) )
73         #elif defined( __ICCARM__ )
74             #define __ALIGN_BEGIN
75         #endif
76     #endif
77 #endif /* if defined( __GNUC__ ) */
78 
79 /* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet
80  * driver will filter incoming packets and only pass the stack those packets it
81  * considers need processing. */
82 #if ( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 )
83     #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer )    eProcessBuffer
84 #else
85     #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer )    eConsiderFrameForProcessing( ( pucEthernetBuffer ) )
86 #endif
87 
88 #ifndef NETWORK_INTERFACE_RX_PRIORITY
89     #define NETWORK_INTERFACE_RX_PRIORITY    ( configMAX_PRIORITIES - 1 )
90 #endif
91 
92 /*******************************************************************************
93  * Variables
94  ******************************************************************************/
95 #if defined( __ICCARM__ )
96     #pragma data_alignment = ENET_BUFF_ALIGNMENT
97 #endif
98 __ALIGN_BEGIN enet_rx_bd_struct_t g_rxBuffDescrip[ ENET_RXBD_NUM ] __ALIGN_END;
99 #if defined( __ICCARM__ )
100     #pragma data_alignment = ENET_BUFF_ALIGNMENT
101 #endif
102 __ALIGN_BEGIN enet_tx_bd_struct_t g_txBuffDescrip[ ENET_TXBD_NUM ] __ALIGN_END;
103 
104 enet_handle_t g_handle = { 0 };
105 /* The MAC address for ENET device. */
106 uint8_t g_macAddr[ 6 ] = { 0xde, 0xad, 0x00, 0xbe, 0xef, 0x01 };
107 
108 bool g_linkStatus = false;
109 
110 /*! @brief Enet PHY and MDIO interface handler. */
111 static mdio_handle_t mdioHandle = { .ops = &EXAMPLE_MDIO_OPS };
112 static phy_handle_t phyHandle = { .phyAddr = PHY_ADDRESS, .mdioHandle = &mdioHandle, .ops = &EXAMPLE_PHY_OPS };
113 
114 __ALIGN_BEGIN uint32_t receiveBuffer[ ENET_RXBD_NUM ][ ENET_RXBUFF_SIZE / sizeof( uint32_t ) + 1 ] __ALIGN_END;
115 uint32_t rxbuffer[ ENET_RXBD_NUM ];
116 
117 TaskHandle_t receiveTaskHandle;
118 
ENET_IntCallback(ENET_Type * base,enet_handle_t * handle,enet_event_t event,uint8_t channel,void * param)119 void ENET_IntCallback( ENET_Type * base,
120                        enet_handle_t * handle,
121                        enet_event_t event,
122                        uint8_t channel,
123                        void * param )
124 {
125     BaseType_t needsToYield = pdFALSE;
126 
127     switch( event )
128     {
129         case kENET_TxIntEvent:
130             break;
131 
132         case kENET_RxIntEvent:
133             vTaskNotifyGiveFromISR( receiveTaskHandle, &needsToYield );
134             portEND_SWITCHING_ISR( needsToYield );
135             break;
136 
137         default:
138             break;
139     }
140 }
141 
prvProcessFrame(int length)142 static void prvProcessFrame( int length )
143 {
144     NetworkBufferDescriptor_t * pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( length, 0 );
145 
146     if( pxBufferDescriptor != NULL )
147     {
148         ENET_ReadFrame( ENET, &g_handle, pxBufferDescriptor->pucEthernetBuffer, length, 0 );
149         pxBufferDescriptor->xDataLength = length;
150 
151         if( ipCONSIDER_FRAME_FOR_PROCESSING( pxBufferDescriptor->pucEthernetBuffer ) == eProcessBuffer )
152         {
153             IPStackEvent_t xRxEvent;
154             xRxEvent.eEventType = eNetworkRxEvent;
155             xRxEvent.pvData = ( void * ) pxBufferDescriptor;
156 
157             if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE )
158             {
159                 vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor );
160                 iptraceETHERNET_RX_EVENT_LOST();
161                 PRINTF( "RX Event Lost\n" );
162             }
163         }
164         else
165         {
166             PRINTF( "RX Event not to be considered\n" );
167             vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor );
168             /* Not sure if a trace is required.  The stack did not want this message */
169         }
170     }
171     else
172     {
173         PRINTF( "RX No Buffer Available\n" );
174         ENET_ReadFrame( ENET, &g_handle, NULL, 0, 0 );
175         /* No buffer available to receive this message */
176         iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER();
177     }
178 }
179 
rx_task(void * parameter)180 static void rx_task( void * parameter )
181 {
182     while( pdTRUE )
183     {
184         if( ulTaskNotifyTake( pdTRUE, pdMS_TO_TICKS( 500 ) ) == pdFALSE ) /* no RX packets for a bit so check for a link */
185         {
186             PHY_GetLinkStatus( &phyHandle, &g_linkStatus );
187         }
188         else
189         {
190             BaseType_t receiving = pdTRUE;
191 
192             while( receiving == pdTRUE )
193             {
194                 uint32_t length;
195                 const status_t status = ENET_GetRxFrameSize( ENET, &g_handle, &length, 0 );
196 
197                 switch( status )
198                 {
199                     case kStatus_Success: /* there is a frame.  process it */
200 
201                         if( length )
202                         {
203                             prvProcessFrame( length );
204                         }
205 
206                         break;
207 
208                     case kStatus_ENET_RxFrameEmpty: /* Received an empty frame.  Ignore it */
209                         receiving = pdFALSE;
210                         break;
211 
212                     case kStatus_ENET_RxFrameError: /* Received an error frame.  Read & drop it */
213                         PRINTF( "RX Receive Error\n" );
214                         ENET_ReadFrame( ENET, &g_handle, NULL, 0, 0 );
215                         /* Not sure if a trace is required.  The MAC had an error and needed to dump bytes */
216                         break;
217 
218                     default:
219                         PRINTF( "RX Receive default\n" );
220                         break;
221                 }
222             }
223         }
224     }
225 }
226 
xGetPhyLinkStatus(void)227 BaseType_t xGetPhyLinkStatus( void )
228 {
229     return g_linkStatus ? pdTRUE : pdFALSE;
230 }
231 
232 
xNetworkInterfaceInitialise(void)233 BaseType_t xNetworkInterfaceInitialise( void )
234 {
235     BaseType_t returnValue = pdFAIL;
236     static enum
237     {
238         initPhy, waitForLink, startReceiver, configurePhy
239     }
240     networkInitialisePhase = initPhy;
241 
242     switch( networkInitialisePhase )
243     {
244         default:
245             networkInitialisePhase = initPhy;
246 
247         /* fall through */
248         case initPhy:
249            {
250                phy_config_t phyConfig;
251                phyConfig.phyAddr = PHY_ADDRESS;
252                phyConfig.autoNeg = true;
253                mdioHandle.resource.base = ENET;
254 
255                status_t status = PHY_Init( &phyHandle, &phyConfig );
256 
257                if( status == kStatus_PHY_AutoNegotiateFail )
258                {
259                    PRINTF( "\nPHY Auto-negotiation failed. Please check the cable connection and link partner setting.\n" );
260                    break;
261                }
262            }
263 
264         case startReceiver:
265             networkInitialisePhase = startReceiver;
266 
267             if( xTaskCreate( rx_task, "rx_task", 512, NULL, NETWORK_INTERFACE_RX_PRIORITY, &receiveTaskHandle ) != pdPASS )
268             {
269                 PRINTF( "Network Receive Task creation failed!.\n" );
270                 break;
271             }
272 
273         /* fall through */
274         case waitForLink:
275             networkInitialisePhase = waitForLink;
276             {
277                 if( !xGetPhyLinkStatus() )
278                 {
279                     PRINTF( "No Link\n" );
280                     break;
281                 }
282             }
283 
284         /* fall through */
285         case configurePhy:
286            {
287                networkInitialisePhase = configurePhy;
288                enet_config_t config;
289                phy_speed_t speed;
290                phy_duplex_t duplex;
291                PHY_GetLinkSpeedDuplex( &phyHandle, &speed, &duplex );
292                /* Get default configuration 100M RMII. */
293                ENET_GetDefaultConfig( &config );
294 
295                /* Use the actual speed and duplex when phy success to finish the autonegotiation. */
296                config.miiSpeed = ( enet_mii_speed_t ) speed;
297                config.miiDuplex = ( enet_mii_duplex_t ) duplex;
298 
299                /* Initialize ENET. */
300                uint32_t refClock = 50000000; /* 50MHZ for rmii reference clock. */
301                ENET_Init( ENET, &config, g_macAddr, refClock );
302 
303                /* Enable the rx interrupt. */
304                ENET_EnableInterrupts( ENET, ( kENET_DmaRx ) );
305 
306                /* Initialize Descriptor. */
307                int bufferIndex;
308 
309                for( bufferIndex = 0; bufferIndex < ENET_RXBD_NUM; bufferIndex++ )
310                {
311                    rxbuffer[ bufferIndex ] = ( uint32_t ) &receiveBuffer[ bufferIndex ];
312                }
313 
314                /* prepare the buffer configuration. */
315                enet_buffer_config_t buffConfig[ 1 ] =
316                {
317                    {
318                        ENET_RXBD_NUM, ENET_TXBD_NUM,
319                        &g_txBuffDescrip[ 0 ], &g_txBuffDescrip[ 0 ],
320                        &g_rxBuffDescrip[ 0 ], &g_rxBuffDescrip[ ENET_RXBD_NUM ],
321                        &rxbuffer[ 0 ], ENET_BuffSizeAlign( ENET_RXBUFF_SIZE ),
322                    }
323                };
324                ENET_DescriptorInit( ENET, &config, &buffConfig[ 0 ] );
325 
326                /* Create the handler. */
327                ENET_CreateHandler( ENET, &g_handle, &config, &buffConfig[ 0 ], ENET_IntCallback, NULL );
328                NVIC_SetPriority( 65 - 16, 4 ); /* TODO this is a hack and I would expect a nice ENET API for priority. */
329 
330                /* Active TX/RX. */
331                ENET_StartRxTx( ENET, 1, 1 );
332            }
333             returnValue = pdPASS;
334             break;
335     }
336 
337     return returnValue;
338 }
339 
xNetworkInterfaceOutput(NetworkBufferDescriptor_t * const pxNetworkBuffer,BaseType_t xReleaseAfterSend)340 BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer,
341                                     BaseType_t xReleaseAfterSend )
342 {
343     BaseType_t response = pdFALSE;
344     status_t status;
345 
346     if( xGetPhyLinkStatus() )
347     {
348         status = ENET_SendFrame( ENET, &g_handle, pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength );
349 
350         switch( status )
351         {
352             default: /* anything not Success will be a failure */
353             case kStatus_ENET_TxFrameBusy:
354                 PRINTF( "TX Frame Busy\n" );
355                 break;
356 
357             case kStatus_Success:
358                 iptraceNETWORK_INTERFACE_TRANSMIT();
359                 response = pdTRUE;
360                 break;
361         }
362     }
363 
364     if( xReleaseAfterSend != pdFALSE )
365     {
366         vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
367     }
368 
369     return response;
370 }
371 
372 /* statically allocate the buffers */
373 /* allocating them as uint32_t's to force them into word alignment, a requirement of the DMA. */
374 __ALIGN_BEGIN static uint32_t buffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ][ ( ipBUFFER_PADDING + ENET_RXBUFF_SIZE ) / sizeof( uint32_t ) + 1 ] __ALIGN_END;
vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS])375 void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
376 {
377     for( int x = 0; x < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; x++ )
378     {
379         pxNetworkBuffers[ x ].pucEthernetBuffer = ( uint8_t * ) &buffers[ x ][ 0 ] + ipBUFFER_PADDING;
380         buffers[ x ][ 0 ] = ( uint32_t ) &pxNetworkBuffers[ x ];
381     }
382 }
383