zephyr/subsys/modbus/modbus_internal.h
Henrik Lindblom 01757cfd32 modbus: add support for defining custom functions
Enables support for custom function codes. Modbus specification allows
vendor specific function codes in the range 65-72 & 100-110 [1] and this
feature allows users to implement custom logic for those codes.
Additionally, since the Zephyr Modbus stack doesn't implement all defined
Modbus fcs this feature allows users to add support for codes outside the
basic register reading / writing functionality offered by Zephyr.

Custom function codes can be added on a per-interface basis and the handler
structures are allocated by the caller.

[1]: https://modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf

Signed-off-by: Henrik Lindblom <henrik.lindblom@vaisala.com>
2023-09-29 13:05:42 +02:00

256 lines
6.9 KiB
C

/*
* Copyright (c) 2020 PHYTEC Messtechnik GmbH
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Parts of this file are based on mb.h from uC/Modbus Stack.
*
* uC/Modbus
* The Embedded Modbus Stack
*
* Copyright 2003-2020 Silicon Laboratories Inc. www.silabs.com
*
* SPDX-License-Identifier: APACHE-2.0
*
* This software is subject to an open source license and is distributed by
* Silicon Laboratories Inc. pursuant to the terms of the Apache License,
* Version 2.0 available at www.apache.org/licenses/LICENSE-2.0.
*/
#ifndef ZEPHYR_INCLUDE_MODBUS_INTERNAL_H_
#define ZEPHYR_INCLUDE_MODBUS_INTERNAL_H_
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/modbus/modbus.h>
#ifdef CONFIG_MODBUS_FP_EXTENSIONS
#define MODBUS_FP_EXTENSIONS_ADDR 5000
#else
#define MODBUS_FP_EXTENSIONS_ADDR UINT16_MAX
#endif
#define MODBUS_RTU_MTU 256
/* Modbus function codes */
#define MODBUS_FC01_COIL_RD 1
#define MODBUS_FC02_DI_RD 2
#define MODBUS_FC03_HOLDING_REG_RD 3
#define MODBUS_FC04_IN_REG_RD 4
#define MODBUS_FC05_COIL_WR 5
#define MODBUS_FC06_HOLDING_REG_WR 6
#define MODBUS_FC08_DIAGNOSTICS 8
#define MODBUS_FC15_COILS_WR 15
#define MODBUS_FC16_HOLDING_REGS_WR 16
/* Diagnostic sub-function codes */
#define MODBUS_FC08_SUBF_QUERY 0
#define MODBUS_FC08_SUBF_CLR_CTR 10
#define MODBUS_FC08_SUBF_BUS_MSG_CTR 11
#define MODBUS_FC08_SUBF_BUS_CRC_CTR 12
#define MODBUS_FC08_SUBF_BUS_EXCEPT_CTR 13
#define MODBUS_FC08_SUBF_SERVER_MSG_CTR 14
#define MODBUS_FC08_SUBF_SERVER_NO_RESP_CTR 15
/* Modbus RTU (ASCII) constants */
#define MODBUS_COIL_OFF_CODE 0x0000
#define MODBUS_COIL_ON_CODE 0xFF00
#define MODBUS_RTU_MIN_MSG_SIZE 4
#define MODBUS_CRC16_POLY 0xA001
#define MODBUS_ASCII_MIN_MSG_SIZE 11
#define MODBUS_ASCII_START_FRAME_CHAR ':'
#define MODBUS_ASCII_END_FRAME_CHAR1 '\r'
#define MODBUS_ASCII_END_FRAME_CHAR2 '\n'
/* Modbus ADU constants */
#define MODBUS_ADU_PROTO_ID 0x0000
struct modbus_serial_config {
/* UART device */
const struct device *dev;
/* RTU timeout (maximum inter-frame delay) */
uint32_t rtu_timeout;
/* Pointer to current position in buffer */
uint8_t *uart_buf_ptr;
/* Pointer to driver enable (DE) pin config */
struct gpio_dt_spec *de;
/* Pointer to receiver enable (nRE) pin config */
struct gpio_dt_spec *re;
/* RTU timer to detect frame end point */
struct k_timer rtu_timer;
/* Number of bytes received or to send */
uint16_t uart_buf_ctr;
/* Storage of received characters or characters to send */
uint8_t uart_buf[CONFIG_MODBUS_BUFFER_SIZE];
};
#define MODBUS_STATE_CONFIGURED 0
struct modbus_context {
/* Interface name */
const char *iface_name;
union {
/* Serial line configuration */
struct modbus_serial_config *cfg;
/* RAW TX callback */
struct modbus_raw_cb rawcb;
};
/* MODBUS mode */
enum modbus_mode mode;
/* True if interface is configured as client */
bool client;
/* Amount of time client is willing to wait for response from server */
uint32_t rxwait_to;
/* Pointer to user server callbacks */
struct modbus_user_callbacks *mbs_user_cb;
/* Interface state */
atomic_t state;
/* Client's mutually exclusive access */
struct k_mutex iface_lock;
/* Wait for response semaphore */
struct k_sem client_wait_sem;
/* Server work item */
struct k_work server_work;
/* Received frame */
struct modbus_adu rx_adu;
/* Frame to transmit */
struct modbus_adu tx_adu;
/* Records error from frame reception, e.g. CRC error */
int rx_adu_err;
#ifdef CONFIG_MODBUS_FC08_DIAGNOSTIC
uint16_t mbs_msg_ctr;
uint16_t mbs_crc_err_ctr;
uint16_t mbs_except_ctr;
uint16_t mbs_server_msg_ctr;
uint16_t mbs_noresp_ctr;
#endif
/* A linked list of function code, handler pairs */
sys_slist_t user_defined_cbs;
/* Unit ID */
uint8_t unit_id;
};
/**
* @brief Get Modbus interface context.
*
* @param ctx Modbus interface context
*
* @retval Pointer to interface context or NULL
* if interface not available or not configured;
*/
struct modbus_context *modbus_get_context(const uint8_t iface);
/**
* @brief Get Modbus interface index.
*
* @param ctx Pointer to Modbus interface context
*
* @retval Interface index or negative error value.
*/
int modbus_iface_get_by_ctx(const struct modbus_context *ctx);
/**
* @brief Send ADU.
*
* @param ctx Modbus interface context
*/
void modbus_tx_adu(struct modbus_context *ctx);
/**
* @brief Send ADU and wait certain time for response.
*
* @param ctx Modbus interface context
*
* @retval 0 If the function was successful,
* -ENOTSUP if Modbus mode is not supported,
* -ETIMEDOUT on timeout,
* -EMSGSIZE on length error,
* -EIO on CRC error.
*/
int modbus_tx_wait_rx_adu(struct modbus_context *ctx);
/**
* @brief Let server handle the received ADU.
*
* @param ctx Modbus interface context
*
* @retval True if the server has prepared a response ADU
* that should be sent.
*/
bool modbus_server_handler(struct modbus_context *ctx);
/**
* @brief Reset server stats.
*
* @param ctx Modbus interface context
*/
void modbus_reset_stats(struct modbus_context *ctx);
/**
* @brief Disable serial line reception.
*
* @param ctx Modbus interface context
*/
void modbus_serial_rx_disable(struct modbus_context *ctx);
/**
* @brief Enable serial line reception.
*
* @param ctx Modbus interface context
*/
void modbus_serial_rx_enable(struct modbus_context *ctx);
/**
* @brief Assemble ADU from serial line RX buffer
*
* @param ctx Modbus interface context
*
* @retval 0 If the function was successful,
* -ENOTSUP if serial line mode is not supported,
* -EMSGSIZE on length error,
* -EIO on CRC error.
*/
int modbus_serial_rx_adu(struct modbus_context *ctx);
/**
* @brief Assemble ADU from serial line RX buffer
*
* @param ctx Modbus interface context
*
* @retval 0 If the function was successful,
* -ENOTSUP if serial line mode is not supported.
*/
int modbus_serial_tx_adu(struct modbus_context *ctx);
/**
* @brief Initialize serial line support.
*
* @param ctx Modbus interface context
* @param param Configuration parameter of the interface
*
* @retval 0 If the function was successful.
*/
int modbus_serial_init(struct modbus_context *ctx,
struct modbus_iface_param param);
/**
* @brief Disable serial line support.
*
* @param ctx Modbus interface context
*/
void modbus_serial_disable(struct modbus_context *ctx);
int modbus_raw_rx_adu(struct modbus_context *ctx);
int modbus_raw_tx_adu(struct modbus_context *ctx);
int modbus_raw_init(struct modbus_context *ctx,
struct modbus_iface_param param);
#endif /* ZEPHYR_INCLUDE_MODBUS_INTERNAL_H_ */