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