forked from espressif/esp-modbus
196 lines
7.4 KiB
C
196 lines
7.4 KiB
C
/*
|
|
* FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
|
|
* Copyright (c) 2016, 2017 Nucleron R&D LLC <main@nucleron.ru>
|
|
* Copyright (c) 2006 Christian Walter <wolti@sil.at>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* File: $Id: mbfuncother.c, v 1.8 2006/12/07 22:10:34 wolti Exp $
|
|
*/
|
|
#include <sys/param.h>
|
|
#include "mb_common.h"
|
|
#include "mb_proto.h"
|
|
#include "mb_slave.h"
|
|
#include "mb_master.h"
|
|
|
|
#define MB_PDU_BYTECNT_OFF (MB_PDU_DATA_OFF + 0)
|
|
#define MB_PDU_FUNC_DATA_OFF (MB_PDU_DATA_OFF + 1)
|
|
#define MB_CMD_SL_ID_LEN (1)
|
|
#define MB_SLAVE_ID_CHUNK_SIZE (MIN(MB_FUNC_OTHER_REP_SLAVEID_BUF, 32))
|
|
|
|
/* ----------------------- Start implementation -----------------------------*/
|
|
mb_exception_t mb_error_to_exception(mb_err_enum_t error_code);
|
|
|
|
/**
|
|
* This helper function performs the custom request.
|
|
*
|
|
* @param uid slave address
|
|
* @param fc custom function code
|
|
* @param buf additional data to send
|
|
* @param buf_size size of data to send
|
|
* @param timeout timeout
|
|
*
|
|
* @return error code (mb_err_enum_t)
|
|
*/
|
|
mb_err_enum_t mbm_rq_custom(mb_base_t *inst, uint8_t uid, uint8_t fc, uint8_t *buf, uint16_t buf_size, uint32_t tout)
|
|
{
|
|
uint8_t *mb_frame_ptr;
|
|
if (!buf || (uid > MB_ADDRESS_MAX) || (buf_size >= (MB_BUFFER_SIZE - 2))) {
|
|
return MB_EINVAL;
|
|
}
|
|
if (!mb_port_event_res_take(inst->port_obj, tout)) {
|
|
return MB_EBUSY;
|
|
}
|
|
inst->get_send_buf(inst, &mb_frame_ptr);
|
|
inst->set_dest_addr(inst, uid);
|
|
|
|
mb_frame_ptr[MB_PDU_FUNC_OFF] = fc;
|
|
|
|
memcpy(&mb_frame_ptr[MB_PDU_DATA_OFF], buf, buf_size);
|
|
|
|
inst->set_send_len(inst, MB_PDU_SIZE_MIN + buf_size);
|
|
|
|
(void)mb_port_event_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START));
|
|
return mb_port_event_wait_req_finish(inst->port_obj);
|
|
}
|
|
|
|
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED && MB_FUNC_OTHER_REP_SLAVEID_BUF
|
|
|
|
mb_err_enum_t mbm_rq_report_slave_id(mb_base_t *inst, uint8_t slave_addr, uint32_t timeout)
|
|
{
|
|
uint8_t *mb_frame_ptr = NULL;
|
|
mb_err_enum_t err = MB_ENOERR;
|
|
if (!inst || !inst->port_obj || (slave_addr > MB_ADDRESS_MAX)) {
|
|
err = MB_EINVAL;
|
|
} else if (!mb_port_event_res_take(inst->port_obj, timeout)) {
|
|
err = MB_EBUSY;
|
|
} else {
|
|
inst->get_send_buf(inst, &mb_frame_ptr);
|
|
inst->set_dest_addr(inst, slave_addr);
|
|
mb_frame_ptr[MB_PDU_FUNC_OFF] = MB_FUNC_OTHER_REPORT_SLAVEID;
|
|
inst->set_send_len(inst, MB_CMD_SL_ID_LEN);
|
|
(void)mb_port_event_post(inst->port_obj, EVENT(EV_FRAME_TRANSMIT | EV_TRANS_START));
|
|
err = mb_port_event_wait_req_finish(inst->port_obj);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
mb_exception_t mbm_fn_report_slave_id(mb_base_t *inst, uint8_t *pframe, uint16_t *plen)
|
|
{
|
|
uint8_t byte_count = 0;
|
|
mb_exception_t status = MB_EX_NONE;
|
|
mb_err_enum_t err;
|
|
|
|
if (!inst || !plen || !pframe) {
|
|
status = MB_EX_SLAVE_DEVICE_FAILURE;
|
|
} else if (*plen <= MB_BUFFER_SIZE - 2) {
|
|
byte_count = pframe[MB_PDU_BYTECNT_OFF];
|
|
// Transfer data from command buffer.
|
|
err = mbc_reg_common_cb(inst, &pframe[MB_PDU_FUNC_DATA_OFF], 0, byte_count);
|
|
// If an err occured convert it into a Modbus exception.
|
|
if (err != MB_ENOERR) {
|
|
status = mb_error_to_exception(err);
|
|
}
|
|
} else {
|
|
// Can't be a valid request because the length is incorrect.
|
|
status = MB_EX_ILLEGAL_DATA_VALUE;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
mb_exception_t mbs_fn_report_slave_id(mb_base_t *inst, uint8_t *pframe, uint16_t *plen_buf)
|
|
{
|
|
mb_exception_t status = MB_EX_NONE;
|
|
if (!inst || !pframe || !plen_buf || !inst->pobj_id || !inst->obj_id_len) {
|
|
status = MB_EX_SLAVE_DEVICE_FAILURE;
|
|
} else if ((inst->obj_id_len <= MB_BUFFER_SIZE - 2)
|
|
&& (*plen_buf == MB_CMD_SL_ID_LEN)) {
|
|
CRITICAL_SECTION(inst->lock) {
|
|
pframe[MB_PDU_FUNC_OFF] = MB_FUNC_OTHER_REPORT_SLAVEID; // rewrite the FC
|
|
*plen_buf = (uint16_t)(inst->obj_id_len);
|
|
pframe[MB_PDU_BYTECNT_OFF] = *plen_buf;
|
|
memcpy(&pframe[MB_PDU_FUNC_DATA_OFF], inst->pobj_id, (size_t)inst->obj_id_len);
|
|
*plen_buf += 2; // count function code + length in frame length
|
|
}
|
|
} else {
|
|
status = MB_EX_ILLEGAL_DATA_VALUE;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
mb_err_enum_t mbs_set_slave_id(mb_base_t *inst, uint8_t slave_id, bool is_running, uint8_t const *pdata, uint8_t data_len)
|
|
{
|
|
mb_err_enum_t status = MB_ENOERR;
|
|
// the first byte and second byte in the buffer is reserved for
|
|
// the parameter slave_id and the running flag. The rest of
|
|
// the buffer is available for additional data.
|
|
if (inst && inst->lock && (data_len + 2 <= MB_FUNC_OTHER_REP_SLAVEID_BUF)) {
|
|
uint8_t chunk_num = ((data_len + 2) / MB_SLAVE_ID_CHUNK_SIZE) + 1;
|
|
if (!inst->pobj_id || inst->obj_id_chunks != chunk_num) {
|
|
CRITICAL_SECTION(inst->lock) {
|
|
inst->pobj_id = realloc(inst->pobj_id, (chunk_num * MB_SLAVE_ID_CHUNK_SIZE));
|
|
}
|
|
}
|
|
if (!inst->pobj_id) {
|
|
return MB_ENORES;
|
|
}
|
|
CRITICAL_SECTION(inst->lock) {
|
|
inst->obj_id_len = 0;
|
|
inst->pobj_id[inst->obj_id_len++] = slave_id;
|
|
inst->pobj_id[inst->obj_id_len++] = (uint8_t)(is_running ? 0xFF : 0x00);
|
|
if (data_len > 0) {
|
|
memcpy(&inst->pobj_id[inst->obj_id_len], pdata, (size_t)data_len);
|
|
inst->obj_id_len += data_len;
|
|
inst->obj_id_chunks = chunk_num;
|
|
}
|
|
}
|
|
} else {
|
|
status = MB_ENORES;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
mb_err_enum_t mbs_get_slave_id(mb_base_t *inst, uint8_t *pdata, uint8_t *pdata_len)
|
|
{
|
|
mb_err_enum_t status = MB_ENOERR;
|
|
if (inst && inst->lock && pdata_len) {
|
|
if (!inst->pobj_id) {
|
|
return MB_ENOREG;
|
|
}
|
|
if (pdata && (*pdata_len >= inst->obj_id_len)) {
|
|
CRITICAL_SECTION(inst->lock) {
|
|
memcpy(pdata, &inst->pobj_id[0],(size_t)inst->obj_id_len);
|
|
}
|
|
} else {
|
|
status = MB_ENORES;
|
|
}
|
|
*pdata_len = inst->obj_id_len;
|
|
} else {
|
|
status = MB_EINVAL;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
#endif
|