Commit 63942989 authored by Johann Fischer's avatar Johann Fischer Committed by Carles Cufi
Browse files

modbus: add raw ADU support



MODBUS raw ADU support allows to implement
MODBUS messaging service over TCP or UDP.

Signed-off-by: default avatarJohann Fischer <johann.fischer@nordicsemi.no>
parent 4ff616b6
Loading
Loading
Loading
Loading
+93 −0
Original line number Diff line number Diff line
@@ -36,6 +36,31 @@
extern "C" {
#endif

/** Length of MBAP Header */
#define MODBUS_MBAP_LENGTH		7
/** Length of MBAP Header plus function code */
#define MODBUS_MBAP_AND_FC_LENGTH	(MODBUS_MBAP_LENGTH + 1)

/**
 * @brief Frame struct used internally and for raw ADU support.
 */
struct modbus_adu {
	/** Transaction Identifier */
	uint16_t trans_id;
	/** Protocol Identifier */
	uint16_t proto_id;
	/** Length of the data only (not the length of unit ID + PDU) */
	uint16_t length;
	/** Unit Identifier */
	uint8_t unit_id;
	/** Function Code */
	uint8_t fc;
	/** Transaction Data */
	uint8_t data[CONFIG_MODBUS_BUFFER_SIZE - 4];
	/** RTU CRC */
	uint16_t crc;
};

/**
 * @brief Coil read (FC01)
 *
@@ -347,6 +372,16 @@ struct modbus_user_callbacks {
 */
int modbus_iface_get_by_name(const char *iface_name);

/**
 * @brief ADU raw callback function signature
 *
 * @param iface      Modbus RTU interface index
 * @param adu        Pointer to the RAW ADU struct to send
 *
 * @retval           0 If transfer was successful
 */
typedef int (*modbus_raw_cb_t)(const int iface, const struct modbus_adu *adu);

/**
 * @brief Modbus interface mode
 */
@@ -355,6 +390,8 @@ enum modbus_mode {
	MODBUS_MODE_RTU,
	/** Modbus over serial line ASCII mode */
	MODBUS_MODE_ASCII,
	/** Modbus raw ADU mode */
	MODBUS_MODE_RAW,
};

/**
@@ -398,6 +435,8 @@ struct modbus_iface_param {
	union {
		/** Serial support parameter of the interface */
		struct modbus_serial_param serial;
		/** Pointer to raw ADU callback function */
		modbus_raw_cb_t raw_tx_cb;
	};
};

@@ -432,6 +471,60 @@ int modbus_init_client(const int iface, struct modbus_iface_param param);
 */
int modbus_disable(const uint8_t iface);

/**
 * @brief Submit raw ADU
 *
 * @param iface      Modbus RTU interface index
 * @param adu        Pointer to the RAW ADU struct that is received
 *
 * @retval           0 If transfer was successful
 */
int modbus_raw_submit_rx(const int iface, const struct modbus_adu *adu);

/**
 * @brief Put MBAP header into a buffer
 *
 * @param adu        Pointer to the RAW ADU struct
 * @param header     Pointer to the buffer in which MBAP header
 *                   will be placed.
 *
 * @retval           0 If transfer was successful
 */
void modbus_raw_put_header(const struct modbus_adu *adu, uint8_t *header);

/**
 * @brief Get MBAP header from a buffer
 *
 * @param adu        Pointer to the RAW ADU struct
 * @param header     Pointer to the buffer containing MBAP header
 *
 * @retval           0 If transfer was successful
 */
void modbus_raw_get_header(struct modbus_adu *adu, const uint8_t *header);

/**
 * @brief Set Server Device Failure exception
 *
 * This function modifies ADU passed by the pointer.
 *
 * @param adu        Pointer to the RAW ADU struct
 */
void modbus_raw_set_server_failure(struct modbus_adu *adu);

/**
 * @brief Use interface as backend to send and receive ADU
 *
 * This function overwrites ADU passed by the pointer and generates
 * exception responses if backend interface is misconfigured or
 * target device is unreachable.
 *
 * @param iface      Modbus client interface index
 * @param adu        Pointer to the RAW ADU struct
 *
 * @retval           0 If transfer was successful
 */
int modbus_raw_backend_txn(const int iface, struct modbus_adu *adu);

#ifdef __cplusplus
}
#endif
+5 −0
Original line number Diff line number Diff line
@@ -15,6 +15,11 @@ if(CONFIG_MODBUS)
		modbus_serial.c
	)

	zephyr_library_sources_ifdef(
		CONFIG_MODBUS_RAW_ADU
		modbus_raw.c
	)

	zephyr_library_sources_ifdef(
		CONFIG_MODBUS_SERVER
		modbus_server.c
+12 −0
Original line number Diff line number Diff line
@@ -53,6 +53,18 @@ config MODBUS_ASCII_MODE
	help
	  Enable ASCII transmission mode.

config MODBUS_RAW_ADU
	bool "Modbus raw ADU support"
	help
	  Enable Modbus raw ADU support.

config MODBUS_NUMOF_RAW_ADU
	int "Number of raw ADU instances"
	depends on MODBUS_RAW_ADU
	range 1 4
	help
	  Number of raw ADU instances.

config MODBUS_FP_EXTENSIONS
	bool "Floating-Point extensions"
	default y
+50 −0
Original line number Diff line number Diff line
@@ -51,8 +51,18 @@ static struct modbus_serial_config modbus_serial_cfg[] = {
		.cfg = &modbus_serial_cfg[n],			\
	},

#define DEFINE_MODBUS_RAW_ADU(x, _) {				\
		.iface_name = "RAW_"#x,				\
		.raw_tx_cb = NULL,				\
		.mode = MODBUS_MODE_RAW,			\
	},


