From e021f61946ca88cd762469762096710420bbe559 Mon Sep 17 00:00:00 2001 From: Maksim Salau Date: Fri, 21 Mar 2025 17:16:46 +0100 Subject: [PATCH] modbus: Add locking API Locking API allows atomic updates of modbus multi-register values in server mode and pausing of an interface in client mode. Signed-off-by: Maksim Salau --- include/zephyr/modbus/modbus.h | 33 ++++++++++++++++++++++++++++++++ subsys/modbus/Kconfig | 14 ++++++++++++++ subsys/modbus/modbus_core.c | 34 +++++++++++++++++++++++++++++++++ subsys/modbus/modbus_internal.h | 2 +- 4 files changed, 82 insertions(+), 1 deletion(-) diff --git a/include/zephyr/modbus/modbus.h b/include/zephyr/modbus/modbus.h index 7043594b8ec6..1d9e2d28c27d 100644 --- a/include/zephyr/modbus/modbus.h +++ b/include/zephyr/modbus/modbus.h @@ -30,6 +30,7 @@ #ifndef ZEPHYR_INCLUDE_MODBUS_H_ #define ZEPHYR_INCLUDE_MODBUS_H_ +#include #include #include #ifdef __cplusplus @@ -561,6 +562,38 @@ int modbus_init_client(const int iface, struct modbus_iface_param param); */ int modbus_disable(const uint8_t iface); +#if IS_ENABLED(CONFIG_MODBUS_LOCKING) +/** + * @brief Lock Modbus Interface + * + * The lock ensures that no new requests are handled while the inetraface is locked.asm + * May be used to ensure atomic update of register values in server role. + * + * @param iface Modbus interface index + * @param timeout Waiting period to lock the interface, + * or one of the special values K_NO_WAIT and + * K_FOREVER. + * + * @retval 0 Interface locked. + * @retval -ENODEV Invalid interface index + * @retval -EBUSY Returned without waiting. + * @retval -EAGAIN Waiting period timed out. + */ +int modbus_lock(const int iface, k_timeout_t timeout); + +/** + * @brief Unlock Modbus Interface + * + * @param iface Modbus interface index + * + * @retval 0 Interface unlocked. + * @retval -ENODEV Invalid interface index + * @retval -EPERM The current thread does not own the lock + * @retval -EINVAL The interface is not locked + */ +int modbus_unlock(const int iface); +#endif + /** * @brief Submit raw ADU * diff --git a/subsys/modbus/Kconfig b/subsys/modbus/Kconfig index 4c49d6c09851..d5bad3da1eb9 100644 --- a/subsys/modbus/Kconfig +++ b/subsys/modbus/Kconfig @@ -79,6 +79,20 @@ config MODBUS_FC08_DIAGNOSTIC help Enable function code 08 Diagnostic support +config MODBUS_LOCKING + bool "Interface locking support" + default n + help + Enable interface locking API + +config MODBUS_SERVER_LOCKING + bool "Server locking support" + depends on MODBUS_SERVER + select MODBUS_LOCKING + default n + help + Enable server locking to allow for atomic request handling + module = MODBUS module-str = Modbus Support module-help = Sets log level for Modbus support diff --git a/subsys/modbus/modbus_core.c b/subsys/modbus/modbus_core.c index 3e6f87c053b9..e197ebe3a1df 100644 --- a/subsys/modbus/modbus_core.c +++ b/subsys/modbus/modbus_core.c @@ -92,8 +92,16 @@ static void modbus_rx_handler(struct k_work *item) if (ctx->client == true) { k_sem_give(&ctx->client_wait_sem); } else if (IS_ENABLED(CONFIG_MODBUS_SERVER)) { + if (IS_ENABLED(CONFIG_MODBUS_SERVER_LOCKING)) { + k_mutex_lock(&ctx->iface_lock, K_FOREVER); + } + bool respond = modbus_server_handler(ctx); + if (IS_ENABLED(CONFIG_MODBUS_SERVER_LOCKING)) { + k_mutex_unlock(&ctx->iface_lock); + } + if (respond) { modbus_tx_adu(ctx); } else { @@ -407,3 +415,29 @@ int modbus_disable(const uint8_t iface) return 0; } + +#if IS_ENABLED(CONFIG_MODBUS_LOCKING) + +int modbus_lock(const int iface, k_timeout_t timeout) +{ + struct modbus_context *ctx = modbus_get_context(iface); + + if (ctx == NULL) { + return -ENODEV; + } + + return k_mutex_lock(&ctx->iface_lock, timeout); +} + +int modbus_unlock(const int iface) +{ + struct modbus_context *ctx = modbus_get_context(iface); + + if (ctx == NULL) { + return -ENODEV; + } + + return k_mutex_unlock(&ctx->iface_lock); +} + +#endif diff --git a/subsys/modbus/modbus_internal.h b/subsys/modbus/modbus_internal.h index 080da58ea09e..5b3071f5a22b 100644 --- a/subsys/modbus/modbus_internal.h +++ b/subsys/modbus/modbus_internal.h @@ -109,7 +109,7 @@ struct modbus_context { /* Interface state */ atomic_t state; - /* Client's mutually exclusive access */ + /* Interface lock */ struct k_mutex iface_lock; /* Wait for response semaphore */ struct k_sem client_wait_sem;