first submission
This commit is contained in:
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright (c) 1997-2015, Wind River Systems, Inc.
|
||||
* Copyright (c) 2021 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_SYS_ATOMIC_H_
|
||||
#define ZEPHYR_INCLUDE_SYS_ATOMIC_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <toolchain.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef int atomic_t;
|
||||
typedef atomic_t atomic_val_t;
|
||||
typedef void *atomic_ptr_t;
|
||||
typedef atomic_ptr_t atomic_ptr_val_t;
|
||||
|
||||
/* Low-level primitives come in several styles: */
|
||||
|
||||
#if defined(CONFIG_ATOMIC_OPERATIONS_C)
|
||||
/* Generic-but-slow implementation based on kernel locking and syscalls */
|
||||
#include <sys/atomic_c.h>
|
||||
#elif defined(CONFIG_ATOMIC_OPERATIONS_ARCH)
|
||||
/* Some architectures need their own implementation */
|
||||
# ifdef CONFIG_XTENSA
|
||||
/* Not all Xtensa toolchains support GCC-style atomic intrinsics */
|
||||
# include <arch/xtensa/atomic_xtensa.h>
|
||||
# else
|
||||
/* Other arch specific implementation */
|
||||
# include <sys/atomic_arch.h>
|
||||
# endif /* CONFIG_XTENSA */
|
||||
#else
|
||||
/* Default. See this file for the Doxygen reference: */
|
||||
#include <sys/atomic_builtin.h>
|
||||
#endif
|
||||
|
||||
/* Portable higher-level utilities: */
|
||||
|
||||
/**
|
||||
* @defgroup atomic_apis Atomic Services APIs
|
||||
* @ingroup kernel_apis
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Initialize an atomic variable.
|
||||
*
|
||||
* This macro can be used to initialize an atomic variable. For example,
|
||||
* @code atomic_t my_var = ATOMIC_INIT(75); @endcode
|
||||
*
|
||||
* @param i Value to assign to atomic variable.
|
||||
*/
|
||||
#define ATOMIC_INIT(i) (i)
|
||||
|
||||
/**
|
||||
* @brief Initialize an atomic pointer variable.
|
||||
*
|
||||
* This macro can be used to initialize an atomic pointer variable. For
|
||||
* example,
|
||||
* @code atomic_ptr_t my_ptr = ATOMIC_PTR_INIT(&data); @endcode
|
||||
*
|
||||
* @param p Pointer value to assign to atomic pointer variable.
|
||||
*/
|
||||
#define ATOMIC_PTR_INIT(p) (p)
|
||||
|
||||
/**
|
||||
* @cond INTERNAL_HIDDEN
|
||||
*/
|
||||
|
||||
#define ATOMIC_BITS (sizeof(atomic_val_t) * 8)
|
||||
#define ATOMIC_MASK(bit) (1U << ((uint32_t)(bit) & (ATOMIC_BITS - 1U)))
|
||||
#define ATOMIC_ELEM(addr, bit) ((addr) + ((bit) / ATOMIC_BITS))
|
||||
|
||||
/**
|
||||
* INTERNAL_HIDDEN @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief This macro computes the number of atomic variables necessary to
|
||||
* represent a bitmap with @a num_bits.
|
||||
*
|
||||
* @param num_bits Number of bits.
|
||||
*/
|
||||
#define ATOMIC_BITMAP_SIZE(num_bits) (1 + ((num_bits) - 1) / ATOMIC_BITS)
|
||||
|
||||
/**
|
||||
* @brief Define an array of atomic variables.
|
||||
*
|
||||
* This macro defines an array of atomic variables containing at least
|
||||
* @a num_bits bits.
|
||||
*
|
||||
* @note
|
||||
* If used from file scope, the bits of the array are initialized to zero;
|
||||
* if used from within a function, the bits are left uninitialized.
|
||||
*
|
||||
* @cond INTERNAL_HIDDEN
|
||||
* @note
|
||||
* This macro should be replicated in the PREDEFINED field of the documentation
|
||||
* Doxyfile.
|
||||
* @endcond
|
||||
*
|
||||
* @param name Name of array of atomic variables.
|
||||
* @param num_bits Number of bits needed.
|
||||
*/
|
||||
#define ATOMIC_DEFINE(name, num_bits) \
|
||||
atomic_t name[ATOMIC_BITMAP_SIZE(num_bits)]
|
||||
|
||||
/**
|
||||
* @brief Atomically test a bit.
|
||||
*
|
||||
* This routine tests whether bit number @a bit of @a target is set or not.
|
||||
* The target may be a single atomic variable or an array of them.
|
||||
*
|
||||
* @param target Address of atomic variable or array.
|
||||
* @param bit Bit number (starting from 0).
|
||||
*
|
||||
* @return true if the bit was set, false if it wasn't.
|
||||
*/
|
||||
static inline bool atomic_test_bit(const atomic_t *target, int bit)
|
||||
{
|
||||
atomic_val_t val = atomic_get(ATOMIC_ELEM(target, bit));
|
||||
|
||||
return (1 & (val >> (bit & (ATOMIC_BITS - 1)))) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Atomically test and clear a bit.
|
||||
*
|
||||
* Atomically clear bit number @a bit of @a target and return its old value.
|
||||
* The target may be a single atomic variable or an array of them.
|
||||
*
|
||||
* @param target Address of atomic variable or array.
|
||||
* @param bit Bit number (starting from 0).
|
||||
*
|
||||
* @return true if the bit was set, false if it wasn't.
|
||||
*/
|
||||
static inline bool atomic_test_and_clear_bit(atomic_t *target, int bit)
|
||||
{
|
||||
atomic_val_t mask = ATOMIC_MASK(bit);
|
||||
atomic_val_t old;
|
||||
|
||||
old = atomic_and(ATOMIC_ELEM(target, bit), ~mask);
|
||||
|
||||
return (old & mask) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Atomically set a bit.
|
||||
*
|
||||
* Atomically set bit number @a bit of @a target and return its old value.
|
||||
* The target may be a single atomic variable or an array of them.
|
||||
*
|
||||
* @param target Address of atomic variable or array.
|
||||
* @param bit Bit number (starting from 0).
|
||||
*
|
||||
* @return true if the bit was set, false if it wasn't.
|
||||
*/
|
||||
static inline bool atomic_test_and_set_bit(atomic_t *target, int bit)
|
||||
{
|
||||
atomic_val_t mask = ATOMIC_MASK(bit);
|
||||
atomic_val_t old;
|
||||
|
||||
old = atomic_or(ATOMIC_ELEM(target, bit), mask);
|
||||
|
||||
return (old & mask) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Atomically clear a bit.
|
||||
*
|
||||
* Atomically clear bit number @a bit of @a target.
|
||||
* The target may be a single atomic variable or an array of them.
|
||||
*
|
||||
* @param target Address of atomic variable or array.
|
||||
* @param bit Bit number (starting from 0).
|
||||
*
|
||||
* @return N/A
|
||||
*/
|
||||
static inline void atomic_clear_bit(atomic_t *target, int bit)
|
||||
{
|
||||
atomic_val_t mask = ATOMIC_MASK(bit);
|
||||
|
||||
(void)atomic_and(ATOMIC_ELEM(target, bit), ~mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Atomically set a bit.
|
||||
*
|
||||
* Atomically set bit number @a bit of @a target.
|
||||
* The target may be a single atomic variable or an array of them.
|
||||
*
|
||||
* @param target Address of atomic variable or array.
|
||||
* @param bit Bit number (starting from 0).
|
||||
*
|
||||
* @return N/A
|
||||
*/
|
||||
static inline void atomic_set_bit(atomic_t *target, int bit)
|
||||
{
|
||||
atomic_val_t mask = ATOMIC_MASK(bit);
|
||||
|
||||
(void)atomic_or(ATOMIC_ELEM(target, bit), mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Atomically set a bit to a given value.
|
||||
*
|
||||
* Atomically set bit number @a bit of @a target to value @a val.
|
||||
* The target may be a single atomic variable or an array of them.
|
||||
*
|
||||
* @param target Address of atomic variable or array.
|
||||
* @param bit Bit number (starting from 0).
|
||||
* @param val true for 1, false for 0.
|
||||
*
|
||||
* @return N/A
|
||||
*/
|
||||
static inline void atomic_set_bit_to(atomic_t *target, int bit, bool val)
|
||||
{
|
||||
atomic_val_t mask = ATOMIC_MASK(bit);
|
||||
|
||||
if (val) {
|
||||
(void)atomic_or(ATOMIC_ELEM(target, bit), mask);
|
||||
} else {
|
||||
(void)atomic_and(ATOMIC_ELEM(target, bit), ~mask);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_SYS_ATOMIC_H_ */
|
||||
@@ -0,0 +1,303 @@
|
||||
/* atomic operations */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1997-2015, Wind River Systems, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_SYS_ATOMIC_BUILTIN_H_
|
||||
#define ZEPHYR_INCLUDE_SYS_ATOMIC_BUILTIN_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Included from <atomic.h> */
|
||||
|
||||
/**
|
||||
* @addtogroup atomic_apis Atomic Services APIs
|
||||
* @ingroup kernel_apis
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Atomic compare-and-set.
|
||||
*
|
||||
* This routine performs an atomic compare-and-set on @a target. If the current
|
||||
* value of @a target equals @a old_value, @a target is set to @a new_value.
|
||||
* If the current value of @a target does not equal @a old_value, @a target
|
||||
* is left unchanged.
|
||||
*
|
||||
* @param target Address of atomic variable.
|
||||
* @param old_value Original value to compare against.
|
||||
* @param new_value New value to store.
|
||||
* @return true if @a new_value is written, false otherwise.
|
||||
*/
|
||||
static inline bool atomic_cas(atomic_t *target, atomic_val_t old_value,
|
||||
atomic_val_t new_value)
|
||||
{
|
||||
return __atomic_compare_exchange_n(target, &old_value, new_value,
|
||||
0, __ATOMIC_SEQ_CST,
|
||||
__ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Atomic compare-and-set with pointer values
|
||||
*
|
||||
* This routine performs an atomic compare-and-set on @a target. If the current
|
||||
* value of @a target equals @a old_value, @a target is set to @a new_value.
|
||||
* If the current value of @a target does not equal @a old_value, @a target
|
||||
* is left unchanged.
|
||||
*
|
||||
* @param target Address of atomic variable.
|
||||
* @param old_value Original value to compare against.
|
||||
* @param new_value New value to store.
|
||||
* @return true if @a new_value is written, false otherwise.
|
||||
*/
|
||||
static inline bool atomic_ptr_cas(atomic_ptr_t *target, atomic_ptr_val_t old_value,
|
||||
atomic_ptr_val_t new_value)
|
||||
{
|
||||
return __atomic_compare_exchange_n(target, &old_value, new_value,
|
||||
0, __ATOMIC_SEQ_CST,
|
||||
__ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Atomic addition.
|
||||
*
|
||||
* This routine performs an atomic addition on @a target.
|
||||
*
|
||||
* @param target Address of atomic variable.
|
||||
* @param value Value to add.
|
||||
*
|
||||
* @return Previous value of @a target.
|
||||
*/
|
||||
static inline atomic_val_t atomic_add(atomic_t *target, atomic_val_t value)
|
||||
{
|
||||
return __atomic_fetch_add(target, value, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Atomic subtraction.
|
||||
*
|
||||
* This routine performs an atomic subtraction on @a target.
|
||||
*
|
||||
* @param target Address of atomic variable.
|
||||
* @param value Value to subtract.
|
||||
*
|
||||
* @return Previous value of @a target.
|
||||
*/
|
||||
static inline atomic_val_t atomic_sub(atomic_t *target, atomic_val_t value)
|
||||
{
|
||||
return __atomic_fetch_sub(target, value, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Atomic increment.
|
||||
*
|
||||
* This routine performs an atomic increment by 1 on @a target.
|
||||
*
|
||||
* @param target Address of atomic variable.
|
||||
*
|
||||
* @return Previous value of @a target.
|
||||
*/
|
||||
static inline atomic_val_t atomic_inc(atomic_t *target)
|
||||
{
|
||||
return atomic_add(target, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Atomic decrement.
|
||||
*
|
||||
* This routine performs an atomic decrement by 1 on @a target.
|
||||
*
|
||||
* @param target Address of atomic variable.
|
||||
*
|
||||
* @return Previous value of @a target.
|
||||
*/
|
||||
static inline atomic_val_t atomic_dec(atomic_t *target)
|
||||
{
|
||||
return atomic_sub(target, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Atomic get.
|
||||
*
|
||||
* This routine performs an atomic read on @a target.
|
||||
*
|
||||
* @param target Address of atomic variable.
|
||||
*
|
||||
* @return Value of @a target.
|
||||
*/
|
||||
static inline atomic_val_t atomic_get(const atomic_t *target)
|
||||
{
|
||||
return __atomic_load_n(target, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Atomic get a pointer value
|
||||
*
|
||||
* This routine performs an atomic read on @a target.
|
||||
*
|
||||
* @param target Address of pointer variable.
|
||||
*
|
||||
* @return Value of @a target.
|
||||
*/
|
||||
static inline atomic_ptr_val_t atomic_ptr_get(const atomic_ptr_t *target)
|
||||
{
|
||||
return __atomic_load_n(target, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Atomic get-and-set.
|
||||
*
|
||||
* This routine atomically sets @a target to @a value and returns
|
||||
* the previous value of @a target.
|
||||
*
|
||||
* @param target Address of atomic variable.
|
||||
* @param value Value to write to @a target.
|
||||
*
|
||||
* @return Previous value of @a target.
|
||||
*/
|
||||
static inline atomic_val_t atomic_set(atomic_t *target, atomic_val_t value)
|
||||
{
|
||||
/* This builtin, as described by Intel, is not a traditional
|
||||
* test-and-set operation, but rather an atomic exchange operation. It
|
||||
* writes value into *ptr, and returns the previous contents of *ptr.
|
||||
*/
|
||||
return __atomic_exchange_n(target, value, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Atomic get-and-set for pointer values
|
||||
*
|
||||
* This routine atomically sets @a target to @a value and returns
|
||||
* the previous value of @a target.
|
||||
*
|
||||
* @param target Address of atomic variable.
|
||||
* @param value Value to write to @a target.
|
||||
*
|
||||
* @return Previous value of @a target.
|
||||
*/
|
||||
static inline atomic_ptr_val_t atomic_ptr_set(atomic_ptr_t *target, atomic_ptr_val_t value)
|
||||
{
|
||||
return __atomic_exchange_n(target, value, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Atomic clear.
|
||||
*
|
||||
* This routine atomically sets @a target to zero and returns its previous
|
||||
* value. (Hence, it is equivalent to atomic_set(target, 0).)
|
||||
*
|
||||
* @param target Address of atomic variable.
|
||||
*
|
||||
* @return Previous value of @a target.
|
||||
*/
|
||||
static inline atomic_val_t atomic_clear(atomic_t *target)
|
||||
{
|
||||
return atomic_set(target, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Atomic clear of a pointer value
|
||||
*
|
||||
* This routine atomically sets @a target to zero and returns its previous
|
||||
* value. (Hence, it is equivalent to atomic_set(target, 0).)
|
||||
*
|
||||
* @param target Address of atomic variable.
|
||||
*
|
||||
* @return Previous value of @a target.
|
||||
*/
|
||||
static inline atomic_ptr_val_t atomic_ptr_clear(atomic_ptr_t *target)
|
||||
{
|
||||
return atomic_ptr_set(target, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Atomic bitwise inclusive OR.
|
||||
*
|
||||
* This routine atomically sets @a target to the bitwise inclusive OR of
|
||||
* @a target and @a value.
|
||||
*
|
||||
* @param target Address of atomic variable.
|
||||
* @param value Value to OR.
|
||||
*
|
||||
* @return Previous value of @a target.
|
||||
*/
|
||||
static inline atomic_val_t atomic_or(atomic_t *target, atomic_val_t value)
|
||||
{
|
||||
return __atomic_fetch_or(target, value, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Atomic bitwise exclusive OR (XOR).
|
||||
*
|
||||
* This routine atomically sets @a target to the bitwise exclusive OR (XOR) of
|
||||
* @a target and @a value.
|
||||
*
|
||||
* @param target Address of atomic variable.
|
||||
* @param value Value to XOR
|
||||
*
|
||||
* @return Previous value of @a target.
|
||||
*/
|
||||
static inline atomic_val_t atomic_xor(atomic_t *target, atomic_val_t value)
|
||||
{
|
||||
return __atomic_fetch_xor(target, value, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Atomic bitwise AND.
|
||||
*
|
||||
* This routine atomically sets @a target to the bitwise AND of @a target
|
||||
* and @a value.
|
||||
*
|
||||
* @param target Address of atomic variable.
|
||||
* @param value Value to AND.
|
||||
*
|
||||
* @return Previous value of @a target.
|
||||
*/
|
||||
static inline atomic_val_t atomic_and(atomic_t *target, atomic_val_t value)
|
||||
{
|
||||
return __atomic_fetch_and(target, value, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Atomic bitwise NAND.
|
||||
*
|
||||
* This routine atomically sets @a target to the bitwise NAND of @a target
|
||||
* and @a value. (This operation is equivalent to target = ~(target & value).)
|
||||
*
|
||||
* @param target Address of atomic variable.
|
||||
* @param value Value to NAND.
|
||||
*
|
||||
* @return Previous value of @a target.
|
||||
*/
|
||||
static inline atomic_val_t atomic_nand(atomic_t *target, atomic_val_t value)
|
||||
{
|
||||
return __atomic_fetch_nand(target, value, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_SYS_ATOMIC_BUILTIN_H_ */
|
||||
@@ -0,0 +1,568 @@
|
||||
/** @file
|
||||
* @brief Byte order helpers.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016, Intel Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_SYS_BYTEORDER_H_
|
||||
#define ZEPHYR_INCLUDE_SYS_BYTEORDER_H_
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <stddef.h>
|
||||
// #include <sys/__assert.h>
|
||||
#include <toolchain.h>
|
||||
|
||||
/* Internal helpers only used by the sys_* APIs further below */
|
||||
#define __bswap_16(x) ((uint16_t) ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8)))
|
||||
#define __bswap_24(x) ((uint32_t) ((((x) >> 16) & 0xff) | \
|
||||
(((x)) & 0xff00) | \
|
||||
(((x) & 0xff) << 16)))
|
||||
#define __bswap_32(x) ((uint32_t) ((((x) >> 24) & 0xff) | \
|
||||
(((x) >> 8) & 0xff00) | \
|
||||
(((x) & 0xff00) << 8) | \
|
||||
(((x) & 0xff) << 24)))
|
||||
#define __bswap_48(x) ((uint64_t) ((((x) >> 40) & 0xff) | \
|
||||
(((x) >> 24) & 0xff00) | \
|
||||
(((x) >> 8) & 0xff0000) | \
|
||||
(((x) & 0xff0000) << 8) | \
|
||||
(((x) & 0xff00) << 24) | \
|
||||
(((x) & 0xff) << 40)))
|
||||
#define __bswap_64(x) ((uint64_t) ((((x) >> 56) & 0xff) | \
|
||||
(((x) >> 40) & 0xff00) | \
|
||||
(((x) >> 24) & 0xff0000) | \
|
||||
(((x) >> 8) & 0xff000000) | \
|
||||
(((x) & 0xff000000) << 8) | \
|
||||
(((x) & 0xff0000) << 24) | \
|
||||
(((x) & 0xff00) << 40) | \
|
||||
(((x) & 0xff) << 56)))
|
||||
|
||||
/** @def sys_le16_to_cpu
|
||||
* @brief Convert 16-bit integer from little-endian to host endianness.
|
||||
*
|
||||
* @param val 16-bit integer in little-endian format.
|
||||
*
|
||||
* @return 16-bit integer in host endianness.
|
||||
*/
|
||||
|
||||
/** @def sys_cpu_to_le16
|
||||
* @brief Convert 16-bit integer from host endianness to little-endian.
|
||||
*
|
||||
* @param val 16-bit integer in host endianness.
|
||||
*
|
||||
* @return 16-bit integer in little-endian format.
|
||||
*/
|
||||
|
||||
/** @def sys_le24_to_cpu
|
||||
* @brief Convert 24-bit integer from little-endian to host endianness.
|
||||
*
|
||||
* @param val 24-bit integer in little-endian format.
|
||||
*
|
||||
* @return 24-bit integer in host endianness.
|
||||
*/
|
||||
|
||||
/** @def sys_cpu_to_le24
|
||||
* @brief Convert 24-bit integer from host endianness to little-endian.
|
||||
*
|
||||
* @param val 24-bit integer in host endianness.
|
||||
*
|
||||
* @return 24-bit integer in little-endian format.
|
||||
*/
|
||||
|
||||
/** @def sys_le32_to_cpu
|
||||
* @brief Convert 32-bit integer from little-endian to host endianness.
|
||||
*
|
||||
* @param val 32-bit integer in little-endian format.
|
||||
*
|
||||
* @return 32-bit integer in host endianness.
|
||||
*/
|
||||
|
||||
/** @def sys_cpu_to_le32
|
||||
* @brief Convert 32-bit integer from host endianness to little-endian.
|
||||
*
|
||||
* @param val 32-bit integer in host endianness.
|
||||
*
|
||||
* @return 32-bit integer in little-endian format.
|
||||
*/
|
||||
|
||||
/** @def sys_le48_to_cpu
|
||||
* @brief Convert 48-bit integer from little-endian to host endianness.
|
||||
*
|
||||
* @param val 48-bit integer in little-endian format.
|
||||
*
|
||||
* @return 48-bit integer in host endianness.
|
||||
*/
|
||||
|
||||
/** @def sys_cpu_to_le48
|
||||
* @brief Convert 48-bit integer from host endianness to little-endian.
|
||||
*
|
||||
* @param val 48-bit integer in host endianness.
|
||||
*
|
||||
* @return 48-bit integer in little-endian format.
|
||||
*/
|
||||
|
||||
/** @def sys_be16_to_cpu
|
||||
* @brief Convert 16-bit integer from big-endian to host endianness.
|
||||
*
|
||||
* @param val 16-bit integer in big-endian format.
|
||||
*
|
||||
* @return 16-bit integer in host endianness.
|
||||
*/
|
||||
|
||||
/** @def sys_cpu_to_be16
|
||||
* @brief Convert 16-bit integer from host endianness to big-endian.
|
||||
*
|
||||
* @param val 16-bit integer in host endianness.
|
||||
*
|
||||
* @return 16-bit integer in big-endian format.
|
||||
*/
|
||||
|
||||
/** @def sys_be24_to_cpu
|
||||
* @brief Convert 24-bit integer from big-endian to host endianness.
|
||||
*
|
||||
* @param val 24-bit integer in big-endian format.
|
||||
*
|
||||
* @return 24-bit integer in host endianness.
|
||||
*/
|
||||
|
||||
/** @def sys_cpu_to_be24
|
||||
* @brief Convert 24-bit integer from host endianness to big-endian.
|
||||
*
|
||||
* @param val 24-bit integer in host endianness.
|
||||
*
|
||||
* @return 24-bit integer in big-endian format.
|
||||
*/
|
||||
|
||||
/** @def sys_be32_to_cpu
|
||||
* @brief Convert 32-bit integer from big-endian to host endianness.
|
||||
*
|
||||
* @param val 32-bit integer in big-endian format.
|
||||
*
|
||||
* @return 32-bit integer in host endianness.
|
||||
*/
|
||||
|
||||
/** @def sys_cpu_to_be32
|
||||
* @brief Convert 32-bit integer from host endianness to big-endian.
|
||||
*
|
||||
* @param val 32-bit integer in host endianness.
|
||||
*
|
||||
* @return 32-bit integer in big-endian format.
|
||||
*/
|
||||
|
||||
/** @def sys_be48_to_cpu
|
||||
* @brief Convert 48-bit integer from big-endian to host endianness.
|
||||
*
|
||||
* @param val 48-bit integer in big-endian format.
|
||||
*
|
||||
* @return 48-bit integer in host endianness.
|
||||
*/
|
||||
|
||||
/** @def sys_cpu_to_be48
|
||||
* @brief Convert 48-bit integer from host endianness to big-endian.
|
||||
*
|
||||
* @param val 48-bit integer in host endianness.
|
||||
*
|
||||
* @return 48-bit integer in big-endian format.
|
||||
*/
|
||||
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
#define sys_le16_to_cpu(val) (val)
|
||||
#define sys_cpu_to_le16(val) (val)
|
||||
#define sys_le24_to_cpu(val) (val)
|
||||
#define sys_cpu_to_le24(val) (val)
|
||||
#define sys_le32_to_cpu(val) (val)
|
||||
#define sys_cpu_to_le32(val) (val)
|
||||
#define sys_le48_to_cpu(val) (val)
|
||||
#define sys_cpu_to_le48(val) (val)
|
||||
#define sys_le64_to_cpu(val) (val)
|
||||
#define sys_cpu_to_le64(val) (val)
|
||||
#define sys_be16_to_cpu(val) __bswap_16(val)
|
||||
#define sys_cpu_to_be16(val) __bswap_16(val)
|
||||
#define sys_be24_to_cpu(val) __bswap_24(val)
|
||||
#define sys_cpu_to_be24(val) __bswap_24(val)
|
||||
#define sys_be32_to_cpu(val) __bswap_32(val)
|
||||
#define sys_cpu_to_be32(val) __bswap_32(val)
|
||||
#define sys_be48_to_cpu(val) __bswap_48(val)
|
||||
#define sys_cpu_to_be48(val) __bswap_48(val)
|
||||
#define sys_be64_to_cpu(val) __bswap_64(val)
|
||||
#define sys_cpu_to_be64(val) __bswap_64(val)
|
||||
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
#define sys_le16_to_cpu(val) __bswap_16(val)
|
||||
#define sys_cpu_to_le16(val) __bswap_16(val)
|
||||
#define sys_le24_to_cpu(val) __bswap_24(val)
|
||||
#define sys_cpu_to_le24(val) __bswap_24(val)
|
||||
#define sys_le32_to_cpu(val) __bswap_32(val)
|
||||
#define sys_cpu_to_le32(val) __bswap_32(val)
|
||||
#define sys_le48_to_cpu(val) __bswap_48(val)
|
||||
#define sys_cpu_to_le48(val) __bswap_48(val)
|
||||
#define sys_le64_to_cpu(val) __bswap_64(val)
|
||||
#define sys_cpu_to_le64(val) __bswap_64(val)
|
||||
#define sys_be16_to_cpu(val) (val)
|
||||
#define sys_cpu_to_be16(val) (val)
|
||||
#define sys_be24_to_cpu(val) (val)
|
||||
#define sys_cpu_to_be24(val) (val)
|
||||
#define sys_be32_to_cpu(val) (val)
|
||||
#define sys_cpu_to_be32(val) (val)
|
||||
#define sys_be48_to_cpu(val) (val)
|
||||
#define sys_cpu_to_be48(val) (val)
|
||||
#define sys_be64_to_cpu(val) (val)
|
||||
#define sys_cpu_to_be64(val) (val)
|
||||
#else
|
||||
#error "Unknown byte order"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Put a 16-bit integer as big-endian to arbitrary location.
|
||||
*
|
||||
* Put a 16-bit integer, originally in host endianness, to a
|
||||
* potentially unaligned memory location in big-endian format.
|
||||
*
|
||||
* @param val 16-bit integer in host endianness.
|
||||
* @param dst Destination memory address to store the result.
|
||||
*/
|
||||
static inline void sys_put_be16(uint16_t val, uint8_t dst[2])
|
||||
{
|
||||
dst[0] = val >> 8;
|
||||
dst[1] = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Put a 24-bit integer as big-endian to arbitrary location.
|
||||
*
|
||||
* Put a 24-bit integer, originally in host endianness, to a
|
||||
* potentially unaligned memory location in big-endian format.
|
||||
*
|
||||
* @param val 24-bit integer in host endianness.
|
||||
* @param dst Destination memory address to store the result.
|
||||
*/
|
||||
static inline void sys_put_be24(uint32_t val, uint8_t dst[3])
|
||||
{
|
||||
dst[0] = val >> 16;
|
||||
sys_put_be16(val, &dst[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Put a 32-bit integer as big-endian to arbitrary location.
|
||||
*
|
||||
* Put a 32-bit integer, originally in host endianness, to a
|
||||
* potentially unaligned memory location in big-endian format.
|
||||
*
|
||||
* @param val 32-bit integer in host endianness.
|
||||
* @param dst Destination memory address to store the result.
|
||||
*/
|
||||
static inline void sys_put_be32(uint32_t val, uint8_t dst[4])
|
||||
{
|
||||
sys_put_be16(val >> 16, dst);
|
||||
sys_put_be16(val, &dst[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Put a 48-bit integer as big-endian to arbitrary location.
|
||||
*
|
||||
* Put a 48-bit integer, originally in host endianness, to a
|
||||
* potentially unaligned memory location in big-endian format.
|
||||
*
|
||||
* @param val 48-bit integer in host endianness.
|
||||
* @param dst Destination memory address to store the result.
|
||||
*/
|
||||
static inline void sys_put_be48(uint64_t val, uint8_t dst[6])
|
||||
{
|
||||
sys_put_be16(val >> 32, dst);
|
||||
sys_put_be32(val, &dst[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Put a 64-bit integer as big-endian to arbitrary location.
|
||||
*
|
||||
* Put a 64-bit integer, originally in host endianness, to a
|
||||
* potentially unaligned memory location in big-endian format.
|
||||
*
|
||||
* @param val 64-bit integer in host endianness.
|
||||
* @param dst Destination memory address to store the result.
|
||||
*/
|
||||
static inline void sys_put_be64(uint64_t val, uint8_t dst[8])
|
||||
{
|
||||
sys_put_be32(val >> 32, dst);
|
||||
sys_put_be32(val, &dst[4]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Put a 16-bit integer as little-endian to arbitrary location.
|
||||
*
|
||||
* Put a 16-bit integer, originally in host endianness, to a
|
||||
* potentially unaligned memory location in little-endian format.
|
||||
*
|
||||
* @param val 16-bit integer in host endianness.
|
||||
* @param dst Destination memory address to store the result.
|
||||
*/
|
||||
static inline void sys_put_le16(uint16_t val, uint8_t dst[2])
|
||||
{
|
||||
dst[0] = val;
|
||||
dst[1] = val >> 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Put a 24-bit integer as little-endian to arbitrary location.
|
||||
*
|
||||
* Put a 24-bit integer, originally in host endianness, to a
|
||||
* potentially unaligned memory location in littel-endian format.
|
||||
*
|
||||
* @param val 24-bit integer in host endianness.
|
||||
* @param dst Destination memory address to store the result.
|
||||
*/
|
||||
static inline void sys_put_le24(uint32_t val, uint8_t dst[3])
|
||||
{
|
||||
sys_put_le16(val, dst);
|
||||
dst[2] = val >> 16;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Put a 32-bit integer as little-endian to arbitrary location.
|
||||
*
|
||||
* Put a 32-bit integer, originally in host endianness, to a
|
||||
* potentially unaligned memory location in little-endian format.
|
||||
*
|
||||
* @param val 32-bit integer in host endianness.
|
||||
* @param dst Destination memory address to store the result.
|
||||
*/
|
||||
static inline void sys_put_le32(uint32_t val, uint8_t dst[4])
|
||||
{
|
||||
sys_put_le16(val, dst);
|
||||
sys_put_le16(val >> 16, &dst[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Put a 48-bit integer as little-endian to arbitrary location.
|
||||
*
|
||||
* Put a 48-bit integer, originally in host endianness, to a
|
||||
* potentially unaligned memory location in little-endian format.
|
||||
*
|
||||
* @param val 48-bit integer in host endianness.
|
||||
* @param dst Destination memory address to store the result.
|
||||
*/
|
||||
static inline void sys_put_le48(uint64_t val, uint8_t dst[6])
|
||||
{
|
||||
sys_put_le32(val, dst);
|
||||
sys_put_le16(val >> 32, &dst[4]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Put a 64-bit integer as little-endian to arbitrary location.
|
||||
*
|
||||
* Put a 64-bit integer, originally in host endianness, to a
|
||||
* potentially unaligned memory location in little-endian format.
|
||||
*
|
||||
* @param val 64-bit integer in host endianness.
|
||||
* @param dst Destination memory address to store the result.
|
||||
*/
|
||||
static inline void sys_put_le64(uint64_t val, uint8_t dst[8])
|
||||
{
|
||||
sys_put_le32(val, dst);
|
||||
sys_put_le32(val >> 32, &dst[4]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a 16-bit integer stored in big-endian format.
|
||||
*
|
||||
* Get a 16-bit integer, stored in big-endian format in a potentially
|
||||
* unaligned memory location, and convert it to the host endianness.
|
||||
*
|
||||
* @param src Location of the big-endian 16-bit integer to get.
|
||||
*
|
||||
* @return 16-bit integer in host endianness.
|
||||
*/
|
||||
static inline uint16_t sys_get_be16(const uint8_t src[2])
|
||||
{
|
||||
return ((uint16_t)src[0] << 8) | src[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a 24-bit integer stored in big-endian format.
|
||||
*
|
||||
* Get a 24-bit integer, stored in big-endian format in a potentially
|
||||
* unaligned memory location, and convert it to the host endianness.
|
||||
*
|
||||
* @param src Location of the big-endian 24-bit integer to get.
|
||||
*
|
||||
* @return 24-bit integer in host endianness.
|
||||
*/
|
||||
static inline uint32_t sys_get_be24(const uint8_t src[3])
|
||||
{
|
||||
return ((uint32_t)src[0] << 16) | sys_get_be16(&src[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a 32-bit integer stored in big-endian format.
|
||||
*
|
||||
* Get a 32-bit integer, stored in big-endian format in a potentially
|
||||
* unaligned memory location, and convert it to the host endianness.
|
||||
*
|
||||
* @param src Location of the big-endian 32-bit integer to get.
|
||||
*
|
||||
* @return 32-bit integer in host endianness.
|
||||
*/
|
||||
static inline uint32_t sys_get_be32(const uint8_t src[4])
|
||||
{
|
||||
return ((uint32_t)sys_get_be16(&src[0]) << 16) | sys_get_be16(&src[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a 48-bit integer stored in big-endian format.
|
||||
*
|
||||
* Get a 48-bit integer, stored in big-endian format in a potentially
|
||||
* unaligned memory location, and convert it to the host endianness.
|
||||
*
|
||||
* @param src Location of the big-endian 48-bit integer to get.
|
||||
*
|
||||
* @return 48-bit integer in host endianness.
|
||||
*/
|
||||
static inline uint64_t sys_get_be48(const uint8_t src[6])
|
||||
{
|
||||
return ((uint64_t)sys_get_be32(&src[0]) << 16) | sys_get_be16(&src[4]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a 64-bit integer stored in big-endian format.
|
||||
*
|
||||
* Get a 64-bit integer, stored in big-endian format in a potentially
|
||||
* unaligned memory location, and convert it to the host endianness.
|
||||
*
|
||||
* @param src Location of the big-endian 64-bit integer to get.
|
||||
*
|
||||
* @return 64-bit integer in host endianness.
|
||||
*/
|
||||
static inline uint64_t sys_get_be64(const uint8_t src[8])
|
||||
{
|
||||
return ((uint64_t)sys_get_be32(&src[0]) << 32) | sys_get_be32(&src[4]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a 16-bit integer stored in little-endian format.
|
||||
*
|
||||
* Get a 16-bit integer, stored in little-endian format in a potentially
|
||||
* unaligned memory location, and convert it to the host endianness.
|
||||
*
|
||||
* @param src Location of the little-endian 16-bit integer to get.
|
||||
*
|
||||
* @return 16-bit integer in host endianness.
|
||||
*/
|
||||
static inline uint16_t sys_get_le16(const uint8_t src[2])
|
||||
{
|
||||
return ((uint16_t)src[1] << 8) | src[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a 24-bit integer stored in big-endian format.
|
||||
*
|
||||
* Get a 24-bit integer, stored in big-endian format in a potentially
|
||||
* unaligned memory location, and convert it to the host endianness.
|
||||
*
|
||||
* @param src Location of the big-endian 24-bit integer to get.
|
||||
*
|
||||
* @return 24-bit integer in host endianness.
|
||||
*/
|
||||
static inline uint32_t sys_get_le24(const uint8_t src[3])
|
||||
{
|
||||
return ((uint32_t)src[2] << 16) | sys_get_le16(&src[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a 32-bit integer stored in little-endian format.
|
||||
*
|
||||
* Get a 32-bit integer, stored in little-endian format in a potentially
|
||||
* unaligned memory location, and convert it to the host endianness.
|
||||
*
|
||||
* @param src Location of the little-endian 32-bit integer to get.
|
||||
*
|
||||
* @return 32-bit integer in host endianness.
|
||||
*/
|
||||
static inline uint32_t sys_get_le32(const uint8_t src[4])
|
||||
{
|
||||
return ((uint32_t)sys_get_le16(&src[2]) << 16) | sys_get_le16(&src[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a 48-bit integer stored in little-endian format.
|
||||
*
|
||||
* Get a 48-bit integer, stored in little-endian format in a potentially
|
||||
* unaligned memory location, and convert it to the host endianness.
|
||||
*
|
||||
* @param src Location of the little-endian 48-bit integer to get.
|
||||
*
|
||||
* @return 48-bit integer in host endianness.
|
||||
*/
|
||||
static inline uint64_t sys_get_le48(const uint8_t src[6])
|
||||
{
|
||||
return ((uint64_t)sys_get_le32(&src[2]) << 16) | sys_get_le16(&src[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a 64-bit integer stored in little-endian format.
|
||||
*
|
||||
* Get a 64-bit integer, stored in little-endian format in a potentially
|
||||
* unaligned memory location, and convert it to the host endianness.
|
||||
*
|
||||
* @param src Location of the little-endian 64-bit integer to get.
|
||||
*
|
||||
* @return 64-bit integer in host endianness.
|
||||
*/
|
||||
static inline uint64_t sys_get_le64(const uint8_t src[8])
|
||||
{
|
||||
return ((uint64_t)sys_get_le32(&src[4]) << 32) | sys_get_le32(&src[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swap one buffer content into another
|
||||
*
|
||||
* Copy the content of src buffer into dst buffer in reversed order,
|
||||
* i.e.: src[n] will be put in dst[end-n]
|
||||
* Where n is an index and 'end' the last index in both arrays.
|
||||
* The 2 memory pointers must be pointing to different areas, and have
|
||||
* a minimum size of given length.
|
||||
*
|
||||
* @param dst A valid pointer on a memory area where to copy the data in
|
||||
* @param src A valid pointer on a memory area where to copy the data from
|
||||
* @param length Size of both dst and src memory areas
|
||||
*/
|
||||
static inline void sys_memcpy_swap(void *dst, const void *src, size_t length)
|
||||
{
|
||||
uint8_t *pdst = (uint8_t *)dst;
|
||||
const uint8_t *psrc = (const uint8_t *)src;
|
||||
|
||||
// __ASSERT(((psrc < pdst && (psrc + length) <= pdst) ||
|
||||
// (psrc > pdst && (pdst + length) <= psrc)),
|
||||
// "Source and destination buffers must not overlap");
|
||||
|
||||
psrc += length - 1;
|
||||
|
||||
for (; length > 0; length--) {
|
||||
*pdst++ = *psrc--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Swap buffer content
|
||||
*
|
||||
* In-place memory swap, where final content will be reversed.
|
||||
* I.e.: buf[n] will be put in buf[end-n]
|
||||
* Where n is an index and 'end' the last index of buf.
|
||||
*
|
||||
* @param buf A valid pointer on a memory area to swap
|
||||
* @param length Size of buf memory area
|
||||
*/
|
||||
static inline void sys_mem_swap(void *buf, size_t length)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < (length/2); i++) {
|
||||
uint8_t tmp = ((uint8_t *)buf)[i];
|
||||
|
||||
((uint8_t *)buf)[i] = ((uint8_t *)buf)[length - 1 - i];
|
||||
((uint8_t *)buf)[length - 1 - i] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_SYS_BYTEORDER_H_ */
|
||||
@@ -0,0 +1,433 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2014, Wind River Systems, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Misc utilities
|
||||
*
|
||||
* Misc utilities usable by the kernel and application code.
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_SYS_UTIL_H_
|
||||
#define ZEPHYR_INCLUDE_SYS_UTIL_H_
|
||||
|
||||
#include <sys/util_macro.h>
|
||||
|
||||
/* needs to be outside _ASMLANGUAGE so 'true' and 'false' can turn
|
||||
* into '1' and '0' for asm or linker scripts
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef _ASMLANGUAGE
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @defgroup sys-util Utility Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @brief Cast @p x, a pointer, to an unsigned integer. */
|
||||
#define POINTER_TO_UINT(x) ((uintptr_t) (x))
|
||||
/** @brief Cast @p x, an unsigned integer, to a <tt>void*</tt>. */
|
||||
#define UINT_TO_POINTER(x) ((void *) (uintptr_t) (x))
|
||||
/** @brief Cast @p x, a pointer, to a signed integer. */
|
||||
#define POINTER_TO_INT(x) ((intptr_t) (x))
|
||||
/** @brief Cast @p x, a signed integer, to a <tt>void*</tt>. */
|
||||
#define INT_TO_POINTER(x) ((void *) (intptr_t) (x))
|
||||
|
||||
#if !(defined(__CHAR_BIT__) && defined(__SIZEOF_LONG__))
|
||||
# error Missing required predefined macros for BITS_PER_LONG calculation
|
||||
#endif
|
||||
|
||||
/** Number of bits in a long int. */
|
||||
#define BITS_PER_LONG (__CHAR_BIT__ * __SIZEOF_LONG__)
|
||||
|
||||
/**
|
||||
* @brief Create a contiguous bitmask starting at bit position @p l
|
||||
* and ending at position @p h.
|
||||
*/
|
||||
#define GENMASK(h, l) \
|
||||
(((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h))))
|
||||
|
||||
/** @brief Extract the Least Significant Bit from @p value. */
|
||||
#define LSB_GET(value) ((value) & -(value))
|
||||
|
||||
/**
|
||||
* @brief Extract a bitfield element from @p value corresponding to
|
||||
* the field mask @p mask.
|
||||
*/
|
||||
#define FIELD_GET(mask, value) (((value) & (mask)) / LSB_GET(mask))
|
||||
|
||||
/**
|
||||
* @brief Prepare a bitfield element using @p value with @p mask representing
|
||||
* its field position and width. The result should be combined
|
||||
* with other fields using a logical OR.
|
||||
*/
|
||||
#define FIELD_PREP(mask, value) (((value) * LSB_GET(mask)) & (mask))
|
||||
|
||||
/** @brief 0 if @p cond is true-ish; causes a compile error otherwise. */
|
||||
#define ZERO_OR_COMPILE_ERROR(cond) ((int) sizeof(char[1 - 2 * !(cond)]) - 1)
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
||||
/* The built-in function used below for type checking in C is not
|
||||
* supported by GNU C++.
|
||||
*/
|
||||
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
|
||||
|
||||
#else /* __cplusplus */
|
||||
|
||||
/**
|
||||
* @brief Zero if @p array has an array type, a compile error otherwise
|
||||
*
|
||||
* This macro is available only from C, not C++.
|
||||
*/
|
||||
#define IS_ARRAY(array) \
|
||||
ZERO_OR_COMPILE_ERROR( \
|
||||
!__builtin_types_compatible_p(__typeof__(array), \
|
||||
__typeof__(&(array)[0])))
|
||||
|
||||
/**
|
||||
* @brief Number of elements in the given @p array
|
||||
*
|
||||
* In C++, due to language limitations, this will accept as @p array
|
||||
* any type that implements <tt>operator[]</tt>. The results may not be
|
||||
* particulary meaningful in this case.
|
||||
*
|
||||
* In C, passing a pointer as @p array causes a compile error.
|
||||
*/
|
||||
#define ARRAY_SIZE(array) \
|
||||
((long) (IS_ARRAY(array) + (sizeof(array) / sizeof((array)[0]))))
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/**
|
||||
* @brief Check if a pointer @p ptr lies within @p array.
|
||||
*
|
||||
* In C but not C++, this causes a compile error if @p array is not an array
|
||||
* (e.g. if @p ptr and @p array are mixed up).
|
||||
*
|
||||
* @param ptr a pointer
|
||||
* @param array an array
|
||||
* @return 1 if @p ptr is part of @p array, 0 otherwise
|
||||
*/
|
||||
#define PART_OF_ARRAY(array, ptr) \
|
||||
((ptr) && ((ptr) >= &array[0] && (ptr) < &array[ARRAY_SIZE(array)]))
|
||||
|
||||
/**
|
||||
* @brief Get a pointer to a structure containing the element
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* struct foo {
|
||||
* int bar;
|
||||
* };
|
||||
*
|
||||
* struct foo my_foo;
|
||||
* int *ptr = &my_foo.bar;
|
||||
*
|
||||
* struct foo *container = CONTAINER_OF(ptr, struct foo, bar);
|
||||
*
|
||||
* Above, @p container points at @p my_foo.
|
||||
*
|
||||
* @param ptr pointer to a structure element
|
||||
* @param type name of the type that @p ptr is an element of
|
||||
* @param field the name of the field within the struct @p ptr points to
|
||||
* @return a pointer to the structure that contains @p ptr
|
||||
*/
|
||||
#define CONTAINER_OF(ptr, type, field) \
|
||||
((type *)(((char *)(ptr)) - offsetof(type, field)))
|
||||
|
||||
/**
|
||||
* @brief Value of @p x rounded up to the next multiple of @p align,
|
||||
* which must be a power of 2.
|
||||
*/
|
||||
#define ROUND_UP(x, align) \
|
||||
(((unsigned long)(x) + ((unsigned long)(align) - 1)) & \
|
||||
~((unsigned long)(align) - 1))
|
||||
|
||||
/**
|
||||
* @brief Value of @p x rounded down to the previous multiple of @p
|
||||
* align, which must be a power of 2.
|
||||
*/
|
||||
#define ROUND_DOWN(x, align) \
|
||||
((unsigned long)(x) & ~((unsigned long)(align) - 1))
|
||||
|
||||
/** @brief Value of @p x rounded up to the next word boundary. */
|
||||
#define WB_UP(x) ROUND_UP(x, sizeof(void *))
|
||||
|
||||
/** @brief Value of @p x rounded down to the previous word boundary. */
|
||||
#define WB_DN(x) ROUND_DOWN(x, sizeof(void *))
|
||||
|
||||
/**
|
||||
* @brief Ceiling function applied to @p numerator / @p divider as a fraction.
|
||||
*/
|
||||
#define ceiling_fraction(numerator, divider) \
|
||||
(((numerator) + ((divider) - 1)) / (divider))
|
||||
|
||||
/**
|
||||
* @def MAX
|
||||
* @brief The larger value between @p a and @p b.
|
||||
* @note Arguments are evaluated twice.
|
||||
*/
|
||||
#ifndef MAX
|
||||
/* Use Z_MAX for a GCC-only, single evaluation version */
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def MIN
|
||||
* @brief The smaller value between @p a and @p b.
|
||||
* @note Arguments are evaluated twice.
|
||||
*/
|
||||
#ifndef MIN
|
||||
/* Use Z_MIN for a GCC-only, single evaluation version */
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def CLAMP
|
||||
* @brief Clamp a value to a given range.
|
||||
* @note Arguments are evaluated multiple times.
|
||||
*/
|
||||
#ifndef CLAMP
|
||||
/* Use Z_CLAMP for a GCC-only, single evaluation version */
|
||||
#define CLAMP(val, low, high) (((val) <= (low)) ? (low) : MIN(val, high))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Is @p x a power of two?
|
||||
* @param x value to check
|
||||
* @return true if @p x is a power of two, false otherwise
|
||||
*/
|
||||
static inline bool is_power_of_two(unsigned int x)
|
||||
{
|
||||
return (x != 0U) && ((x & (x - 1U)) == 0U);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Arithmetic shift right
|
||||
* @param value value to shift
|
||||
* @param shift number of bits to shift
|
||||
* @return @p value shifted right by @p shift; opened bit positions are
|
||||
* filled with the sign bit
|
||||
*/
|
||||
static inline int64_t arithmetic_shift_right(int64_t value, uint8_t shift)
|
||||
{
|
||||
int64_t sign_ext;
|
||||
|
||||
if (shift == 0U) {
|
||||
return value;
|
||||
}
|
||||
|
||||
/* extract sign bit */
|
||||
sign_ext = (value >> 63) & 1;
|
||||
|
||||
/* make all bits of sign_ext be the same as the value's sign bit */
|
||||
sign_ext = -sign_ext;
|
||||
|
||||
/* shift value and fill opened bit positions with sign bit */
|
||||
return (value >> shift) | (sign_ext << (64 - shift));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief byte by byte memcpy.
|
||||
*
|
||||
* Copy `size` bytes of `src` into `dest`. This is guaranteed to be done byte by byte.
|
||||
*
|
||||
* @param dst Pointer to the destination memory.
|
||||
* @param src Pointer to the source of the data.
|
||||
* @param size The number of bytes to copy.
|
||||
*/
|
||||
static inline void bytecpy(void *dst, const void *src, size_t size)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < size; ++i) {
|
||||
((volatile uint8_t *)dst)[i] = ((volatile const uint8_t *)src)[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief byte by byte swap.
|
||||
*
|
||||
* Swap @a size bytes between memory regions @a a and @a b. This is
|
||||
* guaranteed to be done byte by byte.
|
||||
*
|
||||
* @param a Pointer to the the first memory region.
|
||||
* @param b Pointer to the the second memory region.
|
||||
* @param size The number of bytes to swap.
|
||||
*/
|
||||
static inline void byteswp(void *a, void *b, size_t size)
|
||||
{
|
||||
uint8_t t;
|
||||
uint8_t *aa = (uint8_t *)a;
|
||||
uint8_t *bb = (uint8_t *)b;
|
||||
|
||||
for (; size > 0; --size) {
|
||||
t = *aa;
|
||||
*aa++ = *bb;
|
||||
*bb++ = t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a single character into a hexadecimal nibble.
|
||||
*
|
||||
* @param c The character to convert
|
||||
* @param x The address of storage for the converted number.
|
||||
*
|
||||
* @return Zero on success or (negative) error code otherwise.
|
||||
*/
|
||||
int char2hex(char c, uint8_t *x);
|
||||
|
||||
/**
|
||||
* @brief Convert a single hexadecimal nibble into a character.
|
||||
*
|
||||
* @param c The number to convert
|
||||
* @param x The address of storage for the converted character.
|
||||
*
|
||||
* @return Zero on success or (negative) error code otherwise.
|
||||
*/
|
||||
int hex2char(uint8_t x, char *c);
|
||||
|
||||
/**
|
||||
* @brief Convert a binary array into string representation.
|
||||
*
|
||||
* @param buf The binary array to convert
|
||||
* @param buflen The length of the binary array to convert
|
||||
* @param hex Address of where to store the string representation.
|
||||
* @param hexlen Size of the storage area for string representation.
|
||||
*
|
||||
* @return The length of the converted string, or 0 if an error occurred.
|
||||
*/
|
||||
size_t bin2hex(const uint8_t *buf, size_t buflen, char *hex, size_t hexlen);
|
||||
|
||||
/**
|
||||
* @brief Convert a hexadecimal string into a binary array.
|
||||
*
|
||||
* @param hex The hexadecimal string to convert
|
||||
* @param hexlen The length of the hexadecimal string to convert.
|
||||
* @param buf Address of where to store the binary data
|
||||
* @param buflen Size of the storage area for binary data
|
||||
*
|
||||
* @return The length of the binary array, or 0 if an error occurred.
|
||||
*/
|
||||
size_t hex2bin(const char *hex, size_t hexlen, uint8_t *buf, size_t buflen);
|
||||
|
||||
/**
|
||||
* @brief Convert a binary coded decimal (BCD 8421) value to binary.
|
||||
*
|
||||
* @param bcd BCD 8421 value to convert.
|
||||
*
|
||||
* @return Binary representation of input value.
|
||||
*/
|
||||
static inline uint8_t bcd2bin(uint8_t bcd)
|
||||
{
|
||||
return ((10 * (bcd >> 4)) + (bcd & 0x0F));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a binary value to binary coded decimal (BCD 8421).
|
||||
*
|
||||
* @param bin Binary value to convert.
|
||||
*
|
||||
* @return BCD 8421 representation of input value.
|
||||
*/
|
||||
static inline uint8_t bin2bcd(uint8_t bin)
|
||||
{
|
||||
return (((bin / 10) << 4) | (bin % 10));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a uint8_t into a decimal string representation.
|
||||
*
|
||||
* Convert a uint8_t value into its ASCII decimal string representation.
|
||||
* The string is terminated if there is enough space in buf.
|
||||
*
|
||||
* @param buf Address of where to store the string representation.
|
||||
* @param buflen Size of the storage area for string representation.
|
||||
* @param value The value to convert to decimal string
|
||||
*
|
||||
* @return The length of the converted string (excluding terminator if
|
||||
* any), or 0 if an error occurred.
|
||||
*/
|
||||
uint8_t u8_to_dec(char *buf, uint8_t buflen, uint8_t value);
|
||||
|
||||
/**
|
||||
* @brief Properly truncate a NULL-terminated UTF-8 string
|
||||
*
|
||||
* Take a NULL-terminated UTF-8 string and ensure that if the string has been
|
||||
* truncated (by setting the NULL terminator) earlier by other means, that
|
||||
* the string ends with a properly formatted UTF-8 character (1-4 bytes).
|
||||
*
|
||||
* @htmlonly
|
||||
* Example:
|
||||
* char test_str[] = "€€€";
|
||||
* char trunc_utf8[8];
|
||||
*
|
||||
* printf("Original : %s\n", test_str); // €€€
|
||||
* strncpy(trunc_utf8, test_str, sizeof(trunc_utf8));
|
||||
* trunc_utf8[sizeof(trunc_utf8) - 1] = '\0';
|
||||
* printf("Bad : %s\n", trunc_utf8); // €€�
|
||||
* utf8_trunc(trunc_utf8);
|
||||
* printf("Truncated: %s\n", trunc_utf8); // €€
|
||||
* @endhtmlonly
|
||||
*
|
||||
* @param utf8_str NULL-terminated string
|
||||
*
|
||||
* @return Pointer to the @p utf8_str
|
||||
*/
|
||||
char *utf8_trunc(char *utf8_str);
|
||||
|
||||
/**
|
||||
* @brief Copies a UTF-8 encoded string from @p src to @p dst
|
||||
*
|
||||
* The resulting @p dst will always be NULL terminated, and the @p dst string
|
||||
* will always be properly UTF-8 truncated.
|
||||
*
|
||||
* @param dst The destination of the UTF-8 string.
|
||||
* @param src The source string
|
||||
* @param n The size of the @p dst buffer. Shall not be 0.
|
||||
*
|
||||
* return Pointer to the @p dst
|
||||
*/
|
||||
char *utf8_lcpy(char *dst, const char *src, size_t n);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !_ASMLANGUAGE */
|
||||
|
||||
/** @brief Number of bytes in @p x kibibytes */
|
||||
#ifdef _LINKER
|
||||
/* This is used in linker scripts so need to avoid type casting there */
|
||||
#define KB(x) ((x) << 10)
|
||||
#else
|
||||
#define KB(x) (((size_t)x) << 10)
|
||||
#endif
|
||||
/** @brief Number of bytes in @p x mebibytes */
|
||||
#define MB(x) (KB(x) << 10)
|
||||
/** @brief Number of bytes in @p x gibibytes */
|
||||
#define GB(x) (MB(x) << 10)
|
||||
|
||||
/** @brief Number of Hz in @p x kHz */
|
||||
#define KHZ(x) ((x) * 1000)
|
||||
/** @brief Number of Hz in @p x MHz */
|
||||
#define MHZ(x) (KHZ(x) * 1000)
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_SYS_UTIL_H_ */
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2014, Wind River Systems, Inc.
|
||||
* Copyright (c) 2020, Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Misc utilities
|
||||
*
|
||||
* Repetitive or obscure helper macros needed by sys/util.h.
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_SYS_UTIL_INTERNAL_H_
|
||||
#define ZEPHYR_INCLUDE_SYS_UTIL_INTERNAL_H_
|
||||
|
||||
#include "util_loops.h"
|
||||
|
||||
/* IS_ENABLED() helpers */
|
||||
|
||||
/* This is called from IS_ENABLED(), and sticks on a "_XXXX" prefix,
|
||||
* it will now be "_XXXX1" if config_macro is "1", or just "_XXXX" if it's
|
||||
* undefined.
|
||||
* ENABLED: Z_IS_ENABLED2(_XXXX1)
|
||||
* DISABLED Z_IS_ENABLED2(_XXXX)
|
||||
*/
|
||||
#define Z_IS_ENABLED1(config_macro) Z_IS_ENABLED2(_XXXX##config_macro)
|
||||
|
||||
/* Here's the core trick, we map "_XXXX1" to "_YYYY," (i.e. a string
|
||||
* with a trailing comma), so it has the effect of making this a
|
||||
* two-argument tuple to the preprocessor only in the case where the
|
||||
* value is defined to "1"
|
||||
* ENABLED: _YYYY, <--- note comma!
|
||||
* DISABLED: _XXXX
|
||||
*/
|
||||
#define _XXXX1 _YYYY,
|
||||
|
||||
/* Then we append an extra argument to fool the gcc preprocessor into
|
||||
* accepting it as a varargs macro.
|
||||
* arg1 arg2 arg3
|
||||
* ENABLED: Z_IS_ENABLED3(_YYYY, 1, 0)
|
||||
* DISABLED Z_IS_ENABLED3(_XXXX 1, 0)
|
||||
*/
|
||||
#define Z_IS_ENABLED2(one_or_two_args) Z_IS_ENABLED3(one_or_two_args 1, 0)
|
||||
|
||||
/* And our second argument is thus now cooked to be 1 in the case
|
||||
* where the value is defined to 1, and 0 if not:
|
||||
*/
|
||||
#define Z_IS_ENABLED3(ignore_this, val, ...) val
|
||||
|
||||
/* Used internally by COND_CODE_1 and COND_CODE_0. */
|
||||
#define Z_COND_CODE_1(_flag, _if_1_code, _else_code) \
|
||||
__COND_CODE(_XXXX##_flag, _if_1_code, _else_code)
|
||||
#define Z_COND_CODE_0(_flag, _if_0_code, _else_code) \
|
||||
__COND_CODE(_ZZZZ##_flag, _if_0_code, _else_code)
|
||||
#define _ZZZZ0 _YYYY,
|
||||
#define __COND_CODE(one_or_two_args, _if_code, _else_code) \
|
||||
__GET_ARG2_DEBRACKET(one_or_two_args _if_code, _else_code)
|
||||
|
||||
/* Gets second argument and removes brackets around that argument. It
|
||||
* is expected that the parameter is provided in brackets/parentheses.
|
||||
*/
|
||||
#define __GET_ARG2_DEBRACKET(ignore_this, val, ...) __DEBRACKET val
|
||||
|
||||
/* Used to remove brackets from around a single argument. */
|
||||
#define __DEBRACKET(...) __VA_ARGS__
|
||||
|
||||
/* Used by IS_EMPTY() */
|
||||
/* reference: https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/ */
|
||||
#define Z_HAS_COMMA(...) \
|
||||
NUM_VA_ARGS_LESS_1_IMPL(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, \
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
|
||||
#define Z_TRIGGER_PARENTHESIS_(...) ,
|
||||
#define Z_IS_EMPTY_(...) \
|
||||
Z_IS_EMPTY__( \
|
||||
Z_HAS_COMMA(__VA_ARGS__), \
|
||||
Z_HAS_COMMA(Z_TRIGGER_PARENTHESIS_ __VA_ARGS__), \
|
||||
Z_HAS_COMMA(__VA_ARGS__ (/*empty*/)), \
|
||||
Z_HAS_COMMA(Z_TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/)))
|
||||
#define Z_CAT5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4
|
||||
#define Z_IS_EMPTY__(_0, _1, _2, _3) \
|
||||
Z_HAS_COMMA(Z_CAT5(Z_IS_EMPTY_CASE_, _0, _1, _2, _3))
|
||||
#define Z_IS_EMPTY_CASE_0001 ,
|
||||
|
||||
/* Used by LIST_DROP_EMPTY() */
|
||||
/* Adding ',' after each element would add empty element at the end of
|
||||
* list, which is hard to remove, so instead precede each element with ',',
|
||||
* this way first element is empty, and this one is easy to drop.
|
||||
*/
|
||||
#define Z_LIST_ADD_ELEM(e) EMPTY, e
|
||||
#define Z_LIST_DROP_FIRST(...) GET_ARGS_LESS_N(1, __VA_ARGS__)
|
||||
#define Z_LIST_NO_EMPTIES(e) \
|
||||
COND_CODE_1(IS_EMPTY(e), (), (Z_LIST_ADD_ELEM(e)))
|
||||
|
||||
#define UTIL_CAT(a, ...) UTIL_PRIMITIVE_CAT(a, __VA_ARGS__)
|
||||
#define UTIL_PRIMITIVE_CAT(a, ...) a##__VA_ARGS__
|
||||
#define UTIL_CHECK_N(x, n, ...) n
|
||||
#define UTIL_CHECK(...) UTIL_CHECK_N(__VA_ARGS__, 0,)
|
||||
#define UTIL_NOT(x) UTIL_CHECK(UTIL_PRIMITIVE_CAT(UTIL_NOT_, x))
|
||||
#define UTIL_NOT_0 ~, 1,
|
||||
#define UTIL_COMPL(b) UTIL_PRIMITIVE_CAT(UTIL_COMPL_, b)
|
||||
#define UTIL_COMPL_0 1
|
||||
#define UTIL_COMPL_1 0
|
||||
#define UTIL_BOOL(x) UTIL_COMPL(UTIL_NOT(x))
|
||||
|
||||
#define UTIL_EVAL(...) __VA_ARGS__
|
||||
#define UTIL_EXPAND(...) __VA_ARGS__
|
||||
#define UTIL_REPEAT(...) UTIL_LISTIFY(__VA_ARGS__)
|
||||
|
||||
/* Implementation details for NUM_VA_ARGS_LESS_1 */
|
||||
#define NUM_VA_ARGS_LESS_1_IMPL( \
|
||||
_ignored, \
|
||||
_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
|
||||
_11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \
|
||||
_21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \
|
||||
_31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \
|
||||
_41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \
|
||||
_51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \
|
||||
_61, _62, N, ...) N
|
||||
|
||||
/* Used by MACRO_MAP_CAT */
|
||||
#define MACRO_MAP_CAT_(...) \
|
||||
/* To make sure it works also for 2 arguments in total */ \
|
||||
MACRO_MAP_CAT_N(NUM_VA_ARGS_LESS_1(__VA_ARGS__), __VA_ARGS__)
|
||||
#define MACRO_MAP_CAT_N_(N, ...) UTIL_CAT(MACRO_MC_, N)(__VA_ARGS__,)
|
||||
#define MACRO_MC_0(...)
|
||||
#define MACRO_MC_1(m, a, ...) m(a)
|
||||
#define MACRO_MC_2(m, a, ...) UTIL_CAT(m(a), MACRO_MC_1(m, __VA_ARGS__,))
|
||||
#define MACRO_MC_3(m, a, ...) UTIL_CAT(m(a), MACRO_MC_2(m, __VA_ARGS__,))
|
||||
#define MACRO_MC_4(m, a, ...) UTIL_CAT(m(a), MACRO_MC_3(m, __VA_ARGS__,))
|
||||
#define MACRO_MC_5(m, a, ...) UTIL_CAT(m(a), MACRO_MC_4(m, __VA_ARGS__,))
|
||||
#define MACRO_MC_6(m, a, ...) UTIL_CAT(m(a), MACRO_MC_5(m, __VA_ARGS__,))
|
||||
#define MACRO_MC_7(m, a, ...) UTIL_CAT(m(a), MACRO_MC_6(m, __VA_ARGS__,))
|
||||
#define MACRO_MC_8(m, a, ...) UTIL_CAT(m(a), MACRO_MC_7(m, __VA_ARGS__,))
|
||||
#define MACRO_MC_9(m, a, ...) UTIL_CAT(m(a), MACRO_MC_8(m, __VA_ARGS__,))
|
||||
#define MACRO_MC_10(m, a, ...) UTIL_CAT(m(a), MACRO_MC_9(m, __VA_ARGS__,))
|
||||
#define MACRO_MC_11(m, a, ...) UTIL_CAT(m(a), MACRO_MC_10(m, __VA_ARGS__,))
|
||||
#define MACRO_MC_12(m, a, ...) UTIL_CAT(m(a), MACRO_MC_11(m, __VA_ARGS__,))
|
||||
#define MACRO_MC_13(m, a, ...) UTIL_CAT(m(a), MACRO_MC_12(m, __VA_ARGS__,))
|
||||
#define MACRO_MC_14(m, a, ...) UTIL_CAT(m(a), MACRO_MC_13(m, __VA_ARGS__,))
|
||||
#define MACRO_MC_15(m, a, ...) UTIL_CAT(m(a), MACRO_MC_14(m, __VA_ARGS__,))
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_SYS_UTIL_INTERNAL_H_ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,601 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2014, Wind River Systems, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Macro utilities
|
||||
*
|
||||
* Macro utilities are the public interface for C/C++ code and device tree
|
||||
* related implementation. In general, C/C++ will include <sys/util.h>
|
||||
* instead this file directly. For device tree implementation, this file
|
||||
* should be include instead <sys/util_internal.h>
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_SYS_UTIL_MACROS_H_
|
||||
#define ZEPHYR_INCLUDE_SYS_UTIL_MACROS_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @addtogroup sys-util
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*
|
||||
* Most of the eldritch implementation details for all the macrobatics
|
||||
* below (APIs like IS_ENABLED(), COND_CODE_1(), etc.) are hidden away
|
||||
* in this file.
|
||||
*/
|
||||
#include <sys/util_internal.h>
|
||||
|
||||
#ifndef BIT
|
||||
#if defined(_ASMLANGUAGE)
|
||||
#define BIT(n) (1 << (n))
|
||||
#else
|
||||
/**
|
||||
* @brief Unsigned integer with bit position @p n set (signed in
|
||||
* assembly language).
|
||||
*/
|
||||
#define BIT(n) (1UL << (n))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/** @brief 64-bit unsigned integer with bit position @p _n set. */
|
||||
#define BIT64(_n) (1ULL << (_n))
|
||||
|
||||
/**
|
||||
* @brief Set or clear a bit depending on a boolean value
|
||||
*
|
||||
* The argument @p var is a variable whose value is written to as a
|
||||
* side effect.
|
||||
*
|
||||
* @param var Variable to be altered
|
||||
* @param bit Bit number
|
||||
* @param set if 0, clears @p bit in @p var; any other value sets @p bit
|
||||
*/
|
||||
#define WRITE_BIT(var, bit, set) \
|
||||
((var) = (set) ? ((var) | BIT(bit)) : ((var) & ~BIT(bit)))
|
||||
|
||||
/**
|
||||
* @brief Bit mask with bits 0 through <tt>n-1</tt> (inclusive) set,
|
||||
* or 0 if @p n is 0.
|
||||
*/
|
||||
#define BIT_MASK(n) (BIT(n) - 1UL)
|
||||
|
||||
/**
|
||||
* @brief 64-bit bit mask with bits 0 through <tt>n-1</tt> (inclusive) set,
|
||||
* or 0 if @p n is 0.
|
||||
*/
|
||||
#define BIT64_MASK(n) (BIT64(n) - 1ULL)
|
||||
|
||||
/**
|
||||
* @brief Check for macro definition in compiler-visible expressions
|
||||
*
|
||||
* This trick was pioneered in Linux as the config_enabled() macro. It
|
||||
* has the effect of taking a macro value that may be defined to "1"
|
||||
* or may not be defined at all and turning it into a literal
|
||||
* expression that can be handled by the C compiler instead of just
|
||||
* the preprocessor. It is often used with a @p CONFIG_FOO macro which
|
||||
* may be defined to 1 via Kconfig, or left undefined.
|
||||
*
|
||||
* That is, it works similarly to <tt>\#if defined(CONFIG_FOO)</tt>
|
||||
* except that its expansion is a C expression. Thus, much <tt>\#ifdef</tt>
|
||||
* usage can be replaced with equivalents like:
|
||||
*
|
||||
* if (IS_ENABLED(CONFIG_FOO)) {
|
||||
* do_something_with_foo
|
||||
* }
|
||||
*
|
||||
* This is cleaner since the compiler can generate errors and warnings
|
||||
* for @p do_something_with_foo even when @p CONFIG_FOO is undefined.
|
||||
*
|
||||
* @param config_macro Macro to check
|
||||
* @return 1 if @p config_macro is defined to 1, 0 otherwise (including
|
||||
* if @p config_macro is not defined)
|
||||
*/
|
||||
#define IS_ENABLED(config_macro) Z_IS_ENABLED1(config_macro)
|
||||
/* INTERNAL: the first pass above is just to expand any existing
|
||||
* macros, we need the macro value to be e.g. a literal "1" at
|
||||
* expansion time in the next macro, not "(1)", etc... Standard
|
||||
* recursive expansion does not work.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Insert code depending on whether @p _flag expands to 1 or not.
|
||||
*
|
||||
* This relies on similar tricks as IS_ENABLED(), but as the result of
|
||||
* @p _flag expansion, results in either @p _if_1_code or @p
|
||||
* _else_code is expanded.
|
||||
*
|
||||
* To prevent the preprocessor from treating commas as argument
|
||||
* separators, the @p _if_1_code and @p _else_code expressions must be
|
||||
* inside brackets/parentheses: <tt>()</tt>. These are stripped away
|
||||
* during macro expansion.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* COND_CODE_1(CONFIG_FLAG, (uint32_t x;), (there_is_no_flag();))
|
||||
*
|
||||
* If @p CONFIG_FLAG is defined to 1, this expands to:
|
||||
*
|
||||
* uint32_t x;
|
||||
*
|
||||
* It expands to <tt>there_is_no_flag();</tt> otherwise.
|
||||
*
|
||||
* This could be used as an alternative to:
|
||||
*
|
||||
* #if defined(CONFIG_FLAG) && (CONFIG_FLAG == 1)
|
||||
* #define MAYBE_DECLARE(x) uint32_t x
|
||||
* #else
|
||||
* #define MAYBE_DECLARE(x) there_is_no_flag()
|
||||
* #endif
|
||||
*
|
||||
* MAYBE_DECLARE(x);
|
||||
*
|
||||
* However, the advantage of COND_CODE_1() is that code is resolved in
|
||||
* place where it is used, while the @p \#if method defines @p
|
||||
* MAYBE_DECLARE on two lines and requires it to be invoked again on a
|
||||
* separate line. This makes COND_CODE_1() more concise and also
|
||||
* sometimes more useful when used within another macro's expansion.
|
||||
*
|
||||
* @note @p _flag can be the result of preprocessor expansion, e.g.
|
||||
* an expression involving <tt>NUM_VA_ARGS_LESS_1(...)</tt>.
|
||||
* However, @p _if_1_code is only expanded if @p _flag expands
|
||||
* to the integer literal 1. Integer expressions that evaluate
|
||||
* to 1, e.g. after doing some arithmetic, will not work.
|
||||
*
|
||||
* @param _flag evaluated flag
|
||||
* @param _if_1_code result if @p _flag expands to 1; must be in parentheses
|
||||
* @param _else_code result otherwise; must be in parentheses
|
||||
*/
|
||||
#define COND_CODE_1(_flag, _if_1_code, _else_code) \
|
||||
Z_COND_CODE_1(_flag, _if_1_code, _else_code)
|
||||
|
||||
/**
|
||||
* @brief Like COND_CODE_1() except tests if @p _flag is 0.
|
||||
*
|
||||
* This is like COND_CODE_1(), except that it tests whether @p _flag
|
||||
* expands to the integer literal 0. It expands to @p _if_0_code if
|
||||
* so, and @p _else_code otherwise; both of these must be enclosed in
|
||||
* parentheses.
|
||||
*
|
||||
* @param _flag evaluated flag
|
||||
* @param _if_0_code result if @p _flag expands to 0; must be in parentheses
|
||||
* @param _else_code result otherwise; must be in parentheses
|
||||
* @see COND_CODE_1()
|
||||
*/
|
||||
#define COND_CODE_0(_flag, _if_0_code, _else_code) \
|
||||
Z_COND_CODE_0(_flag, _if_0_code, _else_code)
|
||||
|
||||
/**
|
||||
* @brief Insert code if @p _flag is defined and equals 1.
|
||||
*
|
||||
* Like COND_CODE_1(), this expands to @p _code if @p _flag is defined to 1;
|
||||
* it expands to nothing otherwise.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* IF_ENABLED(CONFIG_FLAG, (uint32_t foo;))
|
||||
*
|
||||
* If @p CONFIG_FLAG is defined to 1, this expands to:
|
||||
*
|
||||
* uint32_t foo;
|
||||
*
|
||||
* and to nothing otherwise.
|
||||
*
|
||||
* It can be considered as a more compact alternative to:
|
||||
*
|
||||
* #if defined(CONFIG_FLAG) && (CONFIG_FLAG == 1)
|
||||
* uint32_t foo;
|
||||
* #endif
|
||||
*
|
||||
* @param _flag evaluated flag
|
||||
* @param _code result if @p _flag expands to 1; must be in parentheses
|
||||
*/
|
||||
#define IF_ENABLED(_flag, _code) \
|
||||
COND_CODE_1(_flag, _code, ())
|
||||
|
||||
/**
|
||||
* @brief Check if a macro has a replacement expression
|
||||
*
|
||||
* If @p a is a macro defined to a nonempty value, this will return
|
||||
* true, otherwise it will return false. It only works with defined
|
||||
* macros, so an additional @p \#ifdef test may be needed in some cases.
|
||||
*
|
||||
* This macro may be used with COND_CODE_1() and COND_CODE_0() while
|
||||
* processing <tt>__VA_ARGS__</tt> to avoid processing empty arguments.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* #define EMPTY
|
||||
* #define NON_EMPTY 1
|
||||
* #undef UNDEFINED
|
||||
* IS_EMPTY(EMPTY)
|
||||
* IS_EMPTY(NON_EMPTY)
|
||||
* IS_EMPTY(UNDEFINED)
|
||||
* #if defined(EMPTY) && IS_EMPTY(EMPTY) == true
|
||||
* some_conditional_code
|
||||
* #endif
|
||||
*
|
||||
* In above examples, the invocations of IS_EMPTY(...) return @p true,
|
||||
* @p false, and @p true; @p some_conditional_code is included.
|
||||
*
|
||||
* @param ... macro to check for emptiness (may be <tt>__VA_ARGS__</tt>)
|
||||
*/
|
||||
#define IS_EMPTY(...) Z_IS_EMPTY_(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Remove empty arguments from list.
|
||||
*
|
||||
* During macro expansion, <tt>__VA_ARGS__</tt> and other preprocessor
|
||||
* generated lists may contain empty elements, e.g.:
|
||||
*
|
||||
* #define LIST ,a,b,,d,
|
||||
*
|
||||
* Using EMPTY to show each empty element, LIST contains:
|
||||
*
|
||||
* EMPTY, a, b, EMPTY, d
|
||||
*
|
||||
* When processing such lists, e.g. using FOR_EACH(), all empty elements
|
||||
* will be processed, and may require filtering out.
|
||||
* To make that process easier, it is enough to invoke LIST_DROP_EMPTY
|
||||
* which will remove all empty elements.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* LIST_DROP_EMPTY(LIST)
|
||||
*
|
||||
* expands to:
|
||||
*
|
||||
* a, b, d
|
||||
*
|
||||
* @param ... list to be processed
|
||||
*/
|
||||
#define LIST_DROP_EMPTY(...) \
|
||||
Z_LIST_DROP_FIRST(FOR_EACH(Z_LIST_NO_EMPTIES, (), __VA_ARGS__))
|
||||
|
||||
/**
|
||||
* @brief Macro with an empty expansion
|
||||
*
|
||||
* This trivial definition is provided for readability when a macro
|
||||
* should expand to an empty result, which e.g. is sometimes needed to
|
||||
* silence checkpatch.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* #define LIST_ITEM(n) , item##n
|
||||
*
|
||||
* The above would cause checkpatch to complain, but:
|
||||
*
|
||||
* #define LIST_ITEM(n) EMPTY, item##n
|
||||
*
|
||||
* would not.
|
||||
*/
|
||||
#define EMPTY
|
||||
|
||||
/**
|
||||
* @brief Macro that expands to its argument
|
||||
*
|
||||
* This is useful in macros like @c FOR_EACH() when there is no
|
||||
* transformation required on the list elements.
|
||||
*
|
||||
* @param V any value
|
||||
*/
|
||||
#define IDENTITY(V) V
|
||||
|
||||
/**
|
||||
* @brief Get nth argument from argument list.
|
||||
*
|
||||
* @param N Argument index to fetch. Counter from 1.
|
||||
* @param ... Variable list of argments from which one argument is returned.
|
||||
*
|
||||
* @return Nth argument.
|
||||
*/
|
||||
#define GET_ARG_N(N, ...) Z_GET_ARG_##N(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Strips n first arguments from the argument list.
|
||||
*
|
||||
* @param N Number of arguments to discard.
|
||||
* @param ... Variable list of argments.
|
||||
*
|
||||
* @return argument list without N first arguments.
|
||||
*/
|
||||
#define GET_ARGS_LESS_N(N, ...) Z_GET_ARGS_LESS_##N(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Like <tt>a || b</tt>, but does evaluation and
|
||||
* short-circuiting at C preprocessor time.
|
||||
*
|
||||
* This is not the same as the binary @p || operator; in particular,
|
||||
* @p a should expand to an integer literal 0 or 1. However, @p b
|
||||
* can be any value.
|
||||
*
|
||||
* This can be useful when @p b is an expression that would cause a
|
||||
* build error when @p a is 1.
|
||||
*/
|
||||
#define UTIL_OR(a, b) COND_CODE_1(UTIL_BOOL(a), (a), (b))
|
||||
|
||||
/**
|
||||
* @brief Like <tt>a && b</tt>, but does evaluation and
|
||||
* short-circuiting at C preprocessor time.
|
||||
*
|
||||
* This is not the same as the binary @p &&, however; in particular,
|
||||
* @p a should expand to an integer literal 0 or 1. However, @p b
|
||||
* can be any value.
|
||||
*
|
||||
* This can be useful when @p b is an expression that would cause a
|
||||
* build error when @p a is 0.
|
||||
*/
|
||||
#define UTIL_AND(a, b) COND_CODE_1(UTIL_BOOL(a), (b), (0))
|
||||
|
||||
/**
|
||||
* @brief Generates a sequence of code.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* #define FOO(i, _) MY_PWM ## i ,
|
||||
* { UTIL_LISTIFY(PWM_COUNT, FOO) }
|
||||
*
|
||||
* The above two lines expand to:
|
||||
*
|
||||
* { MY_PWM0 , MY_PWM1 , }
|
||||
*
|
||||
* @param LEN The length of the sequence. Must be an integer literal less
|
||||
* than 255.
|
||||
* @param F A macro function that accepts at least two arguments:
|
||||
* <tt>F(i, ...)</tt>. @p F is called repeatedly in the expansion.
|
||||
* Its first argument @p i is the index in the sequence, and
|
||||
* the variable list of arguments passed to UTIL_LISTIFY are passed
|
||||
* through to @p F.
|
||||
*
|
||||
* @note Calling UTIL_LISTIFY with undefined arguments has undefined
|
||||
* behavior.
|
||||
*/
|
||||
#define UTIL_LISTIFY(LEN, F, ...) UTIL_CAT(Z_UTIL_LISTIFY_, LEN)(F, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Call a macro @p F on each provided argument with a given
|
||||
* separator between each call.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* #define F(x) int a##x
|
||||
* FOR_EACH(F, (;), 4, 5, 6);
|
||||
*
|
||||
* This expands to:
|
||||
*
|
||||
* int a4;
|
||||
* int a5;
|
||||
* int a6;
|
||||
*
|
||||
* @param F Macro to invoke
|
||||
* @param sep Separator (e.g. comma or semicolon). Must be in parentheses;
|
||||
* this is required to enable providing a comma as separator.
|
||||
* @param ... Variable argument list. The macro @p F is invoked as
|
||||
* <tt>F(element)</tt> for each element in the list.
|
||||
*/
|
||||
#define FOR_EACH(F, sep, ...) \
|
||||
Z_FOR_EACH(F, sep, REVERSE_ARGS(__VA_ARGS__))
|
||||
|
||||
/**
|
||||
* @brief Like FOR_EACH(), but with a terminator instead of a separator,
|
||||
* and drops empty elements from the argument list
|
||||
*
|
||||
* The @p sep argument to <tt>FOR_EACH(F, (sep), a, b)</tt> is a
|
||||
* separator which is placed between calls to @p F, like this:
|
||||
*
|
||||
* FOR_EACH(F, (sep), a, b) // F(a) sep F(b)
|
||||
* // ^^^ no sep here!
|
||||
*
|
||||
* By contrast, the @p term argument to <tt>FOR_EACH_NONEMPTY_TERM(F, (term),
|
||||
* a, b)</tt> is added after each time @p F appears in the expansion:
|
||||
*
|
||||
* FOR_EACH_NONEMPTY_TERM(F, (term), a, b) // F(a) term F(b) term
|
||||
* // ^^^^
|
||||
*
|
||||
* Further, any empty elements are dropped:
|
||||
*
|
||||
* FOR_EACH_NONEMPTY_TERM(F, (term), a, EMPTY, b) // F(a) term F(b) term
|
||||
*
|
||||
* This is more convenient in some cases, because FOR_EACH_NONEMPTY_TERM()
|
||||
* expands to nothing when given an empty argument list, and it's
|
||||
* often cumbersome to write a macro @p F that does the right thing
|
||||
* even when given an empty argument.
|
||||
*
|
||||
* One example is when <tt>__VA_ARGS__</tt> may or may not be empty,
|
||||
* and the results are embedded in a larger initializer:
|
||||
*
|
||||
* #define SQUARE(x) ((x)*(x))
|
||||
*
|
||||
* int my_array[] = {
|
||||
* FOR_EACH_NONEMPTY_TERM(SQUARE, (,), FOO(...))
|
||||
* FOR_EACH_NONEMPTY_TERM(SQUARE, (,), BAR(...))
|
||||
* FOR_EACH_NONEMPTY_TERM(SQUARE, (,), BAZ(...))
|
||||
* };
|
||||
*
|
||||
* This is more convenient than:
|
||||
*
|
||||
* 1. figuring out whether the @p FOO, @p BAR, and @p BAZ expansions
|
||||
* are empty and adding a comma manually (or not) between FOR_EACH()
|
||||
* calls
|
||||
* 2. rewriting SQUARE so it reacts appropriately when "x" is empty
|
||||
* (which would be necessary if e.g. @p FOO expands to nothing)
|
||||
*
|
||||
* @param F Macro to invoke on each nonempty element of the variable
|
||||
* arguments
|
||||
* @param term Terminator (e.g. comma or semicolon) placed after each
|
||||
* invocation of F. Must be in parentheses; this is required
|
||||
* to enable providing a comma as separator.
|
||||
* @param ... Variable argument list. The macro @p F is invoked as
|
||||
* <tt>F(element)</tt> for each nonempty element in the list.
|
||||
*/
|
||||
#define FOR_EACH_NONEMPTY_TERM(F, term, ...) \
|
||||
COND_CODE_0( \
|
||||
/* are there zero non-empty arguments ? */ \
|
||||
NUM_VA_ARGS_LESS_1(LIST_DROP_EMPTY(__VA_ARGS__, _)), \
|
||||
/* if so, expand to nothing */ \
|
||||
(), \
|
||||
/* otherwise, expand to: */ \
|
||||
(/* FOR_EACH() on nonempty elements, */ \
|
||||
FOR_EACH(F, term, LIST_DROP_EMPTY(__VA_ARGS__)) \
|
||||
/* plus a final terminator */ \
|
||||
__DEBRACKET term \
|
||||
))
|
||||
|
||||
/**
|
||||
* @brief Call macro @p F on each provided argument, with the argument's index
|
||||
* as an additional parameter.
|
||||
*
|
||||
* This is like FOR_EACH(), except @p F should be a macro which takes two
|
||||
* arguments: <tt>F(index, variable_arg)</tt>.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* #define F(idx, x) int a##idx = x
|
||||
* FOR_EACH_IDX(F, (;), 4, 5, 6);
|
||||
*
|
||||
* This expands to:
|
||||
*
|
||||
* int a0 = 4;
|
||||
* int a1 = 5;
|
||||
* int a2 = 6;
|
||||
*
|
||||
* @param F Macro to invoke
|
||||
* @param sep Separator (e.g. comma or semicolon). Must be in parentheses;
|
||||
* this is required to enable providing a comma as separator.
|
||||
* @param ... Variable argument list. The macro @p F is invoked as
|
||||
* <tt>F(index, element)</tt> for each element in the list.
|
||||
*/
|
||||
#define FOR_EACH_IDX(F, sep, ...) \
|
||||
Z_FOR_EACH_IDX(F, sep, REVERSE_ARGS(__VA_ARGS__))
|
||||
|
||||
/**
|
||||
* @brief Call macro @p F on each provided argument, with an additional fixed
|
||||
* argument as a parameter.
|
||||
*
|
||||
* This is like FOR_EACH(), except @p F should be a macro which takes two
|
||||
* arguments: <tt>F(variable_arg, fixed_arg)</tt>.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* static void func(int val, void *dev);
|
||||
* FOR_EACH_FIXED_ARG(func, (;), dev, 4, 5, 6);
|
||||
*
|
||||
* This expands to:
|
||||
*
|
||||
* func(4, dev);
|
||||
* func(5, dev);
|
||||
* func(6, dev);
|
||||
*
|
||||
* @param F Macro to invoke
|
||||
* @param sep Separator (e.g. comma or semicolon). Must be in parentheses;
|
||||
* this is required to enable providing a comma as separator.
|
||||
* @param fixed_arg Fixed argument passed to @p F as the second macro parameter.
|
||||
* @param ... Variable argument list. The macro @p F is invoked as
|
||||
* <tt>F(element, fixed_arg)</tt> for each element in the list.
|
||||
*/
|
||||
#define FOR_EACH_FIXED_ARG(F, sep, fixed_arg, ...) \
|
||||
Z_FOR_EACH_FIXED_ARG(F, sep, fixed_arg, REVERSE_ARGS(__VA_ARGS__))
|
||||
|
||||
/**
|
||||
* @brief Calls macro @p F for each variable argument with an index and fixed
|
||||
* argument
|
||||
*
|
||||
* This is like the combination of FOR_EACH_IDX() with FOR_EACH_FIXED_ARG().
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* #define F(idx, x, fixed_arg) int fixed_arg##idx = x
|
||||
* FOR_EACH_IDX_FIXED_ARG(F, (;), a, 4, 5, 6);
|
||||
*
|
||||
* This expands to:
|
||||
*
|
||||
* int a0 = 4;
|
||||
* int a1 = 5;
|
||||
* int a2 = 6;
|
||||
*
|
||||
* @param F Macro to invoke
|
||||
* @param sep Separator (e.g. comma or semicolon). Must be in parentheses;
|
||||
* This is required to enable providing a comma as separator.
|
||||
* @param fixed_arg Fixed argument passed to @p F as the third macro parameter.
|
||||
* @param ... Variable list of arguments. The macro @p F is invoked as
|
||||
* <tt>F(index, element, fixed_arg)</tt> for each element in
|
||||
* the list.
|
||||
*/
|
||||
#define FOR_EACH_IDX_FIXED_ARG(F, sep, fixed_arg, ...) \
|
||||
Z_FOR_EACH_IDX_FIXED_ARG(F, sep, fixed_arg, REVERSE_ARGS(__VA_ARGS__))
|
||||
|
||||
/** @brief Reverse arguments order.
|
||||
*
|
||||
* @param ... Variable argument list.
|
||||
*/
|
||||
#define REVERSE_ARGS(...) \
|
||||
Z_FOR_EACH_ENGINE(Z_FOR_EACH_EXEC, (,), Z_BYPASS, _, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Number of arguments in the variable arguments list minus one.
|
||||
*
|
||||
* @param ... List of arguments
|
||||
* @return Number of variadic arguments in the argument list, minus one
|
||||
*/
|
||||
#define NUM_VA_ARGS_LESS_1(...) \
|
||||
NUM_VA_ARGS_LESS_1_IMPL(__VA_ARGS__, 63, 62, 61, \
|
||||
60, 59, 58, 57, 56, 55, 54, 53, 52, 51, \
|
||||
50, 49, 48, 47, 46, 45, 44, 43, 42, 41, \
|
||||
40, 39, 38, 37, 36, 35, 34, 33, 32, 31, \
|
||||
30, 29, 28, 27, 26, 25, 24, 23, 22, 21, \
|
||||
20, 19, 18, 17, 16, 15, 14, 13, 12, 11, \
|
||||
10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, ~)
|
||||
|
||||
/**
|
||||
* @brief Mapping macro that pastes results together
|
||||
*
|
||||
* This is similar to FOR_EACH() in that it invokes a macro repeatedly
|
||||
* on each element of <tt>__VA_ARGS__</tt>. However, unlike FOR_EACH(),
|
||||
* MACRO_MAP_CAT() pastes the results together into a single token.
|
||||
*
|
||||
* For example, with this macro FOO:
|
||||
*
|
||||
* #define FOO(x) item_##x##_
|
||||
*
|
||||
* <tt>MACRO_MAP_CAT(FOO, a, b, c),</tt> expands to the token:
|
||||
*
|
||||
* item_a_item_b_item_c_
|
||||
*
|
||||
* @param ... Macro to expand on each argument, followed by its
|
||||
* arguments. (The macro should take exactly one argument.)
|
||||
* @return The results of expanding the macro on each argument, all pasted
|
||||
* together
|
||||
*/
|
||||
#define MACRO_MAP_CAT(...) MACRO_MAP_CAT_(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Mapping macro that pastes a fixed number of results together
|
||||
*
|
||||
* Similar to @ref MACRO_MAP_CAT(), but expects a fixed number of
|
||||
* arguments. If more arguments are given than are expected, the rest
|
||||
* are ignored.
|
||||
*
|
||||
* @param N Number of arguments to map
|
||||
* @param ... Macro to expand on each argument, followed by its
|
||||
* arguments. (The macro should take exactly one argument.)
|
||||
* @return The results of expanding the macro on each argument, all pasted
|
||||
* together
|
||||
*/
|
||||
#define MACRO_MAP_CAT_N(N, ...) MACRO_MAP_CAT_N_(N, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_SYS_UTIL_MACROS_H_ */
|
||||
Reference in New Issue
Block a user