feat(kfifo): Add the kernel fifo.

This commit is contained in:
MacRsh
2025-05-14 00:51:16 +08:00
parent 6a8628352e
commit 532d6e2bd6
2 changed files with 367 additions and 0 deletions

195
include/kernel/mr_kfifo.h Normal file
View File

@@ -0,0 +1,195 @@
/**
* @copyright (c) 2024, MacRsh
*
* @license SPDX-License-Identifier: Apache-2.0
*
* @date 2024-09-06 MacRsh First version
*/
#ifndef __MR_KFIFO_H__
#define __MR_KFIFO_H__
#include <kernel/mr_kservice.h>
#include <libc/mr_compiler.h>
#include <libc/mr_types.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* @addtogroup Kfifo
* @{
*/
/* Kfifo type */
typedef struct mr_kfifo {
mr_uint8_t *buf;
mr_uint32_t mask;
mr_uint32_t elem_size;
volatile mr_uint32_t in;
volatile mr_uint32_t out;
} mr_kfifo_t;
/**
* @brief This function initializes a kfifo.
*
* @param kfifo The kfifo.
* @param buf The buffer.
* @param size The buffer size(bytes).
* @param elem_size The element size(bytes).
* @return The element capacity(number).
*
* @note The buffer will be aligned down to a power of 2.
*/
mr_uint32_t mr_kfifo_init(mr_kfifo_t *kfifo, void *buf, mr_uint32_t size,
mr_uint32_t elem_size);
/**
* @brief This function allocates a kfifo.
*
* @param kfifo The kfifo.
* @param size The buffer size(bytes).
* @param elem_size The element size(bytes).
* @return The element capacity(number).
*
* @note The buffer will be aligned up to a power of 2.
*/
mr_uint32_t mr_kfifo_alloc(mr_kfifo_t *kfifo, mr_uint32_t size,
mr_uint32_t elem_size);
/**
* @brief This function frees a kfifo.
*
* @param kfifo The kfifo.
*/
void mr_kfifo_free(mr_kfifo_t *kfifo);
/**
* @brief This function pushes data to a kfifo.
*
* @param kfifo The kfifo.
* @param data The data.
* @return The actual pushed element number.
*/
mr_uint32_t mr_kfifo_push(mr_kfifo_t *kfifo, const void *data);
/**
* @brief This function pops data from a kfifo.
*
* @param kfifo The kfifo.
* @param data The data.
* @return The actual popped element number.
*/
mr_uint32_t mr_kfifo_pop(mr_kfifo_t *kfifo, void *data);
/**
* @brief This function puts data to a kfifo.
*
* @param kfifo The kfifo.
* @param buf The buffer.
* @param num The buffer number.
* @return The actual pushed element number.
*/
mr_uint32_t mr_kfifo_in(mr_kfifo_t *kfifo, const void *buf, mr_uint32_t num);
/**
* @brief This function peeks data from a kfifo.
*
* @param kfifo The kfifo.
* @param buf The buffer.
* @param num The buffer number.
* @return The actual peeked element number.
*/
mr_uint32_t mr_kfifo_peek(mr_kfifo_t *kfifo, void *buf, mr_uint32_t num);
/**
* @brief This function gets data from a kfifo.
*
* @param kfifo The kfifo.
* @param buf The buffer.
* @param num The buffer number.
* @return The actual popped element number.
*/
MR_INLINE mr_uint32_t mr_kfifo_out(mr_kfifo_t *kfifo, void *buf,
mr_uint32_t num) {
/* Pop buffer */
num = mr_kfifo_peek(kfifo, buf, num);
kfifo->out += num;
return num;
}
/**
* @brief This function gets the element number in a kfifo.
*
* @param kfifo The kfifo.
* @return The element number.
*/
MR_INLINE mr_uint32_t mr_kfifo_len(mr_kfifo_t *kfifo) {
/* Get kfifo element number */
return kfifo->in - kfifo->out;
}
/**
* @brief This function skips elements in a kfifo.
*
* @param kfifo The kfifo.
* @param num The element number.
* @return The actual skipped element number.
*/
MR_INLINE mr_uint32_t mr_kfifo_skip(mr_kfifo_t *kfifo, mr_uint32_t num) {
/* Skip buffer */
kfifo->out += MR_MIN(num, mr_kfifo_len(kfifo));
return num;
}
/**
* @brief This function gets the available space in a kfifo.
*
* @param kfifo The kfifo.
* @return The available element number.
*/
MR_INLINE mr_uint32_t mr_kfifo_avail(mr_kfifo_t *kfifo) {
/* Get kfifo available space */
return (kfifo->mask + 1) - mr_kfifo_len(kfifo);
}
/**
* @brief This function resets a kfifo.
*
* @param kfifo The kfifo.
*/
MR_INLINE void mr_kfifo_reset(mr_kfifo_t *kfifo) {
/* Reset kfifo */
kfifo->in = kfifo->out = 0;
}
/**
* @brief This function checks if a kfifo is empty.
*
* @param kfifo The kfifo.
* @return MR_TRUE on empty, MR_FALSE otherwise.
*/
MR_INLINE mr_bool_t mr_kfifo_is_empty(mr_kfifo_t *kfifo) {
/* Check if kfifo is empty */
return kfifo->in == kfifo->out;
}
/**
* @brief This function checks if a kfifo is full.
*
* @param kfifo The kfifo.
* @return MR_TRUE on full, MR_FALSE otherwise.
*/
MR_INLINE mr_bool_t mr_kfifo_is_full(mr_kfifo_t *kfifo) {
/* Check if kfifo is full */
return mr_kfifo_len(kfifo) == (kfifo->mask + 1);
}
/** @} */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __MR_KFIFO_H__ */

172
kernel/kfifo.c Normal file
View File

@@ -0,0 +1,172 @@
/**
* @copyright (c) 2024, MacRsh
*
* @license SPDX-License-Identifier: Apache-2.0
*
* @date 2024-09-06 MacRsh First version
*/
#include <kernel/mr_kfifo.h>
#include <libc/mr_string.h>
#include <libc/mr_malloc.h>
/* Kfifo align macro function */
#define KFIFO_ALIGN_POW2_UP(_size) \
(((_size) & ((_size) - 1)) ? (1U << mr_fls32(_size)) : (_size))
#define KFIFO_ALIGN_POW2_DOWN(_size) \
(1U << (mr_fls32(_size) - 1))
mr_uint32_t mr_kfifo_init(mr_kfifo_t *kfifo, void *buf, mr_uint32_t size,
mr_uint32_t elem_size) {
/* Check arguments */
if ((!buf) || (size < 2) || (elem_size == 0) || (elem_size > size)) {
return 0;
}
/* Align size(down to power of 2) */
size = KFIFO_ALIGN_POW2_DOWN(size / elem_size);
/* Init kfifo */
kfifo->buf = buf;
kfifo->mask = size - 1;
kfifo->in = kfifo->out = 0;
kfifo->elem_size = elem_size;
return size;
}
mr_uint32_t mr_kfifo_alloc(mr_kfifo_t *kfifo, mr_uint32_t size,
mr_uint32_t elem_size) {
void *buf;
/* Check arguments */
if ((size == 0) || (elem_size == 0) || (elem_size > size)) {
return 0;
}
/* Align size(up to power of 2) */
size = KFIFO_ALIGN_POW2_UP(size);
/* Allocate buffer */
if (kfifo->buf) {
buf = mr_realloc(kfifo->buf, size);
} else {
buf = mr_malloc(size);
}
if (!buf) {
/* Not enough memory */
return 0;
}
/* Init kfifo */
return mr_kfifo_init(kfifo, buf, size, elem_size);
}
void mr_kfifo_free(mr_kfifo_t *kfifo) {
/* Check need to free */
if (!kfifo->buf) {
return;
}
/* Free buffer */
mr_free(kfifo->buf);
kfifo->buf = MR_NULL;
}
mr_uint32_t mr_kfifo_push(mr_kfifo_t *kfifo, const void *data) {
mr_uint32_t in;
/* Check if kfifo is full */
if (mr_kfifo_is_full(kfifo)) {
return 0;
}
/* Get in index */
in = kfifo->in & kfifo->mask;
if (kfifo->elem_size != 1) {
in *= kfifo->elem_size;
}
/* Push data */
mr_memcpy((mr_uint8_t *)kfifo->buf + in, data, kfifo->elem_size);
kfifo->in += 1;
return 1;
}
mr_uint32_t mr_kfifo_pop(mr_kfifo_t *kfifo, void *data) {
mr_uint32_t out;
/* Check if kfifo is empty */
if (mr_kfifo_is_empty(kfifo)) {
return 0;
}
/* Get out index */
out = kfifo->out & kfifo->mask;
if (kfifo->elem_size != 1) {
out *= kfifo->elem_size;
}
/* Pop data */
mr_memcpy(data, (mr_uint8_t *)kfifo->buf + out, kfifo->elem_size);
kfifo->out += 1;
return 1;
}
mr_uint32_t mr_kfifo_in(mr_kfifo_t *kfifo, const void *buf, mr_uint32_t num) {
mr_uint32_t avail, num2, in, size, n;
/* Limit length */
avail = mr_kfifo_avail(kfifo);
if (num > avail) {
num = avail;
}
if (num == 0) {
return 0;
}
num2 = num;
/* Get in index */
in = kfifo->in & kfifo->mask;
size = kfifo->mask + 1;
if (kfifo->elem_size != 1) {
in *= kfifo->elem_size;
size *= kfifo->elem_size;
num *= kfifo->elem_size;
}
n = MR_MIN(num, size - in);
/* Push buffer */
mr_memcpy((mr_uint8_t *)kfifo->buf + in, buf, n);
mr_memcpy(kfifo->buf, ((mr_uint8_t *)buf) + n, num - n);
kfifo->in += num2;
return num2;
}
mr_uint32_t mr_kfifo_peek(mr_kfifo_t *kfifo, void *buf, mr_uint32_t num) {
mr_uint32_t avail, num2, out, size, n;
/* Limit size */
avail = mr_kfifo_len(kfifo);
if (num > avail) {
num = avail;
}
if (num == 0) {
return 0;
}
num2 = num;
/* Get out index */
out = kfifo->out & kfifo->mask;
size = kfifo->mask + 1;
if (kfifo->elem_size != 1) {
out *= kfifo->elem_size;
size *= kfifo->elem_size;
num *= kfifo->elem_size;
}
/* Pop buffer */
n = MR_MIN(num, size - out);
mr_memcpy(buf, (mr_uint8_t *)kfifo->buf + out, n);
mr_memcpy((mr_uint8_t *)buf + n, kfifo->buf, num - n);
return num2;
}