/**
******************************************************************************
* @file stm_queue.c
* @author MCD Application Team
* @brief Queue management
******************************************************************************
* @attention
*
*
© Copyright (c) 2019 STMicroelectronics.
* All rights reserved.
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
/* Includes ------------------------------------------------------------------*/
#include "utilities_common.h"
#include "stm_queue.h"
/* Private define ------------------------------------------------------------*/
/* Private typedef -------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
#define MOD(X,Y) (((X) >= (Y)) ? ((X)-(Y)) : (X))
/* Private variables ---------------------------------------------------------*/
/* Global variables ----------------------------------------------------------*/
/* Extern variables ----------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/* Public functions ----------------------------------------------------------*/
/**
* @brief Initilaiilze queue strcuture .
* @note This function is used to initialize the global queue strcuture.
* @param q: pointer on queue strcture to be initialised
* @param queueBuffer: pointer on Queue Buffer
* @param queueSize: Size of Queue Buffer
* @param elementSize: Size of an element in the queue. if =0, the queue will manage variable sizze elements
* @retval always 0
*/
int CircularQueue_Init(queue_t *q, uint8_t* queueBuffer, uint32_t queueSize, uint16_t elementSize, uint8_t optionFlags)
{
q->qBuff = queueBuffer;
q->first = 0;
q->last = 0; /* queueSize-1; */
q->byteCount = 0;
q->elementCount = 0;
q->queueMaxSize = queueSize;
q->elementSize = elementSize;
q->optionFlags = optionFlags;
if ((optionFlags & CIRCULAR_QUEUE_SPLIT_IF_WRAPPING_FLAG) && q-> elementSize)
{
/* can not deal with splitting at the end of buffer with fixed size element */
return -1;
}
return 0;
}
/**
* @brief Add element to the queue .
* @note This function is used to add one or more element(s) to the Circular Queue .
* @param q: pointer on queue structure to be handled
* @param X; pointer on element(s) to be added
* @param elementSize: Size of element to be added to the queue. Only used if the queue manage variable size elements
* @param nbElements: number of elements in the in buffer pointed by x
* @retval pointer on last element just added to the queue, NULL if the element to be added do not fit in the queue (too big)
*/
uint8_t* CircularQueue_Add(queue_t *q, uint8_t* x, uint16_t elementSize, uint32_t nbElements)
{
uint8_t* ptr = NULL; /* fct return ptr to the element freshly added, if no room fct return NULL */
uint16_t curElementSize = 0; /* the size of the element currently stored at q->last position */
uint8_t elemSizeStorageRoom = 0 ; /* Indicate the header (which contain only size) of element in case of varaibale size elemenet (q->elementsize == 0) */
uint32_t curBuffPosition; /* the current position in the queue buffer */
uint32_t i; /* loop counter */
uint32_t NbBytesToCopy = 0, NbCopiedBytes = 0 ; /* Indicators for copying bytes in queue */
uint32_t eob_free_size; /* Eof End of Quque Buffer Free Size */
uint8_t wrap_will_occur = 0; /* indicate if a wrap around will occurs */
uint8_t wrapped_element_eob_size; /* In case of Wrap around, indicat size of parta of elemenet that fit at thened of the queuue buffer */
uint16_t overhead = 0; /* In case of CIRCULAR_QUEUE_SPLIT_IF_WRAPPING_FLAG or CIRCULAR_QUEUE_NO_WRAP_FLAG options,
indcate the size overhead that will be generated by adding the element with wrap management (split or no wrap ) */
elemSizeStorageRoom = (q->elementSize == 0) ? 2 : 0;
/* retrieve the size of last element sored: the value stored at the beginning of the queue element if element size is variable otherwise take it from fixed element Size member */
if (q->byteCount)
{
curElementSize = (q->elementSize == 0) ? q->qBuff[q->last] + ((q->qBuff[MOD((q->last+1), q->queueMaxSize)])<<8) + 2 : q->elementSize;
}
/* if queue element have fixed size , reset the elementSize arg with fixed element size value */
if (q->elementSize > 0)
{
elementSize = q->elementSize;
}
eob_free_size = (q->last >= q->first) ? q->queueMaxSize - (q->last + curElementSize) : 0;
/* check how many bytes of wrapped element (if anay) are at end of buffer */
wrapped_element_eob_size = (((elementSize + elemSizeStorageRoom )*nbElements) < eob_free_size) ? 0 : (eob_free_size % (elementSize + elemSizeStorageRoom));
wrap_will_occur = wrapped_element_eob_size > elemSizeStorageRoom;
overhead = (wrap_will_occur && (q->optionFlags & CIRCULAR_QUEUE_NO_WRAP_FLAG)) ? wrapped_element_eob_size : overhead;
overhead = (wrap_will_occur && (q->optionFlags & CIRCULAR_QUEUE_SPLIT_IF_WRAPPING_FLAG)) ? elemSizeStorageRoom : overhead;
/* Store now the elements if ennough room for all elements */
if (elementSize && ((q->byteCount + ((elementSize + elemSizeStorageRoom )*nbElements) + overhead) <= q->queueMaxSize))
{
/* loop to add all elements */
for (i=0; i < nbElements; i++)
{
q->last = MOD ((q->last + curElementSize),q->queueMaxSize);
curBuffPosition = q->last;
/* store the element */
/* store fisrt the element size if element size is varaible */
if (q->elementSize == 0)
{
q->qBuff[curBuffPosition++]= elementSize & 0xFF;
curBuffPosition = MOD(curBuffPosition, q->queueMaxSize);
q->qBuff[curBuffPosition++]= (elementSize & 0xFF00) >> 8 ;
curBuffPosition = MOD(curBuffPosition, q->queueMaxSize);
q->byteCount += 2;
}
/* Identify number of bytes of copy takeing account possible wrap, in this case NbBytesToCopy will contains size that fit at end of the queue buffer */
NbBytesToCopy = MIN((q->queueMaxSize-curBuffPosition),elementSize);
/* check if no wrap (NbBytesToCopy == elementSize) or if Wrap and no spsicf option;
In thi case part of data will copied at the end of the buffer and the rest a the beggining */
if ((NbBytesToCopy == elementSize) || ((NbBytesToCopy < elementSize) && (q->optionFlags == CIRCULAR_QUEUE_NO_FLAG)))
{
/* Copy First part (or emtire buffer ) from current position up to the end of the buffer queue (or before if enough room) */
memcpy(&q->qBuff[curBuffPosition],&x[i*elementSize],NbBytesToCopy);
/* Adjust bytes count */
q->byteCount += NbBytesToCopy;
/* Wrap */
curBuffPosition = 0;
/* set NbCopiedBytes bytes with ampount copied */
NbCopiedBytes = NbBytesToCopy;
/* set the rest to copy if wrao , if no wrap will be 0 */
NbBytesToCopy = elementSize - NbBytesToCopy;
/* set the current element Size, will be used to calaculate next last position at beggining of loop */
curElementSize = (elementSize) + elemSizeStorageRoom ;
}
else if (NbBytesToCopy) /* We have a wrap to manage */
{
/* case of CIRCULAR_QUEUE_NO_WRAP_FLAG option */
if (q->optionFlags & CIRCULAR_QUEUE_NO_WRAP_FLAG)
{
/* if element size are variable and NO_WRAP option, Invalidate end of buffer setting 0xFFFF size*/
if (q->elementSize == 0)
{
q->qBuff[curBuffPosition-2] = 0xFF;
q->qBuff[curBuffPosition-1] = 0xFF;
}
q->byteCount += NbBytesToCopy; /* invalid data at the end of buffer are take into account in byteCount */
/* No bytes coped a the end of buffer */
NbCopiedBytes = 0;
/* all element to be copied at the begnning of buffer */
NbBytesToCopy = elementSize;
/* Wrap */
curBuffPosition = 0;
/* if variable size element, invalidate end of buffer setting OxFFFF in element header (size) */
if (q->elementSize == 0)
{
q->qBuff[curBuffPosition++] = NbBytesToCopy & 0xFF;
q->qBuff[curBuffPosition++] = (NbBytesToCopy & 0xFF00) >> 8 ;
q->byteCount += 2;
}
}
/* case of CIRCULAR_QUEUE_SPLIT_IF_WRAPPING_FLAG option */
else if (q->optionFlags & CIRCULAR_QUEUE_SPLIT_IF_WRAPPING_FLAG)
{
if (q->elementSize == 0)
{
/* reset the size of current element to the nb bytes fitting at the end of buffer */
q->qBuff[curBuffPosition-2] = NbBytesToCopy & 0xFF;
q->qBuff[curBuffPosition-1] = (NbBytesToCopy & 0xFF00) >> 8 ;
/* copy the bytes */
memcpy(&q->qBuff[curBuffPosition],&x[i*elementSize],NbBytesToCopy);
q->byteCount += NbBytesToCopy;
/* set the number of copied bytes */
NbCopiedBytes = NbBytesToCopy;
/* set rest of data to be copied to begnning of buffer */
NbBytesToCopy = elementSize - NbBytesToCopy;
/* one element more dur to split in 2 elements */
q->elementCount++;
/* Wrap */
curBuffPosition = 0;
/* Set new size for rest of data */
q->qBuff[curBuffPosition++] = NbBytesToCopy & 0xFF;
q->qBuff[curBuffPosition++] = (NbBytesToCopy & 0xFF00) >> 8 ;
q->byteCount += 2;
}
else
{
/* Should not occur */
/* can not manage split Flag on Fixed size element */
/* Buffer is corrupted */
return NULL;
}
}
curElementSize = (NbBytesToCopy) + elemSizeStorageRoom ;
q->last = 0;
}
/* some remaning byte to copy */
if (NbBytesToCopy)
{
memcpy(&q->qBuff[curBuffPosition],&x[(i*elementSize)+NbCopiedBytes],NbBytesToCopy);
q->byteCount += NbBytesToCopy;
}
/* One more element */
q->elementCount++;
}
ptr = q->qBuff + (MOD((q->last+elemSizeStorageRoom ),q->queueMaxSize));
}
/* for Breakpoint only...to remove */
else
{
return NULL;
}
return ptr;
}
/**
* @brief Remove element from the queue and copy it in provided buffer
* @note This function is used to remove and element from the Circular Queue .
* @param q: pointer on queue structure to be handled
* @param elementSize: Pointer to return Size of element to be removed
* @param buffer: destination buffer where to copy element
* @retval Pointer on removed element. NULL if queue was empty
*/
uint8_t* CircularQueue_Remove_Copy(queue_t *q, uint16_t* elementSize, uint8_t* buffer)
{
return NULL;
}
/**
* @brief Remove element from the queue.
* @note This function is used to remove and element from the Circular Queue .
* @param q: pointer on queue structure to be handled
* @param elementSize: Pointer to return Size of element to be removed
* @retval Pointer on removed element. NULL if queue was empty
*/
uint8_t* CircularQueue_Remove(queue_t *q, uint16_t* elementSize)
{
uint8_t elemSizeStorageRoom = 0;
uint8_t* ptr= NULL;
elemSizeStorageRoom = (q->elementSize == 0) ? 2 : 0;
*elementSize = 0;
if (q->byteCount > 0)
{
/* retreive element Size */
*elementSize = (q->elementSize == 0) ? q->qBuff[q->first] + ((q->qBuff[MOD((q->first+1), q->queueMaxSize)])<<8) : q->elementSize;
if ((q->optionFlags & CIRCULAR_QUEUE_NO_WRAP_FLAG) && !(q->optionFlags & CIRCULAR_QUEUE_SPLIT_IF_WRAPPING_FLAG))
{
if (((*elementSize == 0xFFFF) && q->elementSize == 0 ) ||
((q->first > q->last) && q->elementSize && ((q->queueMaxSize - q->first) < q->elementSize)))
{
/* all data from current position up to the end of buffer are invalid */
q->byteCount -= (q->queueMaxSize - q->first);
/* Adjust first element pos */
q->first = 0;
/* retrieve the rigth size after the wrap [if varaible size element] */
*elementSize = (q->elementSize == 0) ? q->qBuff[q->first] + ((q->qBuff[MOD((q->first+1), q->queueMaxSize)])<<8) : q->elementSize;
}
}
/* retreive element */
ptr = q->qBuff + (MOD((q->first + elemSizeStorageRoom), q->queueMaxSize));
/* adjust byte count */
q->byteCount -= (*elementSize + elemSizeStorageRoom) ;
/* Adjust q->first */
if (q->byteCount > 0)
{
q->first = MOD((q->first+ *elementSize + elemSizeStorageRoom ), q->queueMaxSize);
}
/* adjust element count */
--q->elementCount;
}
return ptr;
}
/**
* @brief "Sense" first element of the queue, without removing it and copy it in provided buffer
* @note This function is used to return a pointer on the first element of the queue without removing it.
* @param q: pointer on queue structure to be handled
* @param elementSize: Pointer to return Size of element to be removed
* @param buffer: destination buffer where to copy element
* @retval Pointer on sensed element. NULL if queue was empty
*/
uint8_t* CircularQueue_Sense_Copy(queue_t *q, uint16_t* elementSize, uint8_t* buffer)
{
return NULL;
}
/**
* @brief "Sense" first element of the queue, without removing it.
* @note This function is used to return a pointer on the first element of the queue without removing it.
* @param q: pointer on queue structure to be handled
* @param elementSize: Pointer to return Size of element to be removed
* @retval Pointer on sensed element. NULL if queue was empty
*/
uint8_t* CircularQueue_Sense(queue_t *q, uint16_t* elementSize)
{
uint8_t elemSizeStorageRoom = 0;
uint8_t* x= NULL;
elemSizeStorageRoom = (q->elementSize == 0) ? 2 : 0;
*elementSize = 0;
uint32_t FirstElemetPos = 0;
if (q->byteCount > 0)
{
FirstElemetPos = q->first;
*elementSize = (q->elementSize == 0) ? q->qBuff[q->first] + ((q->qBuff[MOD((q->first+1), q->queueMaxSize)])<<8) : q->elementSize;
if ((q->optionFlags & CIRCULAR_QUEUE_NO_WRAP_FLAG) && !(q->optionFlags & CIRCULAR_QUEUE_SPLIT_IF_WRAPPING_FLAG))
{
if (((*elementSize == 0xFFFF) && q->elementSize == 0 ) ||
((q->first > q->last) && q->elementSize && ((q->queueMaxSize - q->first) < q->elementSize)))
{
/* all data from current position up to the end of buffer are invalid */
FirstElemetPos = 0; /* wrap to the begiining of buffer */
/* retrieve the rigth size after the wrap [if varaible size element] */
*elementSize = (q->elementSize == 0) ? q->qBuff[FirstElemetPos]+ ((q->qBuff[MOD((FirstElemetPos+1), q->queueMaxSize)])<<8) : q->elementSize;
}
}
/* retrieve element */
x = q->qBuff + (MOD((FirstElemetPos + elemSizeStorageRoom), q->queueMaxSize));
}
return x;
}
/**
* @brief Check if queue is empty.
* @note This function is used to to check if the queue is empty.
* @param q: pointer on queue structure to be handled
* @retval TRUE (!0) if the queue is empyu otherwise FALSE (0)
*/
int CircularQueue_Empty(queue_t *q)
{
int ret=FALSE;
if (q->byteCount <= 0)
{
ret=TRUE;
}
return ret;
}
int CircularQueue_NbElement(queue_t *q)
{
return q->elementCount;
}