Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
noos_mbed/util/circular_buffer.c
- Committer:
- pmallick
- Date:
- 2021-01-14
- Revision:
- 0:e8a1ba50c46b
File content as of revision 0:e8a1ba50c46b:
/***************************************************************************//**
* @file circular_buffer.c
* @brief Circular buffer implementation
* @author Mihail Chindris (mihail.chindris@analog.com)
********************************************************************************
* @copyright
* Copyright 2020(c) Analog Devices, Inc.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* - Neither the name of Analog Devices, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* - The use of this software may or may not infringe the patent rights
* of one or more patent holders. This license does not release you
* from the requirement that you obtain separate licenses from these
* patent holders to use this software.
* - Use of the software either in source or binary form, must be run
* on or directly connected to an Analog Devices Inc. component.
*
* THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/
/******************************************************************************/
/***************************** Include Files **********************************/
/******************************************************************************/
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include "circular_buffer.h"
#include "error.h"
#include "util.h"
/******************************************************************************/
/*************************** Types Declarations *******************************/
/******************************************************************************/
/**
* @struct cb_ptr
* @brief Circular buffer pointer
*/
struct cb_ptr {
/** Index of data in the buffer */
uint32_t idx;
/** Counts the number of times idx exceeds the liniar buffer */
uint32_t spin_count;
/** Set if async transaction is active */
bool async_started;
/** Number of bytes to update after an async transaction is finished */
uint32_t async_size;
};
/**
* @struct circular_buffer
* @brief Circular buffer descriptor
*/
struct circular_buffer {
/** Size of the buffer in bytes */
uint32_t size;
/** Address of the buffer */
int8_t *buff;
/** Write pointer */
struct cb_ptr write;
/** Read pointer */
struct cb_ptr read;
};
/******************************************************************************/
/************************ Functions Definitions *******************************/
/******************************************************************************/
/**
* @brief Create circular buffer structure
*
* @note Circular buffer implementation is thread safe for one write
* and one reader.
* If multiple writer or multiple readers access the circular buffer then
* function that updates the structure should be called inside a critical
* critical section.
*
* @param desc - Where to store the circular buffer reference
* @param buff_size - Buffer size
* @return
* - \ref SUCCESS : On success
* - \ref FAILURE : Otherwise
*/
int32_t cb_init(struct circular_buffer **desc, uint32_t buff_size)
{
struct circular_buffer *ldesc;
if (!desc || !buff_size)
return -EINVAL;
ldesc = (struct circular_buffer*)calloc(1, sizeof(*ldesc));
if (!ldesc)
return -ENOMEM;
*desc = ldesc;
ldesc->size = buff_size;
ldesc->buff = calloc(1, buff_size);
if (!ldesc->buff) {
free(ldesc);
return -ENOMEM;
}
return SUCCESS;
}
/**
* @brief Free the resources allocated for the circular buffer structure
* @param desc - Circular buffer reference
* @return
* - \ref SUCCESS : On success
* - \ref FAILURE : Otherwise
*/
int32_t cb_remove(struct circular_buffer *desc)
{
if (!desc)
return FAILURE;
if (desc->buff)
free(desc->buff);
free(desc);
return SUCCESS;
}
/**
* @brief Get the number of elements in the buffer
* @param desc - Circular buffer reference
* @param size - Where to store size of data available to read
* @return
* - \ref SUCCESS - No errors
* - -EINVAL - Wrong parameters used
* - -EOVERRUN - A buffer overrun occurred
*/
int32_t cb_size(struct circular_buffer *desc, uint32_t *size)
{
uint32_t nb_spins;
if (!desc || !size)
return -EINVAL;
if (desc->write.spin_count > desc->read.spin_count)
nb_spins = desc->write.spin_count - desc->read.spin_count;
else
/* Integer overflow on desc->write.spin_count */
nb_spins = UINT32_MAX - desc->read.spin_count +
desc->write.spin_count + 1;
if (nb_spins > 0)
*size = desc->size + desc->write.idx - desc->read.idx;
else
*size = desc->write.idx - desc->read.idx;
if (*size > desc->size) {
*size = desc->size;
return -EOVERRUN;
}
return SUCCESS;
}
/*
* Functionality described at cb_prepare_async_write/read having the is_read
* parameter to specifiy if it is a read or write operation
*/
static int32_t cb_prepare_async_operation(struct circular_buffer *desc,
uint32_t requested_size,
void **buff,
uint32_t *raw_size_available,
bool is_read)
{
struct cb_ptr *ptr;
uint32_t available_size;
uint32_t ret;
if (!desc || !buff || !raw_size_available)
return -EINVAL;
ret = SUCCESS;
/* Select if read or write index will be updated */
ptr = is_read ? &desc->read : &desc->write;
/* Only one transaction type possible at a single time */
if (ptr->async_started)
return -EBUSY;
if (is_read) {
ret = cb_size(desc, &available_size);
if (ret == -EOVERRUN) {
/* Update read index */
desc->read.spin_count = desc->write.spin_count - 1;
desc->read.idx = desc->write.idx;
}
/* We can only read available data */
requested_size = min(requested_size, available_size);
if (!requested_size)
return -EAGAIN;
}
/* Size to end of buffer */
ptr->async_size = min(requested_size, desc->size - ptr->idx);
*raw_size_available = ptr->async_size;
/* Convert index to address in the buffer */
*buff = (void *)(desc->buff + ptr->idx);
ptr->async_started = true;
return ret;
}
/*
* Functionality described at cb_end_async_write/read having the is_read
* parameter to specifiy if it is a read or write operation
*/
static int32_t cb_end_async_operation(struct circular_buffer *desc,
bool is_read)
{
struct cb_ptr *ptr;
uint32_t new_val;
if (!desc)
return -EINVAL;
/* Select if read or write index will be updated */
ptr = is_read ? &desc->read : &desc->write;
/* Transaction not started */
if (!ptr->async_started)
return FAILURE;
/* Update pointer value */
new_val = ptr->idx + ptr->async_size;
if (new_val >= desc->size) {
ptr->spin_count++;
new_val %= desc->size;
}
ptr->idx = new_val;
ptr->async_size = 0;
ptr->async_started = false;
return SUCCESS;
}
/*
* Functionality described at cb_write/read having the is_read
* parameter to specifiy if it is a read or write operation
*/
static int32_t cb_operation(struct circular_buffer *desc,
void *data, uint32_t size,
bool is_read)
{
uint8_t *buff;
uint32_t available_size;
int32_t ret;
uint32_t i;
bool sticky_overrun;
if (!desc || !data || !size)
return -EINVAL;
sticky_overrun = 0;
i = 0;
while (i < size) {
do {
ret = cb_prepare_async_operation(desc, size - i,
(void **)&buff,
&available_size,
is_read);
} while (ret == -EBUSY || ret == -EAGAIN);
if (ret == -EOVERRUN)
sticky_overrun = true;
if (is_read)
memcpy((uint8_t *)data + i, buff, available_size);
else
memcpy(buff, (uint8_t *)data + i, available_size);
cb_end_async_operation(desc, is_read);
i += available_size;
}
if (sticky_overrun)
return -EOVERRUN;
return SUCCESS;
}
/**
* @brief Prepare asynchronous write
*
* Get the inside raw buffer to be used in DMA transactions.
*
* @param desc - Circular buffer reference
* @param size_to_write - Number of bytes needed to write to the buffer.
* @param write_buff - Address where to store the buffer where to write to.
* @param size_avilable - min(size_to_write, size until end of allocated buffer)
* @return
* - \ref SUCCESS - No errors
* - -EINVAL - Wrong parameters used
* - -EBUSY - Asynchronous transaction already started
*/
int32_t cb_prepare_async_write(struct circular_buffer *desc,
uint32_t size_to_write,
void **write_buff,
uint32_t *size_avilable)
{
return cb_prepare_async_operation(desc, size_to_write, write_buff,
size_avilable, 0);
}
/**
* @brief Prepare asynchronous read
*
* Get the inside raw buffer to be used in DMA transactions.
*
* @param desc - Circular buffer reference
* @param size_to_read - Number of bytes needed to write to the buffer.
* @param read_buff - Address where to store the buffer where data will be read.
* @param size_avilable - min(size_to_read, size until end of allocated buffer)
* @return
* - \ref SUCCESS - No errors
* - -EAGAIN - No data available at this moment
* - -EINVAL - Wrong parameters used
* - -EBUSY - Asynchronous transaction already started
* - -EOVERRUN - An overrun occurred and some data have been overwritten
*/
int32_t cb_prepare_async_read(struct circular_buffer *desc,
uint32_t size_to_read,
void **read_buff,
uint32_t *size_avilable)
{
return cb_prepare_async_operation(desc, size_to_read, read_buff,
size_avilable, 1);
}
/**
* \defgroup end_async_group End Ashyncronous functions
* @brief End asynchronous transaction
*
* @param desc - Circular buffer reference
* @return
* - \ref SUCCESS - No errors
* - \ref FAILURE - Asynchronous transaction not started
* - -EINVAL - Wrong parameters used
* @{
*/
int32_t cb_end_async_write(struct circular_buffer *desc)
{
return cb_end_async_operation(desc, 0);
}
int32_t cb_end_async_read(struct circular_buffer *desc)
{
return cb_end_async_operation(desc, 1);
}
/** @} */
/**
* @brief Write data to the buffer (Blocking)
* @param desc - Circular buffer reference
* @param data - Buffer from where data is copied to the circular buffer
* @param size - Size to write
* @return
* - \ref SUCCESS - No errors
* - -EINVAL - Wrong parameters used
*/
int32_t cb_write(struct circular_buffer *desc, const void *data, uint32_t size)
{
return cb_operation(desc, (void *)data, size, 0);
}
/**
* @brief Read data from the buffer (Blocking)
* @param desc - Circular buffer reference
* @param data - Buffer where to data is copied from the circular buffer
* @param size - Size to read
* @return
* - \ref SUCCESS - No errors
* - -EINVAL - Wrong parameters used
* - -EOVERRUN - An overrun occurred and some data have been overwritten
*/
int32_t cb_read(struct circular_buffer *desc, void *data, uint32_t size)
{
return cb_operation(desc, data, size, 1);
}