xref: /FreeRTOS-Plus-TCP-v4.0.0/source/portable/NetworkInterface/M487/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 "FreeRTOS.h"
30 #include "list.h"
31 #include "queue.h"
32 #include "semphr.h"
33 #include "task.h"
34 
35 /* FreeRTOS+TCP includes. */
36 #include "FreeRTOS_IP.h"
37 #include "FreeRTOS_Sockets.h"
38 #include "FreeRTOS_IP_Private.h"
39 #include "NetworkBufferManagement.h"
40 #include "NetworkInterface.h"
41 
42 
43 #include "m480_eth.h"
44 
45 /* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet
46  * driver will filter incoming packets and only pass the stack those packets it
47  * considers need processing. */
48 #if ( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 )
49     #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer )    eProcessBuffer
50 #else
51     #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer )    eConsiderFrameForProcessing( ( pucEthernetBuffer ) )
52 #endif
53 
54 /* Default the size of the stack used by the EMAC deferred handler task to twice
55  * the size of the stack used by the idle task - but allow this to be overridden in
56  * FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */
57 #ifndef configEMAC_TASK_STACK_SIZE
58     #define configEMAC_TASK_STACK_SIZE    ( 2 * configMINIMAL_STACK_SIZE )
59 #endif
60 
61 
62 static SemaphoreHandle_t xTXMutex = NULL;
63 
64 /* The handle of the task that processes Rx packets.  The handle is required so
65  * the task can be notified when new packets arrive. */
66 static TaskHandle_t xRxHanderTask = NULL;
67 static TimerHandle_t xPhyHandlerTask = NULL;
68 
69 /*
70  * A task that processes received frames.
71  */
72 static void prvEMACHandlerTask( void * pvParameters );
73 static void prvPhyTmrCallback( TimerHandle_t xTimer );
74 
75 /* The size of each buffer when BufferAllocation_1 is used:
76  * http://www.freertos.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Buffer_Management.html */
77 
78 #define niBUFFER_1_PACKET_SIZE    1536
79 #ifdef __ICCARM__
80     #pragma data_alignment=4
81     static uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ]
82 #else
83     static uint8_t ucNetworkPackets[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * niBUFFER_1_PACKET_SIZE ] __attribute__( ( aligned( 4 ) ) );
84 #endif
85 
xNetworkInterfaceInitialise(void)86 BaseType_t xNetworkInterfaceInitialise( void )
87 {
88     uint8_t hwaddr[ 6 ];
89     BaseType_t xReturn = pdPASS;
90 
91     /* Init ETH */
92     numaker_mac_address( hwaddr );
93     FreeRTOS_UpdateMACAddress( hwaddr );
94     FreeRTOS_printf( ( "mac address %02x-%02x-%02x-%02x-%02x-%02x \r\n", hwaddr[ 0 ], hwaddr[ 1 ], hwaddr[ 2 ], hwaddr[ 3 ], hwaddr[ 4 ], hwaddr[ 5 ] ) );
95 
96     /* Enable clock & set EMAC configuration         */
97     /* Enable MAC and DMA transmission and reception */
98     if( numaker_eth_init( hwaddr ) < 0 )
99     {
100         xReturn = pdFAIL;
101     }
102     else
103     {
104         xReturn = pdPASS;
105 
106         /* Guard against the task being created more than once and the
107          * descriptors being initialized more than once. */
108         /* Timer task to monitor PHY Link status */
109         if( xPhyHandlerTask == NULL )
110         {
111             xPhyHandlerTask = xTimerCreate( "TimerPhy", pdMS_TO_TICKS( 1000 ), pdTRUE, 0, prvPhyTmrCallback );
112             configASSERT( xPhyHandlerTask );
113             xReturn = xTimerStart( xPhyHandlerTask, 0 );
114             configASSERT( xReturn );
115         }
116 
117         /* Rx task */
118         if( xRxHanderTask == NULL )
119         {
120             xReturn = xTaskCreate( prvEMACHandlerTask, "EMAC", configEMAC_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xRxHanderTask );
121             configASSERT( xReturn );
122         }
123 
124         if( xTXMutex == NULL )
125         {
126             xTXMutex = xSemaphoreCreateMutex();
127             configASSERT( xTXMutex );
128         }
129     }
130 
131     NVIC_SetPriority( EMAC_RX_IRQn, configMAC_INTERRUPT_PRIORITY );
132     NVIC_SetPriority( EMAC_TX_IRQn, configMAC_INTERRUPT_PRIORITY );
133 
134     numaker_eth_enable_interrupts();
135 
136     FreeRTOS_printf( ( "ETH-RX priority:%d\n", NVIC_GetPriority( EMAC_RX_IRQn ) ) );
137 
138     return xReturn;
139 }
140 
xNetworkInterfaceOutput(NetworkBufferDescriptor_t * const pxDescriptor,BaseType_t xReleaseAfterSend)141 BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxDescriptor,
142                                     BaseType_t xReleaseAfterSend )
143 {
144     uint8_t * buffer = NULL;
145 
146     if( pxDescriptor->xDataLength >= PACKET_BUFFER_SIZE )
147     {
148         FreeRTOS_printf( ( "TX buffer length %d over %d\n", pxDescriptor->xDataLength, PACKET_BUFFER_SIZE ) );
149         return pdFALSE;
150     }
151 
152     buffer = numaker_eth_get_tx_buf();
153 
154     if( buffer == NULL )
155     {
156         NU_DEBUGF( ( "Eth TX slots are busy\n" ) );
157         return pdFALSE;
158     }
159 
160     /* Get exclusive access */
161     xSemaphoreTake( xTXMutex, portMAX_DELAY );
162     NU_DEBUGF( ( "%s ... buffer=0x%x\r\n", __FUNCTION__, buffer ) );
163     /*SendData: pt = pxDescriptor->pucBuffer, length = pxDescriptor->xDataLength */
164     memcpy( buffer, pxDescriptor->pucEthernetBuffer, pxDescriptor->xDataLength );
165     numaker_eth_trigger_tx( pxDescriptor->xDataLength, NULL );
166     /* Call the standard trace macro to log the send event. */
167     iptraceNETWORK_INTERFACE_TRANSMIT();
168 
169     if( xReleaseAfterSend != pdFALSE )
170     {
171         /* It is assumed SendData() copies the data out of the FreeRTOS+TCP Ethernet
172          * buffer.  The Ethernet buffer is therefore no longer needed, and must be
173          * freed for re-use. */
174         vReleaseNetworkBufferAndDescriptor( pxDescriptor );
175     }
176 
177     xSemaphoreGive( xTXMutex );
178 
179     return pdTRUE;
180 }
181 
182 
vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS])183 void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
184 {
185     uint8_t * ucRAMBuffer = ucNetworkPackets;
186     uint32_t ul;
187 
188     for( ul = 0; ul < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; ul++ )
189     {
190         pxNetworkBuffers[ ul ].pucEthernetBuffer = ucRAMBuffer + ipBUFFER_PADDING;
191         *( ( unsigned * ) ucRAMBuffer ) = ( unsigned ) ( &( pxNetworkBuffers[ ul ] ) );
192         ucRAMBuffer += niBUFFER_1_PACKET_SIZE;
193     }
194 }
195 
196 
xGetPhyLinkStatus(void)197 BaseType_t xGetPhyLinkStatus( void )
198 {
199     BaseType_t xReturn;
200 
201     if( numaker_eth_link_ok() )
202     {
203         xReturn = pdPASS;
204     }
205     else
206     {
207         xReturn = pdFAIL;
208     }
209 
210     return xReturn;
211 }
212 
prvPhyTmrCallback(TimerHandle_t xTimer)213 static void prvPhyTmrCallback( TimerHandle_t xTimer )
214 {
215     IPStackEvent_t xRxEvent;
216     static BaseType_t lastLink = pdFAIL;
217     BaseType_t currLink = xGetPhyLinkStatus();
218 
219     if( currLink != lastLink )
220     {
221         FreeRTOS_printf( ( "PHY Link %s\n", ( currLink ) ? "Up" : "Down" ) );
222 
223         if( !currLink )
224         {
225             xRxEvent.eEventType = eNetworkDownEvent;
226             xSendEventStructToIPTask( &xRxEvent, 0 );
227         }
228 
229         lastLink = currLink;
230     }
231 }
232 
233 
prvEMACHandlerTask(void * pvParameters)234 static void prvEMACHandlerTask( void * pvParameters )
235 {
236     TimeOut_t xPhyTime;
237     TickType_t xPhyRemTime;
238     UBaseType_t uxLastMinBufferCount = 0;
239     UBaseType_t uxCurrentCount;
240     BaseType_t xResult = 0;
241     uint32_t ulStatus;
242     uint16_t dataLength = 0;
243     uint8_t * buffer = NULL;
244     NetworkBufferDescriptor_t * pxBufferDescriptor = NULL;
245     IPStackEvent_t xRxEvent;
246     const TickType_t xBlockTime = pdMS_TO_TICKS( 5000ul );
247 
248     /* Remove compiler warnings about unused parameters. */
249     ( void ) pvParameters;
250     /* A possibility to set some additional task properties. */
251 
252     for( ; ; )
253     {
254         uxCurrentCount = uxGetMinimumFreeNetworkBuffers();
255 
256         if( uxLastMinBufferCount != uxCurrentCount )
257         {
258             /* The logging produced below may be helpful
259              * while tuning +TCP: see how many buffers are in use. */
260             uxLastMinBufferCount = uxCurrentCount;
261             FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n",
262                                uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) );
263         }
264 
265         /* No events to process now, wait for the next. */
266         ulTaskNotifyTake( pdFALSE, portMAX_DELAY );
267 
268         while( 1 )
269         {
270             /* get received frame */
271             if( numaker_eth_get_rx_buf( &dataLength, &buffer ) != 0 )
272             {
273                 /* The event was lost because a network buffer was not available.
274                  * Call the standard trace macro to log the occurrence. */
275                 iptraceETHERNET_RX_EVENT_LOST();
276                 break;
277             }
278 
279             /* Allocate a network buffer descriptor that points to a buffer
280              * large enough to hold the received frame.  As this is the simple
281              * rather than efficient example the received data will just be copied
282              * into this buffer. */
283 
284             pxBufferDescriptor = pxGetNetworkBufferWithDescriptor( PACKET_BUFFER_SIZE, 0 );
285 
286             if( pxBufferDescriptor != NULL )
287             {
288                 memcpy( pxBufferDescriptor->pucEthernetBuffer, buffer, dataLength );
289                 pxBufferDescriptor->xDataLength = dataLength;
290             }
291             else
292             {
293                 numaker_eth_rx_next();
294                 iptraceETHERNET_RX_EVENT_LOST();
295                 break;
296             }
297 
298             /* The event about to be sent to the TCP/IP is an Rx event. */
299             xRxEvent.eEventType = eNetworkRxEvent;
300 
301             /* pvData is used to point to the network buffer descriptor that
302              *  now references the received data. */
303             xRxEvent.pvData = ( void * ) pxBufferDescriptor;
304 
305             /* Send the data to the TCP/IP stack. */
306             if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE )
307             {
308                 /* The buffer could not be sent to the IP task so the buffer
309                  * must be released. */
310                 vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor );
311 
312                 /* Make a call to the standard trace macro to log the
313                  *      occurrence. */
314 
315                 iptraceETHERNET_RX_EVENT_LOST();
316             }
317             else
318             {
319                 /* The message was successfully sent to the TCP/IP stack.
320                 * Call the standard trace macro to log the occurrence. */
321                 iptraceNETWORK_INTERFACE_RECEIVE();
322             }
323 
324             numaker_eth_rx_next();
325         }
326 
327         numaker_eth_trigger_rx();
328     }
329 }
330 
xNetworkCallback(char event)331 void xNetworkCallback( char event )
332 {
333     BaseType_t xHigherPriorityTaskWoken = pdFALSE;
334 
335     switch( event )
336     {
337         case 'R': /*For RX event */
338 
339             /* Wakeup the prvEMACHandlerTask. */
340             if( xRxHanderTask != NULL )
341             {
342                 vTaskNotifyGiveFromISR( xRxHanderTask, &xHigherPriorityTaskWoken );
343                 portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
344             }
345 
346             break;
347 
348         case 'T': /*For TX event */
349             /* ack of tx done, no-op in this stage */
350             break;
351 
352         default:
353             break;
354     }
355 }
356