mirror of
				https://github.com/mfulz/qmk_firmware.git
				synced 2025-10-31 05:12:33 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			307 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			307 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|              LUFA Library
 | |
|      Copyright (C) Dean Camera, 2013.
 | |
| 
 | |
|   dean [at] fourwalledcubicle [dot] com
 | |
|            www.lufa-lib.org
 | |
| */
 | |
| 
 | |
| /*
 | |
|   Copyright 2013  Dean Camera (dean [at] fourwalledcubicle [dot] com)
 | |
| 
 | |
|   Permission to use, copy, modify, distribute, and sell this
 | |
|   software and its documentation for any purpose is hereby granted
 | |
|   without fee, provided that the above copyright notice appear in
 | |
|   all copies and that both that the copyright notice and this
 | |
|   permission notice and warranty disclaimer appear in supporting
 | |
|   documentation, and that the name of the author not be used in
 | |
|   advertising or publicity pertaining to distribution of the
 | |
|   software without specific, written prior permission.
 | |
| 
 | |
|   The author disclaims all warranties with regard to this
 | |
|   software, including all implied warranties of merchantability
 | |
|   and fitness.  In no event shall the author be liable for any
 | |
|   special, indirect or consequential damages or any damages
 | |
|   whatsoever resulting from loss of use, data or profits, whether
 | |
|   in an action of contract, negligence or other tortious action,
 | |
|   arising out of or in connection with the use or performance of
 | |
|   this software.
 | |
| */
 | |
| 
 | |
| /** \file
 | |
|  *  \brief Lightweight ring (circular) buffer, for fast insertion/deletion of bytes.
 | |
|  *
 | |
|  *  Lightweight ring buffer, for fast insertion/deletion. Multiple buffers can be created of
 | |
|  *  different sizes to suit different needs.
 | |
|  *
 | |
|  *  Note that for each buffer, insertion and removal operations may occur at the same time (via
 | |
|  *  a multi-threaded ISR based system) however the same kind of operation (two or more insertions
 | |
|  *  or deletions) must not overlap. If there is possibility of two or more of the same kind of
 | |
|  *  operating occurring at the same point in time, atomic (mutex) locking should be used.
 | |
|  */
 | |
| 
 | |
| /** \ingroup Group_MiscDrivers
 | |
|  *  \defgroup Group_RingBuff Generic Byte Ring Buffer - LUFA/Drivers/Misc/RingBuffer.h
 | |
|  *  \brief Lightweight ring buffer, for fast insertion/deletion of bytes.
 | |
|  *
 | |
|  *  \section Sec_Dependencies Module Source Dependencies
 | |
|  *  The following files must be built with any user project that uses this module:
 | |
|  *    - None
 | |
|  *
 | |
|  *  \section Sec_ModDescription Module Description
 | |
|  *  Lightweight ring buffer, for fast insertion/deletion. Multiple buffers can be created of
 | |
|  *  different sizes to suit different needs.
 | |
|  *
 | |
|  *  Note that for each buffer, insertion and removal operations may occur at the same time (via
 | |
|  *  a multi-threaded ISR based system) however the same kind of operation (two or more insertions
 | |
|  *  or deletions) must not overlap. If there is possibility of two or more of the same kind of
 | |
|  *  operating occurring at the same point in time, atomic (mutex) locking should be used.
 | |
|  *
 | |
|  *  \section Sec_ExampleUsage Example Usage
 | |
|  *  The following snippet is an example of how this module may be used within a typical
 | |
|  *  application.
 | |
|  *
 | |
|  *  \code
 | |
|  *      // Create the buffer structure and its underlying storage array
 | |
|  *      RingBuffer_t Buffer;
 | |
|  *      uint8_t      BufferData[128];
 | |
|  *      
 | |
|  *      // Initialize the buffer with the created storage array
 | |
|  *      RingBuffer_InitBuffer(&Buffer, BufferData, sizeof(BufferData));
 | |
|  *      
 | |
|  *      // Insert some data into the buffer
 | |
|  *      RingBuffer_Insert(Buffer, 'H');
 | |
|  *      RingBuffer_Insert(Buffer, 'E');
 | |
|  *      RingBuffer_Insert(Buffer, 'L');
 | |
|  *      RingBuffer_Insert(Buffer, 'L');
 | |
|  *      RingBuffer_Insert(Buffer, 'O');
 | |
|  *      
 | |
|  *      // Cache the number of stored bytes in the buffer
 | |
|  *      uint16_t BufferCount = RingBuffer_GetCount(&Buffer);
 | |
|  *      
 | |
|  *      // Printer stored data length
 | |
|  *      printf("Buffer Length: %d, Buffer Data: \r\n", BufferCount);
 | |
|  *      
 | |
|  *      // Print contents of the buffer one character at a time
 | |
|  *      while (BufferCount--)
 | |
|  *        putc(RingBuffer_Remove(&Buffer));
 | |
|  *  \endcode
 | |
|  *
 | |
|  *  @{
 | |
|  */
 | |
