lib: bitarray: sys_bitarray_test_and_set_region added

a method to check and set/clear a chosen region in a bitmap
if not previously set/cleared in a single atomic operation.
Useful for keeping track of resources usage, like memory banks

Signed-off-by: Marcin Szkudlinski <marcin.szkudlinski@intel.com>
diff --git a/include/sys/bitarray.h b/include/sys/bitarray.h
index 792e26c..e407a36 100644
--- a/include/sys/bitarray.h
+++ b/include/sys/bitarray.h
@@ -228,6 +228,31 @@
 			    size_t offset);
 
 /**
+ * Test if all bits in a region are cleared/set and set/clear them
+ * in a single atomic operation
+ *
+ * This checks if all the bits (@p num_bits) in region starting
+ * from @p offset are in required state. If even one bit is not,
+ * -EEXIST is returned.  If the whole region is set/cleared
+ * it is set to opposite state. The check and set is performed as a single
+ * atomic operation.
+ *
+ * @param bitarray Bitarray struct
+ * @param num_bits Number of bits to test and set
+ * @param offset   Starting bit position to test and set
+ * @param to_set   if true the region will be set if all bits are cleared
+ *		   if false the region will be cleard if all bits are set
+ *
+ * @retval 0	   Operation successful
+ * @retval -EINVAL Invalid argument (e.g. bit to set exceeds
+ *		   the number of bits in bit array, etc.)
+ * @retval -EEXIST at least one bit in the region is set/cleared,
+ *		   operation cancelled
+ */
+int sys_bitarray_test_and_set_region(sys_bitarray_t *bitarray, size_t num_bits,
+				     size_t offset, bool to_set);
+
+/**
  * Clear all bits in a region.
  *
  * This clears the number of bits (@p num_bits) in region starting
diff --git a/lib/os/bitarray.c b/lib/os/bitarray.c
index f40739e..e63c3b4 100644
--- a/lib/os/bitarray.c
+++ b/lib/os/bitarray.c
@@ -547,6 +547,39 @@
 	return ret;
 }
 
+int sys_bitarray_test_and_set_region(sys_bitarray_t *bitarray, size_t num_bits,
+				     size_t offset, bool to_set)
+{
+	int ret;
+	bool region_clear;
+	struct bundle_data bd;
+
+	size_t off_end = offset + num_bits - 1;
+	k_spinlock_key_t key = k_spin_lock(&bitarray->lock);
+
+	__ASSERT_NO_MSG(bitarray->num_bits > 0);
+
+	if ((num_bits == 0)
+	    || (num_bits > bitarray->num_bits)
+	    || (offset >= bitarray->num_bits)
+	    || (off_end >= bitarray->num_bits)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	region_clear = match_region(bitarray, offset, num_bits, !to_set, &bd, NULL);
+	if (region_clear) {
+		set_region(bitarray, offset, num_bits, to_set, &bd);
+		ret = 0;
+	} else {
+		ret = -EEXIST;
+	}
+
+out:
+	k_spin_unlock(&bitarray->lock, key);
+	return ret;
+}
+
 int sys_bitarray_set_region(sys_bitarray_t *bitarray, size_t num_bits,
 			    size_t offset)
 {