1 /*
2 * Copyright (c) 2021 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <errno.h>
8 #include <stddef.h>
9 #include <stdbool.h>
10 #include <stdio.h>
11 #include <zephyr/sys/bitarray.h>
12 #include <zephyr/sys/check.h>
13 #include <zephyr/sys/sys_io.h>
14
15 /* Number of bits represented by one bundle */
16 #define bundle_bitness(ba) (sizeof((ba)->bundles[0]) * 8)
17
18 struct bundle_data {
19 /* Start and end index of bundles */
20 size_t sidx, eidx;
21
22 /* Offset inside start and end bundles */
23 size_t soff, eoff;
24
25 /* Masks for start/end bundles */
26 uint32_t smask, emask;
27 };
28
setup_bundle_data(sys_bitarray_t * bitarray,struct bundle_data * bd,size_t offset,size_t num_bits)29 static void setup_bundle_data(sys_bitarray_t *bitarray,
30 struct bundle_data *bd,
31 size_t offset, size_t num_bits)
32 {
33 bd->sidx = offset / bundle_bitness(bitarray);
34 bd->soff = offset % bundle_bitness(bitarray);
35
36 bd->eidx = (offset + num_bits - 1) / bundle_bitness(bitarray);
37 bd->eoff = (offset + num_bits - 1) % bundle_bitness(bitarray);
38
39 bd->smask = ~(BIT(bd->soff) - 1);
40 bd->emask = (BIT(bd->eoff) - 1) | BIT(bd->eoff);
41
42 if (bd->sidx == bd->eidx) {
43 /* The region lies within the same bundle. So combine the masks. */
44 bd->smask &= bd->emask;
45 }
46 }
47
48 /*
49 * Find out if the bits in a region is all set or all clear.
50 *
51 * @param[in] bitarray Bitarray struct
52 * @param[in] offset Starting bit location
53 * @param[in] num_bits Number of bits in the region
54 * @param[in] match_set True if matching all set bits,
55 * False if matching all cleared bits
56 * @param[out] bd Data related to matching which can be
57 * used later to find out where the region
58 * lies in the bitarray bundles.
59 * @param[out] mismatch Offset to the mismatched bit.
60 * Can be NULL.
61 *
62 * @retval true If all bits are set or cleared
63 * @retval false Not all bits are set or cleared
64 */
match_region(sys_bitarray_t * bitarray,size_t offset,size_t num_bits,bool match_set,struct bundle_data * bd,size_t * mismatch)65 static bool match_region(sys_bitarray_t *bitarray, size_t offset,
66 size_t num_bits, bool match_set,
67 struct bundle_data *bd,
68 size_t *mismatch)
69 {
70 size_t idx;
71 uint32_t bundle;
72 uint32_t mismatch_bundle;
73 size_t mismatch_bundle_idx;
74 size_t mismatch_bit_off;
75
76 setup_bundle_data(bitarray, bd, offset, num_bits);
77
78 if (bd->sidx == bd->eidx) {
79 bundle = bitarray->bundles[bd->sidx];
80 if (!match_set) {
81 bundle = ~bundle;
82 }
83
84 if ((bundle & bd->smask) != bd->smask) {
85 /* Not matching to mask. */
86 mismatch_bundle = ~bundle & bd->smask;
87 mismatch_bundle_idx = bd->sidx;
88 goto mismatch;
89 } else {
90 /* Matching to mask. */
91 goto out;
92 }
93 }
94
95 /* Region lies in a number of bundles. Need to loop through them. */
96
97 /* Start of bundles */
98 bundle = bitarray->bundles[bd->sidx];
99 if (!match_set) {
100 bundle = ~bundle;
101 }
102
103 if ((bundle & bd->smask) != bd->smask) {
104 /* Start bundle not matching to mask. */
105 mismatch_bundle = ~bundle & bd->smask;
106 mismatch_bundle_idx = bd->sidx;
107 goto mismatch;
108 }
109
110 /* End of bundles */
111 bundle = bitarray->bundles[bd->eidx];
112 if (!match_set) {
113 bundle = ~bundle;
114 }
115
116 if ((bundle & bd->emask) != bd->emask) {
117 /* End bundle not matching to mask. */
118 mismatch_bundle = ~bundle & bd->emask;
119 mismatch_bundle_idx = bd->eidx;
120 goto mismatch;
121 }
122
123 /* In-between bundles */
124 for (idx = bd->sidx + 1; idx < bd->eidx; idx++) {
125 /* Note that this is opposite from above so that
126 * we are simply checking if bundle == 0.
127 */
128 bundle = bitarray->bundles[idx];
129 if (match_set) {
130 bundle = ~bundle;
131 }
132
133 if (bundle != 0U) {
134 /* Bits in "between bundles" do not match */
135 mismatch_bundle = bundle;
136 mismatch_bundle_idx = idx;
137 goto mismatch;
138 }
139 }
140
141 out:
142 /* All bits in region matched. */
143 return true;
144
145 mismatch:
146 if (mismatch != NULL) {
147 /* Must have at least 1 bit set to indicate
148 * where the mismatch is.
149 */
150 __ASSERT_NO_MSG(mismatch_bundle != 0);
151
152 mismatch_bit_off = find_lsb_set(mismatch_bundle) - 1;
153 mismatch_bit_off += mismatch_bundle_idx *
154 bundle_bitness(bitarray);
155 *mismatch = (uint32_t)mismatch_bit_off;
156 }
157 return false;
158 }
159
160 /*
161 * Set or clear a region of bits.
162 *
163 * @param bitarray Bitarray struct
164 * @param offset Starting bit location
165 * @param num_bits Number of bits in the region
166 * @param to_set True if to set all bits.
167 * False if to clear all bits.
168 * @param bd Bundle data. Can reuse the output from
169 * match_region(). NULL if there is no
170 * prior call to match_region().
171 */
set_region(sys_bitarray_t * bitarray,size_t offset,size_t num_bits,bool to_set,struct bundle_data * bd)172 static void set_region(sys_bitarray_t *bitarray, size_t offset,
173 size_t num_bits, bool to_set,
174 struct bundle_data *bd)
175 {
176 size_t idx;
177 struct bundle_data bdata;
178
179 if (bd == NULL) {
180 bd = &bdata;
181 setup_bundle_data(bitarray, bd, offset, num_bits);
182 }
183
184 if (bd->sidx == bd->eidx) {
185 /* Start/end at same bundle */
186 if (to_set) {
187 bitarray->bundles[bd->sidx] |= bd->smask;
188 } else {
189 bitarray->bundles[bd->sidx] &= ~bd->smask;
190 }
191 } else {
192 /* Start/end at different bundle.
193 * So set/clear the bits in start and end bundles
194 * separately. For in-between bundles,
195 * set/clear all bits.
196 */
197 if (to_set) {
198 bitarray->bundles[bd->sidx] |= bd->smask;
199 bitarray->bundles[bd->eidx] |= bd->emask;
200 for (idx = bd->sidx + 1; idx < bd->eidx; idx++) {
201 bitarray->bundles[idx] = ~0U;
202 }
203 } else {
204 bitarray->bundles[bd->sidx] &= ~bd->smask;
205 bitarray->bundles[bd->eidx] &= ~bd->emask;
206 for (idx = bd->sidx + 1; idx < bd->eidx; idx++) {
207 bitarray->bundles[idx] = 0U;
208 }
209 }
210 }
211 }
212
sys_bitarray_popcount_region(sys_bitarray_t * bitarray,size_t num_bits,size_t offset,size_t * count)213 int sys_bitarray_popcount_region(sys_bitarray_t *bitarray, size_t num_bits, size_t offset,
214 size_t *count)
215 {
216 k_spinlock_key_t key;
217 size_t idx;
218 struct bundle_data bd;
219 int ret;
220
221 __ASSERT_NO_MSG(bitarray != NULL);
222 __ASSERT_NO_MSG(bitarray->num_bits > 0);
223
224 key = k_spin_lock(&bitarray->lock);
225
226 if (num_bits == 0 || offset + num_bits > bitarray->num_bits) {
227 ret = -EINVAL;
228 goto out;
229 }
230
231 CHECKIF(count == NULL) {
232 ret = -EINVAL;
233 goto out;
234 }
235
236 setup_bundle_data(bitarray, &bd, offset, num_bits);
237
238 if (bd.sidx == bd.eidx) {
239 /* Start/end at same bundle */
240 *count = POPCOUNT(bitarray->bundles[bd.sidx] & bd.smask);
241 } else {
242 /* Start/end at different bundle.
243 * So count the bits in start and end bundles
244 * separately with correct mask applied. For in-between bundles,
245 * count all bits.
246 */
247 *count = 0;
248 *count += POPCOUNT(bitarray->bundles[bd.sidx] & bd.smask);
249 *count += POPCOUNT(bitarray->bundles[bd.eidx] & bd.emask);
250 for (idx = bd.sidx + 1; idx < bd.eidx; idx++) {
251 *count += POPCOUNT(bitarray->bundles[idx]);
252 }
253 }
254
255 ret = 0;
256
257 out:
258 k_spin_unlock(&bitarray->lock, key);
259 return ret;
260 }
261
sys_bitarray_xor(sys_bitarray_t * dst,sys_bitarray_t * other,size_t num_bits,size_t offset)262 int sys_bitarray_xor(sys_bitarray_t *dst, sys_bitarray_t *other, size_t num_bits, size_t offset)
263 {
264 k_spinlock_key_t key_dst, key_other;
265 int ret;
266 size_t idx;
267 struct bundle_data bd;
268
269 __ASSERT_NO_MSG(dst != NULL);
270 __ASSERT_NO_MSG(dst->num_bits > 0);
271 __ASSERT_NO_MSG(other != NULL);
272 __ASSERT_NO_MSG(other->num_bits > 0);
273
274 key_dst = k_spin_lock(&dst->lock);
275 key_other = k_spin_lock(&other->lock);
276
277
278 if (dst->num_bits != other->num_bits) {
279 ret = -EINVAL;
280 goto out;
281 }
282
283 if (num_bits == 0 || offset + num_bits > dst->num_bits) {
284 ret = -EINVAL;
285 goto out;
286 }
287
288 setup_bundle_data(other, &bd, offset, num_bits);
289
290 if (bd.sidx == bd.eidx) {
291 /* Start/end at same bundle */
292 dst->bundles[bd.sidx] =
293 ((other->bundles[bd.sidx] ^ dst->bundles[bd.sidx]) & bd.smask) |
294 (dst->bundles[bd.sidx] & ~bd.smask);
295 } else {
296 /* Start/end at different bundle.
297 * So xor the bits in start and end bundles according to their bitmasks
298 * separately. For in-between bundles,
299 * xor all bits.
300 */
301 dst->bundles[bd.sidx] =
302 ((other->bundles[bd.sidx] ^ dst->bundles[bd.sidx]) & bd.smask) |
303 (dst->bundles[bd.sidx] & ~bd.smask);
304 dst->bundles[bd.eidx] =
305 ((other->bundles[bd.eidx] ^ dst->bundles[bd.eidx]) & bd.emask) |
306 (dst->bundles[bd.eidx] & ~bd.emask);
307 for (idx = bd.sidx + 1; idx < bd.eidx; idx++) {
308 dst->bundles[idx] ^= other->bundles[idx];
309 }
310 }
311
312 ret = 0;
313
314 out:
315 k_spin_unlock(&other->lock, key_other);
316 k_spin_unlock(&dst->lock, key_dst);
317 return ret;
318 }
319
sys_bitarray_set_bit(sys_bitarray_t * bitarray,size_t bit)320 int sys_bitarray_set_bit(sys_bitarray_t *bitarray, size_t bit)
321 {
322 k_spinlock_key_t key;
323 int ret;
324 size_t idx, off;
325
326 __ASSERT_NO_MSG(bitarray != NULL);
327 __ASSERT_NO_MSG(bitarray->num_bits > 0);
328
329 key = k_spin_lock(&bitarray->lock);
330
331 if (bit >= bitarray->num_bits) {
332 ret = -EINVAL;
333 goto out;
334 }
335
336 idx = bit / bundle_bitness(bitarray);
337 off = bit % bundle_bitness(bitarray);
338
339 bitarray->bundles[idx] |= BIT(off);
340
341 ret = 0;
342
343 out:
344 k_spin_unlock(&bitarray->lock, key);
345 return ret;
346 }
347
sys_bitarray_clear_bit(sys_bitarray_t * bitarray,size_t bit)348 int sys_bitarray_clear_bit(sys_bitarray_t *bitarray, size_t bit)
349 {
350 k_spinlock_key_t key;
351 int ret;
352 size_t idx, off;
353
354 __ASSERT_NO_MSG(bitarray != NULL);
355 __ASSERT_NO_MSG(bitarray->num_bits > 0);
356
357 key = k_spin_lock(&bitarray->lock);
358
359 if (bit >= bitarray->num_bits) {
360 ret = -EINVAL;
361 goto out;
362 }
363
364 idx = bit / bundle_bitness(bitarray);
365 off = bit % bundle_bitness(bitarray);
366
367 bitarray->bundles[idx] &= ~BIT(off);
368
369 ret = 0;
370
371 out:
372 k_spin_unlock(&bitarray->lock, key);
373 return ret;
374 }
375
sys_bitarray_test_bit(sys_bitarray_t * bitarray,size_t bit,int * val)376 int sys_bitarray_test_bit(sys_bitarray_t *bitarray, size_t bit, int *val)
377 {
378 k_spinlock_key_t key;
379 int ret;
380 size_t idx, off;
381
382 __ASSERT_NO_MSG(bitarray != NULL);
383 __ASSERT_NO_MSG(bitarray->num_bits > 0);
384
385 key = k_spin_lock(&bitarray->lock);
386
387 CHECKIF(val == NULL) {
388 ret = -EINVAL;
389 goto out;
390 }
391
392 if (bit >= bitarray->num_bits) {
393 ret = -EINVAL;
394 goto out;
395 }
396
397 idx = bit / bundle_bitness(bitarray);
398 off = bit % bundle_bitness(bitarray);
399
400 if ((bitarray->bundles[idx] & BIT(off)) != 0) {
401 *val = 1;
402 } else {
403 *val = 0;
404 }
405
406 ret = 0;
407
408 out:
409 k_spin_unlock(&bitarray->lock, key);
410 return ret;
411 }
412
sys_bitarray_test_and_set_bit(sys_bitarray_t * bitarray,size_t bit,int * prev_val)413 int sys_bitarray_test_and_set_bit(sys_bitarray_t *bitarray, size_t bit, int *prev_val)
414 {
415 k_spinlock_key_t key;
416 int ret;
417 size_t idx, off;
418
419 __ASSERT_NO_MSG(bitarray != NULL);
420 __ASSERT_NO_MSG(bitarray->num_bits > 0);
421
422 key = k_spin_lock(&bitarray->lock);
423
424 CHECKIF(prev_val == NULL) {
425 ret = -EINVAL;
426 goto out;
427 }
428
429 if (bit >= bitarray->num_bits) {
430 ret = -EINVAL;
431 goto out;
432 }
433
434 idx = bit / bundle_bitness(bitarray);
435 off = bit % bundle_bitness(bitarray);
436
437 if ((bitarray->bundles[idx] & BIT(off)) != 0) {
438 *prev_val = 1;
439 } else {
440 *prev_val = 0;
441 }
442
443 bitarray->bundles[idx] |= BIT(off);
444
445 ret = 0;
446
447 out:
448 k_spin_unlock(&bitarray->lock, key);
449 return ret;
450 }
451
sys_bitarray_test_and_clear_bit(sys_bitarray_t * bitarray,size_t bit,int * prev_val)452 int sys_bitarray_test_and_clear_bit(sys_bitarray_t *bitarray, size_t bit, int *prev_val)
453 {
454 k_spinlock_key_t key;
455 int ret;
456 size_t idx, off;
457
458 __ASSERT_NO_MSG(bitarray != NULL);
459 __ASSERT_NO_MSG(bitarray->num_bits > 0);
460
461 key = k_spin_lock(&bitarray->lock);
462
463 CHECKIF(prev_val == NULL) {
464 ret = -EINVAL;
465 goto out;
466 }
467
468 if (bit >= bitarray->num_bits) {
469 ret = -EINVAL;
470 goto out;
471 }
472
473 idx = bit / bundle_bitness(bitarray);
474 off = bit % bundle_bitness(bitarray);
475
476 if ((bitarray->bundles[idx] & BIT(off)) != 0) {
477 *prev_val = 1;
478 } else {
479 *prev_val = 0;
480 }
481
482 bitarray->bundles[idx] &= ~BIT(off);
483
484 ret = 0;
485
486 out:
487 k_spin_unlock(&bitarray->lock, key);
488 return ret;
489 }
490
sys_bitarray_alloc(sys_bitarray_t * bitarray,size_t num_bits,size_t * offset)491 int sys_bitarray_alloc(sys_bitarray_t *bitarray, size_t num_bits,
492 size_t *offset)
493 {
494 k_spinlock_key_t key;
495 uint32_t bit_idx;
496 int ret;
497 struct bundle_data bd;
498 size_t off_start, off_end;
499 size_t mismatch;
500
501 __ASSERT_NO_MSG(bitarray != NULL);
502 __ASSERT_NO_MSG(bitarray->num_bits > 0);
503
504 key = k_spin_lock(&bitarray->lock);
505
506 CHECKIF(offset == NULL) {
507 ret = -EINVAL;
508 goto out;
509 }
510
511 if ((num_bits == 0) || (num_bits > bitarray->num_bits)) {
512 ret = -EINVAL;
513 goto out;
514 }
515
516 if (bitarray->num_bits <= 32) {
517 int off;
518
519 off = bitmask_find_gap(bitarray->bundles[0], num_bits, bitarray->num_bits, false);
520 if (off < 0) {
521 ret = -ENOSPC;
522 } else {
523 bitarray->bundles[0] |= BIT_MASK(num_bits) << off;
524 *offset = off;
525 ret = 0;
526 }
527 goto out;
528 }
529
530 bit_idx = 0;
531
532 /* Find the first non-allocated bit by looking at bundles
533 * instead of individual bits.
534 *
535 * On RISC-V 64-bit, it complains about undefined reference to `ffs`.
536 * So don't use this on RISCV64.
537 */
538 for (size_t idx = 0; idx < bitarray->num_bundles; idx++) {
539 if (~bitarray->bundles[idx] == 0U) {
540 /* bundle is all 1s => all allocated, skip */
541 bit_idx += bundle_bitness(bitarray);
542 continue;
543 }
544
545 if (bitarray->bundles[idx] != 0U) {
546 /* Find the first free bit in bundle if not all free */
547 off_start = find_lsb_set(~bitarray->bundles[idx]) - 1;
548 bit_idx += off_start;
549 }
550
551 break;
552 }
553
554 off_end = bitarray->num_bits - num_bits;
555 ret = -ENOSPC;
556 while (bit_idx <= off_end) {
557 if (match_region(bitarray, bit_idx, num_bits, false,
558 &bd, &mismatch)) {
559 set_region(bitarray, bit_idx, num_bits, true, &bd);
560
561 *offset = bit_idx;
562 ret = 0;
563 break;
564 }
565
566 /* Fast-forward to the bit just after
567 * the mismatched bit.
568 */
569 bit_idx = mismatch + 1;
570 }
571
572 out:
573 k_spin_unlock(&bitarray->lock, key);
574 return ret;
575 }
576
sys_bitarray_find_nth_set(sys_bitarray_t * bitarray,size_t n,size_t num_bits,size_t offset,size_t * found_at)577 int sys_bitarray_find_nth_set(sys_bitarray_t *bitarray, size_t n, size_t num_bits, size_t offset,
578 size_t *found_at)
579 {
580 k_spinlock_key_t key;
581 size_t count, idx;
582 uint32_t mask;
583 struct bundle_data bd;
584 int ret;
585
586 __ASSERT_NO_MSG(bitarray != NULL);
587 __ASSERT_NO_MSG(bitarray->num_bits > 0);
588
589 key = k_spin_lock(&bitarray->lock);
590
591 if (n == 0 || num_bits == 0 || offset + num_bits > bitarray->num_bits) {
592 ret = -EINVAL;
593 goto out;
594 }
595
596 ret = 1;
597 mask = 0;
598 setup_bundle_data(bitarray, &bd, offset, num_bits);
599
600 count = POPCOUNT(bitarray->bundles[bd.sidx] & bd.smask);
601 /* If we already found more bits set than n, we found the target bundle */
602 if (count >= n) {
603 idx = bd.sidx;
604 mask = bd.smask;
605 goto found;
606 }
607 /* Keep looking if there are more bundles */
608 if (bd.sidx != bd.eidx) {
609 /* We are now only looking for the remaining bits */
610 n -= count;
611 /* First bundle was already checked, keep looking in middle (complete)
612 * bundles.
613 */
614 for (idx = bd.sidx + 1; idx < bd.eidx; idx++) {
615 count = POPCOUNT(bitarray->bundles[idx]);
616 if (count >= n) {
617 mask = ~(mask & 0);
618 goto found;
619 }
620 n -= count;
621 }
622 /* Continue searching in last bundle */
623 count = POPCOUNT(bitarray->bundles[bd.eidx] & bd.emask);
624 if (count >= n) {
625 idx = bd.eidx;
626 mask = bd.emask;
627 goto found;
628 }
629 }
630
631 goto out;
632
633 found:
634 /* The bit we are looking for must be in the current bundle idx.
635 * Find out the exact index of the bit.
636 */
637 for (size_t j = 0; j <= bundle_bitness(bitarray) - 1; j++) {
638 if (bitarray->bundles[idx] & mask & BIT(j)) {
639 if (--n <= 0) {
640 *found_at = idx * bundle_bitness(bitarray) + j;
641 ret = 0;
642 break;
643 }
644 }
645 }
646
647 out:
648 k_spin_unlock(&bitarray->lock, key);
649 return ret;
650 }
651
sys_bitarray_free(sys_bitarray_t * bitarray,size_t num_bits,size_t offset)652 int sys_bitarray_free(sys_bitarray_t *bitarray, size_t num_bits,
653 size_t offset)
654 {
655 k_spinlock_key_t key;
656 int ret;
657 size_t off_end = offset + num_bits - 1;
658 struct bundle_data bd;
659
660 __ASSERT_NO_MSG(bitarray != NULL);
661 __ASSERT_NO_MSG(bitarray->num_bits > 0);
662
663 key = k_spin_lock(&bitarray->lock);
664
665 if ((num_bits == 0)
666 || (num_bits > bitarray->num_bits)
667 || (offset >= bitarray->num_bits)
668 || (off_end >= bitarray->num_bits)) {
669 ret = -EINVAL;
670 goto out;
671 }
672
673 /* Note that we need to make sure the bits in specified region
674 * (offset to offset + num_bits) are all allocated before we clear
675 * them.
676 */
677 if (bitarray->num_bits <= 32) {
678 uint32_t mask = BIT_MASK(num_bits) << offset;
679
680 if ((mask & bitarray->bundles[0]) != mask) {
681 ret = -EFAULT;
682 } else {
683 bitarray->bundles[0] &= ~mask;
684 ret = 0;
685 }
686 } else if (match_region(bitarray, offset, num_bits, true, &bd, NULL)) {
687 set_region(bitarray, offset, num_bits, false, &bd);
688 ret = 0;
689 } else {
690 ret = -EFAULT;
691 }
692
693 out:
694 k_spin_unlock(&bitarray->lock, key);
695 return ret;
696 }
697
is_region_set_clear(sys_bitarray_t * bitarray,size_t num_bits,size_t offset,bool to_set)698 static bool is_region_set_clear(sys_bitarray_t *bitarray, size_t num_bits,
699 size_t offset, bool to_set)
700 {
701 bool ret;
702 struct bundle_data bd;
703 size_t off_end = offset + num_bits - 1;
704 k_spinlock_key_t key = k_spin_lock(&bitarray->lock);
705
706 __ASSERT_NO_MSG(bitarray != NULL);
707 __ASSERT_NO_MSG(bitarray->num_bits > 0);
708
709 if ((num_bits == 0)
710 || (num_bits > bitarray->num_bits)
711 || (offset >= bitarray->num_bits)
712 || (off_end >= bitarray->num_bits)) {
713 ret = false;
714 goto out;
715 }
716
717 ret = match_region(bitarray, offset, num_bits, to_set, &bd, NULL);
718
719 out:
720 k_spin_unlock(&bitarray->lock, key);
721 return ret;
722 }
723
sys_bitarray_is_region_set(sys_bitarray_t * bitarray,size_t num_bits,size_t offset)724 bool sys_bitarray_is_region_set(sys_bitarray_t *bitarray, size_t num_bits,
725 size_t offset)
726 {
727 return is_region_set_clear(bitarray, num_bits, offset, true);
728 }
729
sys_bitarray_is_region_cleared(sys_bitarray_t * bitarray,size_t num_bits,size_t offset)730 bool sys_bitarray_is_region_cleared(sys_bitarray_t *bitarray, size_t num_bits,
731 size_t offset)
732 {
733 return is_region_set_clear(bitarray, num_bits, offset, false);
734 }
735
set_clear_region(sys_bitarray_t * bitarray,size_t num_bits,size_t offset,bool to_set)736 static int set_clear_region(sys_bitarray_t *bitarray, size_t num_bits,
737 size_t offset, bool to_set)
738 {
739 int ret;
740 size_t off_end = offset + num_bits - 1;
741 k_spinlock_key_t key = k_spin_lock(&bitarray->lock);
742
743 __ASSERT_NO_MSG(bitarray != NULL);
744 __ASSERT_NO_MSG(bitarray->num_bits > 0);
745
746 if ((num_bits == 0)
747 || (num_bits > bitarray->num_bits)
748 || (offset >= bitarray->num_bits)
749 || (off_end >= bitarray->num_bits)) {
750 ret = -EINVAL;
751 goto out;
752 }
753
754 set_region(bitarray, offset, num_bits, to_set, NULL);
755 ret = 0;
756
757 out:
758 k_spin_unlock(&bitarray->lock, key);
759 return ret;
760 }
761
sys_bitarray_test_and_set_region(sys_bitarray_t * bitarray,size_t num_bits,size_t offset,bool to_set)762 int sys_bitarray_test_and_set_region(sys_bitarray_t *bitarray, size_t num_bits,
763 size_t offset, bool to_set)
764 {
765 int ret;
766 bool region_clear;
767 struct bundle_data bd;
768
769 __ASSERT_NO_MSG(bitarray != NULL);
770 __ASSERT_NO_MSG(bitarray->num_bits > 0);
771
772 size_t off_end = offset + num_bits - 1;
773 k_spinlock_key_t key = k_spin_lock(&bitarray->lock);
774
775
776 if ((num_bits == 0)
777 || (num_bits > bitarray->num_bits)
778 || (offset >= bitarray->num_bits)
779 || (off_end >= bitarray->num_bits)) {
780 ret = -EINVAL;
781 goto out;
782 }
783
784 region_clear = match_region(bitarray, offset, num_bits, !to_set, &bd, NULL);
785 if (region_clear) {
786 set_region(bitarray, offset, num_bits, to_set, &bd);
787 ret = 0;
788 } else {
789 ret = -EEXIST;
790 }
791
792 out:
793 k_spin_unlock(&bitarray->lock, key);
794 return ret;
795 }
796
sys_bitarray_set_region(sys_bitarray_t * bitarray,size_t num_bits,size_t offset)797 int sys_bitarray_set_region(sys_bitarray_t *bitarray, size_t num_bits,
798 size_t offset)
799 {
800 return set_clear_region(bitarray, num_bits, offset, true);
801 }
802
sys_bitarray_clear_region(sys_bitarray_t * bitarray,size_t num_bits,size_t offset)803 int sys_bitarray_clear_region(sys_bitarray_t *bitarray, size_t num_bits,
804 size_t offset)
805 {
806 return set_clear_region(bitarray, num_bits, offset, false);
807 }
808