1 /*
2 * Copyright (c) 2016 Wind River Systems, Inc.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /**
8 * @brief Mailboxes.
9 */
10
11 #include <zephyr/kernel.h>
12 #include <zephyr/kernel_structs.h>
13
14 #include <zephyr/toolchain.h>
15 #include <zephyr/linker/sections.h>
16 #include <string.h>
17 #include <zephyr/sys/dlist.h>
18 #include <zephyr/init.h>
19 /* private kernel APIs */
20 #include <ksched.h>
21 #include <kthread.h>
22 #include <wait_q.h>
23
24 #ifdef CONFIG_OBJ_CORE_MAILBOX
25 static struct k_obj_type obj_type_mailbox;
26 #endif /* CONFIG_OBJ_CORE_MAILBOX */
27
28 #if (CONFIG_NUM_MBOX_ASYNC_MSGS > 0)
29
30 /* asynchronous message descriptor type */
31 struct k_mbox_async {
32 struct _thread_base thread; /* dummy thread object */
33 struct k_mbox_msg tx_msg; /* transmit message descriptor */
34 };
35
36 /* stack of unused asynchronous message descriptors */
37 K_STACK_DEFINE(async_msg_free, CONFIG_NUM_MBOX_ASYNC_MSGS);
38
39 /* allocate an asynchronous message descriptor */
mbox_async_alloc(struct k_mbox_async ** async)40 static inline void mbox_async_alloc(struct k_mbox_async **async)
41 {
42 (void)k_stack_pop(&async_msg_free, (stack_data_t *)async, K_FOREVER);
43 }
44
45 /* free an asynchronous message descriptor */
mbox_async_free(struct k_mbox_async * async)46 static inline void mbox_async_free(struct k_mbox_async *async)
47 {
48 k_stack_push(&async_msg_free, (stack_data_t)async);
49 }
50
51 /*
52 * Do run-time initialization of mailbox object subsystem.
53 */
init_mbox_module(void)54 static int init_mbox_module(void)
55 {
56 /* array of asynchronous message descriptors */
57 static struct k_mbox_async __noinit async_msg[CONFIG_NUM_MBOX_ASYNC_MSGS];
58
59 /*
60 * Create pool of asynchronous message descriptors.
61 *
62 * A dummy thread requires minimal initialization, since it never gets
63 * to execute. The _THREAD_DUMMY flag is sufficient to distinguish a
64 * dummy thread from a real one. The threads are *not* added to the
65 * kernel's list of known threads.
66 *
67 * Once initialized, the address of each descriptor is added to a stack
68 * that governs access to them.
69 */
70
71 int i;
72
73 for (i = 0; i < CONFIG_NUM_MBOX_ASYNC_MSGS; i++) {
74 z_init_thread_base(&async_msg[i].thread, 0, _THREAD_DUMMY, 0);
75 k_stack_push(&async_msg_free, (stack_data_t)&async_msg[i]);
76 }
77
78 /* Complete initialization of statically defined mailboxes. */
79
80 return 0;
81 }
82
83 SYS_INIT(init_mbox_module, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_OBJECTS);
84
85 #endif /* CONFIG_NUM_MBOX_ASYNC_MSGS > 0 */
86
k_mbox_init(struct k_mbox * mbox)87 void k_mbox_init(struct k_mbox *mbox)
88 {
89 z_waitq_init(&mbox->tx_msg_queue);
90 z_waitq_init(&mbox->rx_msg_queue);
91 mbox->lock = (struct k_spinlock) {};
92
93 #ifdef CONFIG_OBJ_CORE_MAILBOX
94 k_obj_core_init_and_link(K_OBJ_CORE(mbox), &obj_type_mailbox);
95 #endif /* CONFIG_OBJ_CORE_MAILBOX */
96
97 SYS_PORT_TRACING_OBJ_INIT(k_mbox, mbox);
98 }
99
100 /**
101 * @brief Check compatibility of sender's and receiver's message descriptors.
102 *
103 * Compares sender's and receiver's message descriptors to see if they are
104 * compatible. If so, the descriptor fields are updated to reflect that a
105 * match has occurred.
106 *
107 * @param tx_msg Pointer to transmit message descriptor.
108 * @param rx_msg Pointer to receive message descriptor.
109 *
110 * @return 0 if successfully matched, otherwise -1.
111 */
mbox_message_match(struct k_mbox_msg * tx_msg,struct k_mbox_msg * rx_msg)112 static int mbox_message_match(struct k_mbox_msg *tx_msg,
113 struct k_mbox_msg *rx_msg)
114 {
115 uint32_t temp_info;
116
117 if (((tx_msg->tx_target_thread == (k_tid_t)K_ANY) ||
118 (tx_msg->tx_target_thread == rx_msg->tx_target_thread)) &&
119 ((rx_msg->rx_source_thread == (k_tid_t)K_ANY) ||
120 (rx_msg->rx_source_thread == tx_msg->rx_source_thread))) {
121
122 /* update thread identifier fields for both descriptors */
123 rx_msg->rx_source_thread = tx_msg->rx_source_thread;
124 tx_msg->tx_target_thread = rx_msg->tx_target_thread;
125
126 /* update application info fields for both descriptors */
127 temp_info = rx_msg->info;
128 rx_msg->info = tx_msg->info;
129 tx_msg->info = temp_info;
130
131 /* update data size field for receiver only */
132 if (rx_msg->size > tx_msg->size) {
133 rx_msg->size = tx_msg->size;
134 }
135
136 /* update data location fields for receiver only */
137 rx_msg->tx_data = tx_msg->tx_data;
138
139 /* update syncing thread field for receiver only */
140 rx_msg->_syncing_thread = tx_msg->_syncing_thread;
141
142 return 0;
143 }
144
145 return -1;
146 }
147
148 /**
149 * @brief Dispose of received message.
150 *
151 * Notifies the sender that message processing is complete.
152 *
153 * @param rx_msg Pointer to receive message descriptor.
154 */
mbox_message_dispose(struct k_mbox_msg * rx_msg)155 static void mbox_message_dispose(struct k_mbox_msg *rx_msg)
156 {
157 struct k_thread *sending_thread;
158 struct k_mbox_msg *tx_msg;
159
160 /* do nothing if message was disposed of when it was received */
161 if (rx_msg->_syncing_thread == NULL) {
162 return;
163 }
164
165 /* recover sender info */
166 sending_thread = rx_msg->_syncing_thread;
167 rx_msg->_syncing_thread = NULL;
168 tx_msg = (struct k_mbox_msg *)sending_thread->base.swap_data;
169
170 /* update data size field for sender */
171 tx_msg->size = rx_msg->size;
172
173 #if (CONFIG_NUM_MBOX_ASYNC_MSGS > 0)
174 /*
175 * asynchronous send: free asynchronous message descriptor +
176 * dummy thread pair, then give semaphore (if needed)
177 */
178 if ((sending_thread->base.thread_state & _THREAD_DUMMY) != 0U) {
179 struct k_sem *async_sem = tx_msg->_async_sem;
180
181 mbox_async_free((struct k_mbox_async *)sending_thread);
182 if (async_sem != NULL) {
183 k_sem_give(async_sem);
184 }
185 return;
186 }
187 #endif /* CONFIG_NUM_MBOX_ASYNC_MSGS */
188
189 /* synchronous send: wake up sending thread */
190 arch_thread_return_value_set(sending_thread, 0);
191 z_mark_thread_as_not_pending(sending_thread);
192 z_ready_thread(sending_thread);
193 z_reschedule_unlocked();
194 }
195
196 /**
197 * @brief Send a mailbox message.
198 *
199 * Helper routine that handles both synchronous and asynchronous sends.
200 *
201 * @param mbox Pointer to the mailbox object.
202 * @param tx_msg Pointer to transmit message descriptor.
203 * @param timeout Maximum time (milliseconds) to wait for the message to be
204 * received (although not necessarily completely processed).
205 * Use K_NO_WAIT to return immediately, or K_FOREVER to wait as long
206 * as necessary.
207 *
208 * @return 0 if successful, -ENOMSG if failed immediately, -EAGAIN if timed out
209 */
mbox_message_put(struct k_mbox * mbox,struct k_mbox_msg * tx_msg,k_timeout_t timeout)210 static int mbox_message_put(struct k_mbox *mbox, struct k_mbox_msg *tx_msg,
211 k_timeout_t timeout)
212 {
213 struct k_thread *sending_thread;
214 struct k_thread *receiving_thread;
215 struct k_mbox_msg *rx_msg;
216 k_spinlock_key_t key;
217
218 /* save sender id so it can be used during message matching */
219 tx_msg->rx_source_thread = _current;
220
221 /* finish readying sending thread (actual or dummy) for send */
222 sending_thread = tx_msg->_syncing_thread;
223 sending_thread->base.swap_data = tx_msg;
224
225 /* search mailbox's rx queue for a compatible receiver */
226 key = k_spin_lock(&mbox->lock);
227
228 SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_mbox, message_put, mbox, timeout);
229
230 _WAIT_Q_FOR_EACH(&mbox->rx_msg_queue, receiving_thread) {
231 rx_msg = (struct k_mbox_msg *)receiving_thread->base.swap_data;
232
233 if (mbox_message_match(tx_msg, rx_msg) == 0) {
234 /* take receiver out of rx queue */
235 z_unpend_thread(receiving_thread);
236
237 /* ready receiver for execution */
238 arch_thread_return_value_set(receiving_thread, 0);
239 z_ready_thread(receiving_thread);
240
241 #if (CONFIG_NUM_MBOX_ASYNC_MSGS > 0)
242 /*
243 * asynchronous send: swap out current thread
244 * if receiver has priority, otherwise let it continue
245 *
246 * note: dummy sending thread sits (unqueued)
247 * until the receiver consumes the message
248 */
249 if ((sending_thread->base.thread_state & _THREAD_DUMMY)
250 != 0U) {
251 z_reschedule(&mbox->lock, key);
252 return 0;
253 }
254 #endif /* CONFIG_NUM_MBOX_ASYNC_MSGS */
255 SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_mbox, message_put, mbox, timeout);
256
257 /*
258 * synchronous send: pend current thread (unqueued)
259 * until the receiver consumes the message
260 */
261 int ret = z_pend_curr(&mbox->lock, key, NULL, K_FOREVER);
262
263 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mbox, message_put, mbox, timeout, ret);
264
265 return ret;
266 }
267 }
268
269 /* didn't find a matching receiver: don't wait for one */
270 if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
271 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mbox, message_put, mbox, timeout, -ENOMSG);
272
273 k_spin_unlock(&mbox->lock, key);
274 return -ENOMSG;
275 }
276
277 #if (CONFIG_NUM_MBOX_ASYNC_MSGS > 0)
278 /* asynchronous send: dummy thread waits on tx queue for receiver */
279 if ((sending_thread->base.thread_state & _THREAD_DUMMY) != 0U) {
280 z_pend_thread(sending_thread, &mbox->tx_msg_queue, K_FOREVER);
281 k_spin_unlock(&mbox->lock, key);
282 return 0;
283 }
284 #endif /* CONFIG_NUM_MBOX_ASYNC_MSGS */
285 SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_mbox, message_put, mbox, timeout);
286
287 /* synchronous send: sender waits on tx queue for receiver or timeout */
288 int ret = z_pend_curr(&mbox->lock, key, &mbox->tx_msg_queue, timeout);
289
290 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mbox, message_put, mbox, timeout, ret);
291
292 return ret;
293 }
294
k_mbox_put(struct k_mbox * mbox,struct k_mbox_msg * tx_msg,k_timeout_t timeout)295 int k_mbox_put(struct k_mbox *mbox, struct k_mbox_msg *tx_msg,
296 k_timeout_t timeout)
297 {
298 /* configure things for a synchronous send, then send the message */
299 tx_msg->_syncing_thread = _current;
300
301 SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_mbox, put, mbox, timeout);
302
303 int ret = mbox_message_put(mbox, tx_msg, timeout);
304
305 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mbox, put, mbox, timeout, ret);
306
307 return ret;
308 }
309
310 #if (CONFIG_NUM_MBOX_ASYNC_MSGS > 0)
k_mbox_async_put(struct k_mbox * mbox,struct k_mbox_msg * tx_msg,struct k_sem * sem)311 void k_mbox_async_put(struct k_mbox *mbox, struct k_mbox_msg *tx_msg,
312 struct k_sem *sem)
313 {
314 struct k_mbox_async *async;
315
316 SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_mbox, async_put, mbox, sem);
317
318 /*
319 * allocate an asynchronous message descriptor, configure both parts,
320 * then send the message asynchronously
321 */
322 mbox_async_alloc(&async);
323
324 async->thread.prio = _current->base.prio;
325
326 async->tx_msg = *tx_msg;
327 async->tx_msg._syncing_thread = (struct k_thread *)&async->thread;
328 async->tx_msg._async_sem = sem;
329
330 (void)mbox_message_put(mbox, &async->tx_msg, K_FOREVER);
331 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mbox, async_put, mbox, sem);
332 }
333 #endif /* CONFIG_NUM_MBOX_ASYNC_MSGS */
334
k_mbox_data_get(struct k_mbox_msg * rx_msg,void * buffer)335 void k_mbox_data_get(struct k_mbox_msg *rx_msg, void *buffer)
336 {
337 /* handle case where data is to be discarded */
338 if (buffer == NULL) {
339 rx_msg->size = 0;
340 mbox_message_dispose(rx_msg);
341 return;
342 }
343
344 /* copy message data to buffer, then dispose of message */
345 if ((rx_msg->tx_data != NULL) && (rx_msg->size > 0U)) {
346 (void)memcpy(buffer, rx_msg->tx_data, rx_msg->size);
347 }
348 mbox_message_dispose(rx_msg);
349 }
350
351 /**
352 * @brief Handle immediate consumption of received mailbox message data.
353 *
354 * Checks to see if received message data should be kept for later retrieval,
355 * or if the data should consumed immediately and the message disposed of.
356 *
357 * The data is consumed immediately in either of the following cases:
358 * 1) The receiver requested immediate retrieval by supplying a buffer
359 * to receive the data.
360 * 2) There is no data to be retrieved. (i.e. Data size is 0 bytes.)
361 *
362 * @param rx_msg Pointer to receive message descriptor.
363 * @param buffer Pointer to buffer to receive data.
364 *
365 * @return 0
366 */
mbox_message_data_check(struct k_mbox_msg * rx_msg,void * buffer)367 static int mbox_message_data_check(struct k_mbox_msg *rx_msg, void *buffer)
368 {
369 if (buffer != NULL) {
370 /* retrieve data now, then dispose of message */
371 k_mbox_data_get(rx_msg, buffer);
372 } else if (rx_msg->size == 0U) {
373 /* there is no data to get, so just dispose of message */
374 mbox_message_dispose(rx_msg);
375 } else {
376 /* keep message around for later data retrieval */
377 }
378
379 return 0;
380 }
381
k_mbox_get(struct k_mbox * mbox,struct k_mbox_msg * rx_msg,void * buffer,k_timeout_t timeout)382 int k_mbox_get(struct k_mbox *mbox, struct k_mbox_msg *rx_msg, void *buffer,
383 k_timeout_t timeout)
384 {
385 struct k_thread *sending_thread;
386 struct k_mbox_msg *tx_msg;
387 k_spinlock_key_t key;
388 int result;
389
390 /* save receiver id so it can be used during message matching */
391 rx_msg->tx_target_thread = _current;
392
393 /* search mailbox's tx queue for a compatible sender */
394 key = k_spin_lock(&mbox->lock);
395
396 SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_mbox, get, mbox, timeout);
397
398 _WAIT_Q_FOR_EACH(&mbox->tx_msg_queue, sending_thread) {
399 tx_msg = (struct k_mbox_msg *)sending_thread->base.swap_data;
400
401 if (mbox_message_match(tx_msg, rx_msg) == 0) {
402 /* take sender out of mailbox's tx queue */
403 z_unpend_thread(sending_thread);
404
405 k_spin_unlock(&mbox->lock, key);
406
407 /* consume message data immediately, if needed */
408 result = mbox_message_data_check(rx_msg, buffer);
409
410 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mbox, get, mbox, timeout, result);
411 return result;
412 }
413 }
414
415 /* didn't find a matching sender */
416
417 if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
418 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mbox, get, mbox, timeout, -ENOMSG);
419
420 /* don't wait for a matching sender to appear */
421 k_spin_unlock(&mbox->lock, key);
422 return -ENOMSG;
423 }
424
425 SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_mbox, get, mbox, timeout);
426
427 /* wait until a matching sender appears or a timeout occurs */
428 _current->base.swap_data = rx_msg;
429 result = z_pend_curr(&mbox->lock, key, &mbox->rx_msg_queue, timeout);
430
431 /* consume message data immediately, if needed */
432 if (result == 0) {
433 result = mbox_message_data_check(rx_msg, buffer);
434 }
435
436 SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_mbox, get, mbox, timeout, result);
437
438 return result;
439 }
440
441 #ifdef CONFIG_OBJ_CORE_MAILBOX
442
init_mailbox_obj_core_list(void)443 static int init_mailbox_obj_core_list(void)
444 {
445 /* Initialize mailbox object type */
446
447 z_obj_type_init(&obj_type_mailbox, K_OBJ_TYPE_MBOX_ID,
448 offsetof(struct k_mbox, obj_core));
449
450 /* Initialize and link statically defined mailboxes */
451
452 STRUCT_SECTION_FOREACH(k_mbox, mbox) {
453 k_obj_core_init_and_link(K_OBJ_CORE(mbox), &obj_type_mailbox);
454 }
455
456 return 0;
457 }
458
459 SYS_INIT(init_mailbox_obj_core_list, PRE_KERNEL_1,
460 CONFIG_KERNEL_INIT_PRIORITY_OBJECTS);
461 #endif /* CONFIG_OBJ_CORE_MAILBOX */
462