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 /**
29 * @file FreeRTOS_Stream_Buffer.c
30 * @brief Provides the API for managing/creating the stream buffers in the FreeRTOS+TCP network stack.
31 */
32
33 /* Standard includes. */
34 #include <stdint.h>
35
36 /* FreeRTOS includes. */
37 #include "FreeRTOS.h"
38 #include "task.h"
39 #include "semphr.h"
40
41 /* FreeRTOS+TCP includes. */
42 #include "FreeRTOS_IP.h"
43 #include "FreeRTOS_UDP_IP.h"
44 #include "FreeRTOS_Sockets.h"
45 #include "FreeRTOS_IP_Private.h"
46
47
48 /**
49 * @brief Get the space between lower and upper value provided to the function.
50 * @param[in] pxBuffer The circular stream buffer.
51 * @param[in] uxLower The lower value.
52 * @param[in] uxUpper The upper value.
53 * @return The space between uxLower and uxUpper, which equals to the distance
54 * minus 1.
55 */
uxStreamBufferSpace(const StreamBuffer_t * pxBuffer,const size_t uxLower,const size_t uxUpper)56 size_t uxStreamBufferSpace( const StreamBuffer_t * pxBuffer,
57 const size_t uxLower,
58 const size_t uxUpper )
59 {
60 size_t uxCount;
61
62 uxCount = pxBuffer->LENGTH + uxUpper - uxLower - 1U;
63
64 if( uxCount >= pxBuffer->LENGTH )
65 {
66 uxCount -= pxBuffer->LENGTH;
67 }
68
69 return uxCount;
70 }
71
72 /**
73 * @brief Get the distance between lower and upper value provided to the function.
74 * @param[in] pxBuffer The circular stream buffer.
75 * @param[in] uxLower The lower value.
76 * @param[in] uxUpper The upper value.
77 * @return The distance between uxLower and uxUpper.
78 */
uxStreamBufferDistance(const StreamBuffer_t * pxBuffer,const size_t uxLower,const size_t uxUpper)79 size_t uxStreamBufferDistance( const StreamBuffer_t * pxBuffer,
80 const size_t uxLower,
81 const size_t uxUpper )
82 {
83 size_t uxCount;
84
85 uxCount = pxBuffer->LENGTH + uxUpper - uxLower;
86
87 if( uxCount >= pxBuffer->LENGTH )
88 {
89 uxCount -= pxBuffer->LENGTH;
90 }
91
92 return uxCount;
93 }
94
95 /**
96 * @brief Get the number of items which can be added to the buffer at
97 * the head before reaching the tail.
98 * @param[in] pxBuffer The circular stream buffer.
99 * @return The number of items which can still be added to uxHead
100 * before hitting on uxTail
101 */
uxStreamBufferGetSpace(const StreamBuffer_t * pxBuffer)102 size_t uxStreamBufferGetSpace( const StreamBuffer_t * pxBuffer )
103 {
104 size_t uxHead = pxBuffer->uxHead;
105 size_t uxTail = pxBuffer->uxTail;
106
107 return uxStreamBufferSpace( pxBuffer, uxHead, uxTail );
108 }
109 /*-----------------------------------------------------------*/
110
111 /**
112 * @brief Get the distance between the pointer in free space and the tail.
113 * @param[in] pxBuffer The circular stream buffer.
114 * @return Distance between uxFront and uxTail or the number of items
115 * which can still be added to uxFront, before hitting on uxTail.
116 */
uxStreamBufferFrontSpace(const StreamBuffer_t * pxBuffer)117 size_t uxStreamBufferFrontSpace( const StreamBuffer_t * pxBuffer )
118 {
119 size_t uxFront = pxBuffer->uxFront;
120 size_t uxTail = pxBuffer->uxTail;
121
122 return uxStreamBufferSpace( pxBuffer, uxFront, uxTail );
123 }
124 /*-----------------------------------------------------------*/
125
126 /**
127 * @brief Get the number of items which can be read from the tail before
128 * reaching the head.
129 * @param[in] pxBuffer The circular stream buffer.
130 * @return The number of items which can be read from the tail before
131 * reaching the head.
132 */
uxStreamBufferGetSize(const StreamBuffer_t * pxBuffer)133 size_t uxStreamBufferGetSize( const StreamBuffer_t * pxBuffer )
134 {
135 size_t uxHead = pxBuffer->uxHead;
136 size_t uxTail = pxBuffer->uxTail;
137
138 return uxStreamBufferDistance( pxBuffer, uxTail, uxHead );
139 }
140 /*-----------------------------------------------------------*/
141
142 /**
143 * @brief Get the space between the mid pointer and the head in the stream
144 * buffer.
145 * @param[in] pxBuffer The circular stream buffer.
146 * @return The space between the mid pointer and the head.
147 */
uxStreamBufferMidSpace(const StreamBuffer_t * pxBuffer)148 size_t uxStreamBufferMidSpace( const StreamBuffer_t * pxBuffer )
149 {
150 size_t uxHead = pxBuffer->uxHead;
151 size_t uxMid = pxBuffer->uxMid;
152
153 return uxStreamBufferDistance( pxBuffer, uxMid, uxHead );
154 }
155 /*-----------------------------------------------------------*/
156
157 /**
158 * @brief Move Clear the stream buffer.
159 * @param[in] pxBuffer The circular stream buffer.
160 */
vStreamBufferClear(StreamBuffer_t * pxBuffer)161 void vStreamBufferClear( StreamBuffer_t * pxBuffer )
162 {
163 /* Make the circular buffer empty */
164 pxBuffer->uxHead = 0U;
165 pxBuffer->uxTail = 0U;
166 pxBuffer->uxFront = 0U;
167 pxBuffer->uxMid = 0U;
168 }
169
170 /*-----------------------------------------------------------*/
171
172 /**
173 * @brief Move the mid pointer forward by given byte count
174 * @param[in] pxBuffer The circular stream buffer.
175 * @param[in] uxCount The byte count by which the mid pointer is to be moved.
176 */
vStreamBufferMoveMid(StreamBuffer_t * pxBuffer,size_t uxCount)177 void vStreamBufferMoveMid( StreamBuffer_t * pxBuffer,
178 size_t uxCount )
179 {
180 /* Increment uxMid, but no further than uxHead */
181 size_t uxSize = uxStreamBufferMidSpace( pxBuffer );
182 size_t uxMid = pxBuffer->uxMid;
183 size_t uxMoveCount = uxCount;
184
185 if( uxMoveCount > uxSize )
186 {
187 uxMoveCount = uxSize;
188 }
189
190 uxMid += uxMoveCount;
191
192 if( uxMid >= pxBuffer->LENGTH )
193 {
194 uxMid -= pxBuffer->LENGTH;
195 }
196
197 pxBuffer->uxMid = uxMid;
198 }
199 /*-----------------------------------------------------------*/
200
201 /**
202 * @brief Check whether the value in left is less than or equal to the
203 * value in right from the perspective of the circular stream
204 * buffer.
205 * @param[in] pxBuffer The circular stream buffer.
206 * @param[in] uxLeft The left pointer in the stream buffer.
207 * @param[in] uxRight The right value pointer in the stream buffer.
208 * @return pdTRUE if uxLeft <= uxRight, else pdFALSE.
209 */
xStreamBufferLessThenEqual(const StreamBuffer_t * pxBuffer,const size_t uxLeft,const size_t uxRight)210 BaseType_t xStreamBufferLessThenEqual( const StreamBuffer_t * pxBuffer,
211 const size_t uxLeft,
212 const size_t uxRight )
213 {
214 BaseType_t xReturn = pdFALSE;
215 size_t uxTail = pxBuffer->uxTail;
216
217 /* Returns true if ( uxLeft <= uxRight ) */
218 if( ( uxLeft - uxTail ) <= ( uxRight - uxTail ) )
219 {
220 xReturn = pdTRUE;
221 }
222
223 return xReturn;
224 }
225 /*-----------------------------------------------------------*/
226
227 /**
228 * @brief Get the pointer to data and the amount of data which can be read in one go.
229 *
230 * @param[in] pxBuffer The circular stream buffer.
231 * @param[out] ppucData Pointer to the data pointer which will point to the
232 * data which can be read.
233 *
234 * @return The number of bytes which can be read in one go (which might be less than
235 * actual number of available bytes since this is a circular buffer and tail
236 * can loop back to the start of the buffer).
237 */
uxStreamBufferGetPtr(StreamBuffer_t * pxBuffer,uint8_t ** ppucData)238 size_t uxStreamBufferGetPtr( StreamBuffer_t * pxBuffer,
239 uint8_t ** ppucData )
240 {
241 size_t uxNextTail = pxBuffer->uxTail;
242 size_t uxSize = uxStreamBufferGetSize( pxBuffer );
243
244 *ppucData = pxBuffer->ucArray + uxNextTail;
245
246 return FreeRTOS_min_size_t( uxSize, pxBuffer->LENGTH - uxNextTail );
247 }
248 /*-----------------------------------------------------------*/
249
250 /**
251 * @brief Adds data to a stream buffer.
252 *
253 * @param[in,out] pxBuffer The buffer to which the bytes will be added.
254 * @param[in] uxOffset If uxOffset > 0, data will be written at an offset from uxHead
255 * while uxHead will not be moved yet.
256 * @param[in] pucData A pointer to the data to be added. If 'pucData' equals NULL,
257 * the function is called to advance the 'Head' only.
258 * @param[in] uxByteCount The number of bytes to add.
259 *
260 * @return The number of bytes added to the buffer.
261 */
uxStreamBufferAdd(StreamBuffer_t * pxBuffer,size_t uxOffset,const uint8_t * pucData,size_t uxByteCount)262 size_t uxStreamBufferAdd( StreamBuffer_t * pxBuffer,
263 size_t uxOffset,
264 const uint8_t * pucData,
265 size_t uxByteCount )
266 {
267 size_t uxSpace, uxNextHead, uxFirst;
268 size_t uxCount = uxByteCount;
269
270 uxSpace = uxStreamBufferGetSpace( pxBuffer );
271
272 /* If uxOffset > 0, items can be placed in front of uxHead */
273 if( uxSpace > uxOffset )
274 {
275 uxSpace -= uxOffset;
276 }
277 else
278 {
279 uxSpace = 0U;
280 }
281
282 /* The number of bytes that can be written is the minimum of the number of
283 * bytes requested and the number available. */
284 uxCount = FreeRTOS_min_size_t( uxSpace, uxCount );
285
286 if( uxCount != 0U )
287 {
288 uxNextHead = pxBuffer->uxHead;
289
290 if( uxOffset != 0U )
291 {
292 /* ( uxOffset > 0 ) means: write in front if the uxHead marker */
293 uxNextHead += uxOffset;
294
295 if( uxNextHead >= pxBuffer->LENGTH )
296 {
297 uxNextHead -= pxBuffer->LENGTH;
298 }
299 }
300
301 if( pucData != NULL )
302 {
303 /* Calculate the number of bytes that can be added in the first
304 * write - which may be less than the total number of bytes that need
305 * to be added if the buffer will wrap back to the beginning. */
306 uxFirst = FreeRTOS_min_size_t( pxBuffer->LENGTH - uxNextHead, uxCount );
307
308 /* Write as many bytes as can be written in the first write. */
309 ( void ) memcpy( &( pxBuffer->ucArray[ uxNextHead ] ), pucData, uxFirst );
310
311 /* If the number of bytes written was less than the number that
312 * could be written in the first write... */
313 if( uxCount > uxFirst )
314 {
315 /* ...then write the remaining bytes to the start of the
316 * buffer. */
317 ( void ) memcpy( pxBuffer->ucArray, &( pucData[ uxFirst ] ), uxCount - uxFirst );
318 }
319 }
320
321 /* The below update to the stream buffer members must happen
322 * atomically. */
323 vTaskSuspendAll();
324 {
325 if( uxOffset == 0U )
326 {
327 /* ( uxOffset == 0 ) means: write at uxHead position */
328 uxNextHead += uxCount;
329
330 if( uxNextHead >= pxBuffer->LENGTH )
331 {
332 uxNextHead -= pxBuffer->LENGTH;
333 }
334
335 pxBuffer->uxHead = uxNextHead;
336 }
337
338 if( xStreamBufferLessThenEqual( pxBuffer, pxBuffer->uxFront, uxNextHead ) != pdFALSE )
339 {
340 /* Advance the front pointer */
341 pxBuffer->uxFront = uxNextHead;
342 }
343 }
344 ( void ) xTaskResumeAll();
345 }
346
347 return uxCount;
348 }
349 /*-----------------------------------------------------------*/
350
351 /**
352 * @brief Read bytes from stream buffer.
353 *
354 * @param[in] pxBuffer The buffer from which the bytes will be read.
355 * @param[in] uxOffset can be used to read data located at a certain offset from 'lTail'.
356 * @param[in,out] pucData If 'pucData' equals NULL, the function is called to advance 'lTail' only.
357 * @param[in] uxMaxCount The number of bytes to read.
358 * @param[in] xPeek if 'xPeek' is pdTRUE, or if 'uxOffset' is non-zero, the 'lTail' pointer will
359 * not be advanced.
360 *
361 * @return The count of the bytes read.
362 */
uxStreamBufferGet(StreamBuffer_t * pxBuffer,size_t uxOffset,uint8_t * pucData,size_t uxMaxCount,BaseType_t xPeek)363 size_t uxStreamBufferGet( StreamBuffer_t * pxBuffer,
364 size_t uxOffset,
365 uint8_t * pucData,
366 size_t uxMaxCount,
367 BaseType_t xPeek )
368 {
369 size_t uxSize, uxCount, uxFirst, uxNextTail;
370
371 /* How much data is available? */
372 uxSize = uxStreamBufferGetSize( pxBuffer );
373
374 if( uxSize > uxOffset )
375 {
376 uxSize -= uxOffset;
377 }
378 else
379 {
380 uxSize = 0U;
381 }
382
383 /* Use the minimum of the wanted bytes and the available bytes. */
384 uxCount = FreeRTOS_min_size_t( uxSize, uxMaxCount );
385
386 if( uxCount > 0U )
387 {
388 uxNextTail = pxBuffer->uxTail;
389
390 if( uxOffset != 0U )
391 {
392 uxNextTail += uxOffset;
393
394 if( uxNextTail >= pxBuffer->LENGTH )
395 {
396 uxNextTail -= pxBuffer->LENGTH;
397 }
398 }
399
400 if( pucData != NULL )
401 {
402 /* Calculate the number of bytes that can be read - which may be
403 * less than the number wanted if the data wraps around to the start of
404 * the buffer. */
405 uxFirst = FreeRTOS_min_size_t( pxBuffer->LENGTH - uxNextTail, uxCount );
406
407 /* Obtain the number of bytes it is possible to obtain in the first
408 * read. */
409 ( void ) memcpy( pucData, &( pxBuffer->ucArray[ uxNextTail ] ), uxFirst );
410
411 /* If the total number of wanted bytes is greater than the number
412 * that could be read in the first read... */
413 if( uxCount > uxFirst )
414 {
415 /*...then read the remaining bytes from the start of the buffer. */
416 ( void ) memcpy( &( pucData[ uxFirst ] ), pxBuffer->ucArray, uxCount - uxFirst );
417 }
418 }
419
420 if( ( xPeek == pdFALSE ) && ( uxOffset == 0U ) )
421 {
422 /* Move the tail pointer to effectively remove the data read from
423 * the buffer. */
424 uxNextTail += uxCount;
425
426 if( uxNextTail >= pxBuffer->LENGTH )
427 {
428 uxNextTail -= pxBuffer->LENGTH;
429 }
430
431 pxBuffer->uxTail = uxNextTail;
432 }
433 }
434
435 return uxCount;
436 }
437