Pratyush Mallick
/
testing
this is testing
app/noos_mbed/util/circular_buffer.c
- Committer:
- pmallick
- Date:
- 2021-01-14
- Revision:
- 0:3afcd581558d
File content as of revision 0:3afcd581558d:
/***************************************************************************//** * @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); }