]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/BufferManagement/BufferAllocation_2.c
Roll up the minor changes checked into svn since V10.0.0 into new V10.0.1 ready for...
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-Plus-TCP / portable / BufferManagement / BufferAllocation_2.c
1 /*\r
2  * FreeRTOS+TCP V2.0.1\r
3  * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\r
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
6  * this software and associated documentation files (the "Software"), to deal in\r
7  * the Software without restriction, including without limitation the rights to\r
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
9  * the Software, and to permit persons to whom the Software is furnished to do so,\r
10  * subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be included in all\r
13  * copies or substantial portions of the Software.\r
14  *\r
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21  *\r
22  * http://www.FreeRTOS.org\r
23  * http://aws.amazon.com/freertos\r
24  *\r
25  * 1 tab == 4 spaces!\r
26  */\r
27 \r
28 /******************************************************************************\r
29  *\r
30  * See the following web page for essential buffer allocation scheme usage and\r
31  * configuration details:\r
32  * http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Buffer_Management.html\r
33  *\r
34  ******************************************************************************/\r
35 \r
36 /* THIS FILE SHOULD NOT BE USED IF THE PROJECT INCLUDES A MEMORY ALLOCATOR\r
37 THAT WILL FRAGMENT THE HEAP MEMORY.  For example, heap_2 must not be used,\r
38 heap_4 can be used. */\r
39 \r
40 \r
41 /* Standard includes. */\r
42 #include <stdint.h>\r
43 \r
44 /* FreeRTOS includes. */\r
45 #include "FreeRTOS.h"\r
46 #include "task.h"\r
47 #include "semphr.h"\r
48 \r
49 /* FreeRTOS+TCP includes. */\r
50 #include "FreeRTOS_IP.h"\r
51 #include "FreeRTOS_UDP_IP.h"\r
52 #include "FreeRTOS_IP_Private.h"\r
53 #include "NetworkInterface.h"\r
54 #include "NetworkBufferManagement.h"\r
55 \r
56 /* The obtained network buffer must be large enough to hold a packet that might\r
57 replace the packet that was requested to be sent. */\r
58 #if ipconfigUSE_TCP == 1\r
59         #define baMINIMAL_BUFFER_SIZE           sizeof( TCPPacket_t )\r
60 #else\r
61         #define baMINIMAL_BUFFER_SIZE           sizeof( ARPPacket_t )\r
62 #endif /* ipconfigUSE_TCP == 1 */\r
63 \r
64 /*_RB_ This is too complex not to have an explanation. */\r
65 #if defined( ipconfigETHERNET_MINIMUM_PACKET_BYTES )\r
66         #define ASSERT_CONCAT_(a, b) a##b\r
67         #define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b)\r
68         #define STATIC_ASSERT(e) \\r
69                 ;enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(!!(e)) }\r
70 \r
71         STATIC_ASSERT( ipconfigETHERNET_MINIMUM_PACKET_BYTES <= baMINIMAL_BUFFER_SIZE );\r
72 #endif\r
73 \r
74 /* A list of free (available) NetworkBufferDescriptor_t structures. */\r
75 static List_t xFreeBuffersList;\r
76 \r
77 /* Some statistics about the use of buffers. */\r
78 static size_t uxMinimumFreeNetworkBuffers;\r
79 \r
80 /* Declares the pool of NetworkBufferDescriptor_t structures that are available\r
81 to the system.  All the network buffers referenced from xFreeBuffersList exist\r
82 in this array.  The array is not accessed directly except during initialisation,\r
83 when the xFreeBuffersList is filled (as all the buffers are free when the system\r
84 is booted). */\r
85 static NetworkBufferDescriptor_t xNetworkBufferDescriptors[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ];\r
86 \r
87 /* This constant is defined as false to let FreeRTOS_TCP_IP.c know that the\r
88 network buffers have a variable size: resizing may be necessary */\r
89 const BaseType_t xBufferAllocFixedSize = pdFALSE;\r
90 \r
91 /* The semaphore used to obtain network buffers. */\r
92 static SemaphoreHandle_t xNetworkBufferSemaphore = NULL;\r
93 \r
94 /*-----------------------------------------------------------*/\r
95 \r
96 BaseType_t xNetworkBuffersInitialise( void )\r
97 {\r
98 BaseType_t xReturn, x;\r
99 \r
100         /* Only initialise the buffers and their associated kernel objects if they\r
101         have not been initialised before. */\r
102         if( xNetworkBufferSemaphore == NULL )\r
103         {\r
104                 xNetworkBufferSemaphore = xSemaphoreCreateCounting( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS, ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS );\r
105                 configASSERT( xNetworkBufferSemaphore );\r
106                 #if ( configQUEUE_REGISTRY_SIZE > 0 )\r
107                 {\r
108                         vQueueAddToRegistry( xNetworkBufferSemaphore, "NetBufSem" );\r
109                 }\r
110                 #endif /* configQUEUE_REGISTRY_SIZE */\r
111 \r
112                 /* If the trace recorder code is included name the semaphore for viewing\r
113                 in FreeRTOS+Trace.  */\r
114                 #if( ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS == 1 )\r
115                 {\r
116                         extern QueueHandle_t xNetworkEventQueue;\r
117                         vTraceSetQueueName( xNetworkEventQueue, "IPStackEvent" );\r
118                         vTraceSetQueueName( xNetworkBufferSemaphore, "NetworkBufferCount" );\r
119                 }\r
120                 #endif /*  ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS == 1 */\r
121 \r
122                 if( xNetworkBufferSemaphore != NULL )\r
123                 {\r
124                         vListInitialise( &xFreeBuffersList );\r
125 \r
126                         /* Initialise all the network buffers.  No storage is allocated to\r
127                         the buffers yet. */\r
128                         for( x = 0; x < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; x++ )\r
129                         {\r
130                                 /* Initialise and set the owner of the buffer list items. */\r
131                                 xNetworkBufferDescriptors[ x ].pucEthernetBuffer = NULL;\r
132                                 vListInitialiseItem( &( xNetworkBufferDescriptors[ x ].xBufferListItem ) );\r
133                                 listSET_LIST_ITEM_OWNER( &( xNetworkBufferDescriptors[ x ].xBufferListItem ), &xNetworkBufferDescriptors[ x ] );\r
134 \r
135                                 /* Currently, all buffers are available for use. */\r
136                                 vListInsert( &xFreeBuffersList, &( xNetworkBufferDescriptors[ x ].xBufferListItem ) );\r
137                         }\r
138 \r
139                         uxMinimumFreeNetworkBuffers = ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS;\r
140                 }\r
141         }\r
142 \r
143         if( xNetworkBufferSemaphore == NULL )\r
144         {\r
145                 xReturn = pdFAIL;\r
146         }\r
147         else\r
148         {\r
149                 xReturn = pdPASS;\r
150         }\r
151 \r
152         return xReturn;\r
153 }\r
154 /*-----------------------------------------------------------*/\r
155 \r
156 uint8_t *pucGetNetworkBuffer( size_t *pxRequestedSizeBytes )\r
157 {\r
158 uint8_t *pucEthernetBuffer;\r
159 size_t xSize = *pxRequestedSizeBytes;\r
160 \r
161         if( xSize < baMINIMAL_BUFFER_SIZE )\r
162         {\r
163                 /* Buffers must be at least large enough to hold a TCP-packet with\r
164                 headers, or an ARP packet, in case TCP is not included. */\r
165                 xSize = baMINIMAL_BUFFER_SIZE;\r
166         }\r
167 \r
168         /* Round up xSize to the nearest multiple of N bytes,\r
169         where N equals 'sizeof( size_t )'. */\r
170         if( ( xSize & ( sizeof( size_t ) - 1u ) ) != 0u )\r
171         {\r
172                 xSize = ( xSize | ( sizeof( size_t ) - 1u ) ) + 1u;\r
173         }\r
174         *pxRequestedSizeBytes = xSize;\r
175 \r
176         /* Allocate a buffer large enough to store the requested Ethernet frame size\r
177         and a pointer to a network buffer structure (hence the addition of\r
178         ipBUFFER_PADDING bytes). */\r
179         pucEthernetBuffer = ( uint8_t * ) pvPortMalloc( xSize + ipBUFFER_PADDING );\r
180         configASSERT( pucEthernetBuffer );\r
181 \r
182         if( pucEthernetBuffer != NULL )\r
183         {\r
184                 /* Enough space is left at the start of the buffer to place a pointer to\r
185                 the network buffer structure that references this Ethernet buffer.\r
186                 Return a pointer to the start of the Ethernet buffer itself. */\r
187                 pucEthernetBuffer += ipBUFFER_PADDING;\r
188         }\r
189 \r
190         return pucEthernetBuffer;\r
191 }\r
192 /*-----------------------------------------------------------*/\r
193 \r
194 void vReleaseNetworkBuffer( uint8_t *pucEthernetBuffer )\r
195 {\r
196         /* There is space before the Ethernet buffer in which a pointer to the\r
197         network buffer that references this Ethernet buffer is stored.  Remove the\r
198         space before freeing the buffer. */\r
199         if( pucEthernetBuffer != NULL )\r
200         {\r
201                 pucEthernetBuffer -= ipBUFFER_PADDING;\r
202                 vPortFree( ( void * ) pucEthernetBuffer );\r
203         }\r
204 }\r
205 /*-----------------------------------------------------------*/\r
206 \r
207 NetworkBufferDescriptor_t *pxGetNetworkBufferWithDescriptor( size_t xRequestedSizeBytes, TickType_t xBlockTimeTicks )\r
208 {\r
209 NetworkBufferDescriptor_t *pxReturn = NULL;\r
210 size_t uxCount;\r
211 \r
212         if( ( xRequestedSizeBytes != 0u ) && ( xRequestedSizeBytes < ( size_t ) baMINIMAL_BUFFER_SIZE ) )\r
213         {\r
214                 /* ARP packets can replace application packets, so the storage must be\r
215                 at least large enough to hold an ARP. */\r
216                 xRequestedSizeBytes = baMINIMAL_BUFFER_SIZE;\r
217         }\r
218 \r
219         /* Add 2 bytes to xRequestedSizeBytes and round up xRequestedSizeBytes\r
220         to the nearest multiple of N bytes, where N equals 'sizeof( size_t )'. */\r
221         xRequestedSizeBytes += 2u;\r
222         if( ( xRequestedSizeBytes & ( sizeof( size_t ) - 1u ) ) != 0u )\r
223         {\r
224                 xRequestedSizeBytes = ( xRequestedSizeBytes | ( sizeof( size_t ) - 1u ) ) + 1u;\r
225         }\r
226 \r
227         /* If there is a semaphore available, there is a network buffer available. */\r
228         if( xSemaphoreTake( xNetworkBufferSemaphore, xBlockTimeTicks ) == pdPASS )\r
229         {\r
230                 /* Protect the structure as it is accessed from tasks and interrupts. */\r
231                 taskENTER_CRITICAL();\r
232                 {\r
233                         pxReturn = ( NetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &xFreeBuffersList );\r
234                         uxListRemove( &( pxReturn->xBufferListItem ) );\r
235                 }\r
236                 taskEXIT_CRITICAL();\r
237 \r
238                 /* Reading UBaseType_t, no critical section needed. */\r
239                 uxCount = listCURRENT_LIST_LENGTH( &xFreeBuffersList );\r
240 \r
241                 if( uxMinimumFreeNetworkBuffers > uxCount )\r
242                 {\r
243                         uxMinimumFreeNetworkBuffers = uxCount;\r
244                 }\r
245 \r
246                 /* Allocate storage of exactly the requested size to the buffer. */\r
247                 configASSERT( pxReturn->pucEthernetBuffer == NULL );\r
248                 if( xRequestedSizeBytes > 0 )\r
249                 {\r
250                         /* Extra space is obtained so a pointer to the network buffer can\r
251                         be stored at the beginning of the buffer. */\r
252                         pxReturn->pucEthernetBuffer = ( uint8_t * ) pvPortMalloc( xRequestedSizeBytes + ipBUFFER_PADDING );\r
253 \r
254                         if( pxReturn->pucEthernetBuffer == NULL )\r
255                         {\r
256                                 /* The attempt to allocate storage for the buffer payload failed,\r
257                                 so the network buffer structure cannot be used and must be\r
258                                 released. */\r
259                                 vReleaseNetworkBufferAndDescriptor( pxReturn );\r
260                                 pxReturn = NULL;\r
261                         }\r
262                         else\r
263                         {\r
264                                 /* Store a pointer to the network buffer structure in the\r
265                                 buffer storage area, then move the buffer pointer on past the\r
266                                 stored pointer so the pointer value is not overwritten by the\r
267                                 application when the buffer is used. */\r
268                                 *( ( NetworkBufferDescriptor_t ** ) ( pxReturn->pucEthernetBuffer ) ) = pxReturn;\r
269                                 pxReturn->pucEthernetBuffer += ipBUFFER_PADDING;\r
270 \r
271                                 /* Store the actual size of the allocated buffer, which may be\r
272                                 greater than the original requested size. */\r
273                                 pxReturn->xDataLength = xRequestedSizeBytes;\r
274 \r
275                                 #if( ipconfigUSE_LINKED_RX_MESSAGES != 0 )\r
276                                 {\r
277                                         /* make sure the buffer is not linked */\r
278                                         pxReturn->pxNextBuffer = NULL;\r
279                                 }\r
280                                 #endif /* ipconfigUSE_LINKED_RX_MESSAGES */\r
281                         }\r
282                 }\r
283                 else\r
284                 {\r
285                         /* A descriptor is being returned without an associated buffer being\r
286                         allocated. */\r
287                 }\r
288         }\r
289 \r
290         if( pxReturn == NULL )\r
291         {\r
292                 iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER();\r
293         }\r
294         else\r
295         {\r
296                 iptraceNETWORK_BUFFER_OBTAINED( pxReturn );\r
297         }\r
298 \r
299         return pxReturn;\r
300 }\r
301 /*-----------------------------------------------------------*/\r
302 \r
303 void vReleaseNetworkBufferAndDescriptor( NetworkBufferDescriptor_t * const pxNetworkBuffer )\r
304 {\r
305 BaseType_t xListItemAlreadyInFreeList;\r
306 \r
307         /* Ensure the buffer is returned to the list of free buffers before the\r
308         counting semaphore is 'given' to say a buffer is available.  Release the\r
309         storage allocated to the buffer payload.  THIS FILE SHOULD NOT BE USED\r
310         IF THE PROJECT INCLUDES A MEMORY ALLOCATOR THAT WILL FRAGMENT THE HEAP\r
311         MEMORY.  For example, heap_2 must not be used, heap_4 can be used. */\r
312         vReleaseNetworkBuffer( pxNetworkBuffer->pucEthernetBuffer );\r
313         pxNetworkBuffer->pucEthernetBuffer = NULL;\r
314 \r
315         taskENTER_CRITICAL();\r
316         {\r
317                 xListItemAlreadyInFreeList = listIS_CONTAINED_WITHIN( &xFreeBuffersList, &( pxNetworkBuffer->xBufferListItem ) );\r
318 \r
319                 if( xListItemAlreadyInFreeList == pdFALSE )\r
320                 {\r
321                         vListInsertEnd( &xFreeBuffersList, &( pxNetworkBuffer->xBufferListItem ) );\r
322                 }\r
323         }\r
324         taskEXIT_CRITICAL();\r
325 \r
326         if( xListItemAlreadyInFreeList == pdFALSE )\r
327         {\r
328                 xSemaphoreGive( xNetworkBufferSemaphore );\r
329         }\r
330 \r
331         iptraceNETWORK_BUFFER_RELEASED( pxNetworkBuffer );\r
332 }\r
333 /*-----------------------------------------------------------*/\r
334 \r
335 /*\r
336  * Returns the number of free network buffers\r
337  */\r
338 UBaseType_t uxGetNumberOfFreeNetworkBuffers( void )\r
339 {\r
340         return listCURRENT_LIST_LENGTH( &xFreeBuffersList );\r
341 }\r
342 /*-----------------------------------------------------------*/\r
343 \r
344 UBaseType_t uxGetMinimumFreeNetworkBuffers( void )\r
345 {\r
346         return uxMinimumFreeNetworkBuffers;\r
347 }\r
348 /*-----------------------------------------------------------*/\r
349 \r
350 NetworkBufferDescriptor_t *pxResizeNetworkBufferWithDescriptor( NetworkBufferDescriptor_t * pxNetworkBuffer, size_t xNewSizeBytes )\r
351 {\r
352 size_t xOriginalLength;\r
353 uint8_t *pucBuffer;\r
354 \r
355         xOriginalLength = pxNetworkBuffer->xDataLength + ipBUFFER_PADDING;\r
356         xNewSizeBytes = xNewSizeBytes + ipBUFFER_PADDING;\r
357 \r
358         pucBuffer = pucGetNetworkBuffer( &( xNewSizeBytes ) );\r
359 \r
360         if( pucBuffer == NULL )\r
361         {\r
362                 /* In case the allocation fails, return NULL. */\r
363                 pxNetworkBuffer = NULL;\r
364         }\r
365         else\r
366         {\r
367                 pxNetworkBuffer->xDataLength = xNewSizeBytes;\r
368                 if( xNewSizeBytes > xOriginalLength )\r
369                 {\r
370                         xNewSizeBytes = xOriginalLength;\r
371                 }\r
372 \r
373                 memcpy( pucBuffer - ipBUFFER_PADDING, pxNetworkBuffer->pucEthernetBuffer - ipBUFFER_PADDING, xNewSizeBytes );\r
374                 vReleaseNetworkBuffer( pxNetworkBuffer->pucEthernetBuffer );\r
375                 pxNetworkBuffer->pucEthernetBuffer = pucBuffer;\r
376         }\r
377         \r
378         return pxNetworkBuffer;\r
379 }\r
380 \r