Setting socket option so that TCP Modbus slave can re-use same port immediately after freeing it
/*
* Copyright (c) 2001-2003,2016 Mario de Sousa (msousa@fe.up.pt)
*
* This file is part of the Modbus library for Beremiz and matiec.
*
* This Modbus library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this Modbus library. If not, see <http://www.gnu.org/licenses/>.
*
* This code is made available on the understanding that it will not be
* used in safety-critical situations without a full and competent review.
*/
/* mb_master.c */
#include <fcntl.h> /* File control definitions */
#include <stdio.h> /* Standard input/output */
#include <string.h>
#include <stdlib.h>
#include <termio.h> /* POSIX terminal control definitions */
#include <sys/time.h> /* Time structures for select() */
#include <unistd.h> /* POSIX Symbolic Constants */
#include <errno.h> /* Error definitions */
#include <pthread.h> /* pthread_mutex_[un]lock() */
#include <netinet/in.h> /* required for htons() and ntohs() */
#include "mb_layer1.h"
#include "mb_master.h"
#include "mb_master_private.h"
/* #define DEBUG */ /* uncomment to see the data sent and received */
#define modbus_write fptr_[layer1_fin].modbus_write
#define modbus_read fptr_[layer1_fin].modbus_read
#define modbus_init fptr_[layer1_fin].modbus_init
#define modbus_done fptr_[layer1_fin].modbus_done
#define modbus_connect fptr_[layer1_fin].modbus_connect
#define modbus_listen fptr_[layer1_fin].modbus_listen
#define modbus_close fptr_[layer1_fin].modbus_close
#define modbus_silence_init fptr_[layer1_fin].modbus_silence_init
#define modbus_get_min_timeout fptr_[layer1_fin].modbus_get_min_timeout
/* the lower two bits of ttyfd are used to store the index to layer1 function pointers */
/* layer1_fin index to fptr_[] is in lowest 2 bits of fd */
#define get_ttyfd() int layer1_fin = fd & 3; int ttyfd = fd / 4;
/******************************************/
/******************************************/
/** **/
/** Global Variables... **/
/** **/
/******************************************/
/******************************************/
/* The layer 1 (RTU, ASCII, TCP) implementation will be adding some headers and CRC (at the end)
* of the packet we build here (actually currently it is only at the end). Since we want to
* re-use the same buffer so as not to continuosly copy the same info from buffer to buffer,
* we need tp allocate more bytes than the ones we need for this layer. Therefore, the
* extra_bytes parameter.
*
* Note that we add one more extra byte. This is because some packets will not be
* starting off at byte 0, but rather at byte 1 of the buffer. This is in order to guarantee
* that the data that is sent on the buffer is aligned on even bytes (the 16 bit words!).
* This will allow us to reference this memory as an u16 *, without producing 'bus error'
* messages in some embedded devices that do not allow acessing u16 on odd numbered addresses.
*/
static int buff_extra_bytes_;
#define QUERY_BUFFER_SIZE (MAX_L2_FRAME_LENGTH + buff_extra_bytes_ + 1)
/******************************************/
/******************************************/
/** **/
/** Local Utility functions... **/
/** **/
/******************************************/
/******************************************/
/*
* Function to determine next transaction id.
*
* We use a library wide transaction id, which means that we
* use a new transaction id no matter what slave to which we will
* be sending the request...
*/
static inline u16 next_transaction_id(void) {
static u16 next_id = 0;
return next_id++;
}
/*
* Functions to convert u16 variables
* between network and host byte order
*
* NOTE: Modbus uses MSByte first, just like
* tcp/ip, so we use the htons() and
* ntoh() functions to guarantee
* code portability.
*/
static inline u16 mb_hton(u16 h_value) {return htons(h_value);}
static inline u16 mb_ntoh(u16 m_value) {return ntohs(m_value);}
static inline u8 msb (u16 value) {return (value >> 8) & 0xFF;}
static inline u8 lsb (u16 value) {return value & 0xFF;}
/*************************************************/
/*************************************************/
/** **/
/** Common functions for Modbus Protocol. **/
/** **/
/*************************************************/
/*************************************************/
/* build the common elements of a query frame */
static inline int build_packet(u8 slave,
u8 function,
u16 start_addr,
u16 count,
u8 *packet) {
union {
u16 u16;
u8 u8[2];
} tmp;
packet[0] = slave,
packet[1] = function;
/* NOTE:
* Modbus uses high level addressing starting off from 1, but
* this is sent as 0 on the wire!
* We could expect the user to specify high level addressing
* starting at 1, and do the conversion to start off at 0 here.
* However, to do this we would then need to use an u32 data type
* to correctly hold the address supplied by the user (which could
* correctly be 65536, which does not fit in an u16), which would
* in turn require us to check whether the address supplied by the user
* is correct (i.e. <= 65536).
* I decided to go with the other option of using an u16, and
* requiring the user to use addressing starting off at 0!
*/
/* NOTE: we do not use up casting - i.e. the following
* *((u16 *)(packet+2)) = mb_hton(start_addr);
* because packet+2 is NOT aligned with an even address, and would
* therefore result in 'bus error' when using compilers that do not
* automatically do the required decomposing of this supposedly
* single bus access into two distinct bus accesses.
* (Note that some compilers do do this decomposing automatically
* in which case the following is not necessary).
* At the moment, I (Mario de Sousa) know of at least one cross-compiler
* that does not do the decomposing automatically, i.e. the
* AVR32 cross-compiler.
*/
tmp.u16 = mb_hton(start_addr);
packet[2] = tmp.u8[0];
packet[3] = tmp.u8[1];
tmp.u16 = mb_hton(count);
packet[4] = tmp.u8[0];
packet[5] = tmp.u8[1];
return 6;
}
/* Execute a Query/Response transaction between client and server */
/* returns: <0 -> ERROR: error codes
* >2 -> SUCCESS: frame length
* 0..2 -> will never be returned!
*/
static int mb_transaction(u8 *packet,
int query_length,
u8 **data,
int fd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout) {
int error = TIMEOUT;
int response_length = INTERNAL_ERROR;
u16 send_transaction_id, recv_transaction_id;
get_ttyfd(); /* declare the ttyfd variable, ... */
/* We must also initialize the recv_transaction_id with the same value,
* since some layer 1 protocols do not support transaction id's, so
* simply return the recv_transaction_id variable without any changes...
*/
/* NOTE: we re-use the same transaction id for all send re-tries., since, in truth,
* it is still the same transaction. This will also simplify re-synchronising with
* some slaves that keep a buffer of outstanding requests, and will reply to all of
* them, in FIFO order. In this case, once an error occurs we will be swamping the
* slave with requests. By using the same transaction id, we may correctly consider
* the reply to the first request sent as the reply to the third request! This means
* we stop re-trying the sending of further requests, and no longer swamp the slave...
*/
send_transaction_id = recv_transaction_id = next_transaction_id();
for (send_retries++; send_retries > 0; send_retries--) {
error = TIMEOUT;
if (modbus_write(ttyfd, packet, query_length, send_transaction_id, response_timeout) < 0)
{error = PORT_FAILURE; continue;}
/* if we receive a correct response but with a wrong transaction id or wrong modbus function, we try to
* receive another frame instead of returning an error or re-sending the request! This first frame could
* have been a response to a previous request of ours that timed out waiting for a response, and the
* response we are waiting for could be coming 'any minute now'.
*/
do {
response_length = modbus_read(&ttyfd, data, &recv_transaction_id,
packet, query_length, response_timeout);
/* TIMEOUT condition */
/* However, if we had previously received an invalid frame, or some other error,
* we return that error instead!
* Note that the 'error' variable was initialised with the TIMEOUT error
* condition, so if no previous error ocurred, we will be returning the
* TIMEOUT error condition here!
*/
if(response_length == -2) return error;
/* NOTE we want to break out of this while loop without even running the while()
* condition, as that condition is only valid if response_length > 3 !!
*/
if(response_length < 0) {error = PORT_FAILURE; break;}
/* This should never occur! Modbus_read() should only return valid frames! */
if(response_length < 3) return INTERNAL_ERROR;
} while (/* we have the wrong transaction id */
(send_transaction_id != recv_transaction_id)
/* not a response frame to _our_ query */
||
(((*data)[1] & ~0x80) != packet[1])
/* NOTE: no need to check whether (*data)[0] = slave! */
/* This has already been done by the modbus_read() function! */
);
if(response_length < 0) {error = PORT_FAILURE; continue;}
/* Now check whether we received a Modbus Exception frame */
if (((*data)[1] & 0x80) != 0) { /* we have an exception frame! */
/* NOTE: we have already checked above that data[2] exists! */
if (error_code != NULL) *error_code = (*data)[2];
return MODBUS_ERROR;
}
/* success! Let's get out of the send retry loop... */
return response_length;
}
/* reached the end of the retries... */
return error;
}
/**************************************/
/**************************************/
/** **/
/** Modbus Protocol Functions. **/
/** **/
/**************************************/
/**************************************/
/* Execute a transaction for functions that READ BITS.
* Bits are stored on an int array, one bit per int.
* Called by: read_input_bits()
* read_output_bits()
*/
static int read_bits(u8 function,
u8 slave,
u16 start_addr,
u16 count,
u16 *dest,
int dest_size,
int ttyfd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout,
pthread_mutex_t *data_access_mutex) {
u8 packet[QUERY_BUFFER_SIZE];
u8 *data;
int response_length, query_length;
int temp, i, bit, dest_pos = 0;
int coils_processed = 0;
query_length = build_packet(slave, function, start_addr, count, packet);
if (query_length < 0) return INTERNAL_ERROR;
response_length = mb_transaction(packet, query_length, &data, ttyfd,
send_retries, error_code, response_timeout);
if (response_length < 0) return response_length;
/* NOTE: Integer division. (count+7)/8 is equivalent to ceil(count/8) */
if (response_length != 3 + (count+7)/8) return INVALID_FRAME;
if (data[2] != (count+7)/8) return INVALID_FRAME;
if (NULL != data_access_mutex) pthread_mutex_lock(data_access_mutex);
for( i = 0; (i < data[2]) && (i < dest_size); i++ ) {
temp = data[3 + i];
for( bit = 0x01; (bit & 0xff) && (coils_processed < count); ) {
dest[dest_pos] = (temp & bit)?1:0;
coils_processed++;
dest_pos++;
bit = bit << 1;
}
}
if (NULL != data_access_mutex) pthread_mutex_unlock(data_access_mutex);
return response_length;
}
/* Execute a transaction for functions that READ BITS.
* Bits are stored on an u32 array, 32 bits per u32.
* Unused bits in last u32 are set to 0.
* Called by: read_input_bits_u32()
* read_output_bits_u32()
*/
static int read_bits_u32(u8 function,
u8 slave,
u16 start_addr,
u16 count, /* number of bits !! */
u32 *dest,
int ttyfd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout) {
u8 packet[QUERY_BUFFER_SIZE];
u8 *data;
int response_length, query_length;
int byte_count, i, dest_pos = 0;
query_length = build_packet(slave, function, start_addr, count, packet);
if (query_length < 0) return INTERNAL_ERROR;
response_length = mb_transaction(packet, query_length, &data, ttyfd,
send_retries, error_code, response_timeout);
if (response_length < 0) return response_length;
/* NOTE: Integer division. (count+7)/8 is equivalent to ceil(count/8) */
if (response_length != 3 + (count+7)/8) return INVALID_FRAME;
if (data[2] != (count+7)/8) return INVALID_FRAME;
byte_count = data[2];
data += 3;
/* handle groups of 4 bytes... */
for(i = 0, dest_pos = 0; i + 3 < byte_count; i += 4, dest_pos++)
dest[dest_pos] = data[i] + data[i+1]*0x100 + data[i+2]*0x10000 + data[i+3]*0x1000000;
/* handle any remaining bytes... begining with the last! */
if (i < byte_count) dest[dest_pos] = 0;
for(byte_count--; i <= byte_count; byte_count--)
dest[dest_pos] = dest[dest_pos]*0x100 + data[byte_count];
return response_length;
}
/* FUNCTION 0x01 - Read Coils
* Bits are stored on an int array, one bit per int.
*/
int read_output_bits(u8 slave,
u16 start_addr,
u16 count,
u16 *dest,
int dest_size,
int ttyfd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout,
pthread_mutex_t *data_access_mutex) {
if( count > MAX_READ_BITS ) {
count = MAX_READ_BITS;
#ifdef DEBUG
fprintf( stderr, "Too many coils requested.\n" );
#endif
}
return read_bits(0x01 /* function */,
slave, start_addr, count, dest, dest_size, ttyfd,
send_retries, error_code, response_timeout, data_access_mutex);
}
/* FUNCTION 0x01 - Read Coils
* Bits are stored on an u32 array, 32 bits per u32.
* Unused bits in last u32 are set to 0.
*/
int read_output_bits_u32(u8 slave,
u16 start_addr,
u16 count,
u32 *dest,
int ttyfd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout) {
if( count > MAX_READ_BITS ) {
count = MAX_READ_BITS;
#ifdef DEBUG
fprintf( stderr, "Too many coils requested.\n" );
#endif
}
return read_bits_u32(0x01 /* function */,
slave, start_addr, count, dest, ttyfd,
send_retries, error_code, response_timeout);
}
/* FUNCTION 0x02 - Read Discrete Inputs
* Bits are stored on an int array, one bit per int.
*/
int read_input_bits(u8 slave,
u16 start_addr,
u16 count,
u16 *dest,
int dest_size,
int ttyfd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout,
pthread_mutex_t *data_access_mutex) {
if( count > MAX_READ_BITS ) {
count = MAX_READ_BITS;
#ifdef DEBUG
fprintf( stderr, "Too many coils requested.\n" );
#endif
}
return read_bits(0x02 /* function */,
slave, start_addr, count, dest, dest_size, ttyfd,
send_retries, error_code, response_timeout, data_access_mutex);
}
/* FUNCTION 0x02 - Read Discrete Inputs
* Bits are stored on an u32 array, 32 bits per u32.
* Unused bits in last u32 are set to 0.
*/
int read_input_bits_u32(u8 slave,
u16 start_addr,
u16 count,
u32 *dest,
int ttyfd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout) {
if( count > MAX_READ_BITS ) {
count = MAX_READ_BITS;
#ifdef DEBUG
fprintf( stderr, "Too many coils requested.\n" );
#endif
}
return read_bits_u32(0x02 /* function */,
slave, start_addr, count, dest, ttyfd,
send_retries, error_code, response_timeout);
}
/* Execute a transaction for functions that READ REGISTERS.
* Called by: read_input_words()
* read_output_words()
*/
static int read_registers(u8 function,
u8 slave,
u16 start_addr,
u16 count,
u16 *dest,
int dest_size,
int ttyfd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout,
pthread_mutex_t *data_access_mutex) {
u8 *data;
u8 packet[QUERY_BUFFER_SIZE];
int response_length;
int query_length;
int temp,i;
query_length = build_packet(slave, function, start_addr, count, packet);
if (query_length < 0) return INTERNAL_ERROR;
response_length = mb_transaction(packet, query_length, &data, ttyfd,
send_retries, error_code, response_timeout);
if (response_length < 0) return response_length;
if (response_length != 3 + 2*count) return INVALID_FRAME;
if (data[2] != 2*count) return INVALID_FRAME;
if (NULL != data_access_mutex) pthread_mutex_lock(data_access_mutex);
for(i = 0; (i < (data[2]*2)) && (i < dest_size); i++ ) {
temp = data[3 + i *2] << 8; /* copy reg hi byte to temp hi byte*/
temp = temp | data[4 + i * 2]; /* copy reg lo byte to temp lo byte*/
dest[i] = temp;
}
if (NULL != data_access_mutex) pthread_mutex_unlock(data_access_mutex);
return response_length;
}
/* Execute a transaction for functions that READ REGISTERS.
* return the array with the data to the calling function
* Called by: read_input_words_u16_ref()
* read_output_words_u16_ref()
*/
static int read_registers_u16_ref(u8 function,
u8 slave,
u16 start_addr,
u16 count,
u16 **dest,
int ttyfd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout) {
u8 *data;
u8 packet[QUERY_BUFFER_SIZE];
int response_length;
int query_length;
int i, byte_count;
query_length = build_packet(slave, function, start_addr, count, packet);
if (query_length < 0) return INTERNAL_ERROR;
response_length = mb_transaction(packet, query_length, &data, ttyfd,
send_retries, error_code, response_timeout);
if (response_length < 0) return response_length;
if (response_length != 3 + 2*count) return INVALID_FRAME;
if (data[2] != 2*count) return INVALID_FRAME;
byte_count = data[2];
data = data + 3; /* & data[3] */
if (ntohs(0x0102) != 0x0102) {
/* little endian host... => we need to swap the bytes! */
for(i = 0; i < byte_count; i++ ) {
/* the following 3 lines result in the two values being exchanged! */
data[i ] = data[i] ^ data[i+1];
data[i+1] = data[i] ^ data[i+1];
data[i ] = data[i] ^ data[i+1];
}
}
*dest = (u16 *)data;
return byte_count;
}
/* Execute a transaction for functions that READ REGISTERS.
* u16 registers are stored in array of u32, two registers per u32.
* Unused bits of last u32 element are set to 0.
* Called by: read_input_words_u32()
* read_output_words_u32()
*/
static int read_registers_u32(u8 function,
u8 slave,
u16 start_addr,
u16 count,
u32 *dest,
int ttyfd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout) {
u8 *data;
u8 packet[QUERY_BUFFER_SIZE];
int response_length;
int query_length;
int i, byte_count, dest_pos;
query_length = build_packet(slave, function, start_addr, count, packet);
if (query_length < 0) return INTERNAL_ERROR;
response_length = mb_transaction(packet, query_length, &data, ttyfd,
send_retries, error_code, response_timeout);
if (response_length < 0) return response_length;
if (response_length != 3 + 2*count) return INVALID_FRAME;
if (data[2] != 2*count) return INVALID_FRAME;
byte_count = data[2];
data += 3;
if (ntohs(0x0102) == 0x0102) {
/* big endian host... */
/* handle groups of 4 bytes... */
for(i = 0, dest_pos = 0; i + 3 < byte_count; i += 4, dest_pos++) {
*(((u8 *)(dest + dest_pos))+ 0) = *(data+i+3);
*(((u8 *)(dest + dest_pos))+ 1) = *(data+i+4);
*(((u8 *)(dest + dest_pos))+ 2) = *(data+i+0);
*(((u8 *)(dest + dest_pos))+ 3) = *(data+i+1);
}
/* handle any remaining bytes...
* since byte_count is supposed to be multiple of 2,
* (and has already been verified above 'if (data[2] != 2*count)')
* this will be either 2, or none at all!
*/
if (i + 1 < byte_count)
*(((u8 *)(dest + dest_pos))+ 0) = 0;
*(((u8 *)(dest + dest_pos))+ 1) = 0;
*(((u8 *)(dest + dest_pos))+ 2) = *(data+i+0);
*(((u8 *)(dest + dest_pos))+ 3) = *(data+i+1);
} else {
/* little endian host... */
/* handle groups of 4 bytes... */
for(i = 0, dest_pos = 0; i + 3 < byte_count; i += 4, dest_pos++) {
*(((u8 *)(dest + dest_pos))+ 0) = *(data+i+1);
*(((u8 *)(dest + dest_pos))+ 1) = *(data+i+0);
*(((u8 *)(dest + dest_pos))+ 2) = *(data+i+3);
*(((u8 *)(dest + dest_pos))+ 3) = *(data+i+2);
}
/* handle any remaining bytes...
* since byte_count is supposed to be multiple of 2,
* (and has already been verified above 'if (data[2] != 2*count)')
* this will be either 2, or none at all!
*/
if (i + 1 < byte_count)
*(((u8 *)(dest + dest_pos))+ 0) = *(data+i+1);
*(((u8 *)(dest + dest_pos))+ 1) = *(data+i+0);
*(((u8 *)(dest + dest_pos))+ 2) = 0;
*(((u8 *)(dest + dest_pos))+ 3) = 0;
}
return response_length;
}
/* FUNCTION 0x03 - Read Holding Registers */
int read_output_words(u8 slave,
u16 start_addr,
u16 count,
u16 *dest,
int dest_size,
int ttyfd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout,
pthread_mutex_t *data_access_mutex) {
if( count > MAX_READ_REGS ) {
count = MAX_READ_REGS;
#ifdef DEBUG
fprintf( stderr, "Too many registers requested.\n" );
#endif
}
return read_registers(0x03 /* function */,
slave, start_addr, count, dest, dest_size, ttyfd,
send_retries, error_code, response_timeout, data_access_mutex);
}
/* FUNCTION 0x03 - Read Holding Registers
* u16 registers are stored in array of u32, two registers per u32.
* Unused bits of last u32 element are set to 0.
*/
int read_output_words_u32(u8 slave,
u16 start_addr,
u16 count,
u32 *dest,
int ttyfd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout) {
if( count > MAX_READ_REGS ) {
count = MAX_READ_REGS;
#ifdef DEBUG
fprintf( stderr, "Too many registers requested.\n" );
#endif
}
return read_registers_u32(0x03 /* function */,
slave, start_addr, count, dest, ttyfd,
send_retries, error_code, response_timeout);
}
/* FUNCTION 0x03 - Read Holding Registers
* return the array with the data to the calling function
*/
int read_output_words_u16_ref(u8 slave,
u16 start_addr,
u16 count,
u16 **dest,
int ttyfd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout) {
if( count > MAX_READ_REGS ) {
count = MAX_READ_REGS;
#ifdef DEBUG
fprintf( stderr, "Too many registers requested.\n" );
#endif
}
return read_registers_u16_ref(0x03 /* function */,
slave, start_addr, count, dest, ttyfd, send_retries,
error_code, response_timeout);
}
/* FUNCTION 0x04 - Read Input Registers */
int read_input_words(u8 slave,
u16 start_addr,
u16 count,
u16 *dest,
int dest_size,
int ttyfd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout,
pthread_mutex_t *data_access_mutex) {
if( count > MAX_READ_REGS ) {
count = MAX_READ_REGS;
#ifdef DEBUG
fprintf( stderr, "Too many input registers requested.\n" );
#endif
}
return read_registers(0x04 /* function */,
slave, start_addr, count, dest, dest_size, ttyfd, send_retries,
error_code, response_timeout, data_access_mutex);
}
/* FUNCTION 0x04 - Read Input Registers
* u16 registers are stored in array of u32, two registers per u32.
* Unused bits of last u32 element are set to 0.
*/
int read_input_words_u32(u8 slave,
u16 start_addr,
u16 count,
u32 *dest,
int ttyfd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout) {
if( count > MAX_READ_REGS ) {
count = MAX_READ_REGS;
#ifdef DEBUG
fprintf( stderr, "Too many input registers requested.\n" );
#endif
}
return read_registers_u32(0x04 /* function */,
slave, start_addr, count, dest, ttyfd, send_retries,
error_code, response_timeout);
}
/* FUNCTION 0x04 - Read Input Registers
* return the array with the data to the calling function
*/
int read_input_words_u16_ref(u8 slave,
u16 start_addr,
u16 count,
u16 **dest,
int ttyfd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout) {
if( count > MAX_READ_REGS ) {
count = MAX_READ_REGS;
#ifdef DEBUG
fprintf( stderr, "Too many input registers requested.\n" );
#endif
}
return read_registers_u16_ref(0x04 /* function */,
slave, start_addr, count, dest, ttyfd, send_retries,
error_code, response_timeout);
}
/* Execute a transaction for functions that WRITE a sinlge BIT.
* Called by: write_output_bit()
* write_output_word()
*/
static int set_single(u8 function,
u8 slave,
u16 addr,
u16 value,
int ttyfd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout,
pthread_mutex_t *data_access_mutex) {
u8 packet[QUERY_BUFFER_SIZE];
u8 *data;
int query_length, response_length;
if (NULL != data_access_mutex) pthread_mutex_lock(data_access_mutex);
query_length = build_packet(slave, function, addr, value, packet);
if (NULL != data_access_mutex) pthread_mutex_unlock(data_access_mutex);
if (query_length < 0) return INTERNAL_ERROR;
response_length = mb_transaction(packet, query_length, &data, ttyfd, send_retries,
error_code, response_timeout);
if (response_length < 0) return response_length;
if (response_length != 6) return INVALID_FRAME;
if ((data[2] != packet[2]) || (data[3] != packet[3]) ||
(data[4] != packet[4]) || (data[5] != packet[5]))
return INVALID_FRAME;
return response_length;
}
/* FUNCTION 0x05 - Force Single Coil */
int write_output_bit(u8 slave,
u16 coil_addr,
u16 state,
int fd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout,
pthread_mutex_t *data_access_mutex) {
if (state) state = 0xFF00;
return set_single(0x05 /* function */,
slave, coil_addr, state, fd, send_retries,
error_code, response_timeout, data_access_mutex);
}
/* FUNCTION 0x06 - Write Single Register */
int write_output_word(u8 slave,
u16 reg_addr,
u16 value,
int fd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout,
pthread_mutex_t *data_access_mutex) {
return set_single(0x06 /* function */,
slave, reg_addr, value, fd, send_retries,
error_code, response_timeout, data_access_mutex);
}
/* FUNCTION 0x0F - Force Multiple Coils */
int write_output_bits(u8 slave,
u16 start_addr,
u16 coil_count,
u16 *data,
int ttyfd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout,
pthread_mutex_t *data_access_mutex) {
int byte_count, i;
u8 bit;
int coil_check = 0;
int data_array_pos = 0;
int query_length, response_length;
u8 packet[QUERY_BUFFER_SIZE];
u8 *rdata;
if( coil_count > MAX_WRITE_COILS ) {
coil_count = MAX_WRITE_COILS;
#ifdef DEBUG
fprintf( stderr, "Writing to too many coils.\n" );
#endif
}
query_length = build_packet(slave, 0x0F /* function */,
start_addr, coil_count, packet);
if (query_length < 0) return INTERNAL_ERROR;
/* NOTE: Integer division. (count+7)/8 is equivalent to ceil(count/8) */
byte_count = (coil_count+7)/8;
packet[query_length] = byte_count;
if (NULL != data_access_mutex) pthread_mutex_lock(data_access_mutex);
bit = 0x01;
for(i = 0; i < byte_count; i++) {
packet[++query_length] = 0;
while((bit & 0xFF) && (coil_check++ < coil_count)) {
if(data[data_array_pos++]) {packet[query_length] |= bit;}
else {packet[query_length] &= ~bit;}
bit <<= 1;
}
bit = 0x01;
}
if (NULL != data_access_mutex) pthread_mutex_unlock(data_access_mutex);
response_length = mb_transaction(packet, ++query_length, &rdata, ttyfd, send_retries,
error_code, response_timeout);
if (response_length < 0) return response_length;
if (response_length != 6) return INVALID_FRAME;
if ((rdata[2] != packet[2]) ||
(rdata[3] != packet[3]) ||
(rdata[4] != packet[4]) ||
(rdata[5] != packet[5])) return INVALID_FRAME;
return response_length;
}
/* FUNCTION 0x0F - Force Multiple Coils
* Bits should be stored on an u32 array, 32 bits per u32.
* Unused bits in last u32 should be set to 0.
*/
int write_output_bits_u32(u8 slave,
u16 start_addr,
u16 coil_count,
u32 *data,
int ttyfd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout) {
int org_pos, byte_count, i;
int query_length, response_length;
u8 packet[QUERY_BUFFER_SIZE];
u8 *rdata;
if( coil_count > MAX_WRITE_COILS ) {
coil_count = MAX_WRITE_COILS;
#ifdef DEBUG
fprintf( stderr, "Writing to too many coils.\n" );
#endif
}
query_length = build_packet(slave, 0x0F /* function */,
start_addr, coil_count, packet);
if (query_length < 0) return INTERNAL_ERROR;
/* NOTE: Integer division. This is equivalent of determining the ceil(count/8) */
byte_count = (coil_count+7)/8;
packet[query_length] = byte_count;
/* handle groups of 4 bytes... */
for(i = 0, org_pos = 0; i + 3 < byte_count; i += 4, org_pos++) {
packet[++query_length] = data[org_pos] & 0xFF; data[org_pos] >>= 8;
packet[++query_length] = data[org_pos] & 0xFF; data[org_pos] >>= 8;
packet[++query_length] = data[org_pos] & 0xFF; data[org_pos] >>= 8;
packet[++query_length] = data[org_pos] & 0xFF;
}
/* handle any remaining bytes... */
for(; i < byte_count; i++) {
packet[++query_length] = data[org_pos] & 0xFF;
data[org_pos] >>= 8;
}
response_length = mb_transaction(packet, ++query_length, &rdata, ttyfd, send_retries,
error_code, response_timeout);
if (response_length < 0) return response_length;
if (response_length != 6) return INVALID_FRAME;
if ((rdata[2] != packet[2]) ||
(rdata[3] != packet[3]) ||
(rdata[4] != packet[4]) ||
(rdata[5] != packet[5])) return INVALID_FRAME;
return response_length;
}
/* FUNCTION 0x10 - Force Multiple Registers */
int write_output_words(u8 slave,
u16 start_addr,
u16 reg_count,
u16 *data,
int ttyfd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout,
pthread_mutex_t *data_access_mutex) {
u8 byte_count;
int i, query_length, response_length;
u8 packet[QUERY_BUFFER_SIZE];
u8 *rdata;
if( reg_count > MAX_WRITE_REGS ) {
reg_count = MAX_WRITE_REGS;
#ifdef DEBUG
fprintf( stderr, "Trying to write to too many registers.\n" );
#endif
}
query_length = build_packet(slave, 0x10 /* function */,
start_addr, reg_count, packet);
if (query_length < 0) return INTERNAL_ERROR;
byte_count = reg_count*2;
packet[query_length] = byte_count;
if (NULL != data_access_mutex) pthread_mutex_lock(data_access_mutex);
for( i = 0; i < reg_count; i++ ) {
packet[++query_length] = data[i] >> 8;
packet[++query_length] = data[i] & 0x00FF;
}
if (NULL != data_access_mutex) pthread_mutex_unlock(data_access_mutex);
response_length = mb_transaction(packet, ++query_length, &rdata, ttyfd, send_retries,
error_code, response_timeout);
if (response_length < 0) return response_length;
if (response_length != 6) return INVALID_FRAME;
if ((rdata[2] != packet[2]) ||
(rdata[3] != packet[3]) ||
(rdata[4] != packet[4]) ||
(rdata[5] != packet[5])) return INVALID_FRAME;
return response_length;
}
/* FUNCTION 0x10 - Force Multiple Registers
* u16 registers are stored in array of u32, two registers per u32.
* Unused bits of last u32 element are set to 0.
*/
int write_output_words_u32(u8 slave,
u16 start_addr,
/* number of 16 bit registers packed in the u32 array! */
u16 reg_count,
u32 *data,
int ttyfd,
int send_retries,
u8 *error_code,
const struct timespec *response_timeout) {
u8 byte_count;
int i, query_length, response_length;
u8 packet_[QUERY_BUFFER_SIZE];
u8 *packet = packet_; /* remove the const'ness of packet_ */
u8 *rdata;
if( reg_count > MAX_WRITE_REGS ) {
reg_count = MAX_WRITE_REGS;
#ifdef DEBUG
fprintf( stderr, "Trying to write to too many registers.\n" );
#endif
}
/* Make sure that the de-referencing and up-casting going on later on in
* this function, i.e. code like the following line:
* *((u16 *)packet) = XXX
* will result in u16 words starting off on even addresses.
* If we don't do this, some compilers (e.g. AVR32 cross-compiler) will
* generate code which, when executed, will result in 'bus error'.
*
* The following packet++ means that the first byte of the packet array is
* essentially never used. Notice too that the size of thepacket array
* already takes into account this un-used byte.
*/
packet++;
query_length = build_packet(slave, 0x10 /* function */,
start_addr, reg_count, packet);
if (query_length < 0) return INTERNAL_ERROR;
byte_count = reg_count*2;
packet[query_length] = byte_count;
/* handle groups of 4 bytes... */
for(i = 0; 4*i + 3 < byte_count; i++) {
*((u16 *)(packet+(++query_length))) = mb_hton(data[i]); ++query_length;
*((u16 *)(packet+(++query_length))) = mb_hton(data[i] >> 16); ++query_length;
}
/* handle any remaining bytes...
* since byte_count is supposed to be multiple of 2,
* (and has already been verified above 'if (data[2] != 2*count)')
* this will be either 2, or none at all!
*/
if (4*i + 1 < byte_count) {
*((u16 *)(packet+(++query_length))) = mb_hton(data[i]); ++query_length;
}
response_length = mb_transaction(packet, ++query_length, &rdata, ttyfd, send_retries,
error_code, response_timeout);
if (response_length < 0) return response_length;
if (response_length != 6) return INVALID_FRAME;
if ((rdata[2] != packet[2]) ||
(rdata[3] != packet[3]) ||
(rdata[4] != packet[4]) ||
(rdata[5] != packet[5])) return INVALID_FRAME;
return response_length;
}
/************************************************/
/************************************************/
/** **/
/** Modbus Library Management Functions. **/
/** **/
/************************************************/
/************************************************/
/* Initialise the Modbus Master Layer */
int mb_master_init__(int extra_bytes) {
#ifdef DEBUG
fprintf(stderr, "mb_master_init__(extra_bytes=%d), QUERY_BUFFER_SIZE=%d\n", extra_bytes, QUERY_BUFFER_SIZE);
#endif
buff_extra_bytes_ = extra_bytes;
return 0;
}
/* Shut down the Modbus Master Layer */
int mb_master_done__(void) {
return 0;
}
#if 0
int mb_master_init(int nd_count) {
int extra_bytes;
#ifdef DEBUG
fprintf( stderr, "mb_master_init()\n");
fprintf( stderr, "creating %d nodes\n", nd_count);
#endif
/* initialise layer 1 library */
if (modbus_init(nd_count, DEF_OPTIMIZATION, &extra_bytes) < 0)
goto error_exit_0;
/* initialise this library */
if (mb_master_init__(extra_bytes) < 0)
goto error_exit_1;
return 0;
error_exit_1:
modbus_done();
error_exit_0:
return -1;
}
int mb_master_done(void) {
mb_master_done__();
return modbus_done();
}
#endif
/* Establish a connection to a remote server/slave */
/* NOTE: We use the lower 2 bits of the returned node id to identify which
* layer1 implementation to use.
* 0 -> TCP
* 1 -> RTU
* 2 -> ASCII
* 4 -> unused
* The node id used by the layer1 is shifted left 2 bits
* before returning the node id to the caller!
*/
int mb_master_connect(node_addr_t node_addr) {
int res = -1;
#ifdef DEBUG
fprintf( stderr, "mb_master_tcp connect()\n");
#endif
/* call layer 1 library */
switch(node_addr.naf) {
case naf_tcp:
res = modbus_tcp_connect(node_addr);
if (res >= 0) res = res*4 + 0 /* offset into fptr_ with TCP functions */;
return res;
case naf_rtu:
res = modbus_rtu_connect(node_addr);
if (res >= 0) res = res*4 + 1 /* offset into fptr_ with RTU functions */;
return res;
case naf_ascii:
res = modbus_ascii_connect(node_addr);
if (res >= 0) res = res*4 + 2 /* offset into fptr_ with ASCII functions */;
return res;
}
return -1;
}
/* Shut down a connection to a remote server/slave */
int mb_master_close(int fd) {
#ifdef DEBUG
fprintf( stderr, "mb_master_close(): nd = %d\n", fd);
#endif
get_ttyfd(); /* declare the ttyfd variable, ... */
/* call layer 1 library */
return modbus_close(ttyfd);
}
/* Tell the library that communications will be suspended for some time. */
/* RTU and ASCII versions ignore this function
* TCP version closes all the open tcp connections (connections are automatically
* re-established the next time an IO function to the slave is requested).
* To be more precise, the TCP version makes an estimate of how long
* the silence will be based on previous invocations to this exact same
* function, and will only close the connections if this silence is
* expected to be longer than 1 second!
* (The closing of connections is specified in Modbus specification)
*/
int mb_master_tcp_silence_init(void) {
#ifdef DEBUG
fprintf( stderr, "mb_master_silence_init():\n");
#endif
/* call layer 1 library */
return modbus_tcp_silence_init();
}