static struct modbus_context mb_ctx_tbl[] = {
	DT_INST_FOREACH_STATUS_OKAY(MODBUS_DT_GET_DEV)
#ifdef CONFIG_MODBUS_RAW_ADU
	UTIL_LISTIFY(CONFIG_MODBUS_NUMOF_RAW_ADU, DEFINE_MODBUS_RAW_ADU, _)
#endif
};

static void modbus_rx_handler(struct k_work *item)
@@ -73,6 +83,11 @@ static void modbus_rx_handler(struct k_work *item)
			ctx->rx_adu_err = modbus_serial_rx_adu(ctx);
		}
		break;
	case MODBUS_MODE_RAW:
		if (IS_ENABLED(CONFIG_MODBUS_RAW_ADU)) {
			ctx->rx_adu_err = modbus_raw_rx_adu(ctx);
		}
		break;
	default:
		LOG_ERR("Unknown MODBUS mode");
		return;
@@ -113,6 +128,12 @@ void modbus_tx_adu(struct modbus_context *ctx)
			LOG_ERR("Unsupported MODBUS serial mode");
		}
		break;
	case MODBUS_MODE_RAW:
		if (IS_ENABLED(CONFIG_MODBUS_RAW_ADU) &&
		    modbus_raw_tx_adu(ctx)) {
			LOG_ERR("Unsupported MODBUS raw mode");
		}
		break;
	default:
		LOG_ERR("Unknown MODBUS mode");
	}
@@ -149,6 +170,17 @@ struct modbus_context *modbus_get_context(const uint8_t iface)
	return ctx;
}

int modbus_iface_get_by_ctx(const struct modbus_context *ctx)
{
	for (int i = 0; i < ARRAY_SIZE(mb_ctx_tbl); i++) {
		if (&mb_ctx_tbl[i] == ctx) {
			return i;
		}
	}

	return -ENODEV;
}

int modbus_iface_get_by_name(const char *iface_name)
{
	for (int i = 0; i < ARRAY_SIZE(mb_ctx_tbl); i++) {
@@ -216,6 +248,14 @@ int modbus_init_server(const int iface, struct modbus_iface_param param)
			goto init_server_error;
		}
		break;
	case MODBUS_MODE_RAW:
		if (IS_ENABLED(CONFIG_MODBUS_RAW_ADU) &&
		    modbus_raw_init(ctx, param) != 0) {
			LOG_ERR("Failed to init MODBUS raw ADU support");
			rc = -EINVAL;
			goto init_server_error;
		}
		break;
	default:
		LOG_ERR("Unknown MODBUS mode");
		rc = -ENOTSUP;
@@ -268,6 +308,14 @@ int modbus_init_client(const int iface, struct modbus_iface_param param)
			goto init_client_error;
		}
		break;
	case MODBUS_MODE_RAW:
		if (IS_ENABLED(CONFIG_MODBUS_RAW_ADU) &&
		    modbus_raw_init(ctx, param) != 0) {
			LOG_ERR("Failed to init MODBUS raw ADU support");
			rc = -EINVAL;
			goto init_client_error;
		}
		break;
	default:
		LOG_ERR("Unknown MODBUS mode");
		rc = -ENOTSUP;
@@ -306,6 +354,8 @@ int modbus_disable(const uint8_t iface)
			modbus_serial_disable(ctx);
		}
		break;
	case MODBUS_MODE_RAW:
		break;
	default:
		LOG_ERR("Unknown MODBUS mode");
	}
+27 −9
Original line number Diff line number Diff line
@@ -61,6 +61,11 @@
#define MODBUS_EXC_ILLEGAL_DATA_ADDR		2
#define MODBUS_EXC_ILLEGAL_DATA_VAL		3
#define MODBUS_EXC_SERVER_DEVICE_FAILURE	4
#define MODBUS_EXC_ACK				5
#define MODBUS_EXC_SERVER_DEVICE_BUSY		6
#define MODBUS_EXC_MEM_PARITY_ERROR		8
#define MODBUS_EXC_GW_PATH_UNAVAILABLE		10
#define MODBUS_EXC_GW_TARGET_FAILED_TO_RESP	11

/* Modbus RTU (ASCII) constants */
#define MODBUS_COIL_OFF_CODE			0x0000
@@ -72,13 +77,8 @@
#define MODBUS_ASCII_END_FRAME_CHAR1		'\r'
#define MODBUS_ASCII_END_FRAME_CHAR2		'\n'

struct modbus_adu {
	uint16_t length;
	uint8_t unit_id;
	uint8_t fc;
	uint8_t data[CONFIG_MODBUS_BUFFER_SIZE - 4];
	uint16_t crc;
};
/* Modbus ADU constants */
#define MODBUS_ADU_PROTO_ID			0x0000

struct mb_rtu_gpio_config {
	const char *name;
@@ -113,8 +113,12 @@ struct modbus_serial_config {
struct modbus_context {
	/* Interface name */
	const char *iface_name;
	union {
		/* Serial line configuration */
		struct modbus_serial_config *cfg;
		/* RAW TX callback */
		modbus_raw_cb_t raw_tx_cb;
	};
	/* MODBUS mode */
	enum modbus_mode mode;
	/* True if interface is configured as client */
@@ -162,6 +166,15 @@ struct modbus_context {
 */
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.
 *
@@ -253,4 +266,9 @@ int modbus_serial_init(struct modbus_context *ctx,
 */
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_ */
Loading