1 /* atomic operations */
2 
3 /*
4  * SPDX-FileCopyrightText: 1997-2015 Wind River Systems, Inc.
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 #ifndef _BLE_MESH_ATOMIC_H_
10 #define _BLE_MESH_ATOMIC_H_
11 
12 #include "mesh_types.h"
13 
14 #ifdef __cplusplus
15 extern "C" {
16 #endif
17 
18 typedef bt_mesh_atomic_t bt_mesh_atomic_val_t;
19 
20 /**
21  * @defgroup atomic_apis Atomic Services APIs
22  * @ingroup kernel_apis
23  * @{
24  */
25 
26 /**
27  *
28  * @brief Atomic increment.
29  *
30  * This routine performs an atomic increment by 1 on @a target.
31  *
32  * @param target Address of atomic variable.
33  *
34  * @return Previous value of @a target.
35  */
36 #ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN
bt_mesh_atomic_inc(bt_mesh_atomic_t * target)37 static inline bt_mesh_atomic_val_t bt_mesh_atomic_inc(bt_mesh_atomic_t *target)
38 {
39     return bt_mesh_atomic_add(target, 1);
40 }
41 #else
42 extern bt_mesh_atomic_val_t bt_mesh_atomic_inc(bt_mesh_atomic_t *target);
43 #endif
44 
45 /**
46  *
47  * @brief Atomic decrement.
48  *
49  * This routine performs an atomic decrement by 1 on @a target.
50  *
51  * @param target Address of atomic variable.
52  *
53  * @return Previous value of @a target.
54  */
55 #ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN
bt_mesh_atomic_dec(bt_mesh_atomic_t * target)56 static inline bt_mesh_atomic_val_t bt_mesh_atomic_dec(bt_mesh_atomic_t *target)
57 {
58     return bt_mesh_atomic_sub(target, 1);
59 }
60 #else
61 extern bt_mesh_atomic_val_t bt_mesh_atomic_dec(bt_mesh_atomic_t *target);
62 #endif
63 
64 /**
65  *
66  * @brief Atomic get.
67  *
68  * This routine performs an atomic read on @a target.
69  *
70  * @param target Address of atomic variable.
71  *
72  * @return Value of @a target.
73  */
74 #ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN
bt_mesh_atomic_get(const bt_mesh_atomic_t * target)75 static inline bt_mesh_atomic_val_t bt_mesh_atomic_get(const bt_mesh_atomic_t *target)
76 {
77     return __atomic_load_n(target, __ATOMIC_SEQ_CST);
78 }
79 #else
80 extern bt_mesh_atomic_val_t bt_mesh_atomic_get(const bt_mesh_atomic_t *target);
81 #endif
82 
83 /**
84  *
85  * @brief Atomic get-and-set.
86  *
87  * This routine atomically sets @a target to @a value and returns
88  * the previous value of @a target.
89  *
90  * @param target Address of atomic variable.
91  * @param value Value to write to @a target.
92  *
93  * @return Previous value of @a target.
94  */
95 #ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN
bt_mesh_atomic_set(bt_mesh_atomic_t * target,bt_mesh_atomic_val_t value)96 static inline bt_mesh_atomic_val_t bt_mesh_atomic_set(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t value)
97 {
98     /* This builtin, as described by Intel, is not a traditional
99      * test-and-set operation, but rather an atomic exchange operation. It
100      * writes value into *ptr, and returns the previous contents of *ptr.
101      */
102     return __atomic_exchange_n(target, value, __ATOMIC_SEQ_CST);
103 }
104 #else
105 extern bt_mesh_atomic_val_t bt_mesh_atomic_set(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t value);
106 #endif
107 
108 /**
109  *
110  * @brief Atomic bitwise inclusive OR.
111  *
112  * This routine atomically sets @a target to the bitwise inclusive OR of
113  * @a target and @a value.
114  *
115  * @param target Address of atomic variable.
116  * @param value Value to OR.
117  *
118  * @return Previous value of @a target.
119  */
120 #ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN
bt_mesh_atomic_or(bt_mesh_atomic_t * target,bt_mesh_atomic_val_t value)121 static inline bt_mesh_atomic_val_t bt_mesh_atomic_or(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t value)
122 {
123     return __atomic_fetch_or(target, value, __ATOMIC_SEQ_CST);
124 }
125 #else
126 extern bt_mesh_atomic_val_t bt_mesh_atomic_or(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t value);
127 #endif
128 
129 /**
130  *
131  * @brief Atomic bitwise AND.
132  *
133  * This routine atomically sets @a target to the bitwise AND of @a target
134  * and @a value.
135  *
136  * @param target Address of atomic variable.
137  * @param value Value to AND.
138  *
139  * @return Previous value of @a target.
140  */
141 #ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN
bt_mesh_atomic_and(bt_mesh_atomic_t * target,bt_mesh_atomic_val_t value)142 static inline bt_mesh_atomic_val_t bt_mesh_atomic_and(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t value)
143 {
144     return __atomic_fetch_and(target, value, __ATOMIC_SEQ_CST);
145 }
146 #else
147 extern bt_mesh_atomic_val_t bt_mesh_atomic_and(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t value);
148 #endif
149 
150 /**
151  * @brief Atomic CAS operation.
152  *
153  * This compares the contents of @a *target
154  * with the contents of @a excepted. If equal,
155  * the operation is a read-modify-write operation
156  * that writes @a new_val into @a *target and return true.
157  * If they are not equal, the operation is a read
158  * and return false.
159  *
160  * @param target Address of atomic variable.
161  * @param excepted Value of excepted.
162  * @param new_val Write if target value is equal to expected one.
163  *
164  * @return
165  *         - true: Target value updated.
166  *         - false: Target value not updated.
167  */
168 #ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN
bt_mesh_atomic_cas(bt_mesh_atomic_t * target,bt_mesh_atomic_val_t excepted,bt_mesh_atomic_val_t new_val)169 static inline bool bt_mesh_atomic_cas(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t excepted, bt_mesh_atomic_val_t new_val)
170 {
171     return __atomic_compare_exchange_n(target, &excepted, &new_val, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
172 }
173 #else
174 extern bool bt_mesh_atomic_cas(bt_mesh_atomic_t *target, bt_mesh_atomic_val_t excepted, bt_mesh_atomic_val_t new_val);
175 #endif
176 
177 /**
178  * @cond INTERNAL_HIDDEN
179  */
180 
181 #define BLE_MESH_ATOMIC_BITS            (sizeof(bt_mesh_atomic_val_t) * 8)
182 #define BLE_MESH_ATOMIC_MASK(bit)       (1 << ((bit) & (BLE_MESH_ATOMIC_BITS - 1)))
183 #define BLE_MESH_ATOMIC_ELEM(addr, bit) ((addr) + ((bit) / BLE_MESH_ATOMIC_BITS))
184 
185 /**
186  * INTERNAL_HIDDEN @endcond
187  */
188 
189 /**
190  * @brief Define an array of atomic variables.
191  *
192  * This macro defines an array of atomic variables containing at least
193  * @a num_bits bits.
194  *
195  * @note
196  * If used from file scope, the bits of the array are initialized to zero;
197  * if used from within a function, the bits are left uninitialized.
198  *
199  * @param name Name of array of atomic variables.
200  * @param num_bits Number of bits needed.
201  */
202 #define BLE_MESH_ATOMIC_DEFINE(name, num_bits) \
203         bt_mesh_atomic_t name[1 + ((num_bits) - 1) / BLE_MESH_ATOMIC_BITS]
204 
205 /**
206  * @brief Atomically test a bit.
207  *
208  * This routine tests whether bit number @a bit of @a target is set or not.
209  * The target may be a single atomic variable or an array of them.
210  *
211  * @param target Address of atomic variable or array.
212  * @param bit Bit number (starting from 0).
213  *
214  * @return 1 if the bit was set, 0 if it wasn't.
215  */
bt_mesh_atomic_test_bit(const bt_mesh_atomic_t * target,int bit)216 static inline int bt_mesh_atomic_test_bit(const bt_mesh_atomic_t *target, int bit)
217 {
218     bt_mesh_atomic_val_t val = bt_mesh_atomic_get(BLE_MESH_ATOMIC_ELEM(target, bit));
219 
220     return (1 & (val >> (bit & (BLE_MESH_ATOMIC_BITS - 1))));
221 }
222 
223 /**
224  * @brief Atomically test and clear a bit.
225  *
226  * Atomically clear bit number @a bit of @a target and return its old value.
227  * The target may be a single atomic variable or an array of them.
228  *
229  * @param target Address of atomic variable or array.
230  * @param bit Bit number (starting from 0).
231  *
232  * @return 1 if the bit was set, 0 if it wasn't.
233  */
bt_mesh_atomic_test_and_clear_bit(bt_mesh_atomic_t * target,int bit)234 static inline int bt_mesh_atomic_test_and_clear_bit(bt_mesh_atomic_t *target, int bit)
235 {
236     bt_mesh_atomic_val_t mask = BLE_MESH_ATOMIC_MASK(bit);
237     bt_mesh_atomic_val_t old;
238 
239     old = bt_mesh_atomic_and(BLE_MESH_ATOMIC_ELEM(target, bit), ~mask);
240 
241     return (old & mask) != 0;
242 }
243 
244 /**
245  * @brief Atomically set a bit.
246  *
247  * Atomically set bit number @a bit of @a target and return its old value.
248  * The target may be a single atomic variable or an array of them.
249  *
250  * @param target Address of atomic variable or array.
251  * @param bit Bit number (starting from 0).
252  *
253  * @return 1 if the bit was set, 0 if it wasn't.
254  */
bt_mesh_atomic_test_and_set_bit(bt_mesh_atomic_t * target,int bit)255 static inline int bt_mesh_atomic_test_and_set_bit(bt_mesh_atomic_t *target, int bit)
256 {
257     bt_mesh_atomic_val_t mask = BLE_MESH_ATOMIC_MASK(bit);
258     bt_mesh_atomic_val_t old;
259 
260     old = bt_mesh_atomic_or(BLE_MESH_ATOMIC_ELEM(target, bit), mask);
261 
262     return (old & mask) != 0;
263 }
264 
265 /**
266  * @brief Atomically clear a bit.
267  *
268  * Atomically clear bit number @a bit of @a target.
269  * The target may be a single atomic variable or an array of them.
270  *
271  * @param target Address of atomic variable or array.
272  * @param bit Bit number (starting from 0).
273  *
274  * @return N/A
275  */
bt_mesh_atomic_clear_bit(bt_mesh_atomic_t * target,int bit)276 static inline void bt_mesh_atomic_clear_bit(bt_mesh_atomic_t *target, int bit)
277 {
278     bt_mesh_atomic_val_t mask = BLE_MESH_ATOMIC_MASK(bit);
279 
280     (void)bt_mesh_atomic_and(BLE_MESH_ATOMIC_ELEM(target, bit), ~mask);
281 }
282 
283 /**
284  * @brief Atomically set a bit.
285  *
286  * Atomically set bit number @a bit of @a target.
287  * The target may be a single atomic variable or an array of them.
288  *
289  * @param target Address of atomic variable or array.
290  * @param bit Bit number (starting from 0).
291  *
292  * @return N/A
293  */
bt_mesh_atomic_set_bit(bt_mesh_atomic_t * target,int bit)294 static inline void bt_mesh_atomic_set_bit(bt_mesh_atomic_t *target, int bit)
295 {
296     bt_mesh_atomic_val_t mask = BLE_MESH_ATOMIC_MASK(bit);
297 
298     (void)bt_mesh_atomic_or(BLE_MESH_ATOMIC_ELEM(target, bit), mask);
299 }
300 
301 /**
302  * @brief Atomically set a bit to a given value.
303  *
304  * Atomically set bit number @a bit of @a target to value @a val.
305  * The target may be a single atomic variable or an array of them.
306  *
307  * @param target Address of atomic variable or array.
308  * @param bit Bit number (starting from 0).
309  * @param val true for 1, false for 0.
310  *
311  * @return N/A
312  */
bt_mesh_atomic_set_bit_to(bt_mesh_atomic_t * target,int bit,bool val)313 static inline void bt_mesh_atomic_set_bit_to(bt_mesh_atomic_t *target, int bit, bool val)
314 {
315     bt_mesh_atomic_val_t mask = BLE_MESH_ATOMIC_MASK(bit);
316 
317     if (val) {
318         (void)bt_mesh_atomic_or(BLE_MESH_ATOMIC_ELEM(target, bit), mask);
319     } else {
320         (void)bt_mesh_atomic_and(BLE_MESH_ATOMIC_ELEM(target, bit), ~mask);
321     }
322 }
323 
324 /**
325  * @}
326  */
327 
328 #ifdef __cplusplus
329 }
330 #endif
331 
332 #endif /* _BLE_MESH_ATOMIC_H_ */
333