| 
 | |
| #ifndef __RING_BUFFER_H__
 | |
| #define __RING_BUFFER_H__
 | |
| 
 | |
| 	/* Includes: */
 | |
| 		#include "../../Common/Common.h"
 | |
| 
 | |
| 	/* Enable C linkage for C++ Compilers: */
 | |
| 		#if defined(__cplusplus)
 | |
| 			extern "C" {
 | |
| 		#endif
 | |
| 
 | |
| 	/* Type Defines: */
 | |
| 		/** \brief Ring Buffer Management Structure.
 | |
| 		 *
 | |
| 		 *  Type define for a new ring buffer object. Buffers should be initialized via a call to
 | |
| 		 *  \ref RingBuffer_InitBuffer() before use.
 | |
| 		 */
 | |
| 		typedef struct
 | |
| 		{
 | |
| 			uint8_t* In; /**< Current storage location in the circular buffer. */
 | |
| 			uint8_t* Out; /**< Current retrieval location in the circular buffer. */
 | |
| 			uint8_t* Start; /**< Pointer to the start of the buffer's underlying storage array. */
 | |
| 			uint8_t* End; /**< Pointer to the end of the buffer's underlying storage array. */
 | |
| 			uint16_t Size; /**< Size of the buffer's underlying storage array. */
 | |
| 			uint16_t Count; /**< Number of bytes currently stored in the buffer. */
 | |
| 		} RingBuffer_t;
 | |
| 
 | |
| 	/* Inline Functions: */
 | |
| 		/** Initializes a ring buffer ready for use. Buffers must be initialized via this function
 | |
| 		 *  before any operations are called upon them. Already initialized buffers may be reset
 | |
| 		 *  by re-initializing them using this function.
 | |
| 		 *
 | |
| 		 *  \param[out] Buffer   Pointer to a ring buffer structure to initialize.
 | |
| 		 *  \param[out] DataPtr  Pointer to a global array that will hold the data stored into the ring buffer.
 | |
| 		 *  \param[out] Size     Maximum number of bytes that can be stored in the underlying data array.
 | |
| 		 */
 | |
| 		static inline void RingBuffer_InitBuffer(RingBuffer_t* Buffer,
 | |
| 		                                         uint8_t* const DataPtr,
 | |
| 		                                         const uint16_t Size) ATTR_NON_NULL_PTR_ARG(1) ATTR_NON_NULL_PTR_ARG(2);
 | |
| 		static inline void RingBuffer_InitBuffer(RingBuffer_t* Buffer,
 | |
| 		                                         uint8_t* const DataPtr,
 | |
| 		                                         const uint16_t Size)
 | |
| 		{
 | |
| 			GCC_FORCE_POINTER_ACCESS(Buffer);
 | |
| 
 | |
| 			uint_reg_t CurrentGlobalInt = GetGlobalInterruptMask();
 | |
| 			GlobalInterruptDisable();
 | |
| 
 | |
| 			Buffer->In     = DataPtr;
 | |
| 			Buffer->Out    = DataPtr;
 | |
| 			Buffer->Start  = &DataPtr[0];
 | |
| 			Buffer->End    = &DataPtr[Size];
 | |
| 			Buffer->Size   = Size;
 | |
| 			Buffer->Count  = 0;
 | |
| 
 | |
| 			SetGlobalInterruptMask(CurrentGlobalInt);
 | |
| 		}
 | |
| 
 | |
| 		/** Retrieves the current number of bytes stored in a particular buffer. This value is computed
 | |
| 		 *  by entering an atomic lock on the buffer, so that the buffer cannot be modified while the
 | |
| 		 *  computation takes place. This value should be cached when reading out the contents of the buffer,
 | |
| 		 *  so that as small a time as possible is spent in an atomic lock.
 | |
| 		 *
 | |
| 		 *  \note The value returned by this function is guaranteed to only be the minimum number of bytes
 | |
| 		 *        stored in the given buffer; this value may change as other threads write new data, thus
 | |
| 		 *        the returned number should be used only to determine how many successive reads may safely
 | |
| 		 *        be performed on the buffer.
 | |
| 		 *
 | |
| 		 *  \param[in] Buffer  Pointer to a ring buffer structure whose count is to be computed.
 | |
| 		 *
 | |
| 		 *  \return Number of bytes currently stored in the buffer.
 | |
| 		 */
 | |
| 		static inline uint16_t RingBuffer_GetCount(RingBuffer_t* const Buffer) ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(1);
 | |
| 		static inline uint16_t RingBuffer_GetCount(RingBuffer_t* const Buffer)
 | |
| 		{
 | |
| 			uint16_t Count;
 | |
| 
 | |
| 			uint_reg_t CurrentGlobalInt = GetGlobalInterruptMask();
 | |
| 			GlobalInterruptDisable();
 | |
| 
 | |
| 			Count = Buffer->Count;
 | |
| 
 | |
| 			SetGlobalInterruptMask(CurrentGlobalInt);
 | |
| 			return Count;
 | |
| 		}
 | |
| 
 | |
| 		/** Retrieves the free space in a particular buffer. This value is computed by entering an atomic lock
 | |
| 		 *  on the buffer, so that the buffer cannot be modified while the computation takes place.
 | |
| 		 *
 | |
| 		 *  \note The value returned by this function is guaranteed to only be the maximum number of bytes
 | |
| 		 *        free in the given buffer; this value may change as other threads write new data, thus
 | |
| 		 *        the returned number should be used only to determine how many successive writes may safely
 | |
| 		 *        be performed on the buffer when there is a single writer thread.
 | |
| 		 *
 | |
| 		 *  \param[in] Buffer  Pointer to a ring buffer structure whose free count is to be computed.
 | |
| 		 *
 | |
| 		 *  \return Number of free bytes in the buffer.
 | |
| 		 */
 | |
| 		static inline uint16_t RingBuffer_GetFreeCount(RingBuffer_t* const Buffer) ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(1);
 | |
| 		static inline uint16_t RingBuffer_GetFreeCount(RingBuffer_t* const Buffer)
 | |
| 		{
 | |
| 			return (Buffer->Size - RingBuffer_GetCount(Buffer));
 | |
| 		}
 | |
| 
 | |
| 		/** Atomically determines if the specified ring buffer contains any data. This should
 | |
| 		 *  be tested before removing data from the buffer, to ensure that the buffer does not
 | |
| 		 *  underflow.
 | |
| 		 *
 | |
| 		 *  If the data is to be removed in a loop, store the total number of bytes stored in the
 | |
| 		 *  buffer (via a call to the \ref RingBuffer_GetCount() function) in a temporary variable
 | |
| 		 *  to reduce the time spent in atomicity locks.
 | |
| 		 *
 | |
| 		 *  \param[in,out] Buffer  Pointer to a ring buffer structure to insert into.
 | |
| 		 *
 | |
| 		 *  \return Boolean \c true if the buffer contains no free space, \c false otherwise.
 | |
| 		 */
 | |
| 		static inline bool RingBuffer_IsEmpty(RingBuffer_t* const Buffer) ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(1);
 | |
| 		static inline bool RingBuffer_IsEmpty(RingBuffer_t* const Buffer)
 | |
| 		{
 | |
| 			return (RingBuffer_GetCount(Buffer) == 0);
 | |
| 		}
 | |
| 
 | |
| 		/** Atomically determines if the specified ring buffer contains any free space. This should
 | |
| 		 *  be tested before storing data to the buffer, to ensure that no data is lost due to a
 | |
| 		 *  buffer overrun.
 | |
| 		 *
 | |
| 		 *  \param[in,out] Buffer  Pointer to a ring buffer structure to insert into.
 | |
| 		 *
 | |
| 		 *  \return Boolean \c true if the buffer contains no free space, \c false otherwise.
 | |
| 		 */
 | |
| 		static inline bool RingBuffer_IsFull(RingBuffer_t* const Buffer) ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(1);
 | |
| 		static inline bool RingBuffer_IsFull(RingBuffer_t* const Buffer)
 | |
| 		{
 | |
| 			return (RingBuffer_GetCount(Buffer) == Buffer->Size);
 | |
| 		}
 | |
| 
 | |
| 		/** Inserts an element into the ring buffer.
 | |
| 		 *
 | |
| 		 *  \warning Only one execution thread (main program thread or an ISR) may insert into a single buffer
 | |
| 		 *           otherwise data corruption may occur. Insertion and removal may occur from different execution
 | |
| 		 *           threads.
 | |
| 		 *
 | |
| 		 *  \param[in,out] Buffer  Pointer to a ring buffer structure to insert into.
 | |
| 		 *  \param[in]     Data    Data element to insert into the buffer.
 | |
| 		 */
 | |
| 		static inline void RingBuffer_Insert(RingBuffer_t* Buffer, const uint8_t Data) ATTR_NON_NULL_PTR_ARG(1);
 | |
| 		static inline void RingBuffer_Insert(RingBuffer_t* Buffer, const uint8_t Data)
 | |
| 		{
 | |
| 			GCC_FORCE_POINTER_ACCESS(Buffer);
 | |
| 
 | |
| 			*Buffer->In = Data;
 | |
| 
 | |
| 			if (++Buffer->In == Buffer->End)
 | |
| 			  Buffer->In = Buffer->Start;
 | |
| 
 | |
| 			uint_reg_t CurrentGlobalInt = GetGlobalInterruptMask();
 | |
| 			GlobalInterruptDisable();
 | |
| 
 | |
| 			Buffer->Count++;
 | |
| 
 | |
| 			SetGlobalInterruptMask(CurrentGlobalInt);
 | |
| 		}
 | |
| 
 | |
| 		/** Removes an element from the ring buffer.
 | |
| 		 *
 | |
| 		 *  \warning Only one execution thread (main program thread or an ISR) may remove from a single buffer
 | |
| 		 *           otherwise data corruption may occur. Insertion and removal may occur from different execution
 | |
| 		 *           threads.
 | |
| 		 *
 | |
| 		 *  \param[in,out] Buffer  Pointer to a ring buffer structure to retrieve from.
 | |
| 		 *
 | |
| 		 *  \return Next data element stored in the buffer.
 | |
| 		 */
 | |
| 		static inline uint8_t RingBuffer_Remove(RingBuffer_t* Buffer) ATTR_NON_NULL_PTR_ARG(1);
 | |
| 		static inline uint8_t RingBuffer_Remove(RingBuffer_t* Buffer)
 | |
| 		{
 | |
| 			GCC_FORCE_POINTER_ACCESS(Buffer);
 | |
| 
 | |
| 			uint8_t Data = *Buffer->Out;
 | |
| 
 | |
| 			if (++Buffer->Out == Buffer->End)
 | |
| 			  Buffer->Out = Buffer->Start;
 | |
| 
 | |
| 			uint_reg_t CurrentGlobalInt = GetGlobalInterruptMask();
 | |
| 			GlobalInterruptDisable();
 | |
| 
 | |
| 			Buffer->Count--;
 | |
| 
 | |
| 			SetGlobalInterruptMask(CurrentGlobalInt);
 | |
| 
 | |
| 			return Data;
 | |
| 		}
 | |
| 
 | |
| 		/** Returns the next element stored in the ring buffer, without removing it.
 | |
| 		 *
 | |
| 		 *  \param[in,out] Buffer  Pointer to a ring buffer structure to retrieve from.
 | |
| 		 *
 | |
| 		 *  \return Next data element stored in the buffer.
 | |
| 		 */
 | |
| 		static inline uint8_t RingBuffer_Peek(RingBuffer_t* const Buffer) ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(1);
 | |
| 		static inline uint8_t RingBuffer_Peek(RingBuffer_t* const Buffer)
 | |
| 		{
 | |
| 			return *Buffer->Out;
 | |
| 		}
 | |
| 
 | |
| 	/* Disable C linkage for C++ Compilers: */
 | |
| 		#if defined(__cplusplus)
 | |
| 			}
 | |
| 		#endif
 | |
| 
 | |
| #endif
 | |
| 
 | |
| /** @} */
 | |
| 
 | 
