162 lines
5.3 KiB
C
162 lines
5.3 KiB
C
#include "blocking_fifo.h"
|
|
|
|
#include <memory.h>
|
|
|
|
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
|
|
|
bool bfifo_create(BFifo *fifo, uint8_t *mem, uint32_t size) {
|
|
memset(fifo, 0, sizeof(BFifo));
|
|
fifo->size = size; // store size and memory pointer
|
|
fifo->mem = mem;
|
|
fifo->readIdx = 0; // store read and write indices
|
|
fifo->writeIdx = 0;
|
|
fifo->readSem = osSemaphoreNew(1, 0, NULL); // create reading and writing semaphores
|
|
fifo->writeSem = osSemaphoreNew(1, 1, NULL);
|
|
return (fifo->readSem != NULL) && (fifo->writeSem != NULL);
|
|
}
|
|
|
|
void bfifo_destroy(BFifo *fifo) {
|
|
osSemaphoreDelete(fifo->readSem);
|
|
osSemaphoreDelete(fifo->writeSem);
|
|
memset(fifo, 0, sizeof(BFifo));
|
|
}
|
|
|
|
// get a monotonic write index with respect to read index
|
|
uint32_t bfifo_monotonic_write_index(const BFifo *fifo) {
|
|
return (fifo->readIdx > fifo->writeIdx) ? (fifo->writeIdx + fifo->size) : fifo->writeIdx;
|
|
}
|
|
|
|
// uint32_t bfifo_monotonic_read_index(const BFifo *fifo) {
|
|
// return (fifo->writeIdx > fifo->readIdx) ? (fifo->readIdx + fifo->size) : fifo->readIdx;
|
|
// }
|
|
|
|
uint32_t bfifo_get_free(const BFifo *fifo) {
|
|
return fifo->size - bfifo_get_used(fifo);
|
|
}
|
|
|
|
uint32_t bfifo_get_used(const BFifo *fifo) {
|
|
return (bfifo_monotonic_write_index(fifo) - fifo->readIdx);
|
|
}
|
|
|
|
uint32_t bfifo_push(BFifo * fifo, const uint8_t * data, uint32_t size) {
|
|
// shortcut zero length pushes
|
|
if (size == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// first, take the write semaphore
|
|
osSemaphoreAcquire(fifo->writeSem, osWaitForever);
|
|
|
|
// then, determine the push size
|
|
uint32_t freeSize = bfifo_get_free(fifo);
|
|
uint32_t pushSize = MIN(freeSize, size); // push size cannot exceed free size
|
|
|
|
// do the actual pushing, if pushing involves an index rollover, then
|
|
// pushing can be performed in two consecutive blocks
|
|
uint32_t bytesToMemEnd = fifo->size - fifo->writeIdx; // calculate the number of bytes to end of the memory block counted from the current write index
|
|
uint32_t firstBlockSize = MIN(pushSize, bytesToMemEnd); // determine the first copy block size
|
|
uint32_t secondBlockSize = pushSize - firstBlockSize; // determine the second copy block size (might by zero)
|
|
|
|
// copy data
|
|
memcpy(fifo->mem + fifo->writeIdx, data, firstBlockSize); // first block
|
|
if (secondBlockSize > 0) {
|
|
memcpy(fifo->mem, data + firstBlockSize, secondBlockSize); // second block
|
|
}
|
|
|
|
// advance write index
|
|
uint32_t newWriteIndex = fifo->writeIdx + pushSize;
|
|
if (newWriteIndex >= fifo->size) {
|
|
newWriteIndex = newWriteIndex - fifo->size;
|
|
}
|
|
fifo->writeIdx = newWriteIndex;
|
|
|
|
// handle semaphores
|
|
freeSize = bfifo_get_free(fifo); // get free size again considering the changes
|
|
if (freeSize > 0) { // if there's some free space remaining, then release the write semaphore
|
|
osSemaphoreRelease(fifo->writeSem);
|
|
}
|
|
|
|
osSemaphoreRelease(fifo->readSem); // it's ceratain, that the read semaphore can be released after a succesful push
|
|
|
|
// return with the pushed size
|
|
return pushSize;
|
|
}
|
|
|
|
uint32_t bfifo_push_all(BFifo *fifo, const uint8_t *data, uint32_t size) {
|
|
uint32_t pushLeft = size;
|
|
uint32_t pushedSize = 0;
|
|
while (pushLeft > 0) {
|
|
uint32_t currentPush = bfifo_push(fifo, data + pushedSize, pushLeft);
|
|
pushedSize += currentPush;
|
|
pushLeft -= currentPush;
|
|
}
|
|
return pushedSize;
|
|
}
|
|
|
|
uint32_t bfifo_read(BFifo *fifo, uint8_t *dest, uint32_t maxSize) {
|
|
// shortcut zero-length reads
|
|
if (maxSize == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// determine read size
|
|
uint32_t usedSize = bfifo_get_used(fifo);
|
|
uint32_t readSize = MIN(usedSize, maxSize);
|
|
|
|
// read data in two blocks (similar to how pushing worked)
|
|
uint32_t bytesToEnd = fifo->size - fifo->readIdx;
|
|
uint32_t firstBlockSize = MIN(bytesToEnd, readSize);
|
|
uint32_t secondBlockSize = readSize - firstBlockSize;
|
|
|
|
// copy data
|
|
memcpy(dest, fifo->mem + fifo->readIdx, firstBlockSize); // first block
|
|
if (secondBlockSize > 0) { // second block
|
|
memcpy(dest + firstBlockSize, fifo->mem, secondBlockSize);
|
|
}
|
|
|
|
return readSize;
|
|
}
|
|
|
|
uint32_t bfifo_pop(BFifo * fifo, uint32_t size, uint32_t timeout) {
|
|
// shortcut zero-length pops
|
|
if (size == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// acquire read semaphore
|
|
osSemaphoreAcquire(fifo->readSem, timeout);
|
|
|
|
// limit pop count to actual data content size
|
|
uint32_t usedSize = bfifo_get_used(fifo);
|
|
uint32_t popSize = MIN(usedSize, size);
|
|
|
|
// advance read index
|
|
uint32_t newReadIdx = fifo->readIdx + popSize;
|
|
if (newReadIdx >= fifo->size) {
|
|
newReadIdx -= fifo->size;
|
|
}
|
|
fifo->readIdx = newReadIdx;
|
|
|
|
// handle semaphores
|
|
osSemaphoreRelease(fifo->writeSem); // it's ceratin, that the FIFO can be written
|
|
|
|
usedSize = bfifo_get_used(fifo);
|
|
if (usedSize > 0) { // if the FIFO is not empty, then allow popping
|
|
osSemaphoreRelease(fifo->readSem);
|
|
}
|
|
|
|
return popSize; // return with the number of bytes popped
|
|
}
|
|
|
|
void bfifo_wait_avail(BFifo *fifo) {
|
|
// attempt to acquire read semaphore
|
|
osSemaphoreAcquire(fifo->readSem, osWaitForever);
|
|
|
|
// if acquiring was successful, then release it immediately,
|
|
// because taking the semaphore was only needed for synchronization
|
|
osSemaphoreRelease(fifo->readSem);
|
|
|
|
return;
|
|
}
|