diff --git a/include/device.h b/include/device.h index 50cf1d6a515..5767d7b3b48 100644 --- a/include/device.h +++ b/include/device.h @@ -304,6 +304,25 @@ struct device { void _sys_device_do_config_level(int level); struct device* device_get_binding(char *name); +/** + * @brief Indicate that the device is in the middle of a transaction + * + * Called by a device driver to indicate that it is in the middle of a + * transaction. + * + * @param busy_dev Pointer to device structure of the driver instance. + */ +void device_busy_set(struct device *busy_dev); + +/** + * @brief Indicate that the device has completed its transaction + * + * Called by a device driver to indicate the end of a transaction. + * + * @param busy_dev Pointer to device structure of the driver instance. + */ +void device_busy_clear(struct device *busy_dev); + #ifdef CONFIG_DEVICE_POWER_MANAGEMENT /** * Device PM functions @@ -370,6 +389,30 @@ static inline int device_resume(struct device *device, int pm_policy) */ void device_list_get(struct device **device_list, int *device_count); +/** + * @brief Check if any device is in the middle of a transaction + * + * Called by an application to see if any device is in the middle + * of a critical transaction that cannot be interrupted. + * + * @retval 0 if no device is busy + * @retval -EBUSY if any device is busy + */ +int device_any_busy_check(void); + +/** + * @brief Check if a specific device is in the middle of a transaction + * + * Called by an application to see if a particular device is in the + * middle of a critical transaction that cannot be interrupted. + * + * @param chk_dev Pointer to device structure of the specific device driver + * the caller is interested in. + * @retval 0 if the device is not busy + * @retval -EBUSY if the device is busy + */ +int device_busy_check(struct device *chk_dev); + #endif /** diff --git a/include/linker-defs.h b/include/linker-defs.h index 26a01a6d6e3..947ead1a702 100644 --- a/include/linker-defs.h +++ b/include/linker-defs.h @@ -45,6 +45,24 @@ #ifdef _LINKER + +/* + * Space for storing per device busy bitmap. Since we do not know beforehand + * the number of devices, we go through the below mechanism to allocate the + * required space. + */ +#ifdef CONFIG_DEVICE_POWER_MANAGEMENT +#define DEVICE_COUNT ((__device_init_end - __device_init_start) / __DEVICE_STR_SIZEOF) +#define DEV_BUSY_SZ (((DEVICE_COUNT + 31) / 32) * 4) +#define DEVICE_BUSY_BITFIELD() \ + FILL(0x00) ; \ + __device_busy_start = .; \ + . = . + DEV_BUSY_SZ; \ + __device_busy_end = .; +#else +#define DEVICE_BUSY_BITFIELD() +#endif + /* * generate a symbol to mark the start of the device initialization objects for * the specified level, then link all of those objects (sorted by priority); @@ -69,7 +87,8 @@ DEVICE_INIT_LEVEL(NANOKERNEL) \ DEVICE_INIT_LEVEL(MICROKERNEL) \ DEVICE_INIT_LEVEL(APPLICATION) \ - __device_init_end = .; \ + __device_init_end = .; \ + DEVICE_BUSY_BITFIELD() \ /* define a section for undefined device initialization levels */ diff --git a/kernel/nanokernel/device.c b/kernel/nanokernel/device.c index 34be89ff39d..1b774a29fbf 100644 --- a/kernel/nanokernel/device.c +++ b/kernel/nanokernel/device.c @@ -17,6 +17,7 @@ #include #include #include +#include extern struct device __device_init_start[]; extern struct device __device_PRIMARY_start[]; @@ -37,6 +38,9 @@ static struct device *config_levels[] = { #ifdef CONFIG_DEVICE_POWER_MANAGEMENT struct device_pm_ops device_pm_ops_nop = {device_pm_nop, device_pm_nop}; +extern uint32_t __device_busy_start[]; +extern uint32_t __device_busy_end[]; +#define DEVICE_BUSY_SIZE (__device_busy_end - __device_busy_start) #endif /** @@ -98,4 +102,43 @@ void device_list_get(struct device **device_list, int *device_count) *device_list = __device_init_start; *device_count = __device_init_end - __device_init_start; } + + +int device_any_busy_check(void) +{ + int i = 0; + + for (i = 0; i < DEVICE_BUSY_SIZE; i++) { + if (__device_busy_start[i] != 0) { + return -EBUSY; + } + } + return 0; +} + +int device_busy_check(struct device *chk_dev) +{ + if (atomic_test_bit((const atomic_t *)__device_busy_start, + (chk_dev - __device_init_start))) { + return -EBUSY; + } + return 0; +} + #endif + +void device_busy_set(struct device *busy_dev) +{ +#ifdef CONFIG_DEVICE_POWER_MANAGEMENT + atomic_set_bit((atomic_t *) __device_busy_start, + (busy_dev - __device_init_start)); +#endif +} + +void device_busy_clear(struct device *busy_dev) +{ +#ifdef CONFIG_DEVICE_POWER_MANAGEMENT + atomic_clear_bit((atomic_t *) __device_busy_start, + (busy_dev - __device_init_start)); +#endif +} diff --git a/kernel/nanokernel/include/nano_offsets.h b/kernel/nanokernel/include/nano_offsets.h index 043c7d38c05..8dbd2fca52d 100644 --- a/kernel/nanokernel/include/nano_offsets.h +++ b/kernel/nanokernel/include/nano_offsets.h @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include #ifndef _NANO_OFFSETS__H_ #define _NANO_OFFSETS__H_ @@ -64,4 +65,7 @@ GEN_OFFSET_SYM(tTCS, errno_var); GEN_ABSOLUTE_SYM(__tTCS_SIZEOF, sizeof(tTCS)); +/* size of the device structure. Used by linker scripts */ +GEN_ABSOLUTE_SYM(__DEVICE_STR_SIZEOF, sizeof(struct device)); + #endif /* _NANO_OFFSETS__H_ */