Skip to content

Commit de7a5ac

Browse files
committed
lib: bm_fifo: a FIFO queue implementation
A simple FIFO queue implementation using a circular buffer, that is ISR-safe. Signed-off-by: Emanuele Di Santo <[email protected]>
1 parent 0210b39 commit de7a5ac

File tree

9 files changed

+820
-0
lines changed

9 files changed

+820
-0
lines changed

include/bm_fifo.h

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
#include <stddef.h>
7+
#include <stdint.h>
8+
9+
struct bm_fifo {
10+
/**
11+
* @brief FIFO buffer.
12+
*/
13+
void *buf;
14+
/**
15+
* @brief FIFO maximum capacity (number of items).
16+
*/
17+
uint32_t capacity;
18+
/**
19+
* @brief FIFO item size.
20+
*/
21+
size_t item_size;
22+
/**
23+
* @brief Number of items in the queue.
24+
*/
25+
uint32_t count;
26+
/**
27+
* @brief FIFO head.
28+
*/
29+
int head;
30+
/**
31+
* @brief FIFO tail.
32+
*/
33+
int tail;
34+
};
35+
36+
/**
37+
* @brief Statically define a FIFO.
38+
*
39+
* Avoids the @ref bm_fifo_init() call.
40+
*/
41+
#define BM_FIFO_INIT(_name, _capacity, _item_size) \
42+
static uint8_t _name_buf[_item_size * _capacity]; \
43+
static struct bm_fifo _name = { \
44+
.buf = _name_buf, \
45+
.item_size = _item_size, \
46+
.capacity = _capacity, \
47+
}
48+
49+
/**
50+
* @brief Initialize a queue.
51+
*
52+
* @param fifo FIFO queue.
53+
* @param buf Queue buffer.
54+
* @param capacity Buffer capacity, in number of items.
55+
* @param item_size Size of a queue element, in bytes.
56+
*
57+
* @retval 0 on success.
58+
* @retval -EFAULT If @p fifo or @p buf are @c NULL.
59+
* @retval -EINVAL If @p capacity or @p item_size are 0.
60+
*/
61+
int bm_fifo_init(struct bm_fifo *fifo, void *buf, size_t capacity, size_t item_size);
62+
63+
/**
64+
* @brief Check whether the queue is full.
65+
*
66+
* @param fifo FIFO queue.
67+
*
68+
* @return true Queue is full.
69+
* @return false Queue is not full.
70+
*/
71+
bool bm_fifo_is_full(const struct bm_fifo *const fifo);
72+
73+
/**
74+
* @brief Check whether the queue is empty.
75+
*
76+
* @param fifo FIFO queue.
77+
*
78+
* @return true Queue is empty.
79+
* @return false Queue is not empty.
80+
*/
81+
bool bm_fifo_is_empty(const struct bm_fifo *const fifo);
82+
83+
/**
84+
* @brief Queue an element.
85+
*
86+
* The element is copied into the queue's own buffer.
87+
* Interrupts are disabled during the copy.
88+
*
89+
* @param fifo FIFO queue.
90+
* @param buf Buffer pointing to the element.
91+
*
92+
* @retval 0 on success.
93+
* @retval -EFAULT If @p fifo or @p buf are @c NULL.
94+
* @retval -ENOBUFS If there are no buffers available in the queue.
95+
*/
96+
int bm_fifo_enqueue(struct bm_fifo *fifo, void *buf);
97+
98+
/**
99+
* @brief Dequeue an element.
100+
*
101+
* Dequeue an element from the queue's head.
102+
*
103+
* @param fifo FIFO queue.
104+
* @param buf Buffer to copy the element into.
105+
*
106+
* @retval 0 on success.
107+
* @retval -EFAULT If @p fifo or @p buf are @c NULL.
108+
* @retval -ENOENT If the queue is empty.
109+
*/
110+
int bm_fifo_dequeue(struct bm_fifo *fifo, void *buf);
111+
112+
/**
113+
* @brief Peek at the queue.
114+
*
115+
* Peek at the queue's head.
116+
*
117+
* @param fifo FIFO queue.
118+
* @param buf Buffer to copy the element into.
119+
*
120+
* @retval 0 on success.
121+
* @retval -EFAULT If @p fifo or @p buf are @c NULL.
122+
* @retval -ENOENT If the queue is empty.
123+
*/
124+
int bm_fifo_peek(const struct bm_fifo *fifo, void *buf);
125+
126+
/**
127+
* @brief Dequeue one element and discard it.
128+
*
129+
* Dequeue an element and discard it.
130+
*
131+
* @param fifo FIFO queue.
132+
*
133+
* @retval 0 on success.
134+
* @retval -EFAULT If @p fifo is @c NULL.
135+
* @retval -ENOENT If the queue is empty.
136+
*/
137+
int bm_fifo_discard(struct bm_fifo *fifo);
138+
139+
/**
140+
* @brief Clear the queue, discarding all elements.
141+
*
142+
* @param fifo FIFO queue.
143+
*
144+
* @retval 0 on success.
145+
* @retval -EFAULT If @p fifo is @c NULL.
146+
*/
147+
int bm_fifo_clear(struct bm_fifo *fifo);

