1 /* 2 * Copyright (c) 2023 Nordic Semiconductor ASA 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7 #ifndef ZEPHYR_INCLUDE_IPC_PBUF_H_ 8 #define ZEPHYR_INCLUDE_IPC_PBUF_H_ 9 10 #include <zephyr/cache.h> 11 #include <zephyr/devicetree.h> 12 13 #ifdef __cplusplus 14 extern "C" { 15 #endif 16 17 /** 18 * @brief Packed buffer API 19 * @defgroup pbuf Packed Buffer API 20 * @ingroup ipc 21 * @{ 22 */ 23 24 /** @brief Size of packet length field. */ 25 #define PBUF_PACKET_LEN_SZ sizeof(uint32_t) 26 27 /* Amount of data that is left unused to distinguish between empty and full. */ 28 #define _PBUF_IDX_SIZE sizeof(uint32_t) 29 30 /* Minimal length of the data field in the buffer to store the smalest packet 31 * possible. 32 * (+1) for at least one byte of data. 33 * (+_PBUF_IDX_SIZE) to distinguish buffer full and buffer empty. 34 * Rounded up to keep wr/rd indexes pointing to aligned address. 35 */ 36 #define _PBUF_MIN_DATA_LEN ROUND_UP(PBUF_PACKET_LEN_SZ + 1 + _PBUF_IDX_SIZE, _PBUF_IDX_SIZE) 37 38 #if defined(CONFIG_ARCH_POSIX) 39 /* For the native simulated boards we need to modify some pointers at init */ 40 #define PBUF_MAYBE_CONST 41 #else 42 #define PBUF_MAYBE_CONST const 43 #endif 44 45 /** @brief Control block of packet buffer. 46 * 47 * The structure contains configuration data. 48 */ 49 struct pbuf_cfg { 50 volatile uint32_t *rd_idx_loc; /* Address of the variable holding 51 * index value of the first valid byte 52 * in data[]. 53 */ 54 volatile uint32_t *handshake_loc;/* Address of the variable holding 55 * handshake information. 56 */ 57 volatile uint32_t *wr_idx_loc; /* Address of the variable holding 58 * index value of the first free byte 59 * in data[]. 60 */ 61 uint32_t dcache_alignment; /* CPU data cache line size in bytes. 62 * Used for validation - TODO: To be 63 * replaced by flags. 64 */ 65 uint32_t len; /* Length of data[] in bytes. */ 66 uint8_t *data_loc; /* Location of the data[]. */ 67 }; 68 69 /** 70 * @brief Data block of the packed buffer. 71 * 72 * The structure contains local copies of wr and rd indexes used by writer and 73 * reader respectively. 74 */ 75 struct pbuf_data { 76 volatile uint32_t wr_idx; /* Index of the first holding first 77 * free byte in data[]. Used for 78 * writing. 79 */ 80 volatile uint32_t rd_idx; /* Index of the first holding first 81 * valid byte in data[]. Used for 82 * reading. 83 */ 84 }; 85 86 87 /** 88 * @brief Scure packed buffer. 89 * 90 * The packet buffer implements lightweight unidirectional packet 91 * buffer with read/write semantics on top of a memory region shared 92 * by the reader and writer. It embeds cache and memory barrier management to 93 * ensure correct data access. 94 * 95 * This structure supports single writer and reader. Data stored in the buffer 96 * is encapsulated to a message (with length header). The read/write API is 97 * written in a way to protect the data from being corrupted. 98 */ 99 struct pbuf { 100 PBUF_MAYBE_CONST struct pbuf_cfg *const cfg; /* Configuration of the 101 * buffer. 102 */ 103 struct pbuf_data data; /* Data used to read and write 104 * to the buffer 105 */ 106 }; 107 108 /** 109 * @brief Macro for configuration initialization. 110 * 111 * It is recommended to use this macro to initialize packed buffer 112 * configuration. 113 * 114 * @param mem_addr Memory address for pbuf. 115 * @param size Size of the memory. 116 * @param dcache_align Data cache alignment. 117 * @param use_handshake Add handshake word inside shared memory that can be access with 118 * @ref pbuf_handshake_read and @ref pbuf_handshake_write. 119 */ 120 #define PBUF_CFG_INIT(mem_addr, size, dcache_align, use_handshake) \ 121 { \ 122 .rd_idx_loc = (uint32_t *)(mem_addr), \ 123 .handshake_loc = use_handshake ? (uint32_t *)((uint8_t *)(mem_addr) + \ 124 _PBUF_IDX_SIZE) : NULL, \ 125 .wr_idx_loc = (uint32_t *)((uint8_t *)(mem_addr) + MAX(dcache_align, \ 126 (use_handshake ? 2 : 1) * _PBUF_IDX_SIZE)), \ 127 .data_loc = (uint8_t *)((uint8_t *)(mem_addr) + \ 128 MAX(dcache_align, (use_handshake ? 2 : 1) * \ 129 _PBUF_IDX_SIZE) + _PBUF_IDX_SIZE), \ 130 .len = (uint32_t)((uint32_t)(size) - MAX(dcache_align, \ 131 (use_handshake ? 2 : 1) * _PBUF_IDX_SIZE) - _PBUF_IDX_SIZE), \ 132 .dcache_alignment = (dcache_align), \ 133 } 134 135 /** 136 * @brief Macro calculates memory overhead taken by the header in shared memory. 137 * 138 * It contains the read index, write index and padding. 139 * 140 * @param dcache_align Data cache alignment. 141 */ 142 #define PBUF_HEADER_OVERHEAD(dcache_align) \ 143 (MAX(dcache_align, _PBUF_IDX_SIZE) + _PBUF_IDX_SIZE) 144 145 /** 146 * @brief Statically define and initialize pbuf. 147 * 148 * @param name Name of the pbuf. 149 * @param mem_addr Memory address for pbuf. 150 * @param size Size of the memory. 151 * @param dcache_align Data cache line size. 152 * @param use_handshake Add handshake word inside shared memory that can be access with 153 * @ref pbuf_handshake_read and @ref pbuf_handshake_write. 154 */ 155 #define PBUF_DEFINE(name, mem_addr, size, dcache_align, use_handshake, compatibility) \ 156 BUILD_ASSERT(dcache_align >= 0, \ 157 "Cache line size must be non negative."); \ 158 BUILD_ASSERT((size) > 0 && IS_PTR_ALIGNED_BYTES(size, _PBUF_IDX_SIZE), \ 159 "Incorrect size."); \ 160 BUILD_ASSERT(IS_PTR_ALIGNED_BYTES(mem_addr, MAX(dcache_align, _PBUF_IDX_SIZE)), \ 161 "Misaligned memory."); \ 162 BUILD_ASSERT(size >= (MAX(dcache_align, _PBUF_IDX_SIZE) + _PBUF_IDX_SIZE + \ 163 _PBUF_MIN_DATA_LEN), "Insufficient size."); \ 164 BUILD_ASSERT(!(compatibility) || (dcache_align) >= 8, \ 165 "Data cache alignment must be at least 8 if compatibility is enabled.");\ 166 static PBUF_MAYBE_CONST struct pbuf_cfg cfg_##name = \ 167 PBUF_CFG_INIT(mem_addr, size, dcache_align, use_handshake); \ 168 static struct pbuf name = { \ 169 .cfg = &cfg_##name, \ 170 } 171 172 /** 173 * @brief Initialize the Tx packet buffer. 174 * 175 * This function initializes the Tx packet buffer based on provided configuration. 176 * If the configuration is incorrect, the function will return error. 177 * 178 * It is recommended to use PBUF_DEFINE macro for build time initialization. 179 * 180 * @param pb Pointer to the packed buffer containing 181 * configuration and data. Configuration has to be 182 * fixed before the initialization. 183 * @retval 0 on success. 184 * @retval -EINVAL when the input parameter is incorrect. 185 */ 186 int pbuf_tx_init(struct pbuf *pb); 187 188 /** 189 * @brief Initialize the Rx packet buffer. 190 * 191 * This function initializes the Rx packet buffer. 192 * If the configuration is incorrect, the function will return error. 193 * 194 * It is recommended to use PBUF_DEFINE macro for build time initialization. 195 * 196 * @param pb Pointer to the packed buffer containing 197 * configuration and data. Configuration has to be 198 * fixed before the initialization. 199 * @retval 0 on success. 200 * @retval -EINVAL when the input parameter is incorrect. 201 */ 202 int pbuf_rx_init(struct pbuf *pb); 203 204 /** 205 * @brief Write specified amount of data to the packet buffer. 206 * 207 * This function call writes specified amount of data to the packet buffer if 208 * the buffer will fit the data. 209 * 210 * @param pb A buffer to which to write. 211 * @param buf Pointer to the data to be written to the buffer. 212 * @param len Number of bytes to be written to the buffer. Must be positive. 213 * @retval int Number of bytes written, negative error code on fail. 214 * -EINVAL, if any of input parameter is incorrect. 215 * -ENOMEM, if len is bigger than the buffer can fit. 216 */ 217 218 int pbuf_write(struct pbuf *pb, const char *buf, uint16_t len); 219 220 /** 221 * @brief Read specified amount of data from the packet buffer. 222 * 223 * Single read allows to read the message send by the single write. 224 * The provided %p buf must be big enough to store the whole message. 225 * 226 * @param pb A buffer from which data will be read. 227 * @param buf Data pointer to which read data will be written. 228 * If NULL, len of stored message is returned. 229 * @param len Number of bytes to be read from the buffer. 230 * @retval int Bytes read, negative error code on fail. 231 * Bytes to be read, if buf == NULL. 232 * -EINVAL, if any of input parameter is incorrect. 233 * -ENOMEM, if message can not fit in provided buf. 234 * -EAGAIN, if not whole message is ready yet. 235 */ 236 int pbuf_read(struct pbuf *pb, char *buf, uint16_t len); 237 238 /** 239 * @brief Read handshake word from pbuf. 240 * 241 * The pb must be defined with "PBUF_DEFINE" with "use_handshake" set. 242 * 243 * @param pb A buffer from which data will be read. 244 * @retval uint32_t The handshake word value. 245 */ 246 uint32_t pbuf_handshake_read(struct pbuf *pb); 247 248 /** 249 * @brief Write handshake word to pbuf. 250 * 251 * The pb must be defined with "PBUF_DEFINE" with "use_handshake" set. 252 * 253 * @param pb A buffer to which data will be written. 254 * @param value A handshake value. 255 */ 256 void pbuf_handshake_write(struct pbuf *pb, uint32_t value); 257 258 /** 259 * @brief Get first buffer from pbuf. 260 * 261 * This function retrieves buffer located at the beginning of queue. 262 * It will be continuous block since it is the first buffer. 263 * 264 * @param pb A buffer from which data will be read. 265 * @param[out] buf A pointer to output pointer to the date of the first buffer. 266 * @param[out] len A pointer to output length the first buffer. 267 * @retval 0 on success. 268 * -EINVAL when there is no buffer at the beginning of queue. 269 */ 270 int pbuf_get_initial_buf(struct pbuf *pb, volatile char **buf, uint16_t *len); 271 272 /** 273 * @} 274 */ 275 276 #ifdef __cplusplus 277 } 278 #endif 279 280 #endif /* ZEPHYR_INCLUDE_IPC_PBUF_H_ */ 281