1 /*
2  * Copyright (c) 2019 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  *
10  * @brief public sys_sem APIs.
11  */
12 
13 #ifndef ZEPHYR_INCLUDE_SYS_SEM_H_
14 #define ZEPHYR_INCLUDE_SYS_SEM_H_
15 
16 /*
17  * sys_sem exists in user memory working as counter semaphore for
18  * user mode thread when user mode enabled. When user mode isn't
19  * enabled, sys_sem behaves like k_sem.
20  */
21 
22 #include <zephyr/kernel.h>
23 #include <zephyr/sys/atomic.h>
24 #include <zephyr/types.h>
25 #include <zephyr/sys/iterable_sections.h>
26 #include <zephyr/sys/__assert.h>
27 
28 #ifdef __cplusplus
29 extern "C" {
30 #endif
31 
32 /**
33  * sys_sem structure
34  */
35 struct sys_sem {
36 #ifdef CONFIG_USERSPACE
37 	struct k_futex futex;
38 	int limit;
39 #else
40 	struct k_sem kernel_sem;
41 #endif
42 };
43 
44 /**
45  * @defgroup user_semaphore_apis User mode semaphore APIs
46  * @ingroup kernel_apis
47  * @{
48  */
49 
50 /**
51  * @brief Statically define and initialize a sys_sem
52  *
53  * The semaphore can be accessed outside the module where it is defined using:
54  *
55  * @code extern struct sys_sem <name>; @endcode
56  *
57  * Route this to memory domains using K_APP_DMEM().
58  *
59  * @param _name Name of the semaphore.
60  * @param _initial_count Initial semaphore count.
61  * @param _count_limit Maximum permitted semaphore count.
62  */
63 #ifdef CONFIG_USERSPACE
64 #define SYS_SEM_DEFINE(_name, _initial_count, _count_limit) \
65 	struct sys_sem _name = { \
66 		.futex = { _initial_count }, \
67 		.limit = _count_limit \
68 	}; \
69 	BUILD_ASSERT(((_count_limit) != 0) && \
70 		     ((_initial_count) <= (_count_limit)))
71 #else
72 /* Stuff this in the section with the rest of the k_sem objects, since they
73  * are identical and can be treated as a k_sem in the boot initialization code
74  */
75 #define SYS_SEM_DEFINE(_name, _initial_count, _count_limit) \
76 	STRUCT_SECTION_ITERABLE_ALTERNATE(k_sem, sys_sem, _name) = { \
77 		.kernel_sem = Z_SEM_INITIALIZER(_name.kernel_sem, \
78 						_initial_count, _count_limit) \
79 	}; \
80 	BUILD_ASSERT(((_count_limit) != 0) && \
81 		     ((_initial_count) <= (_count_limit)))
82 #endif
83 
84 /**
85  * @brief Initialize a semaphore.
86  *
87  * This routine initializes a semaphore instance, prior to its first use.
88  *
89  * @param sem Address of the semaphore.
90  * @param initial_count Initial semaphore count.
91  * @param limit Maximum permitted semaphore count.
92  *
93  * @retval 0 Initial success.
94  * @retval -EINVAL Bad parameters, the value of limit should be located in
95  *         (0, INT_MAX] and initial_count shouldn't be greater than limit.
96  */
97 int sys_sem_init(struct sys_sem *sem, unsigned int initial_count,
98 		unsigned int limit);
99 
100 /**
101  * @brief Give a semaphore.
102  *
103  * This routine gives @a sem, unless the semaphore is already at its
104  * maximum permitted count.
105  *
106  * @param sem Address of the semaphore.
107  *
108  * @retval 0 Semaphore given.
109  * @retval -EINVAL Parameter address not recognized.
110  * @retval -EACCES Caller does not have enough access.
111  * @retval -EAGAIN Count reached Maximum permitted count and try again.
112  */
113 int sys_sem_give(struct sys_sem *sem);
114 
115 /**
116  * @brief Take a sys_sem.
117  *
118  * This routine takes @a sem.
119  *
120  * @param sem Address of the sys_sem.
121  * @param timeout Waiting period to take the sys_sem,
122  *                or one of the special values K_NO_WAIT and K_FOREVER.
123  *
124  * @retval 0 sys_sem taken.
125  * @retval -EINVAL Parameter address not recognized.
126  * @retval -ETIMEDOUT Waiting period timed out.
127  * @retval -EACCES Caller does not have enough access.
128  */
129 int sys_sem_take(struct sys_sem *sem, k_timeout_t timeout);
130 
131 /**
132  * @brief Get sys_sem's value
133  *
134  * This routine returns the current value of @a sem.
135  *
136  * @param sem Address of the sys_sem.
137  *
138  * @return Current value of sys_sem.
139  */
140 unsigned int sys_sem_count_get(struct sys_sem *sem);
141 
142 /**
143  * @cond INTERNAL_HIDDEN
144  */
145 
146 #if defined(__GNUC__)
z_sys_sem_lock_onexit(__maybe_unused int * rc)147 static ALWAYS_INLINE void z_sys_sem_lock_onexit(__maybe_unused int *rc)
148 {
149 	__ASSERT(*rc == 1, "SYS_SEM_LOCK exited with goto, break or return, "
150 			   "use SYS_SEM_LOCK_BREAK instead.");
151 }
152 #define SYS_SEM_LOCK_ONEXIT __attribute__((__cleanup__(z_sys_sem_lock_onexit)))
153 #else
154 #define SYS_SEM_LOCK_ONEXIT
155 #endif
156 
157 /**
158  * INTERNAL_HIDDEN @endcond
159  */
160 
161 /**
162  * @brief Leaves a code block guarded with @ref SYS_SEM_LOCK after releasing the
163  * lock.
164  *
165  * See @ref SYS_SEM_LOCK for details.
166  */
167 #define SYS_SEM_LOCK_BREAK continue
168 
169 /**
170  * @brief Guards a code block with the given sys_sem, automatically acquiring
171  * the semaphore before executing the code block. The semaphore will be
172  * released either when reaching the end of the code block or when leaving the
173  * block with @ref SYS_SEM_LOCK_BREAK.
174  *
175  * @details Example usage:
176  *
177  * @code{.c}
178  * SYS_SEM_LOCK(&sem) {
179  *
180  *   ...execute statements with the semaphore held...
181  *
182  *   if (some_condition) {
183  *     ...release the lock and leave the guarded section prematurely:
184  *     SYS_SEM_LOCK_BREAK;
185  *   }
186  *
187  *   ...execute statements with the lock held...
188  *
189  * }
190  * @endcode
191  *
192  * Behind the scenes this pattern expands to a for-loop whose body is executed
193  * exactly once:
194  *
195  * @code{.c}
196  * for (sys_sem_take(&sem, K_FOREVER); ...; sys_sem_give(&sem)) {
197  *     ...
198  * }
199  * @endcode
200  *
201  * @warning The code block must execute to its end or be left by calling
202  * @ref SYS_SEM_LOCK_BREAK. Otherwise, e.g. if exiting the block with a break,
203  * goto or return statement, the semaphore will not be released on exit.
204  *
205  * @param sem Semaphore (@ref sys_sem) used to guard the enclosed code block.
206  */
207 #define SYS_SEM_LOCK(sem)                                                                          \
208 	for (int __rc SYS_SEM_LOCK_ONEXIT = sys_sem_take((sem), K_FOREVER); ({                     \
209 		     __ASSERT(__rc >= 0, "Failed to take sem: %d", __rc);                          \
210 		     __rc == 0;                                                                    \
211 	     });                                                                                   \
212 	     ({                                                                                    \
213 		     __rc = sys_sem_give((sem));                                                   \
214 		     __ASSERT(__rc == 0, "Failed to give sem: %d", __rc);                          \
215 	     }),                                                                                   \
216 		      __rc = 1)
217 
218 /**
219  * @}
220  */
221 
222 #ifdef __cplusplus
223 }
224 #endif
225 
226 #endif
227