lib/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ add_subdirectory_ifdef(CONFIG_BLE_RACP ble_racp)
1111
add_subdirectory_ifdef(CONFIG_EVENT_SCHEDULER event_scheduler)
1212
add_subdirectory_ifdef(CONFIG_BM_BUTTONS bm_buttons)
1313
add_subdirectory_ifdef(CONFIG_BM_TIMER bm_timer)
14+
add_subdirectory_ifdef(CONFIG_BM_FIFO bm_fifo)
1415
add_subdirectory_ifdef(CONFIG_BLE_QWR ble_qwr)
1516
add_subdirectory_ifdef(CONFIG_SENSORSIM sensorsim)
1617
add_subdirectory_ifdef(CONFIG_NCS_BARE_METAL_BOOT_BANNER boot_banner)

lib/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ rsource "ble_racp/Kconfig"
1212
rsource "event_scheduler/Kconfig"
1313
rsource "bm_buttons/Kconfig"
1414
rsource "bm_timer/Kconfig"
15+
rsource "bm_fifo/Kconfig"
1516
rsource "ble_qwr/Kconfig"
1617
rsource "sensorsim/Kconfig"
1718
rsource "boot_banner/Kconfig"

lib/bm_fifo/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#
2+
# Copyright (c) 2025 Nordic Semiconductor ASA
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
zephyr_library()
7+
zephyr_library_sources(bm_fifo.c)

lib/bm_fifo/Kconfig

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#
2+
# Copyright (c) 2025 Nordic Semiconductor
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
menuconfig BM_FIFO
7+
bool "FIFO queue library"
8+
help
9+
A simple FIFO queue using a circular buffer.
10+
11+
if BM_FIFO
12+
13+
module=BM_FIFO
14+
module-dep=LOG
15+
module-str=FIFO library
16+
source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config"
17+
18+
endif

