xref: /FreeRTOS-Plus-TCP-v3.1.0/source/portable/NetworkInterface/libslirp/MBuffNetworkInterface.c (revision 37bdfe577f3b728058de714e2e747d3c78803f26)
1 /*
2  * FreeRTOS+TCP V3.1.0
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 MBuffNetDriverContext_t xDriverCtx = { 0 };
81 
82 /**
83  * @brief Initialize the MessageBuffer backed network interface.
84  *
85  * @return BaseType_t pdTRUE on success
86  */
xNetworkInterfaceInitialise(void)87 BaseType_t xNetworkInterfaceInitialise( void )
88 {
89     BaseType_t xResult = pdTRUE;
90 
91     if( xDriverCtx.xInterfaceState == pdFALSE )
92     {
93         xDriverCtx.xInterfaceState = pdFALSE;
94 
95         #if defined( _WIN32 )
96             xDriverCtx.pvSendEvent = CreateEvent( NULL, FALSE, TRUE, NULL );
97         #else
98             xDriverCtx.pvSendEvent = ( void * ) event_create();
99         #endif
100 
101         if( xDriverCtx.pvSendEvent == NULL )
102         {
103             xResult = pdFALSE;
104         }
105         else
106         {
107             vMBuffNetifBackendInit( &( xDriverCtx.xSendMsgBuffer ),
108                                     &( xDriverCtx.xRecvMsgBuffer ),
109                                     xDriverCtx.pvSendEvent,
110                                     &( xDriverCtx.pvBackendContext ) );
111 
112             if( xDriverCtx.pvBackendContext == NULL )
113             {
114                 xResult = pdFALSE;
115                 #if defined( _WIN32 )
116                     ( void ) CloseHandle( xDriverCtx.pvSendEvent );
117                 #else
118                     ( void ) event_delete( xDriverCtx.pvSendEvent );
119                 #endif
120             }
121         }
122 
123         if( xResult == pdTRUE )
124         {
125             xResult = xTaskCreate( vNetifReceiveTask, "NetRX",
126                                    configMINIMAL_STACK_SIZE, NULL,
127                                    tskIDLE_PRIORITY,
128                                    &( xDriverCtx.xRecvTask ) );
129         }
130 
131         /* Cleanup on failure */
132         if( xResult != pdTRUE )
133         {
134             if( xDriverCtx.pvSendEvent != NULL )
135             {
136                 #if defined( _WIN32 )
137                     ( void ) CloseHandle( xDriverCtx.pvSendEvent );
138                 #else
139                     event_delete( xDriverCtx.pvSendEvent );
140                 #endif
141             }
142 
143             if( xDriverCtx.pvBackendContext != NULL )
144             {
145                 vMBuffNetifBackendDeInit( xDriverCtx.pvBackendContext );
146             }
147         }
148 
149         xDriverCtx.xInterfaceState = xResult;
150     }
151 
152     return xResult;
153 }
154 
155 /**
156  * @brief Deinitialize the message buffer backed network interface.
157  *
158  * @return BaseType_t pdTRUE
159  */
xNetworkInterfaceDeInitialise(void)160 BaseType_t xNetworkInterfaceDeInitialise( void )
161 {
162     #if defined( _WIN32 )
163         ( void ) CloseHandle( xDriverCtx.pvSendEvent );
164     #else
165         event_delete( xDriverCtx.pvSendEvent );
166     #endif
167 
168     vTaskDelete( xDriverCtx.xRecvTask );
169 
170     return pdTRUE;
171 }
172 
173 /*!
174  * @brief FreeRTOS task which reads from xRecvMsgBuffer and passes new frames to FreeRTOS+TCP.
175  * @param [in] pvParameters not used
176  */
vNetifReceiveTask(void * pvParameters)177 static void vNetifReceiveTask( void * pvParameters )
178 {
179     NetworkBufferDescriptor_t * pxDescriptor = NULL;
180 
181     ( void ) pvParameters;
182 
183     for( ; ; )
184     {
185         size_t uxMessageLen;
186 
187         while( pxDescriptor == NULL )
188         {
189             /* Wait for an MTU + header sized buffer */
190             pxDescriptor = pxGetNetworkBufferWithDescriptor( NETWORK_BUFFER_LEN, portMAX_DELAY );
191             configASSERT( pxDescriptor->xDataLength >= NETWORK_BUFFER_LEN );
192         }
193 
194         /* Read an incoming frame */
195         uxMessageLen = xMessageBufferReceive( xDriverCtx.xRecvMsgBuffer,
196                                               pxDescriptor->pucEthernetBuffer,
197                                               pxDescriptor->xDataLength,
198                                               portMAX_DELAY );
199 
200         if( uxMessageLen > 0 )
201         {
202             IPStackEvent_t xRxEvent;
203             eFrameProcessingResult_t xFrameProcess;
204 
205             pxDescriptor->xDataLength = uxMessageLen;
206 
207             /* eConsiderFrameForProcessing is interrupt safe */
208             xFrameProcess = ipCONSIDER_FRAME_FOR_PROCESSING( pxDescriptor->pucEthernetBuffer );
209 
210             if( xFrameProcess != eProcessBuffer )
211             {
212                 FreeRTOS_debug_printf( ( "Dropping RX frame of length: %lu. eConsiderFrameForProcessing returned %lu.\n",
213                                          uxMessageLen, xFrameProcess ) );
214             }
215 
216             xRxEvent.eEventType = eNetworkRxEvent;
217             xRxEvent.pvData = ( void * ) pxDescriptor;
218 
219             if( xSendEventStructToIPTask( &xRxEvent, 0U ) == pdTRUE )
220             {
221                 iptraceNETWORK_INTERFACE_RECEIVE();
222 
223                 /* Clear pxDescriptor so that the task requests a new buffer */
224                 pxDescriptor = NULL;
225             }
226             else
227             {
228                 FreeRTOS_debug_printf( ( "Dropping TX frame of length: %lu. FreeRTOS+TCP event queue is full.\n",
229                                          pxDescriptor->xDataLength ) );
230                 /* Drop the frame and reuse the descriptor for the next incomming frame */
231                 iptraceETHERNET_RX_EVENT_LOST();
232             }
233         }
234         else
235         {
236             /*
237              * xMessageBufferReceive returned zero.
238              */
239         }
240     }
241 }
242 
243 /*!
244  * @brief API call, called from reeRTOS_IP.c to send a network packet over the
245  *        selected interface
246  * @return pdTRUE if successful else pdFALSE
247  */
xNetworkInterfaceOutput(NetworkBufferDescriptor_t * const pxNetworkBuffer,BaseType_t xReleaseAfterSend)248 BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer,
249                                     BaseType_t xReleaseAfterSend )
250 {
251     BaseType_t xResult = pdFALSE;
252 
253     configASSERT( pxNetworkBuffer != NULL );
254     configASSERT( pxNetworkBuffer->pucEthernetBuffer != NULL );
255     configASSERT( pxNetworkBuffer->xDataLength >= sizeof( EthernetHeader_t ) );
256 
257     if( xDriverCtx.xInterfaceState == pdTRUE )
258     {
259         if( xMessageBufferSpacesAvailable( xDriverCtx.xSendMsgBuffer ) > pxNetworkBuffer->xDataLength + 4U )
260         {
261             size_t uxBytesSent;
262             uxBytesSent = xMessageBufferSend( xDriverCtx.xSendMsgBuffer,
263                                               pxNetworkBuffer->pucEthernetBuffer,
264                                               pxNetworkBuffer->xDataLength,
265                                               0U );
266             ( void ) uxBytesSent;
267             configASSERT( uxBytesSent == pxNetworkBuffer->xDataLength );
268             xResult = pdTRUE;
269         }
270         else
271         {
272             FreeRTOS_debug_printf( ( "Dropping TX frame of length: %lu. xSendMsgBuffer is full.\n",
273                                      pxNetworkBuffer->xDataLength ) );
274         }
275 
276         iptraceNETWORK_INTERFACE_TRANSMIT();
277 
278         if( xReleaseAfterSend != pdFALSE )
279         {
280             vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
281         }
282 
283         if( xResult == pdTRUE )
284         {
285             #if defined( _WIN32 )
286                 SetEvent( xDriverCtx.pvSendEvent );
287             #else
288                 event_signal( xDriverCtx.pvSendEvent );
289             #endif
290         }
291     }
292 
293     return xResult;
294 }
295 
296 #define BUFFER_SIZE               ( ipTOTAL_ETHERNET_FRAME_SIZE + ipBUFFER_PADDING )
297 #define BUFFER_SIZE_ROUNDED_UP    ( ( BUFFER_SIZE + 7 ) & ~0x07UL )
298 
299 /*!
300  * @brief Allocate RAM for packet buffers and set the pucEthernetBuffer field for each descriptor.
301  *        Called when the BufferAllocation1 scheme is used.
302  * @param [in,out] pxNetworkBuffers Pointer to an array of NetworkBufferDescriptor_t to populate.
303  */
vNetworkInterfaceAllocateRAMToBuffers(NetworkBufferDescriptor_t pxNetworkBuffers[ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS])304 void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkBuffers[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ] )
305 {
306     static uint8_t * pucNetworkPacketBuffers = NULL;
307     size_t uxIndex;
308 
309     if( pucNetworkPacketBuffers == NULL )
310     {
311         pucNetworkPacketBuffers = ( uint8_t * ) malloc( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS * BUFFER_SIZE_ROUNDED_UP );
312     }
313 
314     if( pucNetworkPacketBuffers == NULL )
315     {
316         FreeRTOS_printf( ( "Failed to allocate memory for pxNetworkBuffers" ) );
317         configASSERT( 0 );
318     }
319     else
320     {
321         for( uxIndex = 0; uxIndex < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; uxIndex++ )
322         {
323             size_t uxOffset = uxIndex * BUFFER_SIZE_ROUNDED_UP;
324             NetworkBufferDescriptor_t ** ppDescriptor;
325 
326             /* At the beginning of each pbuff is a pointer to the relevant descriptor */
327             ppDescriptor = ( NetworkBufferDescriptor_t ** ) &( pucNetworkPacketBuffers[ uxOffset ] );
328 
329             /* Set this pointer to the address of the correct descriptor */
330             *ppDescriptor = &( pxNetworkBuffers[ uxIndex ] );
331 
332             /* pucEthernetBuffer is set to point ipBUFFER_PADDING bytes in from the
333              * beginning of the allocated buffer. */
334             pxNetworkBuffers[ uxIndex ].pucEthernetBuffer = &( pucNetworkPacketBuffers[ uxOffset + ipBUFFER_PADDING ] );
335         }
336     }
337 }
338