diff --git a/doc/Makefile.am b/doc/Makefile.am index b96e226..9862ece 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -12,6 +12,7 @@ TXT3 = \ modbus_get_socket.txt \ modbus_mapping_free.txt \ modbus_mapping_new.txt \ + modbus_mapping_offset_new.txt \ modbus_mask_write_register.txt \ modbus_new_rtu.txt \ modbus_new_tcp_pi.txt \ diff --git a/doc/modbus_mapping_new.txt b/doc/modbus_mapping_new.txt index 49ac0eb..293131b 100644 --- a/doc/modbus_mapping_new.txt +++ b/doc/modbus_mapping_new.txt @@ -18,6 +18,9 @@ The *modbus_mapping_new()* function shall allocate four arrays to store bits, input bits, registers and inputs registers. The pointers are stored in modbus_mapping_t structure. All values of the arrays are initialized to zero. +This function is equivalent to a call of the _modbus_mapping_offset_new()_ function +with all offsets set to zero. + If it isn't necessary to allocate an array for a specific type of data, you can pass the zero value in argument, the associated pointer will be NULL. @@ -40,7 +43,7 @@ EXAMPLE ------- [source,c] ------------------- -/* The fist value of each array is accessible from the 0 address. */ +/* The first value of each array is accessible from the 0 address. */ mb_mapping = modbus_mapping_new(BITS_ADDRESS + BITS_NB, INPUT_BITS_ADDRESS + INPUT_BITS_NB, REGISTERS_ADDRESS + REGISTERS_NB, diff --git a/doc/modbus_mapping_offset_new.txt b/doc/modbus_mapping_offset_new.txt new file mode 100644 index 0000000..d860664 --- /dev/null +++ b/doc/modbus_mapping_offset_new.txt @@ -0,0 +1,70 @@ +modbus_mapping_offset_new(3) +============================ + + +NAME +---- +modbus_mapping_offset_new - allocate four arrays of bits and registers + + +SYNOPSIS +-------- +*modbus_mapping_t* modbus_mapping_new(int 'nb_bits', int 'offset_bits', + int 'nb_input_bits', int 'offset_input_bits', + int 'nb_registers', int 'offset_registers', + int 'nb_input_registers', int 'offset_input_registers');* + + +DESCRIPTION +----------- +The _modbus_mapping_offset_new()_ function shall allocate four arrays to store bits, +input bits, registers and inputs registers. The pointers are stored in +modbus_mapping_t structure. All values of the arrays are initialized to zero. + +The different offsets make it possible to place the mapping at any address in +each address space. + +If it isn't necessary to allocate an array for a specific type of data, you can +pass the zero value in argument, the associated pointer will be NULL. + +This function is convenient to handle requests in a Modbus server/slave. + + +RETURN VALUE +------------ +The _modbus_mapping_offset_new()_ function shall return the new allocated structure if +successful. Otherwise it shall return NULL and set errno. + + +ERRORS +------ +ENOMEM:: +Not enough memory + + +EXAMPLE +------- +[source,c] +------------------- +/* The first value of each array is accessible at address 4. */ +mb_mapping = modbus_mapping_offset_new(BITS_ADDRESS + BITS_NB, 4, + INPUT_BITS_ADDRESS + INPUT_BITS_NB, 4, + REGISTERS_ADDRESS + REGISTERS_NB, 4, + INPUT_REGISTERS_ADDRESS + INPUT_REGISTERS_NB, 4); +if (mb_mapping == NULL) { + fprintf(stderr, "Failed to allocate the mapping: %s\n", + modbus_strerror(errno)); + modbus_free(ctx); + return -1; +} +------------------- + +SEE ALSO +-------- +linkmb:modbus_mapping_free[3] + + +AUTHORS +------- +The libmodbus documentation was written by Stéphane Raimbault + diff --git a/src/modbus.c b/src/modbus.c index 3edbfc8..1a5a778 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -706,6 +706,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, switch (function) { case MODBUS_FC_READ_COILS: { int nb = (req[offset + 3] << 8) + req[offset + 4]; + int addr = address - mb_mapping->offset_bits; if (nb < 1 || MODBUS_MAX_READ_BITS < nb) { if (ctx->debug) { @@ -718,10 +719,10 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, rsp_length = response_exception( ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp); - } else if ((address + nb) > mb_mapping->nb_bits) { + } else if (address < mb_mapping->offset_bits || (addr + nb) > mb_mapping->nb_bits) { if (ctx->debug) { fprintf(stderr, "Illegal data address 0x%0X in read_bits\n", - address + nb); + address < mb_mapping->offset_bits ? address : address + nb); } rsp_length = response_exception( ctx, &sft, @@ -729,7 +730,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, } else { rsp_length = ctx->backend->build_response_basis(&sft, rsp); rsp[rsp_length++] = (nb / 8) + ((nb % 8) ? 1 : 0); - rsp_length = response_io_status(address, nb, + rsp_length = response_io_status(addr, nb, mb_mapping->tab_bits, rsp, rsp_length); } @@ -739,6 +740,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, /* Similar to coil status (but too many arguments to use a * function) */ int nb = (req[offset + 3] << 8) + req[offset + 4]; + int addr = address - mb_mapping->offset_input_bits; if (nb < 1 || MODBUS_MAX_READ_BITS < nb) { if (ctx->debug) { @@ -751,10 +753,10 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, rsp_length = response_exception( ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp); - } else if ((address + nb) > mb_mapping->nb_input_bits) { + } else if (address < mb_mapping->offset_input_bits || (addr + nb) > mb_mapping->nb_input_bits) { if (ctx->debug) { fprintf(stderr, "Illegal data address 0x%0X in read_input_bits\n", - address + nb); + address < mb_mapping->offset_input_bits ? address : address + nb); } rsp_length = response_exception( ctx, &sft, @@ -762,7 +764,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, } else { rsp_length = ctx->backend->build_response_basis(&sft, rsp); rsp[rsp_length++] = (nb / 8) + ((nb % 8) ? 1 : 0); - rsp_length = response_io_status(address, nb, + rsp_length = response_io_status(addr, nb, mb_mapping->tab_input_bits, rsp, rsp_length); } @@ -770,6 +772,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, break; case MODBUS_FC_READ_HOLDING_REGISTERS: { int nb = (req[offset + 3] << 8) + req[offset + 4]; + int addr = address - mb_mapping->offset_registers; if (nb < 1 || MODBUS_MAX_READ_REGISTERS < nb) { if (ctx->debug) { @@ -782,10 +785,10 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, rsp_length = response_exception( ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp); - } else if ((address + nb) > mb_mapping->nb_registers) { + } else if (address < mb_mapping->offset_registers || (addr + nb) > mb_mapping->nb_registers) { if (ctx->debug) { fprintf(stderr, "Illegal data address 0x%0X in read_registers\n", - address + nb); + address < mb_mapping->offset_registers ? address : address + nb); } rsp_length = response_exception( ctx, &sft, @@ -795,7 +798,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, rsp_length = ctx->backend->build_response_basis(&sft, rsp); rsp[rsp_length++] = nb << 1; - for (i = address; i < address + nb; i++) { + for (i = addr; i < addr + nb; i++) { rsp[rsp_length++] = mb_mapping->tab_registers[i] >> 8; rsp[rsp_length++] = mb_mapping->tab_registers[i] & 0xFF; } @@ -806,6 +809,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, /* Similar to holding registers (but too many arguments to use a * function) */ int nb = (req[offset + 3] << 8) + req[offset + 4]; + int addr = address - mb_mapping->offset_input_registers; if (nb < 1 || MODBUS_MAX_READ_REGISTERS < nb) { if (ctx->debug) { @@ -818,10 +822,10 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, rsp_length = response_exception( ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp); - } else if ((address + nb) > mb_mapping->nb_input_registers) { + } else if (address < mb_mapping->offset_input_registers || (addr + nb) > mb_mapping->nb_input_registers) { if (ctx->debug) { fprintf(stderr, "Illegal data address 0x%0X in read_input_registers\n", - address + nb); + address < mb_mapping->offset_input_registers ? address : address + nb); } rsp_length = response_exception( ctx, &sft, @@ -831,15 +835,17 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, rsp_length = ctx->backend->build_response_basis(&sft, rsp); rsp[rsp_length++] = nb << 1; - for (i = address; i < address + nb; i++) { + for (i = addr; i < addr + nb; i++) { rsp[rsp_length++] = mb_mapping->tab_input_registers[i] >> 8; rsp[rsp_length++] = mb_mapping->tab_input_registers[i] & 0xFF; } } } break; - case MODBUS_FC_WRITE_SINGLE_COIL: - if (address >= mb_mapping->nb_bits) { + case MODBUS_FC_WRITE_SINGLE_COIL: { + int addr = address - mb_mapping->offset_bits; + + if (address < mb_mapping->offset_bits || addr >= mb_mapping->nb_bits) { if (ctx->debug) { fprintf(stderr, "Illegal data address 0x%0X in write_bit\n", @@ -852,7 +858,7 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, int data = (req[offset + 3] << 8) + req[offset + 4]; if (data == 0xFF00 || data == 0x0) { - mb_mapping->tab_bits[address] = (data) ? ON : OFF; + mb_mapping->tab_bits[addr] = (data) ? ON : OFF; memcpy(rsp, req, req_length); rsp_length = req_length; } else { @@ -866,9 +872,12 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp); } } + } break; - case MODBUS_FC_WRITE_SINGLE_REGISTER: - if (address >= mb_mapping->nb_registers) { + case MODBUS_FC_WRITE_SINGLE_REGISTER: { + int addr = address - mb_mapping->offset_registers; + + if (address < mb_mapping->offset_registers || addr >= mb_mapping->nb_registers) { if (ctx->debug) { fprintf(stderr, "Illegal data address 0x%0X in write_register\n", address); @@ -879,13 +888,15 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, } else { int data = (req[offset + 3] << 8) + req[offset + 4]; - mb_mapping->tab_registers[address] = data; + mb_mapping->tab_registers[addr] = data; memcpy(rsp, req, req_length); rsp_length = req_length; } + } break; case MODBUS_FC_WRITE_MULTIPLE_COILS: { int nb = (req[offset + 3] << 8) + req[offset + 4]; + int addr = address - mb_mapping->offset_bits; if (nb < 1 || MODBUS_MAX_WRITE_BITS < nb) { if (ctx->debug) { @@ -901,17 +912,17 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, rsp_length = response_exception( ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp); - } else if ((address + nb) > mb_mapping->nb_bits) { + } else if (address < mb_mapping->offset_bits || (addr + nb) > mb_mapping->nb_bits) { if (ctx->debug) { fprintf(stderr, "Illegal data address 0x%0X in write_bits\n", - address + nb); + address < mb_mapping->offset_bits ? address : address + nb); } rsp_length = response_exception( ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp); } else { /* 6 = byte count */ - modbus_set_bits_from_bytes(mb_mapping->tab_bits, address, nb, &req[offset + 6]); + modbus_set_bits_from_bytes(mb_mapping->tab_bits, addr, nb, &req[offset + 6]); rsp_length = ctx->backend->build_response_basis(&sft, rsp); /* 4 to copy the bit address (2) and the quantity of bits */ @@ -922,6 +933,8 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, break; case MODBUS_FC_WRITE_MULTIPLE_REGISTERS: { int nb = (req[offset + 3] << 8) + req[offset + 4]; + int addr = address - mb_mapping->offset_registers; + if (nb < 1 || MODBUS_MAX_WRITE_REGISTERS < nb) { if (ctx->debug) { fprintf(stderr, @@ -936,17 +949,17 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, rsp_length = response_exception( ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp); - } else if ((address + nb) > mb_mapping->nb_registers) { + } else if (address < mb_mapping->offset_registers || (addr + nb) > mb_mapping->nb_registers) { if (ctx->debug) { fprintf(stderr, "Illegal data address 0x%0X in write_registers\n", - address + nb); + address < mb_mapping->offset_registers ? address : address + nb); } rsp_length = response_exception( ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp); } else { int i, j; - for (i = address, j = 6; i < address + nb; i++, j += 2) { + for (i = addr, j = 6; i < addr + nb; i++, j += 2) { /* 6 and 7 = first value */ mb_mapping->tab_registers[i] = (req[offset + j] << 8) + req[offset + j + 1]; @@ -983,8 +996,10 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, errno = ENOPROTOOPT; return -1; break; - case MODBUS_FC_MASK_WRITE_REGISTER: - if (address >= mb_mapping->nb_registers) { + case MODBUS_FC_MASK_WRITE_REGISTER: { + int addr = address - mb_mapping->offset_registers; + + if (address < mb_mapping->offset_registers || addr >= mb_mapping->nb_registers) { if (ctx->debug) { fprintf(stderr, "Illegal data address 0x%0X in write_register\n", address); @@ -993,21 +1008,24 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp); } else { - uint16_t data = mb_mapping->tab_registers[address]; + uint16_t data = mb_mapping->tab_registers[addr]; uint16_t and = (req[offset + 3] << 8) + req[offset + 4]; uint16_t or = (req[offset + 5] << 8) + req[offset + 6]; data = (data & and) | (or & (~and)); - mb_mapping->tab_registers[address] = data; + mb_mapping->tab_registers[addr] = data; memcpy(rsp, req, req_length); rsp_length = req_length; } + } break; case MODBUS_FC_WRITE_AND_READ_REGISTERS: { int nb = (req[offset + 3] << 8) + req[offset + 4]; uint16_t address_write = (req[offset + 5] << 8) + req[offset + 6]; int nb_write = (req[offset + 7] << 8) + req[offset + 8]; int nb_write_bytes = req[offset + 9]; + int addr = address - mb_mapping->offset_registers; + int addr_write = address_write - mb_mapping->offset_registers; if (nb_write < 1 || MODBUS_MAX_WR_WRITE_REGISTERS < nb_write || nb < 1 || MODBUS_MAX_WR_READ_REGISTERS < nb || @@ -1023,12 +1041,15 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, rsp_length = response_exception( ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE, rsp); - } else if ((address + nb) > mb_mapping->nb_registers || - (address_write + nb_write) > mb_mapping->nb_registers) { + } else if (address < mb_mapping->offset_registers || + (addr + nb) > mb_mapping->nb_registers || + address_write < mb_mapping->offset_registers || + (addr_write + nb_write) > mb_mapping->nb_registers) { if (ctx->debug) { fprintf(stderr, "Illegal data read address 0x%0X or write address 0x%0X write_and_read_registers\n", - address + nb, address_write + nb_write); + address < mb_mapping->offset_registers ? address : address + nb, + address_write < mb_mapping->offset_registers ? address_write : address_write + nb_write); } rsp_length = response_exception(ctx, &sft, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS, rsp); @@ -1039,13 +1060,13 @@ int modbus_reply(modbus_t *ctx, const uint8_t *req, /* Write first. 10 and 11 are the offset of the first values to write */ - for (i = address_write, j = 10; i < address_write + nb_write; i++, j += 2) { + for (i = addr_write, j = 10; i < addr_write + nb_write; i++, j += 2) { mb_mapping->tab_registers[i] = (req[offset + j] << 8) + req[offset + j + 1]; } /* and read the data for the response */ - for (i = address; i < address + nb; i++) { + for (i = addr; i < addr + nb; i++) { rsp[rsp_length++] = mb_mapping->tab_registers[i] >> 8; rsp[rsp_length++] = mb_mapping->tab_registers[i] & 0xFF; } @@ -1786,10 +1807,12 @@ int modbus_set_debug(modbus_t *ctx, int flag) /* Allocates 4 arrays to store bits, input bits, registers and inputs registers. The pointers are stored in modbus_mapping structure. - The modbus_mapping_new() function shall return the new allocated structure if + The modbus_mapping_offset_new() function shall return the new allocated structure if successful. Otherwise it shall return NULL and set errno to ENOMEM. */ -modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits, - int nb_registers, int nb_input_registers) +modbus_mapping_t* modbus_mapping_offset_new(int nb_bits, int offset_bits, + int nb_input_bits, int offset_input_bits, + int nb_registers, int offset_registers, + int nb_input_registers, int offset_input_registers) { modbus_mapping_t *mb_mapping; @@ -1800,6 +1823,7 @@ modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits, /* 0X */ mb_mapping->nb_bits = nb_bits; + mb_mapping->offset_bits = offset_bits; if (nb_bits == 0) { mb_mapping->tab_bits = NULL; } else { @@ -1815,6 +1839,7 @@ modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits, /* 1X */ mb_mapping->nb_input_bits = nb_input_bits; + mb_mapping->offset_input_bits = offset_input_bits; if (nb_input_bits == 0) { mb_mapping->tab_input_bits = NULL; } else { @@ -1830,6 +1855,7 @@ modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits, /* 4X */ mb_mapping->nb_registers = nb_registers; + mb_mapping->offset_registers = offset_registers; if (nb_registers == 0) { mb_mapping->tab_registers = NULL; } else { @@ -1846,6 +1872,7 @@ modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits, /* 3X */ mb_mapping->nb_input_registers = nb_input_registers; + mb_mapping->offset_input_registers = offset_input_registers; if (nb_input_registers == 0) { mb_mapping->tab_input_registers = NULL; } else { @@ -1865,6 +1892,12 @@ modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits, return mb_mapping; } +modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits, + int nb_registers, int nb_input_registers) +{ + return modbus_mapping_offset_new(nb_bits, 0, nb_input_bits, 0, nb_registers, 0, nb_input_registers, 0); +} + /* Frees the 4 arrays */ void modbus_mapping_free(modbus_mapping_t *mb_mapping) { diff --git a/src/modbus.h b/src/modbus.h index 1207de3..0ff745d 100644 --- a/src/modbus.h +++ b/src/modbus.h @@ -156,9 +156,13 @@ typedef struct _modbus modbus_t; typedef struct { int nb_bits; + int offset_bits; int nb_input_bits; + int offset_input_bits; int nb_input_registers; + int offset_input_registers; int nb_registers; + int offset_registers; uint8_t *tab_bits; uint8_t *tab_input_bits; uint16_t *tab_input_registers; @@ -209,6 +213,10 @@ MODBUS_API int modbus_write_and_read_registers(modbus_t *ctx, int write_addr, in uint16_t *dest); MODBUS_API int modbus_report_slave_id(modbus_t *ctx, int max_dest, uint8_t *dest); +MODBUS_API modbus_mapping_t* modbus_mapping_offset_new(int nb_bits, int offset_bits, + int nb_input_bits, int offset_input_bits, + int nb_registers, int offset_registers, + int nb_input_registers, int offset_input_registers); MODBUS_API modbus_mapping_t* modbus_mapping_new(int nb_bits, int nb_input_bits, int nb_registers, int nb_input_registers); MODBUS_API void modbus_mapping_free(modbus_mapping_t *mb_mapping);