/* * Copyright (c) 2020 PHYTEC Messtechnik GmbH * * SPDX-License-Identifier: Apache-2.0 */ /* * This file is based on mbs_core.c 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. */ #include #include #include #include LOG_MODULE_REGISTER(modbus_s, CONFIG_MODBUS_LOG_LEVEL); /* * This functions are used to reset and update server's * statistics and communications counters. */ #ifdef CONFIG_MODBUS_FC08_DIAGNOSTIC void modbus_reset_stats(struct modbus_context *ctx) { /* Initialize all MODBUS event counters. */ ctx->mbs_msg_ctr = 0; ctx->mbs_crc_err_ctr = 0; ctx->mbs_except_ctr = 0; ctx->mbs_server_msg_ctr = 0; ctx->mbs_noresp_ctr = 0; } static void update_msg_ctr(struct modbus_context *ctx) { ctx->mbs_msg_ctr++; } static void update_crcerr_ctr(struct modbus_context *ctx) { ctx->mbs_crc_err_ctr++; } static void update_excep_ctr(struct modbus_context *ctx) { ctx->mbs_except_ctr++; } static void update_server_msg_ctr(struct modbus_context *ctx) { ctx->mbs_server_msg_ctr++; } static void update_noresp_ctr(struct modbus_context *ctx) { ctx->mbs_noresp_ctr++; } #else #define modbus_reset_stats(...) #define update_msg_ctr(...) #define update_crcerr_ctr(...) #define update_excep_ctr(...) #define update_server_msg_ctr(...) #define update_noresp_ctr(...) #endif /* CONFIG_MODBUS_FC08_DIAGNOSTIC */ /* * This function sets the indicated error response code into the response frame. * Then the routine is called to calculate the error check value. */ static void mbs_exception_rsp(struct modbus_context *ctx, uint8_t excep_code) { const uint8_t excep_bit = BIT(7); LOG_INF("FC 0x%02x Error 0x%02x", ctx->rx_adu.fc, excep_code); update_excep_ctr(ctx); ctx->tx_adu.fc |= excep_bit; ctx->tx_adu.data[0] = excep_code; ctx->tx_adu.length = 1; } /* * FC 01 (0x01) Read Coils * * Request Payload: * Function code 1 Byte * Starting Address 2 Bytes * Quantity of Coils 2 Bytes * * Response Payload: * Function code 1 Byte * Byte count 1 Bytes * Coil status N * 1 Byte */ static bool mbs_fc01_coil_read(struct modbus_context *ctx) { const uint16_t coils_limit = 2000; const uint8_t request_len = 4; uint8_t *presp; bool coil_state; int err; uint16_t coil_addr; uint16_t coil_qty; uint16_t num_bytes; uint8_t bit_mask; uint16_t coil_cntr; if (ctx->rx_adu.length != request_len) { LOG_ERR("Wrong request length"); return false; } if (ctx->mbs_user_cb->coil_rd == NULL) { mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_FC); return true; } coil_addr = sys_get_be16(&ctx->rx_adu.data[0]); coil_qty = sys_get_be16(&ctx->rx_adu.data[2]); /* Make sure we don't exceed the allowed limit per request */ if (coil_qty == 0 || coil_qty > coils_limit) { LOG_ERR("Number of coils limit exceeded"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_DATA_VAL); return true; } /* Calculate byte count for response. */ num_bytes = ((coil_qty - 1) / 8) + 1; /* Number of data bytes + byte count. */ ctx->tx_adu.length = num_bytes + 1; /* Set number of data bytes in response message. */ ctx->tx_adu.data[0] = (uint8_t)num_bytes; /* Clear bytes in response */ presp = &ctx->tx_adu.data[1]; memset(presp, 0, num_bytes); /* Reset the pointer to the start of the response payload */ presp = &ctx->tx_adu.data[1]; /* Start with bit 0 in response byte data mask. */ bit_mask = BIT(0); /* Initialize loop counter. */ coil_cntr = 0; /* Loop through each coil requested. */ while (coil_cntr < coil_qty) { err = ctx->mbs_user_cb->coil_rd(coil_addr, &coil_state); if (err != 0) { LOG_INF("Coil address not supported"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_DATA_ADDR); return true; } if (coil_state) { *presp |= bit_mask; } coil_addr++; /* Increment coil counter. */ coil_cntr++; /* Determine if 8 data bits have been filled. */ if ((coil_cntr % 8) == 0) { /* Reset the data mask. */ bit_mask = BIT(0); /* Increment frame data index. */ presp++; } else { /* * Still in same data byte, so shift the data mask * to the next higher bit position. */ bit_mask <<= 1; } } return true; } /* * FC 02 (0x02) Read Discrete Inputs * * Request Payload: * Function code 1 Byte * Starting Address 2 Bytes * Quantity of Inputs 2 Bytes * * Response Payload: * Function code 1 Byte * Byte count 1 Bytes * Input status N * 1 Byte */ static bool mbs_fc02_di_read(struct modbus_context *ctx) { const uint16_t di_limit = 2000; const uint8_t request_len = 4; uint8_t *presp; bool di_state; int err; uint16_t di_addr; uint16_t di_qty; uint16_t num_bytes; uint8_t bit_mask; uint16_t di_cntr; if (ctx->rx_adu.length != request_len) { LOG_ERR("Wrong request length"); return false; } if (ctx->mbs_user_cb->discrete_input_rd == NULL) { mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_FC); return true; } di_addr = sys_get_be16(&ctx->rx_adu.data[0]); di_qty = sys_get_be16(&ctx->rx_adu.data[2]); /* Make sure we don't exceed the allowed limit per request */ if (di_qty == 0 || di_qty > di_limit) { LOG_ERR("Number of inputs limit exceeded"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_DATA_VAL); return true; } /* Get number of bytes needed for response. */ num_bytes = ((di_qty - 1) / 8) + 1; /* Number of data bytes + byte count. */ ctx->tx_adu.length = num_bytes + 1; /* Set number of data bytes in response message. */ ctx->tx_adu.data[0] = (uint8_t)num_bytes; /* Clear bytes in response */ presp = &ctx->tx_adu.data[1]; for (di_cntr = 0; di_cntr < num_bytes; di_cntr++) { *presp++ = 0x00; } /* Reset the pointer to the start of the response payload */ presp = &ctx->tx_adu.data[1]; /* Start with bit 0 in response byte data mask. */ bit_mask = BIT(0); /* Initialize loop counter. */ di_cntr = 0; /* Loop through each DI requested. */ while (di_cntr < di_qty) { err = ctx->mbs_user_cb->discrete_input_rd(di_addr, &di_state); if (err != 0) { LOG_INF("Discrete input address not supported"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_DATA_ADDR); return true; } if (di_state) { *presp |= bit_mask; } di_addr++; /* Increment DI counter. */ di_cntr++; /* Determine if 8 data bits have been filled. */ if ((di_cntr % 8) == 0) { /* Reset the data mask. */ bit_mask = BIT(0); /* Increment data frame index. */ presp++; } else { /* * Still in same data byte, so shift the data mask * to the next higher bit position. */ bit_mask <<= 1; } } return true; } /* * 03 (0x03) Read Holding Registers * * Request Payload: * Function code 1 Byte * Starting Address 2 Bytes * Quantity of Registers 2 Bytes * * Response Payload: * Function code 1 Byte * Byte count 1 Bytes * Register Value N * 2 Byte */ static bool mbs_fc03_hreg_read(struct modbus_context *ctx) { const uint16_t regs_limit = 125; const uint8_t request_len = 4; uint8_t *presp; uint16_t err; uint16_t reg_addr; uint16_t reg_qty; uint16_t num_bytes; if (ctx->rx_adu.length != request_len) { LOG_ERR("Wrong request length"); return false; } reg_addr = sys_get_be16(&ctx->rx_adu.data[0]); reg_qty = sys_get_be16(&ctx->rx_adu.data[2]); if ((reg_addr < MODBUS_FP_EXTENSIONS_ADDR) || !IS_ENABLED(CONFIG_MODBUS_FP_EXTENSIONS)) { /* Read integer register */ if (ctx->mbs_user_cb->holding_reg_rd == NULL) { mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_FC); return true; } if (reg_qty == 0 || reg_qty > regs_limit) { LOG_ERR("Number of registers limit exceeded"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_DATA_VAL); return true; } /* Get number of bytes needed for response. */ num_bytes = (uint8_t)(reg_qty * sizeof(uint16_t)); } else { /* Read floating-point register */ if (ctx->mbs_user_cb->holding_reg_rd_fp == NULL) { mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_FC); return true; } if (reg_qty == 0 || reg_qty > (regs_limit / 2)) { LOG_ERR("Number of registers limit exceeded"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_DATA_VAL); return true; } /* Get number of bytes needed for response. */ num_bytes = (uint8_t)(reg_qty * sizeof(float)); } /* Number of data bytes + byte count. */ ctx->tx_adu.length = num_bytes + 1; /* Set number of data bytes in response message. */ ctx->tx_adu.data[0] = (uint8_t)num_bytes; /* Reset the pointer to the start of the response payload */ presp = &ctx->tx_adu.data[1]; /* Loop through each register requested. */ while (reg_qty > 0) { if (reg_addr < MODBUS_FP_EXTENSIONS_ADDR) { uint16_t reg; /* Read integer register */ err = ctx->mbs_user_cb->holding_reg_rd(reg_addr, ®); if (err == 0) { sys_put_be16(reg, presp); presp += sizeof(uint16_t); } } else if (IS_ENABLED(CONFIG_MODBUS_FP_EXTENSIONS)) { float fp; uint32_t reg; /* Read floating-point register */ err = ctx->mbs_user_cb->holding_reg_rd_fp(reg_addr, &fp); if (err == 0) { memcpy(®, &fp, sizeof(reg)); sys_put_be32(reg, presp); presp += sizeof(uint32_t); } } if (err != 0) { LOG_INF("Holding register address not supported"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_DATA_ADDR); return true; } /* Increment current register address */ reg_addr++; reg_qty--; } return true; } /* * 04 (0x04) Read Input Registers * * Request Payload: * Function code 1 Byte * Starting Address 2 Bytes * Quantity of Registers 2 Bytes * * Response Payload: * Function code 1 Byte * Byte count 1 Bytes * Register Value N * 2 Byte */ static bool mbs_fc04_inreg_read(struct modbus_context *ctx) { const uint16_t regs_limit = 125; const uint8_t request_len = 4; uint8_t *presp; int err; uint16_t reg_addr; uint16_t reg_qty; uint16_t num_bytes; if (ctx->rx_adu.length != request_len) { LOG_ERR("Wrong request length"); return false; } reg_addr = sys_get_be16(&ctx->rx_adu.data[0]); reg_qty = sys_get_be16(&ctx->rx_adu.data[2]); if ((reg_addr < MODBUS_FP_EXTENSIONS_ADDR) || !IS_ENABLED(CONFIG_MODBUS_FP_EXTENSIONS)) { /* Read integer register */ if (ctx->mbs_user_cb->input_reg_rd == NULL) { mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_FC); return true; } if (reg_qty == 0 || reg_qty > regs_limit) { LOG_ERR("Number of registers limit exceeded"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_DATA_VAL); return true; } /* Get number of bytes needed for response. */ num_bytes = (uint8_t)(reg_qty * sizeof(uint16_t)); } else { /* Read floating-point register */ if (ctx->mbs_user_cb->input_reg_rd_fp == NULL) { mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_FC); return true; } if (reg_qty == 0 || reg_qty > (regs_limit / 2)) { LOG_ERR("Number of registers limit exceeded"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_DATA_VAL); return true; } /* Get number of bytes needed for response. */ num_bytes = (uint8_t)(reg_qty * sizeof(float)); } /* Number of data bytes + byte count. */ ctx->tx_adu.length = num_bytes + 1; /* Set number of data bytes in response message. */ ctx->tx_adu.data[0] = (uint8_t)num_bytes; /* Reset the pointer to the start of the response payload */ presp = &ctx->tx_adu.data[1]; /* Loop through each register requested. */ while (reg_qty > 0) { if (reg_addr < MODBUS_FP_EXTENSIONS_ADDR) { uint16_t reg; /* Read integer register */ err = ctx->mbs_user_cb->input_reg_rd(reg_addr, ®); if (err == 0) { sys_put_be16(reg, presp); presp += sizeof(uint16_t); } } else if (IS_ENABLED(CONFIG_MODBUS_FP_EXTENSIONS)) { float fp; uint32_t reg; /* Read floating-point register */ err = ctx->mbs_user_cb->input_reg_rd_fp(reg_addr, &fp); if (err == 0) { memcpy(®, &fp, sizeof(reg)); sys_put_be32(reg, presp); presp += sizeof(uint32_t); } } if (err != 0) { LOG_INF("Input register address not supported"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_DATA_ADDR); return true; } /* Increment current register number */ reg_addr++; reg_qty--; } return true; } /* * FC 05 (0x05) Write Single Coil * * Request Payload: * Function code 1 Byte * Output Address 2 Bytes * Output Value 2 Bytes * * Response Payload: * Function code 1 Byte * Output Address 2 Bytes * Output Value 2 Bytes */ static bool mbs_fc05_coil_write(struct modbus_context *ctx) { const uint8_t request_len = 4; const uint8_t response_len = 4; int err; uint16_t coil_addr; uint16_t coil_val; bool coil_state; if (ctx->rx_adu.length != request_len) { LOG_ERR("Wrong request length %u", ctx->rx_adu.length); return false; } if (ctx->mbs_user_cb->coil_wr == NULL) { mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_FC); return true; } /* Get the desired coil address and coil value */ coil_addr = sys_get_be16(&ctx->rx_adu.data[0]); coil_val = sys_get_be16(&ctx->rx_adu.data[2]); /* See if coil needs to be OFF? */ if (coil_val == MODBUS_COIL_OFF_CODE) { coil_state = false; } else { coil_state = true; } err = ctx->mbs_user_cb->coil_wr(coil_addr, coil_state); if (err != 0) { LOG_INF("Coil address not supported"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_DATA_ADDR); return true; } /* Assemble response payload */ ctx->tx_adu.length = response_len; sys_put_be16(coil_addr, &ctx->tx_adu.data[0]); sys_put_be16(coil_val, &ctx->tx_adu.data[2]); return true; } /* * 06 (0x06) Write Single Register * * Request Payload: * Function code 1 Byte * Register Address 2 Bytes * Register Value 2 Bytes * * Response Payload: * Function code 1 Byte * Register Address 2 Bytes * Register Value 2 Bytes */ static bool mbs_fc06_hreg_write(struct modbus_context *ctx) { const uint8_t request_len = 4; const uint8_t response_len = 4; int err; uint16_t reg_addr; uint16_t reg_val; if (ctx->rx_adu.length != request_len) { LOG_ERR("Wrong request length %u", ctx->rx_adu.length); return false; } if (ctx->mbs_user_cb->holding_reg_wr == NULL) { mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_FC); return true; } reg_addr = sys_get_be16(&ctx->rx_adu.data[0]); reg_val = sys_get_be16(&ctx->rx_adu.data[2]); err = ctx->mbs_user_cb->holding_reg_wr(reg_addr, reg_val); if (err != 0) { LOG_INF("Register address not supported"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_DATA_ADDR); return true; } /* Assemble response payload */ ctx->tx_adu.length = response_len; sys_put_be16(reg_addr, &ctx->tx_adu.data[0]); sys_put_be16(reg_val, &ctx->tx_adu.data[2]); return true; } /* * 08 (0x08) Diagnostics * * Request Payload: * Function code 1 Byte * Sub-function code 2 Bytes * Data N * 2 Byte * * Response Payload: * Function code 1 Byte * Sub-function code 2 Bytes * Data N * 2 Byte */ #ifdef CONFIG_MODBUS_FC08_DIAGNOSTIC static bool mbs_fc08_diagnostics(struct modbus_context *ctx) { const uint8_t request_len = 4; const uint8_t response_len = 4; uint16_t sfunc; uint16_t data; if (ctx->rx_adu.length != request_len) { LOG_ERR("Wrong request length %u", ctx->rx_adu.length); return false; } sfunc = sys_get_be16(&ctx->rx_adu.data[0]); data = sys_get_be16(&ctx->rx_adu.data[2]); switch (sfunc) { case MODBUS_FC08_SUBF_QUERY: /* Sub-function 0x00 return Query Data */ break; case MODBUS_FC08_SUBF_CLR_CTR: /* Sub-function 0x0A clear Counters and Diagnostic */ modbus_reset_stats(ctx); break; case MODBUS_FC08_SUBF_BUS_MSG_CTR: /* Sub-function 0x0B return Bus Message Count */ data = ctx->mbs_msg_ctr; break; case MODBUS_FC08_SUBF_BUS_CRC_CTR: /* Sub-function 0x0C return Bus Communication Error Count */ data = ctx->mbs_crc_err_ctr; break; case MODBUS_FC08_SUBF_BUS_EXCEPT_CTR: /* Sub-function 0x0D return Bus Exception Error Count */ data = ctx->mbs_except_ctr; break; case MODBUS_FC08_SUBF_SERVER_MSG_CTR: /* Sub-function 0x0E return Server Message Count */ data = ctx->mbs_server_msg_ctr; break; case MODBUS_FC08_SUBF_SERVER_NO_RESP_CTR: /* Sub-function 0x0F return Server No Response Count */ data = ctx->mbs_noresp_ctr; break; default: LOG_INF("Sub-function not supported"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_FC); return true; } /* Assemble response payload */ ctx->tx_adu.length = response_len; sys_put_be16(sfunc, &ctx->tx_adu.data[0]); sys_put_be16(data, &ctx->tx_adu.data[2]); return true; } #else static bool mbs_fc08_diagnostics(struct modbus_context *ctx) { mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_FC); return true; } #endif /* * FC 15 (0x0F) Write Multiple Coils * * Request Payload: * Function code 1 Byte * Starting Address 2 Bytes * Quantity of Outputs 2 Bytes * Byte Count 1 Byte * Outputs Value N * 1 Byte * * Response Payload: * Function code 1 Byte * Starting Address 2 Bytes * Quantity of Outputs 2 Bytes */ static bool mbs_fc15_coils_write(struct modbus_context *ctx) { const uint16_t coils_limit = 2000; const uint8_t request_len = 6; const uint8_t response_len = 4; uint8_t temp = 0; int err; uint16_t coil_addr; uint16_t coil_qty; uint16_t num_bytes; uint16_t coil_cntr; uint8_t data_ix; bool coil_state; if (ctx->rx_adu.length < request_len) { LOG_ERR("Wrong request length %u", ctx->rx_adu.length); return false; } if (ctx->mbs_user_cb->coil_wr == NULL) { mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_FC); return true; } coil_addr = sys_get_be16(&ctx->rx_adu.data[0]); coil_qty = sys_get_be16(&ctx->rx_adu.data[2]); /* Get the byte count for the data. */ num_bytes = ctx->rx_adu.data[4]; if (coil_qty == 0 || coil_qty > coils_limit) { LOG_ERR("Number of coils limit exceeded"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_DATA_VAL); return true; } /* Be sure byte count is valid for quantity of coils. */ if (((((coil_qty - 1) / 8) + 1) != num_bytes) || (ctx->rx_adu.length != (num_bytes + 5))) { LOG_ERR("Mismatch in the number of coils"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_DATA_VAL); return true; } coil_cntr = 0; /* The 1st coil data byte is 6th element in payload */ data_ix = 5; /* Loop through each coil to be forced. */ while (coil_cntr < coil_qty) { /* Move to the next data byte after every eight bits. */ if ((coil_cntr % 8) == 0) { temp = ctx->rx_adu.data[data_ix++]; } if (temp & BIT(0)) { coil_state = true; } else { coil_state = false; } err = ctx->mbs_user_cb->coil_wr(coil_addr + coil_cntr, coil_state); if (err != 0) { LOG_INF("Coil address not supported"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_DATA_ADDR); return true; } /* Shift the data one bit position * to the right. */ temp >>= 1; /* Increment the COIL counter. */ coil_cntr++; } /* Assemble response payload */ ctx->tx_adu.length = response_len; sys_put_be16(coil_addr, &ctx->tx_adu.data[0]); sys_put_be16(coil_qty, &ctx->tx_adu.data[2]); return true; } /* * FC16 (0x10) Write Multiple registers * * Request Payload: * Function code 1 Byte * Starting Address 2 Bytes * Quantity of Registers 2 Bytes * Byte Count 1 Byte * Registers Value N * 1 Byte * * Response Payload: * Function code 1 Byte * Starting Address 2 Bytes * Quantity of Registers 2 Bytes * * If the address of the request exceeds or is equal to MODBUS_FP_EXTENSIONS_ADDR, * then the function would write to multiple 'floating-point' according to * the 'Daniels Flow Meter' extensions. This means that each register * requested is considered as a 32-bit IEEE-754 floating-point format. */ static bool mbs_fc16_hregs_write(struct modbus_context *ctx) { const uint16_t regs_limit = 125; const uint8_t request_len = 6; const uint8_t response_len = 4; uint8_t *prx_data; int err; uint16_t reg_addr; uint16_t reg_qty; uint16_t num_bytes; uint8_t reg_size; if (ctx->rx_adu.length < request_len) { LOG_ERR("Wrong request length %u", ctx->rx_adu.length); return false; } reg_addr = sys_get_be16(&ctx->rx_adu.data[0]); reg_qty = sys_get_be16(&ctx->rx_adu.data[2]); /* Get the byte count for the data. */ num_bytes = ctx->rx_adu.data[4]; if ((reg_addr < MODBUS_FP_EXTENSIONS_ADDR) || !IS_ENABLED(CONFIG_MODBUS_FP_EXTENSIONS)) { /* Write integer register */ if (ctx->mbs_user_cb->holding_reg_wr == NULL) { mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_FC); return true; } if (reg_qty == 0 || reg_qty > regs_limit) { LOG_ERR("Number of registers limit exceeded"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_DATA_VAL); return true; } reg_size = sizeof(uint16_t); } else { /* Write floating-point register */ if (ctx->mbs_user_cb->holding_reg_wr_fp == NULL) { mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_FC); return true; } if (reg_qty == 0 || reg_qty > (regs_limit / 2)) { LOG_ERR("Number of registers limit exceeded"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_DATA_VAL); return true; } reg_size = sizeof(float); } /* Compare number of bytes and payload length */ if ((ctx->rx_adu.length - 5) != num_bytes) { LOG_ERR("Mismatch in the number of bytes"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_DATA_VAL); return true; } if ((num_bytes / reg_qty) != (uint16_t)reg_size) { LOG_ERR("Mismatch in the number of registers"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_DATA_VAL); return true; } /* The 1st registers data byte is 6th element in payload */ prx_data = &ctx->rx_adu.data[5]; for (uint16_t reg_cntr = 0; reg_cntr < reg_qty; reg_cntr++) { uint16_t addr = reg_addr + reg_cntr; if ((reg_addr < MODBUS_FP_EXTENSIONS_ADDR) || !IS_ENABLED(CONFIG_MODBUS_FP_EXTENSIONS)) { uint16_t reg_val = sys_get_be16(prx_data); prx_data += sizeof(uint16_t); err = ctx->mbs_user_cb->holding_reg_wr(addr, reg_val); } else { uint32_t reg_val = sys_get_be32(prx_data); float fp; /* Write to floating point register */ memcpy(&fp, ®_val, sizeof(float)); prx_data += sizeof(uint32_t); err = ctx->mbs_user_cb->holding_reg_wr_fp(addr, fp); } if (err != 0) { LOG_INF("Register address not supported"); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_DATA_ADDR); return true; } } /* Assemble response payload */ ctx->tx_adu.length = response_len; sys_put_be16(reg_addr, &ctx->tx_adu.data[0]); sys_put_be16(reg_qty, &ctx->tx_adu.data[2]); return true; } static bool mbs_try_user_fc(struct modbus_context *ctx, uint8_t fc) { struct modbus_custom_fc *p; LOG_DBG("Searching for custom Modbus handlers for code %u", fc); SYS_SLIST_FOR_EACH_CONTAINER(&ctx->user_defined_cbs, p, node) { if (p->fc == fc) { int iface = modbus_iface_get_by_ctx(ctx); bool rval; LOG_DBG("Found custom handler"); p->excep_code = MODBUS_EXC_NONE; rval = p->cb(iface, &ctx->rx_adu, &ctx->tx_adu, &p->excep_code, p->user_data); if (p->excep_code != MODBUS_EXC_NONE) { LOG_INF("Custom handler failed with code %d", p->excep_code); mbs_exception_rsp(ctx, p->excep_code); } return rval; } } LOG_ERR("Function code 0x%02x not implemented", fc); mbs_exception_rsp(ctx, MODBUS_EXC_ILLEGAL_FC); return true; } bool modbus_server_handler(struct modbus_context *ctx) { bool send_reply = false; uint8_t addr = ctx->rx_adu.unit_id; uint8_t fc = ctx->rx_adu.fc; LOG_DBG("Server RX handler %p", ctx); update_msg_ctr(ctx); if (ctx->rx_adu_err != 0) { update_noresp_ctr(ctx); if (ctx->rx_adu_err == -EIO) { update_crcerr_ctr(ctx); } return false; } if (addr != 0 && addr != ctx->unit_id) { LOG_DBG("Unit ID doesn't match %u != %u", addr, ctx->unit_id); update_noresp_ctr(ctx); return false; } /* Prepare response header */ ctx->tx_adu.trans_id = ctx->rx_adu.trans_id; ctx->tx_adu.proto_id = ctx->rx_adu.proto_id; ctx->tx_adu.unit_id = addr; ctx->tx_adu.fc = fc; update_server_msg_ctr(ctx); switch (fc) { case MODBUS_FC01_COIL_RD: send_reply = mbs_fc01_coil_read(ctx); break; case MODBUS_FC02_DI_RD: send_reply = mbs_fc02_di_read(ctx); break; case MODBUS_FC03_HOLDING_REG_RD: send_reply = mbs_fc03_hreg_read(ctx); break; case MODBUS_FC04_IN_REG_RD: send_reply = mbs_fc04_inreg_read(ctx); break; case MODBUS_FC05_COIL_WR: send_reply = mbs_fc05_coil_write(ctx); break; case MODBUS_FC06_HOLDING_REG_WR: send_reply = mbs_fc06_hreg_write(ctx); break; case MODBUS_FC08_DIAGNOSTICS: send_reply = mbs_fc08_diagnostics(ctx); break; case MODBUS_FC15_COILS_WR: send_reply = mbs_fc15_coils_write(ctx); break; case MODBUS_FC16_HOLDING_REGS_WR: send_reply = mbs_fc16_hregs_write(ctx); break; default: send_reply = mbs_try_user_fc(ctx, fc); } if (addr == 0) { /* Broadcast address, do not reply */ send_reply = false; } if (send_reply == false) { update_noresp_ctr(ctx); } return send_reply; }