xref: /FreeRTOS-Plus-TCP-v4.0.0/source/portable/NetworkInterface/libslirp/MBuffNetworkInterface.c (revision 40c16fef7b3380982a8e6517654272cca5a9dc0a)
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 #include <stdlib.h>
29 
30 /* FreeRTOS includes. */
31 #include "FreeRTOS.h"
32 #include "list.h"
33 #include "task.h"
34 #include "message_buffer.h"
35 
36 #if !defined( _WIN32 )
37     #include "wait_for_event.h"
38 #endif
39 
40 /* FreeRTOS+TCP includes. */
41 #include "FreeRTOS_IP.h"
42 
43 /* If ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is set to 1, then the Ethernet
44  * driver will filter incoming packets and only pass the stack those packets it
45  * considers need processing. */
46 #if ( ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES == 0 )
47     #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer )    eProcessBuffer
48 #else
49     #define ipCONSIDER_FRAME_FOR_PROCESSING( pucEthernetBuffer )    eConsiderFrameForProcessing( ( pucEthernetBuffer ) )
50 #endif
51 
52 #if ipconfigNETWORK_MTU < 1500U
53     #error ipconfigNETWORK_MTU must be at least 1500
54 #endif
55 
56 #define NETWORK_BUFFER_LEN    ( ipconfigNETWORK_MTU + ipSIZE_OF_ETH_HEADER )
57 
58 #define xSEND_BUFFER_SIZE     ( 32U * NETWORK_BUFFER_LEN )
59 #define xRECV_BUFFER_SIZE     ( 32U * NETWORK_BUFFER_LEN )
60 
61 typedef struct
62 {
63     BaseType_t xInterfaceState;
64     MessageBufferHandle_t xSendMsgBuffer;
65     MessageBufferHandle_t xRecvMsgBuffer;
66     TaskHandle_t xRecvTask;
67     void * pvSendEvent;
68     void * pvBackendContext;
69 } MBuffNetDriverContext_t;
70 
71 extern void vMBuffNetifBackendInit( MessageBufferHandle_t * pxSendMsgBuffer,
72                                     MessageBufferHandle_t * pxRecvMsgBuffer,
73                                     void * pvSendEvent,
74                                     void ** ppvBackendContext );
75 
76 extern void vMBuffNetifBackendDeInit( void * pvBackendContext );
77 
78 static void vNetifReceiveTask( void * pvParameters );
79 
80 /**
81  * @brief Initialize the MessageBuffer backed network interface.
82  *
83  * @return BaseType_t pdTRUE on success
84  */
xNetworkInterfaceInitialise(NetworkInterface_t * pxNetif)85 static BaseType_t xNetworkInterfaceInitialise( NetworkInterface_t * pxNetif )
86 {
87     BaseType_t xResult = pdTRUE;
88 
89     pxNetif->pvArgument = pvPortMalloc( sizeof( MBuffNetDriverContext_t ) );
90 
91     if( pxNetif->pvArgument == NULL )
92     {
93         FreeRTOS_printf( ( "Failed to allocate memory for pxNetif->pvArgument" ) );
94         configASSERT( 0 );
95     }
96 
97     MBuffNetDriverContext_t * pxDriverCtx = ( MBuffNetDriverContext_t * ) pxNetif->pvArgument;
98 
99     if( pxDriverCtx->xInterfaceState == pdFALSE )
100     {
101         pxDriverCtx->xInterfaceState = pdFALSE;
102 
103         #if defined( _WIN32 )
104             pxDriverCtx->pvSendEvent = CreateEvent( NULL, FALSE, TRUE, NULL );
105         #else
106             pxDriverCtx->pvSendEvent = ( void * ) event_create();
107         #endif
108 
109         if( pxDriverCtx->pvSendEvent == NULL )
110         {
111             xResult = pdFALSE;
112         }
113         else
114         {
115             vMBuffNetifBackendInit( &( pxDriverCtx->xSendMsgBuffer ),
116                                     &( pxDriverCtx->xRecvMsgBuffer ),
117                                     pxDriverCtx->pvSendEvent,
118                                     &( pxDriverCtx->pvBackendContext ) );
119 
120             if( pxDriverCtx->pvBackendContext == NULL )
121             {
122                 xResult = pdFALSE;
123                 #if defined( _WIN32 )
124                     ( void ) CloseHandle( pxDriverCtx->pvSendEvent );
125                 #else
126                     ( void ) event_delete( pxDriverCtx->pvSendEvent );
127                 #endif
128             }
129         }
130 
131         static BaseType_t xReceiveTaskCreated = pdFALSE;
132 
133         if( ( xResult == pdTRUE ) && ( xReceiveTaskCreated == pdFALSE ) )
134         {
135             xResult = xTaskCreate( vNetifReceiveTask, "NetRX",
136                                    configMINIMAL_STACK_SIZE,
137                                    pxNetif,
138                                    tskIDLE_PRIORITY,
139                                    &( pxDriverCtx->xRecvTask ) );
140 
141             if( xResult == pdPASS )
142             {
143                 xReceiveTaskCreated = pdTRUE;
144             }
145         }
146 
147         /* Cleanup on failure */
148         if( xResult != pdTRUE )
149         {
150             if( pxDriverCtx->pvSendEvent != NULL )
151             {
152                 #if defined( _WIN32 )
153                     ( void ) CloseHandle( pxDriverCtx->pvSendEvent );
154                 #else
155                     event_delete( pxDriverCtx->pvSendEvent );
156                 #endif
157             }
158 
159             if( pxDriverCtx->pvBackendContext != NULL )
160             {
161                 vMBuffNetifBackendDeInit( pxDriverCtx->pvBackendContext );
162             }
163         }
164 
165         pxDriverCtx->xInterfaceState = xResult;
166     }
167 
168     return xResult;
169 }
170 
171 /**
172  * @brief Deinitialize the message buffer backed network interface.
173  *
174  * @return BaseType_t pdTRUE
175  */
xNetworkInterfaceDeInitialise(NetworkInterface_t * pxNetif)176 BaseType_t xNetworkInterfaceDeInitialise( NetworkInterface_t * pxNetif )
177 {
178     MBuffNetDriverContext_t * pxDriverCtx = ( MBuffNetDriverContext_t * ) pxNetif->pvArgument;
179 
180     #if defined( _WIN32 )
181         ( void ) CloseHandle( pxDriverCtx->pvSendEvent );
182     #else
183         event_delete( pxDriverCtx->pvSendEvent );
184     #endif
185 
186     vTaskDelete( pxDriverCtx->xRecvTask );
187 
188     vMBuffNetifBackendDeInit( pxDriverCtx->pvBackendContext );
189 
190     vPortFree( pxNetif->pvArgument );
191 
192     return pdTRUE;
193 }
194 
195 /*!
196  * @brief FreeRTOS task which reads from xRecvMsgBuffer and passes new frames to FreeRTOS+TCP.
197  * @param [in] pvParameters not used
198  */
vNetifReceiveTask(void * pvParameters)199 static void vNetifReceiveTask( void * pvParameters )
200 {
201     NetworkBufferDescriptor_t * pxDescriptor = NULL;
202     NetworkInterface_t * pxNetif = ( NetworkInterface_t * ) pvParameters;
203 
204     MBuffNetDriverContext_t * pxDriverCtx = ( MBuffNetDriverContext_t * ) pxNetif->pvArgument;
205 
206     for( ; ; )
207     {
208         size_t uxMessageLen;
209 
210         while( pxDescriptor == NULL )
211         {
212             /* Wait for an MTU + header sized buffer */
213             pxDescriptor = pxGetNetworkBufferWithDescriptor( NETWORK_BUFFER_LEN, portMAX_DELAY );
214             configASSERT( pxDescriptor->xDataLength >= NETWORK_BUFFER_LEN );
215         }
216 
217         /* Read an incoming frame */
218         uxMessageLen = xMessageBufferReceive( pxDriverCtx->xRecvMsgBuffer,
219                                               pxDescriptor->pucEthernetBuffer,
220                                               pxDescriptor->xDataLength,
221                                               portMAX_DELAY );
222 
223         if( uxMessageLen > 0 )
224         {
225             IPStackEvent_t xRxEvent;
226             eFrameProcessingResult_t xFrameProcess;
227 
228             pxDescriptor->xDataLength = uxMessageLen;
229 
230             /* eConsiderFrameForProcessing is interrupt safe */
231             xFrameProcess = ipCONSIDER_FRAME_FOR_PROCESSING( pxDescriptor->pucEthernetBuffer );
232 
233             if( xFrameProcess != eProcessBuffer )
234             {
235                 FreeRTOS_debug_printf( ( "Dropping RX frame of length: %lu. eConsiderFrameForProcessing returned %lu.\n",
236                                          uxMessageLen, xFrameProcess ) );
237             }
238 
239             pxDescriptor->pxInterface = pxNetif;
240             pxDescriptor->pxEndPoint = FreeRTOS_MatchingEndpoint( pxNetif, pxDescriptor->pucEthernetBuffer );
241 
242             xRxEvent.eEventType = eNetworkRxEvent;
243             xRxEvent.pvData = ( void * ) pxDescriptor;
244 
245             if( xSendEventStructToIPTask( &xRxEvent, 0U ) == pdTRUE )
246             {
247                 iptraceNETWORK_INTERFACE_RECEIVE();
248 
249                 /* Clear pxDescriptor so that the task requests a new buffer */
250                 pxDescriptor = NULL;
251             }
252             else
253             {
254                 FreeRTOS_debug_printf( ( "Dropping TX frame of length: %lu. FreeRTOS+TCP event queue is full.\n",
255                                          pxDescriptor->xDataLength ) );
256                 /* Drop the frame and reuse the descriptor for the next incomming frame */
257                 iptraceETHERNET_RX_EVENT_LOST();
258             }
259         }
260         else
261         {
262             /*
263              * xMessageBufferReceive returned zero.
264              */
265         }
266     }
267 }
268 
269 /*!
270  * @brief API call, called from FreeRTOS_IP.c to send a network packet over the
271  *        selected interface
272  * @return pdTRUE if successful else pdFALSE
273  */
xNetworkInterfaceOutput(NetworkInterface_t * pxNetif,NetworkBufferDescriptor_t * const pxNetworkBuffer,BaseType_t xReleaseAfterSend)274 static BaseType_t xNetworkInterfaceOutput( NetworkInterface_t * pxNetif,
275                                            NetworkBufferDescriptor_t * const pxNetworkBuffer,
276                                            BaseType_t xReleaseAfterSend )
277 {
278     BaseType_t xResult = pdFALSE;
279 
280     MBuffNetDriverContext_t * pxDriverCtx = ( MBuffNetDriverContext_t * ) pxNetif->pvArgument;
281 
282     FreeRTOS_debug_printf( ( "xNetworkInterfaceOutput: %p:%p\n", pxNetworkBuffer, pxNetworkBuffer->pucEthernetBuffer ) );
283 
284     if( pxDriverCtx->xInterfaceState == pdTRUE )
285     {
286         configASSERT( pxNetworkBuffer != NULL );
287         configASSERT( pxNetworkBuffer->pucEthernetBuffer != NULL );
288         configASSERT( pxNetworkBuffer->xDataLength >= sizeof( EthernetHeader_t ) );
289 
290         if( xMessageBufferSpacesAvailable( pxDriverCtx->xSendMsgBuffer ) > pxNetworkBuffer->xDataLength + 4U )
291         {
292             size_t uxBytesSent;
293             uxBytesSent = xMessageBufferSend( pxDriverCtx->xSendMsgBuffer,
294                                               pxNetworkBuffer->pucEthernetBuffer,
295                                               pxNetworkBuffer->xDataLength,
296                                               0U );
297             ( void ) uxBytesSent;
298             configASSERT( uxBytesSent == pxNetworkBuffer->xDataLength );
299             xResult = pdTRUE;
300         }
301         else
302         {
303             FreeRTOS_debug_printf( ( "Dropping TX frame of length: %lu. xSendMsgBuffer is full.\n",
304                                      pxNetworkBuffer->xDataLength ) );
305         }
306 
307         iptraceNETWORK_INTERFACE_TRANSMIT();
308 
309         if( xReleaseAfterSend != pdFALSE )
310         {
311             vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
312         }
313 
314         if( xResult == pdTRUE )
315         {
316             #if defined( _WIN32 )
317                 SetEvent( pxDriverCtx->pvSendEvent );
318             #else
319                 event_signal( pxDriverCtx->pvSendEvent );
320             #endif
321         }
322     }
323 
324     return xResult;
325 }
326 
327 #define BUFFER_SIZE               ( ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING )
328 #define BUFFER_SIZE_ROUNDED_UP    ( ( BUFFER_SIZE + 7 ) & ~0x07UL )
329 
330 /*!
331  * @brief Allocate RAM for packet buffers and set the pucEthernetBuffer field for each descriptor.
332  *        Called when the BufferAllocation1 scheme is used.
333  * @param [in,out] pxNetworkBuffers Pointer to an array of NetworkBufferDescriptor_t to populate.
334  */
vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS])335 void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
336 {
337     static uint8_t * pucNetworkPacketBuffers = NULL;
338     size_t uxIndex;
339 
340     if( pucNetworkPacketBuffers == NULL )
341     {
342         pucNetworkPacketBuffers = ( uint8_t * ) malloc( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * BUFFER_SIZE_ROUNDED_UP );
343     }
344 
345     if( pucNetworkPacketBuffers == NULL )
346     {
347         FreeRTOS_printf( ( "Failed to allocate memory for pxNetworkBuffers" ) );
348         configASSERT( 0 );
349     }
350     else
351     {
352         for( uxIndex = 0; uxIndex < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; uxIndex++ )
353         {
354             size_t uxOffset = uxIndex * BUFFER_SIZE_ROUNDED_UP;
355             NetworkBufferDescriptor_t ** ppDescriptor;
356 
357             /* At the beginning of each pbuff is a pointer to the relevant descriptor */
358             ppDescriptor = ( NetworkBufferDescriptor_t ** ) &( pucNetworkPacketBuffers[ uxOffset ] );
359 
360             /* Set this pointer to the address of the correct descriptor */
361             *ppDescriptor = &( pxNetworkBuffers[ uxIndex ] );
362 
363             /* pucEthernetBuffer is set to point ipBUFFER_PADDING bytes in from the
364              * beginning of the allocated buffer. */
365             pxNetworkBuffers[ uxIndex ].pucEthernetBuffer = &( pucNetworkPacketBuffers[ uxOffset + ipBUFFER_PADDING ] );
366         }
367     }
368 }
369 
xGetPhyLinkStatus(NetworkInterface_t * pxNetif)370 BaseType_t xGetPhyLinkStatus( NetworkInterface_t * pxNetif )
371 {
372     BaseType_t xResult = pdFALSE;
373 
374     MBuffNetDriverContext_t * pxDriverCtx = ( MBuffNetDriverContext_t * ) pxNetif->pvArgument;
375 
376     if( pxDriverCtx->xInterfaceState == pdTRUE )
377     {
378         xResult = pdTRUE;
379     }
380 
381     return xResult;
382 }
383 
pxLibslirp_FillInterfaceDescriptor(BaseType_t xEMACIndex,NetworkInterface_t * pxInterface)384 NetworkInterface_t * pxLibslirp_FillInterfaceDescriptor( BaseType_t xEMACIndex,
385                                                          NetworkInterface_t * pxInterface )
386 {
387     configASSERT( pxInterface != NULL );
388     static char pcName[ 17 ];
389 
390     snprintf( pcName, sizeof( pcName ), "eth%ld", xEMACIndex );
391 
392     memset( pxInterface, 0, sizeof( NetworkInterface_t ) );
393 
394     pxInterface->pcName = pcName;                    /* Just for logging, debugging. */
395     pxInterface->pvArgument = ( void * ) xEMACIndex; /* Has only meaning for the driver functions. */
396     pxInterface->pfInitialise = xNetworkInterfaceInitialise;
397     pxInterface->pfOutput = xNetworkInterfaceOutput;
398     pxInterface->pfGetPhyLinkStatus = xGetPhyLinkStatus;
399 
400     FreeRTOS_AddNetworkInterface( pxInterface );
401 
402     return pxInterface;
403 }
404