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