lib/bm_fifo/bm_fifo.c

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
#include <stddef.h>
7+
#include <stdbool.h>
8+
#include <stdint.h>
9+
#include <string.h>
10+
#include <errno.h>
11+
#include <bm_fifo.h>
12+
#if CONFIG_CPU_ARM_M
13+
#include <cmsis_compiler.h> /* PRIMASK */
14+
#endif
15+
#include <zephyr/sys/__assert.h> /* __ASSERT */
16+
17+
/* SoftDevice IRQs are defined as Zero latency IRQs.
18+
* We can't lock those with Zephyr's irq_lock().
19+
*/
20+
#if CONFIG_CPU_ARM_M
21+
static int primask;
22+
#endif
23+
24+
static void bm_fifo_critical_region_enter(void)
25+
{
26+
#if CONFIG_CPU_ARM_M
27+
/* Save PRIMASK */
28+
primask = __get_PRIMASK();
29+
__disable_irq();
30+
#endif
31+
}
32+
33+
static void bm_fifo_critical_region_exit(void)
34+
{
35+
#if CONFIG_CPU_ARM_M
36+
/* Restore PRIMASK */
37+
__set_PRIMASK(primask);
38+
#endif
39+
}
40+
41+
bool bm_fifo_is_full(const struct bm_fifo *const fifo)
42+
{
43+
__ASSERT_NO_MSG(fifo);
44+
return (fifo->count == fifo->capacity);
45+
}
46+
47+
bool bm_fifo_is_empty(const struct bm_fifo *const fifo)
48+
{
49+
__ASSERT_NO_MSG(fifo);
50+
return (fifo->count == 0);
51+
}
52+
53+
int bm_fifo_init(struct bm_fifo *fifo, void *buf, size_t capacity, size_t item_size)
54+
{
55+
if (!fifo) {
56+
return -EFAULT;
57+
}
58+
if (!item_size || !capacity) {
59+
return -EINVAL;
60+
}
61+
62+
memset(fifo, 0x00, sizeof(*fifo));
63+
64+
fifo->buf = buf;
65+
fifo->item_size = item_size;
66+
fifo->capacity = capacity;
67+
68+
return 0;
69+
}
70+
71+
int bm_fifo_enqueue(struct bm_fifo *fifo, void *buf)
72+
{
73+
int err;
74+
void *item;
75+
76+
if (!fifo || !buf) {
77+
return -EFAULT;
78+
}
79+
80+
bm_fifo_critical_region_enter();
81+
82+
if (bm_fifo_is_full(fifo)) {
83+
err = -ENOBUFS;
84+
goto out;
85+
}
86+
87+
fifo->count++;
88+
__ASSERT(fifo->count <= fifo->capacity, "Queue overflow");
89+
90+
item = (uint8_t *)fifo->buf + (fifo->tail * fifo->item_size);
91+
fifo->tail = (fifo->tail + 1) % fifo->capacity;
92+
memcpy(item, buf, fifo->item_size);
93+
94+
err = 0;
95+
96+
out:
97+
bm_fifo_critical_region_exit();
98+
return err;
99+
}
100+
101+
int bm_fifo_dequeue(struct bm_fifo *fifo, void *buf)
102+
{
103+
int err;
104+
void *item;
105+
106+
if (!fifo || !buf) {
107+
return -EFAULT;
108+
}
109+
110+
bm_fifo_critical_region_enter();
111+
112+
if (bm_fifo_is_empty(fifo)) {
113+
err = -ENOENT;
114+
goto out;
115+
}
116+
117+
fifo->count--;
118+
__ASSERT(fifo->count <= fifo->capacity, "Queue underflow");
119+
120+
item = (uint8_t *)fifo->buf + (fifo->head * fifo->item_size);
121+
fifo->head = (fifo->head + 1) % fifo->capacity;
122+
memcpy(buf, item, fifo->item_size);
123+
124+
err = 0;
125+
126+
out:
127+
bm_fifo_critical_region_exit();
128+
return err;
129+
}
130+
131+
int bm_fifo_peek(const struct bm_fifo *fifo, void *buf)
132+
{
133+
int err;
134+
void *item;
135+
136+
if (!fifo || !buf) {
137+
return -EFAULT;
138+
}
139+
140+
bm_fifo_critical_region_enter();
141+
142+
if (bm_fifo_is_empty(fifo)) {
143+
err = -ENOENT;
144+
goto out;
145+
}
146+
147+
item = (uint8_t *)fifo->buf + (fifo->head * fifo->item_size);
148+
memcpy(buf, item, fifo->item_size);
149+
150+
err = 0;
151+
152+
out:
153+
bm_fifo_critical_region_exit();
154+
return err;
155+
}
156+
157+
int bm_fifo_discard(struct bm_fifo *fifo)
158+
{
159+
int err;
160+
161+
if (!fifo) {
162+
return -EFAULT;
163+
}
164+
165+
bm_fifo_critical_region_enter();
166+
167+
if (bm_fifo_is_empty(fifo)) {
168+
err = -ENOENT;
169+
goto out;
170+
}
171+
172+
fifo->count--;
173+
__ASSERT(fifo->count <= fifo->capacity, "Queue underflow");
174+
175+
fifo->head = (fifo->head + 1) % fifo->capacity;
176+
177+
err = 0;
178+
179+
out:
180+
bm_fifo_critical_region_exit();
181+
return err;
182+
}
183+
184+
int bm_fifo_clear(struct bm_fifo *fifo)
185+
{
186+
if (!fifo) {
187+
return -EFAULT;
188+
}
189+
190+
bm_fifo_critical_region_enter();
191+
192+
fifo->head = 0;
193+
fifo->tail = 0;
194+
fifo->count = 0;
195+
196+
bm_fifo_critical_region_exit();
197+
198+
return 0;
199+
}

0 commit comments

Comments
 (0)