lib: os: Rename icmsg_buf to spsc_pbuf
Move icmsg_buf to lib/os and rename to spsc_pbuf (Single Producer Single Consumer Packet Buffer). It is a generic module and initially was created as internal module for ipc service. Signed-off-by: Krzysztof Chruscinski <krzysztof.chruscinski@nordicsemi.no>
This commit is contained in:
parent
5139d052d8
commit
2f189e39a5
15 changed files with 240 additions and 241 deletions
|
@ -4,16 +4,16 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef ZEPHYR_INCLUDE_IPC_SERVICE_IPC_ICMSG_BUF_H_
|
#ifndef ZEPHYR_INCLUDE_SYS_SPSC_PBUF_H_
|
||||||
#define ZEPHYR_INCLUDE_IPC_SERVICE_IPC_ICMSG_BUF_H_
|
#define ZEPHYR_INCLUDE_SYS_SPSC_PBUF_H_
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief IPC Service ICMsg buffer API
|
* @brief Single producer, single consumer packet buffer API
|
||||||
* @ingroup ipc_service_icmsg_buffer_api IPC service ICMsg buffer API
|
* @ingroup kernel_apis
|
||||||
* @{
|
* @{
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ extern "C" {
|
||||||
* is encapsulated to a message.
|
* is encapsulated to a message.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
struct icmsg_buf {
|
struct spsc_pbuf {
|
||||||
uint32_t len; /* Length of data[] in bytes. */
|
uint32_t len; /* Length of data[] in bytes. */
|
||||||
uint32_t wr_idx; /* Index of the first free byte in data[] */
|
uint32_t wr_idx; /* Index of the first free byte in data[] */
|
||||||
uint32_t rd_idx; /* Index of the first valid byte in data[] */
|
uint32_t rd_idx; /* Index of the first valid byte in data[] */
|
||||||
|
@ -37,9 +37,9 @@ struct icmsg_buf {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initialize inter core messaging buffer.
|
* @brief Initialize the packet buffer.
|
||||||
*
|
*
|
||||||
* This function initializes inter core messaging buffer on top of dedicated
|
* This function initializes the packet buffer on top of a dedicated
|
||||||
* memory region.
|
* memory region.
|
||||||
*
|
*
|
||||||
* @param buf Pointer to a memory region on which buffer is
|
* @param buf Pointer to a memory region on which buffer is
|
||||||
|
@ -48,39 +48,39 @@ struct icmsg_buf {
|
||||||
* contain the internal structure and at least two
|
* contain the internal structure and at least two
|
||||||
* bytes of data (one is reserved for written
|
* bytes of data (one is reserved for written
|
||||||
* messages length).
|
* messages length).
|
||||||
* @retval struct icmsg_buf* Pointer to the created buffer. The pointer
|
* @retval struct spsc_pbuf* Pointer to the created buffer. The pointer
|
||||||
* points to the same address as buf.
|
* points to the same address as buf.
|
||||||
*/
|
*/
|
||||||
struct icmsg_buf *icmsg_buf_init(void *buf, size_t blen);
|
struct spsc_pbuf *spsc_pbuf_init(void *buf, size_t blen);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Write specified amount of data to the inter core messaging buffer.
|
* @brief Write specified amount of data to the packet buffer.
|
||||||
*
|
*
|
||||||
* @param ib A icmsg buffer to which to write.
|
* @param pb A buffer to which to write.
|
||||||
* @param buf Pointer to the data to be written to icmsg buffer.
|
* @param buf Pointer to the data to be written to the buffer.
|
||||||
* @param len Number of bytes to be written to the icmsg buffer.
|
* @param len Number of bytes to be written to the buffer.
|
||||||
* @retval int Number of bytes written, negative error code on fail.
|
* @retval int Number of bytes written, negative error code on fail.
|
||||||
* -EINVAL, if len == 0.
|
* -EINVAL, if len == 0.
|
||||||
* -ENOMEM, if len is bigger than the icmsg buffer can fit.
|
* -ENOMEM, if len is bigger than the buffer can fit.
|
||||||
*/
|
*/
|
||||||
int icmsg_buf_write(struct icmsg_buf *ib, const char *buf, uint16_t len);
|
int spsc_pbuf_write(struct spsc_pbuf *pb, const char *buf, uint16_t len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read specified amount of data from the inter core messaging buffer.
|
* @brief Read specified amount of data from the packet buffer.
|
||||||
*
|
*
|
||||||
* Single read allows to read the message send by the single write.
|
* Single read allows to read the message send by the single write.
|
||||||
* The provided buf must be big enough to store the whole message.
|
* The provided buf must be big enough to store the whole message.
|
||||||
*
|
*
|
||||||
* @param ib A icmsg buffer to which data are to be written
|
* @param pb A buffer from which data is to be read.
|
||||||
* @param buf Data pointer to which read data will be written.
|
* @param buf Data pointer to which read data will be written.
|
||||||
* If NULL, len of stored message is returned.
|
* If NULL, len of stored message is returned.
|
||||||
* @param len Number of bytes to be read from the icmsg buffer.
|
* @param len Number of bytes to be read from the buffer.
|
||||||
* @retval int Bytes read, negative error code on fail.
|
* @retval int Bytes read, negative error code on fail.
|
||||||
* Bytes to be read, if buf == NULL.
|
* Bytes to be read, if buf == NULL.
|
||||||
* -ENOMEM, if message can not fit in provided buf.
|
* -ENOMEM, if message can not fit in provided buf.
|
||||||
* -EAGAIN, if not whole message is ready yet.
|
* -EAGAIN, if not whole message is ready yet.
|
||||||
*/
|
*/
|
||||||
int icmsg_buf_read(struct icmsg_buf *ib, char *buf, uint16_t len);
|
int spsc_pbuf_read(struct spsc_pbuf *pb, char *buf, uint16_t len);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -91,4 +91,4 @@ int icmsg_buf_read(struct icmsg_buf *ib, char *buf, uint16_t len);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* ZEPHYR_INCLUDE_IPC_SERVICE_IPC_ICMSG_BUF_H_ */
|
#endif /* ZEPHYR_INCLUDE_SYS_SPSC_PBUF_H_ */
|
|
@ -37,11 +37,12 @@ if (CONFIG_ASSERT OR CONFIG_ASSERT_VERBOSE)
|
||||||
zephyr_sources(assert.c)
|
zephyr_sources(assert.c)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
zephyr_sources_ifdef(CONFIG_USERSPACE mutex.c user_work.c)
|
zephyr_sources_ifdef(CONFIG_USERSPACE mutex.c user_work.c)
|
||||||
|
|
||||||
zephyr_sources_ifdef(CONFIG_MPSC_PBUF mpsc_pbuf.c)
|
zephyr_sources_ifdef(CONFIG_MPSC_PBUF mpsc_pbuf.c)
|
||||||
|
|
||||||
|
zephyr_sources_ifdef(CONFIG_SPSC_PBUF spsc_pbuf.c)
|
||||||
|
|
||||||
zephyr_sources_ifdef(CONFIG_SCHED_DEADLINE p4wq.c)
|
zephyr_sources_ifdef(CONFIG_SCHED_DEADLINE p4wq.c)
|
||||||
|
|
||||||
zephyr_sources_ifdef(CONFIG_REBOOT reboot.c)
|
zephyr_sources_ifdef(CONFIG_REBOOT reboot.c)
|
||||||
|
|
|
@ -38,6 +38,13 @@ config MPSC_PBUF
|
||||||
storing variable length packets in a circular way and operate directly
|
storing variable length packets in a circular way and operate directly
|
||||||
on the buffer memory.
|
on the buffer memory.
|
||||||
|
|
||||||
|
config SPSC_PBUF
|
||||||
|
bool "Single producer, single consumer packet buffer"
|
||||||
|
help
|
||||||
|
Enable usage of spsc packet buffer. Packet buffer is capable of
|
||||||
|
storing variable length packets in a circular way and operate directly
|
||||||
|
on the buffer memory.
|
||||||
|
|
||||||
config SHARED_MULTI_HEAP
|
config SHARED_MULTI_HEAP
|
||||||
bool "Shared multi-heap manager"
|
bool "Shared multi-heap manager"
|
||||||
help
|
help
|
||||||
|
|
171
lib/os/spsc_pbuf.c
Normal file
171
lib/os/spsc_pbuf.c
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/zephyr.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <zephyr/cache.h>
|
||||||
|
#include <zephyr/sys/spsc_pbuf.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* Helpers */
|
||||||
|
static uint32_t idx_occupied(uint32_t len, uint32_t a, uint32_t b)
|
||||||
|
{
|
||||||
|
/* It is implicitly assumed a and b cannot differ by more then len. */
|
||||||
|
return (b > a) ? (len - (b - a)) : (a - b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t idx_cut(uint32_t len, uint32_t idx)
|
||||||
|
{
|
||||||
|
/* It is implicitly assumed a and b cannot differ by more then len. */
|
||||||
|
return (idx >= len) ? (idx - len) : (idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct spsc_pbuf *spsc_pbuf_init(void *buf, size_t blen)
|
||||||
|
{
|
||||||
|
/* blen must be big enough to contain spsc_pbuf struct, byte of data
|
||||||
|
* and message len (2 bytes).
|
||||||
|
*/
|
||||||
|
struct spsc_pbuf *pb = buf;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(blen > (sizeof(*pb) + sizeof(uint16_t)));
|
||||||
|
|
||||||
|
pb->len = blen - sizeof(*pb);
|
||||||
|
pb->wr_idx = 0;
|
||||||
|
pb->rd_idx = 0;
|
||||||
|
|
||||||
|
__sync_synchronize();
|
||||||
|
sys_cache_data_range(pb, sizeof(*pb), K_CACHE_WB);
|
||||||
|
|
||||||
|
return pb;
|
||||||
|
}
|
||||||
|
|
||||||
|
int spsc_pbuf_write(struct spsc_pbuf *pb, const char *buf, uint16_t len)
|
||||||
|
{
|
||||||
|
/* The length of buffer is immutable - avoid reloading that may happen
|
||||||
|
* due to memory bariers.
|
||||||
|
*/
|
||||||
|
const uint32_t pblen = pb->len;
|
||||||
|
|
||||||
|
/* rx_idx == wr_idx means the buffer is empty.
|
||||||
|
* Max bytes that can be stored is len - 1.
|
||||||
|
*/
|
||||||
|
const uint32_t max_len = pblen - 1;
|
||||||
|
|
||||||
|
sys_cache_data_range(pb, sizeof(*pb), K_CACHE_INVD);
|
||||||
|
__sync_synchronize();
|
||||||
|
|
||||||
|
uint32_t wr_idx = pb->wr_idx;
|
||||||
|
uint32_t rd_idx = pb->rd_idx;
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
/* Incorrect call. */
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t avail = max_len - idx_occupied(pblen, wr_idx, rd_idx);
|
||||||
|
|
||||||
|
if ((len + sizeof(len) > avail) ||
|
||||||
|
(len + sizeof(len) > max_len)) {
|
||||||
|
/* No free space. */
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store info about the message length. */
|
||||||
|
pb->data[wr_idx] = (uint8_t)len;
|
||||||
|
sys_cache_data_range(&pb->data[wr_idx], sizeof(pb->data[wr_idx]), K_CACHE_WB);
|
||||||
|
wr_idx = idx_cut(pblen, wr_idx + sizeof(pb->data[wr_idx]));
|
||||||
|
|
||||||
|
pb->data[wr_idx] = (uint8_t)(len >> 8);
|
||||||
|
sys_cache_data_range(&pb->data[wr_idx], sizeof(pb->data[wr_idx]), K_CACHE_WB);
|
||||||
|
wr_idx = idx_cut(pblen, wr_idx + sizeof(pb->data[wr_idx]));
|
||||||
|
|
||||||
|
/* Write until the end of the buffer. */
|
||||||
|
uint32_t sz = MIN(len, pblen - wr_idx);
|
||||||
|
|
||||||
|
memcpy(&pb->data[wr_idx], buf, sz);
|
||||||
|
sys_cache_data_range(&pb->data[wr_idx], sz, K_CACHE_WB);
|
||||||
|
|
||||||
|
if (len > sz) {
|
||||||
|
/* Write remaining data at the buffer head. */
|
||||||
|
memcpy(&pb->data[0], buf + sz, len - sz);
|
||||||
|
sys_cache_data_range(&pb->data[0], len - sz, K_CACHE_WB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update write index - make other side aware data was written. */
|
||||||
|
__sync_synchronize();
|
||||||
|
wr_idx = idx_cut(pblen, wr_idx + len);
|
||||||
|
pb->wr_idx = wr_idx;
|
||||||
|
|
||||||
|
sys_cache_data_range(pb, sizeof(*pb), K_CACHE_WB);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int spsc_pbuf_read(struct spsc_pbuf *pb, char *buf, uint16_t len)
|
||||||
|
{
|
||||||
|
/* The length of buffer is immutable - avoid reloading. */
|
||||||
|
const uint32_t pblen = pb->len;
|
||||||
|
|
||||||
|
sys_cache_data_range(pb, sizeof(*pb), K_CACHE_INVD);
|
||||||
|
__sync_synchronize();
|
||||||
|
|
||||||
|
uint32_t rd_idx = pb->rd_idx;
|
||||||
|
uint32_t wr_idx = pb->wr_idx;
|
||||||
|
|
||||||
|
if (rd_idx == wr_idx) {
|
||||||
|
/* The buffer is empty. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t bytes_stored = idx_occupied(pblen, wr_idx, rd_idx);
|
||||||
|
|
||||||
|
/* Read message len. */
|
||||||
|
sys_cache_data_range(&pb->data[rd_idx], sizeof(pb->data[rd_idx]), K_CACHE_INVD);
|
||||||
|
uint16_t mlen = pb->data[rd_idx];
|
||||||
|
|
||||||
|
rd_idx = idx_cut(pblen, rd_idx + sizeof(pb->data[rd_idx]));
|
||||||
|
|
||||||
|
sys_cache_data_range(&pb->data[rd_idx], sizeof(pb->data[rd_idx]), K_CACHE_INVD);
|
||||||
|
mlen |= (pb->data[rd_idx] << 8);
|
||||||
|
rd_idx = idx_cut(pblen, rd_idx + sizeof(pb->data[rd_idx]));
|
||||||
|
|
||||||
|
if (!buf) {
|
||||||
|
return mlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len < mlen) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes_stored < mlen + sizeof(mlen)) {
|
||||||
|
/* Part of message not available. Should not happen. */
|
||||||
|
__ASSERT_NO_MSG(false);
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = MIN(len, mlen);
|
||||||
|
|
||||||
|
/* Read up to the end of the buffer. */
|
||||||
|
uint32_t sz = MIN(len, pblen - rd_idx);
|
||||||
|
|
||||||
|
sys_cache_data_range(&pb->data[rd_idx], sz, K_CACHE_INVD);
|
||||||
|
memcpy(buf, &pb->data[rd_idx], sz);
|
||||||
|
if (len > sz) {
|
||||||
|
/* Read remaining bytes starting from the buffer head. */
|
||||||
|
sys_cache_data_range(&pb->data[0], len - sz, K_CACHE_INVD);
|
||||||
|
memcpy(&buf[sz], &pb->data[0], len - sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update read index - make other side aware data was read. */
|
||||||
|
__sync_synchronize();
|
||||||
|
rd_idx = idx_cut(pblen, rd_idx + len);
|
||||||
|
pb->rd_idx = rd_idx;
|
||||||
|
|
||||||
|
sys_cache_data_range(pb, sizeof(*pb), K_CACHE_WB);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
|
@ -19,13 +19,13 @@ config IPC_SERVICE_BACKEND_RPMSG
|
||||||
select OPENAMP
|
select OPENAMP
|
||||||
|
|
||||||
config IPC_SERVICE_BACKEND_ICMSG
|
config IPC_SERVICE_BACKEND_ICMSG
|
||||||
bool "ICMSG backend with ICMsg buffer"
|
bool "ICMSG backend with SPSC packet buffer"
|
||||||
depends on MBOX
|
depends on MBOX
|
||||||
default $(dt_compat_enabled,$(DT_COMPAT_ZEPHYR_IPC_ICMSG))
|
default $(dt_compat_enabled,$(DT_COMPAT_ZEPHYR_IPC_ICMSG))
|
||||||
select IPC_SERVICE_ICMSG_BUF
|
select SPSC_PBUF
|
||||||
help
|
help
|
||||||
Chosing this backend results in single endpoint implementation based
|
Chosing this backend results in single endpoint implementation based
|
||||||
on ringbuf.
|
on circular packet buffer.
|
||||||
|
|
||||||
config IPC_SERVICE_RPMSG
|
config IPC_SERVICE_RPMSG
|
||||||
bool "RPMsg support library"
|
bool "RPMsg support library"
|
||||||
|
@ -44,10 +44,5 @@ config IPC_SERVICE_STATIC_VRINGS_ALIGNMENT
|
||||||
help
|
help
|
||||||
Static VRINGs alignment
|
Static VRINGs alignment
|
||||||
|
|
||||||
config IPC_SERVICE_ICMSG_BUF
|
|
||||||
bool "Inter core messaging buffer support library"
|
|
||||||
help
|
|
||||||
Inter core messaging buffer library
|
|
||||||
|
|
||||||
rsource "Kconfig.icmsg"
|
rsource "Kconfig.icmsg"
|
||||||
rsource "Kconfig.rpmsg"
|
rsource "Kconfig.rpmsg"
|
||||||
|
|
|
@ -22,8 +22,8 @@ BUILD_ASSERT(CB_BUF_SIZE <= UINT16_MAX);
|
||||||
|
|
||||||
struct backend_data_t {
|
struct backend_data_t {
|
||||||
/* Tx/Rx buffers. */
|
/* Tx/Rx buffers. */
|
||||||
struct icmsg_buf *tx_ib;
|
struct spsc_pbuf *tx_ib;
|
||||||
struct icmsg_buf *rx_ib;
|
struct spsc_pbuf *rx_ib;
|
||||||
|
|
||||||
/* Backend ops for an endpoint. */
|
/* Backend ops for an endpoint. */
|
||||||
const struct ipc_ept_cfg *cfg;
|
const struct ipc_ept_cfg *cfg;
|
||||||
|
@ -49,7 +49,7 @@ static void mbox_callback_process(struct k_work *item)
|
||||||
uint8_t cb_buffer[CB_BUF_SIZE] __aligned(4);
|
uint8_t cb_buffer[CB_BUF_SIZE] __aligned(4);
|
||||||
|
|
||||||
atomic_t state = atomic_get(&dev_data->state);
|
atomic_t state = atomic_get(&dev_data->state);
|
||||||
int len = icmsg_buf_read(dev_data->rx_ib, cb_buffer, CB_BUF_SIZE);
|
int len = spsc_pbuf_read(dev_data->rx_ib, cb_buffer, CB_BUF_SIZE);
|
||||||
|
|
||||||
__ASSERT_NO_MSG(len <= CB_BUF_SIZE);
|
__ASSERT_NO_MSG(len <= CB_BUF_SIZE);
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ static void mbox_callback_process(struct k_work *item)
|
||||||
/* Reading with NULL buffer to know if there are data in the
|
/* Reading with NULL buffer to know if there are data in the
|
||||||
* buffer to be read.
|
* buffer to be read.
|
||||||
*/
|
*/
|
||||||
len = icmsg_buf_read(dev_data->rx_ib, NULL, 0);
|
len = spsc_pbuf_read(dev_data->rx_ib, NULL, 0);
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
(void)k_work_submit(&dev_data->mbox_work);
|
(void)k_work_submit(&dev_data->mbox_work);
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,7 @@ static int register_ept(const struct device *instance, void **token,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = icmsg_buf_write(dev_data->tx_ib, magic, sizeof(magic));
|
ret = spsc_pbuf_write(dev_data->tx_ib, magic, sizeof(magic));
|
||||||
if (ret < sizeof(magic)) {
|
if (ret < sizeof(magic)) {
|
||||||
__ASSERT_NO_MSG(ret == sizeof(magic));
|
__ASSERT_NO_MSG(ret == sizeof(magic));
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -144,7 +144,7 @@ static int register_ept(const struct device *instance, void **token,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = icmsg_buf_read(dev_data->rx_ib, NULL, 0);
|
ret = spsc_pbuf_read(dev_data->rx_ib, NULL, 0);
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
(void)k_work_submit(&dev_data->mbox_work);
|
(void)k_work_submit(&dev_data->mbox_work);
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@ static int send(const struct device *instance, void *token,
|
||||||
return -ENODATA;
|
return -ENODATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = icmsg_buf_write(dev_data->tx_ib, msg, len);
|
ret = spsc_pbuf_write(dev_data->tx_ib, msg, len);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
} else if (ret < len) {
|
} else if (ret < len) {
|
||||||
|
@ -190,9 +190,9 @@ static int backend_init(const struct device *instance)
|
||||||
const struct backend_config_t *conf = instance->config;
|
const struct backend_config_t *conf = instance->config;
|
||||||
struct backend_data_t *dev_data = instance->data;
|
struct backend_data_t *dev_data = instance->data;
|
||||||
|
|
||||||
__ASSERT_NO_MSG(conf->tx_shm_size > sizeof(struct icmsg_buf));
|
__ASSERT_NO_MSG(conf->tx_shm_size > sizeof(struct spsc_pbuf));
|
||||||
|
|
||||||
dev_data->tx_ib = icmsg_buf_init((void *)conf->tx_shm_addr, conf->tx_shm_size);
|
dev_data->tx_ib = spsc_pbuf_init((void *)conf->tx_shm_addr, conf->tx_shm_size);
|
||||||
dev_data->rx_ib = (void *)conf->rx_shm_addr;
|
dev_data->rx_ib = (void *)conf->rx_shm_addr;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <zephyr/ipc/ipc_icmsg_buf.h>
|
#include <zephyr/sys/spsc_pbuf.h>
|
||||||
|
|
||||||
enum icmsg_state {
|
enum icmsg_state {
|
||||||
ICMSG_STATE_OFF,
|
ICMSG_STATE_OFF,
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
zephyr_sources_ifdef(CONFIG_IPC_SERVICE_ICMSG_BUF ipc_icmsg_buf.c)
|
|
||||||
zephyr_sources_ifdef(CONFIG_IPC_SERVICE_RPMSG ipc_rpmsg.c)
|
zephyr_sources_ifdef(CONFIG_IPC_SERVICE_RPMSG ipc_rpmsg.c)
|
||||||
zephyr_sources_ifdef(CONFIG_IPC_SERVICE_STATIC_VRINGS ipc_static_vrings.c)
|
zephyr_sources_ifdef(CONFIG_IPC_SERVICE_STATIC_VRINGS ipc_static_vrings.c)
|
||||||
|
|
|
@ -1,171 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <zephyr/zephyr.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <zephyr/cache.h>
|
|
||||||
#include <zephyr/ipc/ipc_icmsg_buf.h>
|
|
||||||
|
|
||||||
|
|
||||||
/* Helpers */
|
|
||||||
static uint32_t idx_occupied(uint32_t len, uint32_t a, uint32_t b)
|
|
||||||
{
|
|
||||||
/* It is implicitly assumed a and b cannot differ by more then len. */
|
|
||||||
return (b > a) ? (len - (b - a)) : (a - b);
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t idx_cut(uint32_t len, uint32_t idx)
|
|
||||||
{
|
|
||||||
/* It is implicitly assumed a and b cannot differ by more then len. */
|
|
||||||
return (idx >= len) ? (idx - len) : (idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct icmsg_buf *icmsg_buf_init(void *buf, size_t blen)
|
|
||||||
{
|
|
||||||
/* blen must be big enough to contain icmsg_buf struct, byte of data
|
|
||||||
* and message len (2 bytes).
|
|
||||||
*/
|
|
||||||
struct icmsg_buf *ib = buf;
|
|
||||||
|
|
||||||
__ASSERT_NO_MSG(blen > (sizeof(*ib) + sizeof(uint16_t)));
|
|
||||||
|
|
||||||
ib->len = blen - sizeof(*ib);
|
|
||||||
ib->wr_idx = 0;
|
|
||||||
ib->rd_idx = 0;
|
|
||||||
|
|
||||||
__sync_synchronize();
|
|
||||||
sys_cache_data_range(ib, sizeof(*ib), K_CACHE_WB);
|
|
||||||
|
|
||||||
return ib;
|
|
||||||
}
|
|
||||||
|
|
||||||
int icmsg_buf_write(struct icmsg_buf *ib, const char *buf, uint16_t len)
|
|
||||||
{
|
|
||||||
/* The length of buffer is immutable - avoid reloading that may happen
|
|
||||||
* due to memory bariers.
|
|
||||||
*/
|
|
||||||
const uint32_t iblen = ib->len;
|
|
||||||
|
|
||||||
/* rx_idx == wr_idx means the buffer is empty.
|
|
||||||
* Max bytes that can be stored is len - 1.
|
|
||||||
*/
|
|
||||||
const uint32_t max_len = iblen - 1;
|
|
||||||
|
|
||||||
sys_cache_data_range(ib, sizeof(*ib), K_CACHE_INVD);
|
|
||||||
__sync_synchronize();
|
|
||||||
|
|
||||||
uint32_t wr_idx = ib->wr_idx;
|
|
||||||
uint32_t rd_idx = ib->rd_idx;
|
|
||||||
|
|
||||||
if (len == 0) {
|
|
||||||
/* Incorrect call. */
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t avail = max_len - idx_occupied(iblen, wr_idx, rd_idx);
|
|
||||||
|
|
||||||
if ((len + sizeof(len) > avail) ||
|
|
||||||
(len + sizeof(len) > max_len)) {
|
|
||||||
/* No free space. */
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Store info about the message length. */
|
|
||||||
ib->data[wr_idx] = (uint8_t)len;
|
|
||||||
sys_cache_data_range(&ib->data[wr_idx], sizeof(ib->data[wr_idx]), K_CACHE_WB);
|
|
||||||
wr_idx = idx_cut(iblen, wr_idx + sizeof(ib->data[wr_idx]));
|
|
||||||
|
|
||||||
ib->data[wr_idx] = (uint8_t)(len >> 8);
|
|
||||||
sys_cache_data_range(&ib->data[wr_idx], sizeof(ib->data[wr_idx]), K_CACHE_WB);
|
|
||||||
wr_idx = idx_cut(iblen, wr_idx + sizeof(ib->data[wr_idx]));
|
|
||||||
|
|
||||||
/* Write until the end of the buffer. */
|
|
||||||
uint32_t sz = MIN(len, iblen - wr_idx);
|
|
||||||
|
|
||||||
memcpy(&ib->data[wr_idx], buf, sz);
|
|
||||||
sys_cache_data_range(&ib->data[wr_idx], sz, K_CACHE_WB);
|
|
||||||
|
|
||||||
if (len > sz) {
|
|
||||||
/* Write remaining data at the buffer head. */
|
|
||||||
memcpy(&ib->data[0], buf + sz, len - sz);
|
|
||||||
sys_cache_data_range(&ib->data[0], len - sz, K_CACHE_WB);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update write index - make other side aware data was written. */
|
|
||||||
__sync_synchronize();
|
|
||||||
wr_idx = idx_cut(iblen, wr_idx + len);
|
|
||||||
ib->wr_idx = wr_idx;
|
|
||||||
|
|
||||||
sys_cache_data_range(ib, sizeof(*ib), K_CACHE_WB);
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
int icmsg_buf_read(struct icmsg_buf *ib, char *buf, uint16_t len)
|
|
||||||
{
|
|
||||||
/* The length of buffer is immutable - avoid reloading. */
|
|
||||||
const uint32_t iblen = ib->len;
|
|
||||||
|
|
||||||
sys_cache_data_range(ib, sizeof(*ib), K_CACHE_INVD);
|
|
||||||
__sync_synchronize();
|
|
||||||
|
|
||||||
uint32_t rd_idx = ib->rd_idx;
|
|
||||||
uint32_t wr_idx = ib->wr_idx;
|
|
||||||
|
|
||||||
if (rd_idx == wr_idx) {
|
|
||||||
/* The buffer is empty. */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t bytes_stored = idx_occupied(iblen, wr_idx, rd_idx);
|
|
||||||
|
|
||||||
/* Read message len. */
|
|
||||||
sys_cache_data_range(&ib->data[rd_idx], sizeof(ib->data[rd_idx]), K_CACHE_INVD);
|
|
||||||
uint16_t mlen = ib->data[rd_idx];
|
|
||||||
|
|
||||||
rd_idx = idx_cut(iblen, rd_idx + sizeof(ib->data[rd_idx]));
|
|
||||||
|
|
||||||
sys_cache_data_range(&ib->data[rd_idx], sizeof(ib->data[rd_idx]), K_CACHE_INVD);
|
|
||||||
mlen |= (ib->data[rd_idx] << 8);
|
|
||||||
rd_idx = idx_cut(iblen, rd_idx + sizeof(ib->data[rd_idx]));
|
|
||||||
|
|
||||||
if (!buf) {
|
|
||||||
return mlen;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len < mlen) {
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bytes_stored < mlen + sizeof(mlen)) {
|
|
||||||
/* Part of message not available. Should not happen. */
|
|
||||||
__ASSERT_NO_MSG(false);
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
len = MIN(len, mlen);
|
|
||||||
|
|
||||||
/* Read up to the end of the buffer. */
|
|
||||||
uint32_t sz = MIN(len, iblen - rd_idx);
|
|
||||||
|
|
||||||
sys_cache_data_range(&ib->data[rd_idx], sz, K_CACHE_INVD);
|
|
||||||
memcpy(buf, &ib->data[rd_idx], sz);
|
|
||||||
if (len > sz) {
|
|
||||||
/* Read remaining bytes starting from the buffer head. */
|
|
||||||
sys_cache_data_range(&ib->data[0], len - sz, K_CACHE_INVD);
|
|
||||||
memcpy(&buf[sz], &ib->data[0], len - sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update read index - make other side aware data was read. */
|
|
||||||
__sync_synchronize();
|
|
||||||
rd_idx = idx_cut(iblen, rd_idx + len);
|
|
||||||
ib->rd_idx = rd_idx;
|
|
||||||
|
|
||||||
sys_cache_data_range(ib, sizeof(*ib), K_CACHE_WB);
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.20.0)
|
cmake_minimum_required(VERSION 3.20.0)
|
||||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||||
project(icmsg_buf)
|
project(spsc_pbuf)
|
||||||
|
|
||||||
FILE(GLOB app_sources src/main.c)
|
FILE(GLOB app_sources src/main.c)
|
||||||
target_sources(app PRIVATE ${app_sources})
|
target_sources(app PRIVATE ${app_sources})
|
2
tests/lib/spsc_pbuf/prj.conf
Normal file
2
tests/lib/spsc_pbuf/prj.conf
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
CONFIG_ZTEST=y
|
||||||
|
CONFIG_SPSC_PBUF=y
|
|
@ -5,45 +5,45 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <ztest.h>
|
#include <ztest.h>
|
||||||
#include <zephyr/ipc/ipc_icmsg_buf.h>
|
#include <zephyr/sys/spsc_pbuf.h>
|
||||||
|
|
||||||
/* The buffer size itself would be 199 bytes.
|
/* The buffer size itself would be 199 bytes.
|
||||||
* 212 - sizeof(struct icmsg_buf) - 1 = 199.
|
* 212 - sizeof(struct spsc_pbuf) - 1 = 199.
|
||||||
* -1 because internal rd/wr_idx is reserved to mean the buffer is empty.
|
* -1 because internal rd/wr_idx is reserved to mean the buffer is empty.
|
||||||
*/
|
*/
|
||||||
static uint8_t memory_area[212] __aligned(4);
|
static uint8_t memory_area[212] __aligned(4);
|
||||||
|
|
||||||
static void test_icmsg_buf_ut(void)
|
static void test_spsc_pbuf_ut(void)
|
||||||
{
|
{
|
||||||
static uint8_t rbuf[198];
|
static uint8_t rbuf[198];
|
||||||
static uint8_t message[20] = {'a'};
|
static uint8_t message[20] = {'a'};
|
||||||
struct icmsg_buf *ib;
|
struct spsc_pbuf *ib;
|
||||||
int rlen;
|
int rlen;
|
||||||
int wlen;
|
int wlen;
|
||||||
|
|
||||||
ib = icmsg_buf_init(memory_area, sizeof(memory_area));
|
ib = spsc_pbuf_init(memory_area, sizeof(memory_area));
|
||||||
zassert_equal_ptr(ib, memory_area, NULL);
|
zassert_equal_ptr(ib, memory_area, NULL);
|
||||||
zassert_equal(ib->len, (sizeof(memory_area) - sizeof(*ib)), NULL);
|
zassert_equal(ib->len, (sizeof(memory_area) - sizeof(*ib)), NULL);
|
||||||
zassert_equal(ib->wr_idx, 0, NULL);
|
zassert_equal(ib->wr_idx, 0, NULL);
|
||||||
zassert_equal(ib->rd_idx, 0, NULL);
|
zassert_equal(ib->rd_idx, 0, NULL);
|
||||||
|
|
||||||
/* Try to write more than buffer can store. */
|
/* Try to write more than buffer can store. */
|
||||||
rlen = icmsg_buf_write(ib, rbuf, sizeof(rbuf));
|
rlen = spsc_pbuf_write(ib, rbuf, sizeof(rbuf));
|
||||||
zassert_equal(rlen, -ENOMEM, NULL);
|
zassert_equal(rlen, -ENOMEM, NULL);
|
||||||
zassert_equal(ib->wr_idx, 0, NULL);
|
zassert_equal(ib->wr_idx, 0, NULL);
|
||||||
zassert_equal(ib->rd_idx, 0, NULL);
|
zassert_equal(ib->rd_idx, 0, NULL);
|
||||||
|
|
||||||
/* Read empty buffer. */
|
/* Read empty buffer. */
|
||||||
rlen = icmsg_buf_read(ib, rbuf, sizeof(rbuf));
|
rlen = spsc_pbuf_read(ib, rbuf, sizeof(rbuf));
|
||||||
zassert_equal(rlen, 0, NULL);
|
zassert_equal(rlen, 0, NULL);
|
||||||
|
|
||||||
/* Single write and read. */
|
/* Single write and read. */
|
||||||
wlen = icmsg_buf_write(ib, message, sizeof(message));
|
wlen = spsc_pbuf_write(ib, message, sizeof(message));
|
||||||
zassert_equal(wlen, sizeof(message), NULL);
|
zassert_equal(wlen, sizeof(message), NULL);
|
||||||
zassert_equal(ib->wr_idx, (sizeof(message) + sizeof(uint16_t)), NULL);
|
zassert_equal(ib->wr_idx, (sizeof(message) + sizeof(uint16_t)), NULL);
|
||||||
zassert_equal(ib->rd_idx, 0, NULL);
|
zassert_equal(ib->rd_idx, 0, NULL);
|
||||||
|
|
||||||
rlen = icmsg_buf_read(ib, rbuf, sizeof(rbuf));
|
rlen = spsc_pbuf_read(ib, rbuf, sizeof(rbuf));
|
||||||
zassert_equal(rlen, sizeof(message), NULL);
|
zassert_equal(rlen, sizeof(message), NULL);
|
||||||
zassert_equal(ib->wr_idx, (sizeof(message) + sizeof(uint16_t)), NULL);
|
zassert_equal(ib->wr_idx, (sizeof(message) + sizeof(uint16_t)), NULL);
|
||||||
zassert_equal(ib->rd_idx, (sizeof(message) + sizeof(uint16_t)), NULL);
|
zassert_equal(ib->rd_idx, (sizeof(message) + sizeof(uint16_t)), NULL);
|
||||||
|
@ -55,32 +55,32 @@ static void test_icmsg_buf_ut(void)
|
||||||
*
|
*
|
||||||
* Reset the buffer first.
|
* Reset the buffer first.
|
||||||
*/
|
*/
|
||||||
ib = icmsg_buf_init(memory_area, sizeof(memory_area));
|
ib = spsc_pbuf_init(memory_area, sizeof(memory_area));
|
||||||
zassert_equal_ptr(ib, memory_area, NULL);
|
zassert_equal_ptr(ib, memory_area, NULL);
|
||||||
zassert_equal(ib->len, (sizeof(memory_area) - sizeof(*ib)), NULL);
|
zassert_equal(ib->len, (sizeof(memory_area) - sizeof(*ib)), NULL);
|
||||||
zassert_equal(ib->wr_idx, 0, NULL);
|
zassert_equal(ib->wr_idx, 0, NULL);
|
||||||
zassert_equal(ib->rd_idx, 0, NULL);
|
zassert_equal(ib->rd_idx, 0, NULL);
|
||||||
|
|
||||||
for (size_t i = 0; i < 9; i++) {
|
for (size_t i = 0; i < 9; i++) {
|
||||||
wlen = icmsg_buf_write(ib, message, sizeof(message));
|
wlen = spsc_pbuf_write(ib, message, sizeof(message));
|
||||||
zassert_equal(wlen, sizeof(message), NULL);
|
zassert_equal(wlen, sizeof(message), NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
wlen = icmsg_buf_write(ib, message, sizeof(message));
|
wlen = spsc_pbuf_write(ib, message, sizeof(message));
|
||||||
zassert_equal(wlen, -ENOMEM, NULL);
|
zassert_equal(wlen, -ENOMEM, NULL);
|
||||||
|
|
||||||
/* Test reading with buf == NULL, should return len of the next message to read. */
|
/* Test reading with buf == NULL, should return len of the next message to read. */
|
||||||
rlen = icmsg_buf_read(ib, NULL, 0);
|
rlen = spsc_pbuf_read(ib, NULL, 0);
|
||||||
zassert_equal(rlen, sizeof(message), NULL);
|
zassert_equal(rlen, sizeof(message), NULL);
|
||||||
|
|
||||||
/* Read with len == 0 and correct buf pointer. */
|
/* Read with len == 0 and correct buf pointer. */
|
||||||
rlen = icmsg_buf_read(ib, rbuf, 0);
|
rlen = spsc_pbuf_read(ib, rbuf, 0);
|
||||||
zassert_equal(rlen, -ENOMEM, NULL);
|
zassert_equal(rlen, -ENOMEM, NULL);
|
||||||
|
|
||||||
/* Read whole data from the buffer. */
|
/* Read whole data from the buffer. */
|
||||||
for (size_t i = 0; i < 9; i++) {
|
for (size_t i = 0; i < 9; i++) {
|
||||||
zassert_equal(ib->rd_idx, i * (sizeof(message) + sizeof(uint16_t)), NULL);
|
zassert_equal(ib->rd_idx, i * (sizeof(message) + sizeof(uint16_t)), NULL);
|
||||||
wlen = icmsg_buf_read(ib, rbuf, sizeof(rbuf));
|
wlen = spsc_pbuf_read(ib, rbuf, sizeof(rbuf));
|
||||||
zassert_equal(wlen, sizeof(message), NULL);
|
zassert_equal(wlen, sizeof(message), NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ static void test_icmsg_buf_ut(void)
|
||||||
zassert_equal(ib->rd_idx, 9 * (sizeof(message) + sizeof(uint16_t)), NULL);
|
zassert_equal(ib->rd_idx, 9 * (sizeof(message) + sizeof(uint16_t)), NULL);
|
||||||
|
|
||||||
/* Write message that would be wrapped around. */
|
/* Write message that would be wrapped around. */
|
||||||
wlen = icmsg_buf_write(ib, message, sizeof(message));
|
wlen = spsc_pbuf_write(ib, message, sizeof(message));
|
||||||
zassert_equal(wlen, sizeof(message), NULL);
|
zassert_equal(wlen, sizeof(message), NULL);
|
||||||
/* Have to store 22 bytes in total.
|
/* Have to store 22 bytes in total.
|
||||||
* 2 Bytes left in tail of the buffer 20 bytes go in front.
|
* 2 Bytes left in tail of the buffer 20 bytes go in front.
|
||||||
|
@ -96,15 +96,15 @@ static void test_icmsg_buf_ut(void)
|
||||||
zassert_equal(ib->wr_idx, 20, NULL);
|
zassert_equal(ib->wr_idx, 20, NULL);
|
||||||
|
|
||||||
/* Read wrapped message. */
|
/* Read wrapped message. */
|
||||||
rlen = icmsg_buf_read(ib, rbuf, sizeof(rbuf));
|
rlen = spsc_pbuf_read(ib, rbuf, sizeof(rbuf));
|
||||||
zassert_equal(rlen, sizeof(message), NULL);
|
zassert_equal(rlen, sizeof(message), NULL);
|
||||||
zassert_equal(message[0], 'a', NULL);
|
zassert_equal(message[0], 'a', NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_main(void)
|
void test_main(void)
|
||||||
{
|
{
|
||||||
ztest_test_suite(icmsg_buf,
|
ztest_test_suite(spsc_pbuf,
|
||||||
ztest_unit_test(test_icmsg_buf_ut)
|
ztest_unit_test(test_spsc_pbuf_ut)
|
||||||
);
|
);
|
||||||
ztest_run_test_suite(icmsg_buf);
|
ztest_run_test_suite(spsc_pbuf);
|
||||||
}
|
}
|
4
tests/lib/spsc_pbuf/testcase.yaml
Normal file
4
tests/lib/spsc_pbuf/testcase.yaml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
tests:
|
||||||
|
lib.spsc_pbuf:
|
||||||
|
integration_platforms:
|
||||||
|
- native_posix
|
|
@ -1,4 +0,0 @@
|
||||||
CONFIG_ZTEST=y
|
|
||||||
|
|
||||||
CONFIG_IPC_SERVICE=y
|
|
||||||
CONFIG_IPC_SERVICE_ICMSG_BUF=y
|
|
|
@ -1,5 +0,0 @@
|
||||||
tests:
|
|
||||||
subsys.ipc.icmsg_buf:
|
|
||||||
integration_platforms:
|
|
||||||
- nrf5340dk_nrf5340_cpuapp
|
|
||||||
platform_exclude: qemu_malta ast1030_evb
|
|
Loading…
Add table
Add a link
Reference in a new issue