1 /*
2  * Copyright (c) 2021 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #ifndef ZEPHYR_INCLUDE_SYS_MPSC_PBUF_H_
7 #define ZEPHYR_INCLUDE_SYS_MPSC_PBUF_H_
8 
9 #include <zephyr/kernel.h>
10 #include <zephyr/sys/mpsc_packet.h>
11 #include <string.h>
12 #include <stdint.h>
13 #include <stdbool.h>
14 
15 #ifdef __cplusplus
16 extern "C" {
17 #endif
18 
19 /**
20  * @brief Multi producer, single consumer packet buffer API
21  * @defgroup mpsc_buf MPSC (Multi producer, single consumer) packet buffer API
22  * @ingroup kernel_apis
23  * @{
24  */
25 
26 /*
27  * Multi producer, single consumer packet buffer allows to allocate variable
28  * length consecutive space for storing a packet. When space is allocated
29  * it can be filled by the user (except for the first 2 bits) and when packet
30  * is ready it is committed. It is allowed to allocate another packet before
31  * committing the previous one.
32  *
33  * If buffer is full and packet cannot be allocated then null is returned unless
34  * overwrite mode is selected. In that mode, oldest entry are dropped (user is
35  * notified) until allocation succeeds. It can happen that candidate for
36  * dropping is currently being claimed. In that case, it is omitted and next
37  * packet is dropped and claimed packet is marked as invalid when freeing.
38  *
39  * Reading packets is performed in two steps. First packet is claimed. Claiming
40  * returns pointer to the packet within the buffer. Packet is freed when no
41  * longer in use.
42  */
43 
44 /**@defgroup MPSC_PBUF_FLAGS MPSC packet buffer flags
45  * @{
46  */
47 
48 /** @brief Flag indicating that buffer size is power of 2.
49  *
50  * When buffer size is power of 2 then optimizations are applied.
51  */
52 #define MPSC_PBUF_SIZE_POW2 BIT(0)
53 
54 /** @brief Flag indicating buffer full policy.
55  *
56  * If flag is set then when allocating from a full buffer oldest packets are
57  * dropped. When flag is not set then allocation returns null.
58  */
59 #define MPSC_PBUF_MODE_OVERWRITE BIT(1)
60 
61 /** @brief Flag indicating that maximum buffer usage is tracked. */
62 #define MPSC_PBUF_MAX_UTILIZATION BIT(2)
63 
64 /** @brief Flag indicated that buffer is currently full. */
65 #define MPSC_PBUF_FULL BIT(3)
66 
67 /**@} */
68 
69 /* Forward declaration */
70 struct mpsc_pbuf_buffer;
71 
72 /** @brief Callback prototype for getting length of a packet.
73  *
74  * @param packet User packet.
75  *
76  * @return Size of the packet in 32 bit words.
77  */
78 typedef uint32_t (*mpsc_pbuf_get_wlen)(const union mpsc_pbuf_generic *packet);
79 
80 /** @brief Callback called when packet is dropped.
81  *
82  * @param buffer Packet buffer.
83  *
84  * @param packet Packet that is being dropped.
85  */
86 typedef void (*mpsc_pbuf_notify_drop)(const struct mpsc_pbuf_buffer *buffer,
87 				      const union mpsc_pbuf_generic *packet);
88 
89 /** @brief MPSC packet buffer structure. */
90 struct mpsc_pbuf_buffer {
91 	/** Temporary write index. */
92 	uint32_t tmp_wr_idx;
93 
94 	/** Write index. */
95 	uint32_t wr_idx;
96 
97 	/** Temporary read index. */
98 	uint32_t tmp_rd_idx;
99 
100 	/** Read index. */
101 	uint32_t rd_idx;
102 
103 	/** Flags. */
104 	uint32_t flags;
105 
106 	/** Lock. */
107 	struct k_spinlock lock;
108 
109 	/** User callback called whenever packet is dropped.
110 	 *
111 	 * May be NULL if unneeded.
112 	 */
113 	mpsc_pbuf_notify_drop notify_drop;
114 
115 	/** Callback for getting packet length. */
116 	mpsc_pbuf_get_wlen get_wlen;
117 
118 	/* Buffer. */
119 	uint32_t *buf;
120 
121 	/* Buffer size in 32 bit words. */
122 	uint32_t size;
123 
124 	/* Store max buffer usage. */
125 	uint32_t max_usage;
126 
127 	struct k_sem sem;
128 };
129 
130 /** @brief MPSC packet buffer configuration. */
131 struct mpsc_pbuf_buffer_config {
132 	/* Pointer to a memory used for storing packets. */
133 	uint32_t *buf;
134 
135 	/* Buffer size in 32 bit words. */
136 	uint32_t size;
137 
138 	/* Callbacks. */
139 	mpsc_pbuf_notify_drop notify_drop;
140 	mpsc_pbuf_get_wlen get_wlen;
141 
142 	/* Configuration flags. */
143 	uint32_t flags;
144 };
145 
146 /** @brief Initialize a packet buffer.
147  *
148  * @param buffer Buffer.
149  *
150  * @param config Configuration.
151  */
152 void mpsc_pbuf_init(struct mpsc_pbuf_buffer *buffer,
153 		    const struct mpsc_pbuf_buffer_config *config);
154 
155 /** @brief Allocate a packet.
156  *
157  * If a buffer is configured to overwrite mode then if there is no space to
158  * allocate a new buffer, oldest packets are dropped. Otherwise allocation
159  * fails and null pointer is returned.
160  *
161  * @param buffer Buffer.
162  *
163  * @param wlen Number of words to allocate.
164  *
165  * @param timeout Timeout. If called from thread context it will pend for given
166  * timeout if packet cannot be allocated before dropping the oldest or
167  * returning null.
168  *
169  * @return Pointer to the allocated space or null if it cannot be allocated.
170  */
171 union mpsc_pbuf_generic *mpsc_pbuf_alloc(struct mpsc_pbuf_buffer *buffer,
172 					 size_t wlen, k_timeout_t timeout);
173 
174 /** @brief Commit a packet.
175  *
176  * @param buffer Buffer.
177  *
178  * @param packet Pointer to a packet allocated by @ref mpsc_pbuf_alloc.
179  */
180 void mpsc_pbuf_commit(struct mpsc_pbuf_buffer *buffer,
181 			union mpsc_pbuf_generic *packet);
182 
183 /** @brief Put single word packet into a buffer.
184  *
185  * Function is optimized for storing a packet which fit into a single word.
186  * Note that 2 bits of that word is used by the buffer.
187  *
188  * @param buffer Buffer.
189  *
190  * @param word Packet content consisting of MPSC_PBUF_HDR with valid bit set
191  * and data on remaining bits.
192  */
193 void mpsc_pbuf_put_word(struct mpsc_pbuf_buffer *buffer,
194 			const union mpsc_pbuf_generic word);
195 
196 /** @brief Put a packet consisting of a word and a pointer.
197  *  *
198  * Function is optimized for storing packet consisting of a word and a pointer.
199  * Note that 2 bits of a first word is used by the buffer.
200  *
201  * @param buffer Buffer.
202  *
203  * @param word First word of a packet consisting of MPSC_PBUF_HDR with valid
204  * bit set and data on remaining bits.
205  *
206  * @param data User data.
207  */
208 void mpsc_pbuf_put_word_ext(struct mpsc_pbuf_buffer *buffer,
209 			    const union mpsc_pbuf_generic word,
210 			    const void *data);
211 
212 /** @brief Put a packet into a buffer.
213  *
214  * Copy data into a buffer.
215  * Note that 2 bits of a first word is used by the buffer.
216  *
217  * @param buffer Buffer.
218  *
219  * @param data First word of data must contain MPSC_PBUF_HDR with valid bit set.
220  *
221  * @param wlen Packet size in words.
222  */
223 void mpsc_pbuf_put_data(struct mpsc_pbuf_buffer *buffer,
224 			const uint32_t *data, size_t wlen);
225 
226 /** @brief Claim the first pending packet.
227  *
228  * @param buffer Buffer.
229  *
230  * @return Pointer to the claimed packet or null if none available.
231  */
232 const union mpsc_pbuf_generic *mpsc_pbuf_claim(struct mpsc_pbuf_buffer *buffer);
233 
234 /** @brief Free a packet.
235  *
236  * @param buffer Buffer.
237  *
238  * @param packet Packet.
239  */
240 void mpsc_pbuf_free(struct mpsc_pbuf_buffer *buffer,
241 		    const union mpsc_pbuf_generic *packet);
242 
243 /** @brief Check if there are any message pending.
244  *
245  * @param buffer Buffer.
246  *
247  * @retval true if pending.
248  * @retval false if no message is pending.
249  */
250 bool mpsc_pbuf_is_pending(struct mpsc_pbuf_buffer *buffer);
251 
252 /** @brief Get current memory utilization.
253  *
254  * @param[in, out] buffer Buffer.
255  * @param[out]     size   Buffer size in bytes.
256  * @param[out]     now    Current buffer usage in bytes.
257  */
258 void mpsc_pbuf_get_utilization(struct mpsc_pbuf_buffer *buffer,
259 			       uint32_t *size, uint32_t *now);
260 
261 /** @brief Get maximum memory utilization.
262  *
263  * @param[in, out] buffer Buffer.
264  * @param[out]     max    Maximum buffer usage in bytes.
265  *
266  * retval 0 if utilization data collected successfully.
267  * retval -ENOTSUP if Collecting utilization data is not supported.
268  */
269 int mpsc_pbuf_get_max_utilization(struct mpsc_pbuf_buffer *buffer, uint32_t *max);
270 /**
271  * @}
272  */
273 
274 #ifdef __cplusplus
275 }
276 #endif
277 
278 #endif /* ZEPHYR_INCLUDE_SYS_MPSC_PBUF_H_ */
279