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  * @cond INTERNAL_HIDDEN
152  */
153 
154 #define BLE_MESH_ATOMIC_BITS            (sizeof(bt_mesh_atomic_val_t) * 8)
155 #define BLE_MESH_ATOMIC_MASK(bit)       (1 << ((bit) & (BLE_MESH_ATOMIC_BITS - 1)))
156 #define BLE_MESH_ATOMIC_ELEM(addr, bit) ((addr) + ((bit) / BLE_MESH_ATOMIC_BITS))
157 
158 /**
159  * INTERNAL_HIDDEN @endcond
160  */
161 
162 /**
163  * @brief Define an array of atomic variables.
164  *
165  * This macro defines an array of atomic variables containing at least
166  * @a num_bits bits.
167  *
168  * @note
169  * If used from file scope, the bits of the array are initialized to zero;
170  * if used from within a function, the bits are left uninitialized.
171  *
172  * @param name Name of array of atomic variables.
173  * @param num_bits Number of bits needed.
174  */
175 #define BLE_MESH_ATOMIC_DEFINE(name, num_bits) \
176         bt_mesh_atomic_t name[1 + ((num_bits) - 1) / BLE_MESH_ATOMIC_BITS]
177 
178 /**
179  * @brief Atomically test a bit.
180  *
181  * This routine tests whether bit number @a bit of @a target is set or not.
182  * The target may be a single atomic variable or an array of them.
183  *
184  * @param target Address of atomic variable or array.
185  * @param bit Bit number (starting from 0).
186  *
187  * @return 1 if the bit was set, 0 if it wasn't.
188  */
bt_mesh_atomic_test_bit(const bt_mesh_atomic_t * target,int bit)189 static inline int bt_mesh_atomic_test_bit(const bt_mesh_atomic_t *target, int bit)
190 {
191     bt_mesh_atomic_val_t val = bt_mesh_atomic_get(BLE_MESH_ATOMIC_ELEM(target, bit));
192 
193     return (1 & (val >> (bit & (BLE_MESH_ATOMIC_BITS - 1))));
194 }
195 
196 /**
197  * @brief Atomically test and clear a bit.
198  *
199  * Atomically clear bit number @a bit of @a target and return its old value.
200  * The target may be a single atomic variable or an array of them.
201  *
202  * @param target Address of atomic variable or array.
203  * @param bit Bit number (starting from 0).
204  *
205  * @return 1 if the bit was set, 0 if it wasn't.
206  */
bt_mesh_atomic_test_and_clear_bit(bt_mesh_atomic_t * target,int bit)207 static inline int bt_mesh_atomic_test_and_clear_bit(bt_mesh_atomic_t *target, int bit)
208 {
209     bt_mesh_atomic_val_t mask = BLE_MESH_ATOMIC_MASK(bit);
210     bt_mesh_atomic_val_t old;
211 
212     old = bt_mesh_atomic_and(BLE_MESH_ATOMIC_ELEM(target, bit), ~mask);
213 
214     return (old & mask) != 0;
215 }
216 
217 /**
218  * @brief Atomically set a bit.
219  *
220  * Atomically set bit number @a bit of @a target and return its old value.
221  * The target may be a single atomic variable or an array of them.
222  *
223  * @param target Address of atomic variable or array.
224  * @param bit Bit number (starting from 0).
225  *
226  * @return 1 if the bit was set, 0 if it wasn't.
227  */
bt_mesh_atomic_test_and_set_bit(bt_mesh_atomic_t * target,int bit)228 static inline int bt_mesh_atomic_test_and_set_bit(bt_mesh_atomic_t *target, int bit)
229 {
230     bt_mesh_atomic_val_t mask = BLE_MESH_ATOMIC_MASK(bit);
231     bt_mesh_atomic_val_t old;
232 
233     old = bt_mesh_atomic_or(BLE_MESH_ATOMIC_ELEM(target, bit), mask);
234 
235     return (old & mask) != 0;
236 }
237 
238 /**
239  * @brief Atomically clear a bit.
240  *
241  * Atomically clear bit number @a bit of @a target.
242  * The target may be a single atomic variable or an array of them.
243  *
244  * @param target Address of atomic variable or array.
245  * @param bit Bit number (starting from 0).
246  *
247  * @return N/A
248  */
bt_mesh_atomic_clear_bit(bt_mesh_atomic_t * target,int bit)249 static inline void bt_mesh_atomic_clear_bit(bt_mesh_atomic_t *target, int bit)
250 {
251     bt_mesh_atomic_val_t mask = BLE_MESH_ATOMIC_MASK(bit);
252 
253     (void)bt_mesh_atomic_and(BLE_MESH_ATOMIC_ELEM(target, bit), ~mask);
254 }
255 
256 /**
257  * @brief Atomically set a bit.
258  *
259  * Atomically set bit number @a bit of @a target.
260  * The target may be a single atomic variable or an array of them.
261  *
262  * @param target Address of atomic variable or array.
263  * @param bit Bit number (starting from 0).
264  *
265  * @return N/A
266  */
bt_mesh_atomic_set_bit(bt_mesh_atomic_t * target,int bit)267 static inline void bt_mesh_atomic_set_bit(bt_mesh_atomic_t *target, int bit)
268 {
269     bt_mesh_atomic_val_t mask = BLE_MESH_ATOMIC_MASK(bit);
270 
271     (void)bt_mesh_atomic_or(BLE_MESH_ATOMIC_ELEM(target, bit), mask);
272 }
273 
274 /**
275  * @brief Atomically set a bit to a given value.
276  *
277  * Atomically set bit number @a bit of @a target to value @a val.
278  * The target may be a single atomic variable or an array of them.
279  *
280  * @param target Address of atomic variable or array.
281  * @param bit Bit number (starting from 0).
282  * @param val true for 1, false for 0.
283  *
284  * @return N/A
285  */
bt_mesh_atomic_set_bit_to(bt_mesh_atomic_t * target,int bit,bool val)286 static inline void bt_mesh_atomic_set_bit_to(bt_mesh_atomic_t *target, int bit, bool val)
287 {
288     bt_mesh_atomic_val_t mask = BLE_MESH_ATOMIC_MASK(bit);
289 
290     if (val) {
291         (void)bt_mesh_atomic_or(BLE_MESH_ATOMIC_ELEM(target, bit), mask);
292     } else {
293         (void)bt_mesh_atomic_and(BLE_MESH_ATOMIC_ELEM(target, bit), ~mask);
294     }
295 }
296 
297 /**
298  * @}
299  */
300 
301 #ifdef __cplusplus
302 }
303 #endif
304 
305 #endif /* _BLE_MESH_ATOMIC_H_ */
306