]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/portable/BufferManagement/BufferAllocation_2.c
Added +TCP code to main repo.
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-Plus-TCP / portable / BufferManagement / BufferAllocation_2.c
1 /*\r
2  * FreeRTOS+TCP Labs Build 160919 (C) 2016 Real Time Engineers ltd.\r
3  * Authors include Hein Tibosch and Richard Barry\r
4  *\r
5  *******************************************************************************\r
6  ***** NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ***\r
7  ***                                                                         ***\r
8  ***                                                                         ***\r
9  ***   FREERTOS+TCP IS STILL IN THE LAB (mainly because the FTP and HTTP     ***\r
10  ***   demos have a dependency on FreeRTOS+FAT, which is only in the Labs    ***\r
11  ***   download):                                                            ***\r
12  ***                                                                         ***\r
13  ***   FreeRTOS+TCP is functional and has been used in commercial products   ***\r
14  ***   for some time.  Be aware however that we are still refining its       ***\r
15  ***   design, the source code does not yet quite conform to the strict      ***\r
16  ***   coding and style standards mandated by Real Time Engineers ltd., and  ***\r
17  ***   the documentation and testing is not necessarily complete.            ***\r
18  ***                                                                         ***\r
19  ***   PLEASE REPORT EXPERIENCES USING THE SUPPORT RESOURCES FOUND ON THE    ***\r
20  ***   URL: http://www.FreeRTOS.org/contact  Active early adopters may, at   ***\r
21  ***   the sole discretion of Real Time Engineers Ltd., be offered versions  ***\r
22  ***   under a license other than that described below.                      ***\r
23  ***                                                                         ***\r
24  ***                                                                         ***\r
25  ***** NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ***\r
26  *******************************************************************************\r
27  *\r
28  * FreeRTOS+TCP can be used under two different free open source licenses.  The\r
29  * license that applies is dependent on the processor on which FreeRTOS+TCP is\r
30  * executed, as follows:\r
31  *\r
32  * If FreeRTOS+TCP is executed on one of the processors listed under the Special\r
33  * License Arrangements heading of the FreeRTOS+TCP license information web\r
34  * page, then it can be used under the terms of the FreeRTOS Open Source\r
35  * License.  If FreeRTOS+TCP is used on any other processor, then it can be used\r
36  * under the terms of the GNU General Public License V2.  Links to the relevant\r
37  * licenses follow:\r
38  *\r
39  * The FreeRTOS+TCP License Information Page: http://www.FreeRTOS.org/tcp_license\r
40  * The FreeRTOS Open Source License: http://www.FreeRTOS.org/license\r
41  * The GNU General Public License Version 2: http://www.FreeRTOS.org/gpl-2.0.txt\r
42  *\r
43  * FreeRTOS+TCP is distributed in the hope that it will be useful.  You cannot\r
44  * use FreeRTOS+TCP unless you agree that you use the software 'as is'.\r
45  * FreeRTOS+TCP is provided WITHOUT ANY WARRANTY; without even the implied\r
46  * warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR\r
47  * PURPOSE. Real Time Engineers Ltd. disclaims all conditions and terms, be they\r
48  * implied, expressed, or statutory.\r
49  *\r
50  * 1 tab == 4 spaces!\r
51  *\r
52  * http://www.FreeRTOS.org\r
53  * http://www.FreeRTOS.org/plus\r
54  * http://www.FreeRTOS.org/labs\r
55  *\r
56  */\r
57 \r
58 /******************************************************************************\r
59  *\r
60  * See the following web page for essential buffer allocation scheme usage and\r
61  * configuration details:\r
62  * http://www.FreeRTOS.org/FreeRTOS-Plus/FreeRTOS_Plus_TCP/Embedded_Ethernet_Buffer_Management.html\r
63  *\r
64  ******************************************************************************/\r
65 \r
66 /* THIS FILE SHOULD NOT BE USED IF THE PROJECT INCLUDES A MEMORY ALLOCATOR\r
67 THAT WILL FRAGMENT THE HEAP MEMORY.  For example, heap_2 must not be used,\r
68 heap_4 can be used. */\r
69 \r
70 \r
71 /* Standard includes. */\r
72 #include <stdint.h>\r
73 \r
74 /* FreeRTOS includes. */\r
75 #include "FreeRTOS.h"\r
76 #include "task.h"\r
77 #include "semphr.h"\r
78 \r
79 /* FreeRTOS+TCP includes. */\r
80 #include "FreeRTOS_IP.h"\r
81 #include "FreeRTOS_UDP_IP.h"\r
82 #include "FreeRTOS_IP_Private.h"\r
83 #include "NetworkInterface.h"\r
84 #include "NetworkBufferManagement.h"\r
85 \r
86 /* The obtained network buffer must be large enough to hold a packet that might\r
87 replace the packet that was requested to be sent. */\r
88 #if ipconfigUSE_TCP == 1\r
89         #define baMINIMAL_BUFFER_SIZE           sizeof( TCPPacket_t )\r
90 #else\r
91         #define baMINIMAL_BUFFER_SIZE           sizeof( ARPPacket_t )\r
92 #endif /* ipconfigUSE_TCP == 1 */\r
93 \r
94 /*_RB_ This is too complex not to have an explanation. */\r
95 #if defined( ipconfigETHERNET_MINIMUM_PACKET_BYTES )\r
96         #define ASSERT_CONCAT_(a, b) a##b\r
97         #define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b)\r
98         #define STATIC_ASSERT(e) \\r
99                 ;enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(!!(e)) }\r
100 \r
101         STATIC_ASSERT( ipconfigETHERNET_MINIMUM_PACKET_BYTES <= baMINIMAL_BUFFER_SIZE );\r
102 #endif\r
103 \r
104 /* A list of free (available) NetworkBufferDescriptor_t structures. */\r
105 static List_t xFreeBuffersList;\r
106 \r
107 /* Some statistics about the use of buffers. */\r
108 static size_t uxMinimumFreeNetworkBuffers;\r
109 \r
110 /* Declares the pool of NetworkBufferDescriptor_t structures that are available\r
111 to the system.  All the network buffers referenced from xFreeBuffersList exist\r
112 in this array.  The array is not accessed directly except during initialisation,\r
113 when the xFreeBuffersList is filled (as all the buffers are free when the system\r
114 is booted). */\r
115 static NetworkBufferDescriptor_t xNetworkBufferDescriptors[ ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS ];\r
116 \r
117 /* This constant is defined as false to let FreeRTOS_TCP_IP.c know that the\r
118 network buffers have a variable size: resizing may be necessary */\r
119 const BaseType_t xBufferAllocFixedSize = pdFALSE;\r
120 \r
121 /* The semaphore used to obtain network buffers. */\r
122 static SemaphoreHandle_t xNetworkBufferSemaphore = NULL;\r
123 \r
124 /*-----------------------------------------------------------*/\r
125 \r
126 BaseType_t xNetworkBuffersInitialise( void )\r
127 {\r
128 BaseType_t xReturn, x;\r
129 \r
130         /* Only initialise the buffers and their associated kernel objects if they\r
131         have not been initialised before. */\r
132         if( xNetworkBufferSemaphore == NULL )\r
133         {\r
134                 xNetworkBufferSemaphore = xSemaphoreCreateCounting( ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS, ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS );\r
135                 configASSERT( xNetworkBufferSemaphore );\r
136                 #if ( configQUEUE_REGISTRY_SIZE > 0 )\r
137                 {\r
138                         vQueueAddToRegistry( xNetworkBufferSemaphore, "NetBufSem" );\r
139                 }\r
140                 #endif /* configQUEUE_REGISTRY_SIZE */\r
141 \r
142                 /* If the trace recorder code is included name the semaphore for viewing\r
143                 in FreeRTOS+Trace.  */\r
144                 #if( ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS == 1 )\r
145                 {\r
146                         extern QueueHandle_t xNetworkEventQueue;\r
147                         vTraceSetQueueName( xNetworkEventQueue, "IPStackEvent" );\r
148                         vTraceSetQueueName( xNetworkBufferSemaphore, "NetworkBufferCount" );\r
149                 }\r
150                 #endif /*  ipconfigINCLUDE_EXAMPLE_FREERTOS_PLUS_TRACE_CALLS == 1 */\r
151 \r
152                 if( xNetworkBufferSemaphore != NULL )\r
153                 {\r
154                         vListInitialise( &xFreeBuffersList );\r
155 \r
156                         /* Initialise all the network buffers.  No storage is allocated to\r
157                         the buffers yet. */\r
158                         for( x = 0; x < ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS; x++ )\r
159                         {\r
160                                 /* Initialise and set the owner of the buffer list items. */\r
161                                 xNetworkBufferDescriptors[ x ].pucEthernetBuffer = NULL;\r
162                                 vListInitialiseItem( &( xNetworkBufferDescriptors[ x ].xBufferListItem ) );\r
163                                 listSET_LIST_ITEM_OWNER( &( xNetworkBufferDescriptors[ x ].xBufferListItem ), &xNetworkBufferDescriptors[ x ] );\r
164 \r
165                                 /* Currently, all buffers are available for use. */\r
166                                 vListInsert( &xFreeBuffersList, &( xNetworkBufferDescriptors[ x ].xBufferListItem ) );\r
167                         }\r
168 \r
169                         uxMinimumFreeNetworkBuffers = ipconfigNUM_NETWORK_BUFFER_DESCRIPTORS;\r
170                 }\r
171         }\r
172 \r
173         if( xNetworkBufferSemaphore == NULL )\r
174         {\r
175                 xReturn = pdFAIL;\r
176         }\r
177         else\r
178         {\r
179                 xReturn = pdPASS;\r
180         }\r
181 \r
182         return xReturn;\r
183 }\r
184 /*-----------------------------------------------------------*/\r
185 \r
186 uint8_t *pucGetNetworkBuffer( size_t *pxRequestedSizeBytes )\r
187 {\r
188 uint8_t *pucEthernetBuffer;\r
189 size_t xSize = *pxRequestedSizeBytes;\r
190 \r
191         if( xSize < baMINIMAL_BUFFER_SIZE )\r
192         {\r
193                 /* Buffers must be at least large enough to hold a TCP-packet with\r
194                 headers, or an ARP packet, in case TCP is not included. */\r
195                 xSize = baMINIMAL_BUFFER_SIZE;\r
196         }\r
197 \r
198         /* Round up xSize to the nearest multiple of N bytes,\r
199         where N equals 'sizeof( size_t )'. */\r
200         if( ( xSize & ( sizeof( size_t ) - 1u ) ) != 0u )\r
201         {\r
202                 xSize = ( xSize | ( sizeof( size_t ) - 1u ) ) + 1u;\r
203         }\r
204         *pxRequestedSizeBytes = xSize;\r
205 \r
206         /* Allocate a buffer large enough to store the requested Ethernet frame size\r
207         and a pointer to a network buffer structure (hence the addition of\r
208         ipBUFFER_PADDING bytes). */\r
209         pucEthernetBuffer = ( uint8_t * ) pvPortMalloc( xSize + ipBUFFER_PADDING );\r
210         configASSERT( pucEthernetBuffer );\r
211 \r
212         if( pucEthernetBuffer != NULL )\r
213         {\r
214                 /* Enough space is left at the start of the buffer to place a pointer to\r
215                 the network buffer structure that references this Ethernet buffer.\r
216                 Return a pointer to the start of the Ethernet buffer itself. */\r
217                 pucEthernetBuffer += ipBUFFER_PADDING;\r
218         }\r
219 \r
220         return pucEthernetBuffer;\r
221 }\r
222 /*-----------------------------------------------------------*/\r
223 \r
224 void vReleaseNetworkBuffer( uint8_t *pucEthernetBuffer )\r
225 {\r
226         /* There is space before the Ethernet buffer in which a pointer to the\r
227         network buffer that references this Ethernet buffer is stored.  Remove the\r
228         space before freeing the buffer. */\r
229         if( pucEthernetBuffer != NULL )\r
230         {\r
231                 pucEthernetBuffer -= ipBUFFER_PADDING;\r
232                 vPortFree( ( void * ) pucEthernetBuffer );\r
233         }\r
234 }\r
235 /*-----------------------------------------------------------*/\r
236 \r
237 NetworkBufferDescriptor_t *pxGetNetworkBufferWithDescriptor( size_t xRequestedSizeBytes, TickType_t xBlockTimeTicks )\r
238 {\r
239 NetworkBufferDescriptor_t *pxReturn = NULL;\r
240 size_t uxCount;\r
241 \r
242         if( ( xRequestedSizeBytes != 0u ) && ( xRequestedSizeBytes < ( size_t ) baMINIMAL_BUFFER_SIZE ) )\r
243         {\r
244                 /* ARP packets can replace application packets, so the storage must be\r
245                 at least large enough to hold an ARP. */\r
246                 xRequestedSizeBytes = baMINIMAL_BUFFER_SIZE;\r
247         }\r
248 \r
249         /* Add 2 bytes to xRequestedSizeBytes and round up xRequestedSizeBytes\r
250         to the nearest multiple of N bytes, where N equals 'sizeof( size_t )'. */\r
251         xRequestedSizeBytes += 2u;\r
252         if( ( xRequestedSizeBytes & ( sizeof( size_t ) - 1u ) ) != 0u )\r
253         {\r
254                 xRequestedSizeBytes = ( xRequestedSizeBytes | ( sizeof( size_t ) - 1u ) ) + 1u;\r
255         }\r
256 \r
257         /* If there is a semaphore available, there is a network buffer available. */\r
258         if( xSemaphoreTake( xNetworkBufferSemaphore, xBlockTimeTicks ) == pdPASS )\r
259         {\r
260                 /* Protect the structure as it is accessed from tasks and interrupts. */\r
261                 taskENTER_CRITICAL();\r
262                 {\r
263                         pxReturn = ( NetworkBufferDescriptor_t * ) listGET_OWNER_OF_HEAD_ENTRY( &xFreeBuffersList );\r
264                         uxListRemove( &( pxReturn->xBufferListItem ) );\r
265                 }\r
266                 taskEXIT_CRITICAL();\r
267 \r
268                 /* Reading UBaseType_t, no critical section needed. */\r
269                 uxCount = listCURRENT_LIST_LENGTH( &xFreeBuffersList );\r
270 \r
271                 if( uxMinimumFreeNetworkBuffers > uxCount )\r
272                 {\r
273                         uxMinimumFreeNetworkBuffers = uxCount;\r
274                 }\r
275 \r
276                 /* Allocate storage of exactly the requested size to the buffer. */\r
277                 configASSERT( pxReturn->pucEthernetBuffer == NULL );\r
278                 if( xRequestedSizeBytes > 0 )\r
279                 {\r
280                         /* Extra space is obtained so a pointer to the network buffer can\r
281                         be stored at the beginning of the buffer. */\r
282                         pxReturn->pucEthernetBuffer = ( uint8_t * ) pvPortMalloc( xRequestedSizeBytes + ipBUFFER_PADDING );\r
283 \r
284                         if( pxReturn->pucEthernetBuffer == NULL )\r
285                         {\r
286                                 /* The attempt to allocate storage for the buffer payload failed,\r
287                                 so the network buffer structure cannot be used and must be\r
288                                 released. */\r
289                                 vReleaseNetworkBufferAndDescriptor( pxReturn );\r
290                                 pxReturn = NULL;\r
291                         }\r
292                         else\r
293                         {\r
294                                 /* Store a pointer to the network buffer structure in the\r
295                                 buffer storage area, then move the buffer pointer on past the\r
296                                 stored pointer so the pointer value is not overwritten by the\r
297                                 application when the buffer is used. */\r
298                                 *( ( NetworkBufferDescriptor_t ** ) ( pxReturn->pucEthernetBuffer ) ) = pxReturn;\r
299                                 pxReturn->pucEthernetBuffer += ipBUFFER_PADDING;\r
300 \r
301                                 /* Store the actual size of the allocated buffer, which may be\r
302                                 greater than the original requested size. */\r
303                                 pxReturn->xDataLength = xRequestedSizeBytes;\r
304 \r
305                                 #if( ipconfigUSE_LINKED_RX_MESSAGES != 0 )\r
306                                 {\r
307                                         /* make sure the buffer is not linked */\r
308                                         pxReturn->pxNextBuffer = NULL;\r
309                                 }\r
310                                 #endif /* ipconfigUSE_LINKED_RX_MESSAGES */\r
311                         }\r
312                 }\r
313                 else\r
314                 {\r
315                         /* A descriptor is being returned without an associated buffer being\r
316                         allocated. */\r
317                 }\r
318         }\r
319 \r
320         if( pxReturn == NULL )\r
321         {\r
322                 iptraceFAILED_TO_OBTAIN_NETWORK_BUFFER();\r
323         }\r
324         else\r
325         {\r
326                 iptraceNETWORK_BUFFER_OBTAINED( pxReturn );\r
327         }\r
328 \r
329         return pxReturn;\r
330 }\r
331 /*-----------------------------------------------------------*/\r
332 \r
333 void vReleaseNetworkBufferAndDescriptor( NetworkBufferDescriptor_t * const pxNetworkBuffer )\r
334 {\r
335 BaseType_t xListItemAlreadyInFreeList;\r
336 \r
337         /* Ensure the buffer is returned to the list of free buffers before the\r
338         counting semaphore is 'given' to say a buffer is available.  Release the\r
339         storage allocated to the buffer payload.  THIS FILE SHOULD NOT BE USED\r
340         IF THE PROJECT INCLUDES A MEMORY ALLOCATOR THAT WILL FRAGMENT THE HEAP\r
341         MEMORY.  For example, heap_2 must not be used, heap_4 can be used. */\r
342         vReleaseNetworkBuffer( pxNetworkBuffer->pucEthernetBuffer );\r
343         pxNetworkBuffer->pucEthernetBuffer = NULL;\r
344 \r
345         taskENTER_CRITICAL();\r
346         {\r
347                 xListItemAlreadyInFreeList = listIS_CONTAINED_WITHIN( &xFreeBuffersList, &( pxNetworkBuffer->xBufferListItem ) );\r
348 \r
349                 if( xListItemAlreadyInFreeList == pdFALSE )\r
350                 {\r
351                         vListInsertEnd( &xFreeBuffersList, &( pxNetworkBuffer->xBufferListItem ) );\r
352                 }\r
353         }\r
354         taskEXIT_CRITICAL();\r
355 \r
356         if( xListItemAlreadyInFreeList == pdFALSE )\r
357         {\r
358                 xSemaphoreGive( xNetworkBufferSemaphore );\r
359         }\r
360 \r
361         iptraceNETWORK_BUFFER_RELEASED( pxNetworkBuffer );\r
362 }\r
363 /*-----------------------------------------------------------*/\r
364 \r
365 /*\r
366  * Returns the number of free network buffers\r
367  */\r
368 UBaseType_t uxGetNumberOfFreeNetworkBuffers( void )\r
369 {\r
370         return listCURRENT_LIST_LENGTH( &xFreeBuffersList );\r
371 }\r
372 /*-----------------------------------------------------------*/\r
373 \r
374 UBaseType_t uxGetMinimumFreeNetworkBuffers( void )\r
375 {\r
376         return uxMinimumFreeNetworkBuffers;\r
377 }\r
378 /*-----------------------------------------------------------*/\r
379 \r
380 NetworkBufferDescriptor_t *pxResizeNetworkBufferWithDescriptor( NetworkBufferDescriptor_t * pxNetworkBuffer, size_t xNewSizeBytes )\r
381 {\r
382 size_t xOriginalLength;\r
383 uint8_t *pucBuffer;\r
384 \r
385         xOriginalLength = pxNetworkBuffer->xDataLength + ipBUFFER_PADDING;\r
386         xNewSizeBytes = xNewSizeBytes + ipBUFFER_PADDING;\r
387 \r
388         pucBuffer = pucGetNetworkBuffer( &( xNewSizeBytes ) );\r
389 \r
390         if( pucBuffer == NULL )\r
391         {\r
392                 /* In case the allocation fails, return NULL. */\r
393                 pxNetworkBuffer = NULL;\r
394         }\r
395         else\r
396         {\r
397                 pxNetworkBuffer->xDataLength = xNewSizeBytes;\r
398                 if( xNewSizeBytes > xOriginalLength )\r
399                 {\r
400                         xNewSizeBytes = xOriginalLength;\r
401                 }\r
402 \r
403                 memcpy( pucBuffer - ipBUFFER_PADDING, pxNetworkBuffer->pucEthernetBuffer - ipBUFFER_PADDING, xNewSizeBytes );\r
404                 vReleaseNetworkBuffer( pxNetworkBuffer->pucEthernetBuffer );\r
405                 pxNetworkBuffer->pucEthernetBuffer = pucBuffer;\r
406         }\r
407         \r
408         return pxNetworkBuffer;\r
409 }\r
410 \r