123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905 |
- /*
- * Copyright © 2001-2010 Stéphane Raimbault <stephane.raimbault@gmail.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser 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 Public License for more details.
- *
- * You should have received a copy of the GNU Lesser Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <string.h>
- #ifndef _MSC_VER
- #include <unistd.h>
- #endif
- #include <assert.h>
- #include "modbus-private.h"
- #include "modbus-rtu.h"
- #include "modbus-rtu-private.h"
- #if defined(HAVE_DECL_TIOCSRS485)
- #include <sys/ioctl.h>
- #include <linux/serial.h>
- #endif
- /* Table of CRC values for high-order byte */
- static const uint8_t table_crc_hi[] = {
- 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
- 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
- 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
- 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
- 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
- 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
- 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
- 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
- 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
- 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
- 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
- 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
- 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
- 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
- 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
- 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
- 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
- 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
- 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
- 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
- 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
- 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
- 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
- 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
- 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
- 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
- };
- /* Table of CRC values for low-order byte */
- static const uint8_t table_crc_lo[] = {
- 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
- 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
- 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
- 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
- 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
- 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
- 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
- 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
- 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
- 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
- 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
- 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
- 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
- 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
- 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
- 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
- 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
- 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
- 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
- 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
- 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
- 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
- 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
- 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
- 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
- 0x43, 0x83, 0x41, 0x81, 0x80, 0x40
- };
- /* Define the slave ID of the remote device to talk in master mode or set the
- * internal slave ID in slave mode */
- static int _modbus_set_slave(modbus_t *ctx, int slave)
- {
- /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */
- if (slave >= 0 && slave <= 247) {
- ctx->slave = slave;
- } else {
- errno = EINVAL;
- return -1;
- }
- return 0;
- }
- /* Builds a RTU request header */
- static int _modbus_rtu_build_request_basis(modbus_t *ctx, int function,
- int addr, int nb,
- uint8_t *req)
- {
- assert(ctx->slave != -1);
- req[0] = ctx->slave;
- req[1] = function;
- req[2] = addr >> 8;
- req[3] = addr & 0x00ff;
- req[4] = nb >> 8;
- req[5] = nb & 0x00ff;
- return _MODBUS_RTU_PRESET_REQ_LENGTH;
- }
- /* Builds a RTU response header */
- static int _modbus_rtu_build_response_basis(sft_t *sft, uint8_t *rsp)
- {
- /* In this case, the slave is certainly valid because a check is already
- * done in _modbus_rtu_listen */
- rsp[0] = sft->slave;
- rsp[1] = sft->function;
- return _MODBUS_RTU_PRESET_RSP_LENGTH;
- }
- static uint16_t crc16(uint8_t *buffer, uint16_t buffer_length)
- {
- uint8_t crc_hi = 0xFF; /* high CRC byte initialized */
- uint8_t crc_lo = 0xFF; /* low CRC byte initialized */
- unsigned int i; /* will index into CRC lookup */
- /* pass through message buffer */
- while (buffer_length--) {
- i = crc_hi ^ *buffer++; /* calculate the CRC */
- crc_hi = crc_lo ^ table_crc_hi[i];
- crc_lo = table_crc_lo[i];
- }
- return (crc_hi << 8 | crc_lo);
- }
- int _modbus_rtu_prepare_response_tid(const uint8_t *req, int *req_length)
- {
- (*req_length) -= _MODBUS_RTU_CHECKSUM_LENGTH;
- /* No TID */
- return 0;
- }
- int _modbus_rtu_send_msg_pre(uint8_t *req, int req_length)
- {
- uint16_t crc = crc16(req, req_length);
- req[req_length++] = crc >> 8;
- req[req_length++] = crc & 0x00FF;
- return req_length;
- }
- #if defined(_WIN32)
- /* This simple implementation is sort of a substitute of the select() call,
- * working this way: the win32_ser_select() call tries to read some data from
- * the serial port, setting the timeout as the select() call would. Data read is
- * stored into the receive buffer, that is then consumed by the win32_ser_read()
- * call. So win32_ser_select() does both the event waiting and the reading,
- * while win32_ser_read() only consumes the receive buffer.
- */
- static void win32_ser_init(struct win32_ser *ws) {
- /* Clear everything */
- memset(ws, 0x00, sizeof(struct win32_ser));
- /* Set file handle to invalid */
- ws->fd = INVALID_HANDLE_VALUE;
- }
- /* FIXME Try to remove length_to_read -> max_len argument, only used by win32 */
- static int win32_ser_select(struct win32_ser *ws, int max_len,
- struct timeval *tv) {
- COMMTIMEOUTS comm_to;
- unsigned int msec = 0;
- /* Check if some data still in the buffer to be consumed */
- if (ws->n_bytes > 0) {
- return 1;
- }
- /* Setup timeouts like select() would do.
- FIXME Please someone on Windows can look at this?
- Does it possible to use WaitCommEvent?
- When tv is NULL, MAXDWORD isn't infinite!
- */
- if (tv == NULL) {
- msec = MAXDWORD;
- } else {
- msec = tv->tv_sec * 1000 + tv->tv_usec / 1000;
- if (msec < 1)
- msec = 1;
- }
- comm_to.ReadIntervalTimeout = msec;
- comm_to.ReadTotalTimeoutMultiplier = 0;
- comm_to.ReadTotalTimeoutConstant = msec;
- comm_to.WriteTotalTimeoutMultiplier = 0;
- comm_to.WriteTotalTimeoutConstant = 1000;
- SetCommTimeouts(ws->fd, &comm_to);
- /* Read some bytes */
- if ((max_len > PY_BUF_SIZE) || (max_len < 0)) {
- max_len = PY_BUF_SIZE;
- }
- if (ReadFile(ws->fd, &ws->buf, max_len, &ws->n_bytes, NULL)) {
- /* Check if some bytes available */
- if (ws->n_bytes > 0) {
- /* Some bytes read */
- return 1;
- } else {
- /* Just timed out */
- return 0;
- }
- } else {
- /* Some kind of error */
- return -1;
- }
- }
- static int win32_ser_read(struct win32_ser *ws, uint8_t *p_msg,
- unsigned int max_len) {
- unsigned int n = ws->n_bytes;
- if (max_len < n) {
- n = max_len;
- }
- if (n > 0) {
- memcpy(p_msg, ws->buf, n);
- }
- ws->n_bytes -= n;
- return n;
- }
- #endif
- ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_length)
- {
- #if defined(_WIN32)
- modbus_rtu_t *ctx_rtu = ctx->backend_data;
- DWORD n_bytes = 0;
- return (WriteFile(ctx_rtu->w_ser.fd, req, req_length, &n_bytes, NULL)) ? n_bytes : -1;
- #else
- return write(ctx->s, req, req_length);
- #endif
- }
- ssize_t _modbus_rtu_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length)
- {
- #if defined(_WIN32)
- return win32_ser_read(&((modbus_rtu_t *)ctx->backend_data)->w_ser, rsp, rsp_length);
- #else
- return read(ctx->s, rsp, rsp_length);
- #endif
- }
- int _modbus_rtu_flush(modbus_t *);
- /* The check_crc16 function shall return the message length if the CRC is
- valid. Otherwise it shall return -1 and set errno to EMBADCRC. */
- int _modbus_rtu_check_integrity(modbus_t *ctx, uint8_t *msg,
- const int msg_length)
- {
- uint16_t crc_calculated;
- uint16_t crc_received;
- crc_calculated = crc16(msg, msg_length - 2);
- crc_received = (msg[msg_length - 2] << 8) | msg[msg_length - 1];
- /* Check CRC of msg */
- if (crc_calculated == crc_received) {
- return msg_length;
- } else {
- if (ctx->debug) {
- fprintf(stderr, "ERROR CRC received %0X != CRC calculated %0X\n",
- crc_received, crc_calculated);
- }
- if (ctx->error_recovery & MODBUS_ERROR_RECOVERY_PROTOCOL) {
- _modbus_rtu_flush(ctx);
- }
- errno = EMBBADCRC;
- return -1;
- }
- }
- /* Sets up a serial port for RTU communications */
- static int _modbus_rtu_connect(modbus_t *ctx)
- {
- #if defined(_WIN32)
- DCB dcb;
- #else
- struct termios tios;
- speed_t speed;
- #endif
- modbus_rtu_t *ctx_rtu = ctx->backend_data;
- if (ctx->debug) {
- printf("Opening %s at %d bauds (%c, %d, %d)\n",
- ctx_rtu->device, ctx_rtu->baud, ctx_rtu->parity,
- ctx_rtu->data_bit, ctx_rtu->stop_bit);
- }
- #if defined(_WIN32)
- /* Some references here:
- * http://msdn.microsoft.com/en-us/library/aa450602.aspx
- */
- win32_ser_init(&ctx_rtu->w_ser);
- /* ctx_rtu->device should contain a string like "COMxx:" xx being a decimal
- * number */
- ctx_rtu->w_ser.fd = CreateFileA(ctx_rtu->device,
- GENERIC_READ | GENERIC_WRITE,
- 0,
- NULL,
- OPEN_EXISTING,
- 0,
- NULL);
- /* Error checking */
- if (ctx_rtu->w_ser.fd == INVALID_HANDLE_VALUE) {
- fprintf(stderr, "ERROR Can't open the device %s (%s)\n",
- ctx_rtu->device, strerror(errno));
- return -1;
- }
- /* Save params */
- ctx_rtu->old_dcb.DCBlength = sizeof(DCB);
- if (!GetCommState(ctx_rtu->w_ser.fd, &ctx_rtu->old_dcb)) {
- fprintf(stderr, "ERROR Error getting configuration (LastError %d)\n",
- (int)GetLastError());
- return -1;
- }
- /* Build new configuration (starting from current settings) */
- dcb = ctx_rtu->old_dcb;
- /* Speed setting */
- switch (ctx_rtu->baud) {
- case 110:
- dcb.BaudRate = CBR_110;
- break;
- case 300:
- dcb.BaudRate = CBR_300;
- break;
- case 600:
- dcb.BaudRate = CBR_600;
- break;
- case 1200:
- dcb.BaudRate = CBR_1200;
- break;
- case 2400:
- dcb.BaudRate = CBR_2400;
- break;
- case 4800:
- dcb.BaudRate = CBR_4800;
- break;
- case 9600:
- dcb.BaudRate = CBR_9600;
- break;
- case 19200:
- dcb.BaudRate = CBR_19200;
- break;
- case 38400:
- dcb.BaudRate = CBR_38400;
- break;
- case 57600:
- dcb.BaudRate = CBR_57600;
- break;
- case 115200:
- dcb.BaudRate = CBR_115200;
- break;
- default:
- dcb.BaudRate = CBR_9600;
- printf("WARNING Unknown baud rate %d for %s (B9600 used)\n",
- ctx_rtu->baud, ctx_rtu->device);
- }
- /* Data bits */
- switch (ctx_rtu->data_bit) {
- case 5:
- dcb.ByteSize = 5;
- break;
- case 6:
- dcb.ByteSize = 6;
- break;
- case 7:
- dcb.ByteSize = 7;
- break;
- case 8:
- default:
- dcb.ByteSize = 8;
- break;
- }
- /* Stop bits */
- if (ctx_rtu->stop_bit == 1)
- dcb.StopBits = ONESTOPBIT;
- else /* 2 */
- dcb.StopBits = TWOSTOPBITS;
- /* Parity */
- if (ctx_rtu->parity == 'N') {
- dcb.Parity = NOPARITY;
- dcb.fParity = FALSE;
- } else if (ctx_rtu->parity == 'E') {
- dcb.Parity = EVENPARITY;
- dcb.fParity = TRUE;
- } else {
- /* odd */
- dcb.Parity = ODDPARITY;
- dcb.fParity = TRUE;
- }
- /* Hardware handshaking left as default settings retrieved */
- /* No software handshaking */
- dcb.fTXContinueOnXoff = TRUE;
- dcb.fOutX = FALSE;
- dcb.fInX = FALSE;
- /* Binary mode (it's the only supported on Windows anyway) */
- dcb.fBinary = TRUE;
- /* Don't want errors to be blocking */
- dcb.fAbortOnError = FALSE;
- /* TODO: any other flags!? */
- /* Setup port */
- if (!SetCommState(ctx_rtu->w_ser.fd, &dcb)) {
- fprintf(stderr, "ERROR Error setting new configuration (LastError %d)\n",
- (int)GetLastError());
- return -1;
- }
- #else
- /* The O_NOCTTY flag tells UNIX that this program doesn't want
- to be the "controlling terminal" for that port. If you
- don't specify this then any input (such as keyboard abort
- signals and so forth) will affect your process
- Timeouts are ignored in canonical input mode or when the
- NDELAY option is set on the file via open or fcntl */
- ctx->s = open(ctx_rtu->device, O_RDWR | O_NOCTTY | O_NDELAY | O_EXCL);
- if (ctx->s == -1) {
- fprintf(stderr, "ERROR Can't open the device %s (%s)\n",
- ctx_rtu->device, strerror(errno));
- return -1;
- }
- /* Save */
- tcgetattr(ctx->s, &(ctx_rtu->old_tios));
- memset(&tios, 0, sizeof(struct termios));
- /* C_ISPEED Input baud (new interface)
- C_OSPEED Output baud (new interface)
- */
- switch (ctx_rtu->baud) {
- case 110:
- speed = B110;
- break;
- case 300:
- speed = B300;
- break;
- case 600:
- speed = B600;
- break;
- case 1200:
- speed = B1200;
- break;
- case 2400:
- speed = B2400;
- break;
- case 4800:
- speed = B4800;
- break;
- case 9600:
- speed = B9600;
- break;
- case 19200:
- speed = B19200;
- break;
- case 38400:
- speed = B38400;
- break;
- case 57600:
- speed = B57600;
- break;
- case 115200:
- speed = B115200;
- break;
- default:
- speed = B9600;
- if (ctx->debug) {
- fprintf(stderr,
- "WARNING Unknown baud rate %d for %s (B9600 used)\n",
- ctx_rtu->baud, ctx_rtu->device);
- }
- }
- /* Set the baud rate */
- if ((cfsetispeed(&tios, speed) < 0) ||
- (cfsetospeed(&tios, speed) < 0)) {
- return -1;
- }
- /* C_CFLAG Control options
- CLOCAL Local line - do not change "owner" of port
- CREAD Enable receiver
- */
- tios.c_cflag |= (CREAD | CLOCAL);
- /* CSIZE, HUPCL, CRTSCTS (hardware flow control) */
- /* Set data bits (5, 6, 7, 8 bits)
- CSIZE Bit mask for data bits
- */
- tios.c_cflag &= ~CSIZE;
- switch (ctx_rtu->data_bit) {
- case 5:
- tios.c_cflag |= CS5;
- break;
- case 6:
- tios.c_cflag |= CS6;
- break;
- case 7:
- tios.c_cflag |= CS7;
- break;
- case 8:
- default:
- tios.c_cflag |= CS8;
- break;
- }
- /* Stop bit (1 or 2) */
- if (ctx_rtu->stop_bit == 1)
- tios.c_cflag &=~ CSTOPB;
- else /* 2 */
- tios.c_cflag |= CSTOPB;
- /* PARENB Enable parity bit
- PARODD Use odd parity instead of even */
- if (ctx_rtu->parity == 'N') {
- /* None */
- tios.c_cflag &=~ PARENB;
- } else if (ctx_rtu->parity == 'E') {
- /* Even */
- tios.c_cflag |= PARENB;
- tios.c_cflag &=~ PARODD;
- } else {
- /* Odd */
- tios.c_cflag |= PARENB;
- tios.c_cflag |= PARODD;
- }
- /* Read the man page of termios if you need more information. */
- /* This field isn't used on POSIX systems
- tios.c_line = 0;
- */
- /* C_LFLAG Line options
- ISIG Enable SIGINTR, SIGSUSP, SIGDSUSP, and SIGQUIT signals
- ICANON Enable canonical input (else raw)
- XCASE Map uppercase \lowercase (obsolete)
- ECHO Enable echoing of input characters
- ECHOE Echo erase character as BS-SP-BS
- ECHOK Echo NL after kill character
- ECHONL Echo NL
- NOFLSH Disable flushing of input buffers after
- interrupt or quit characters
- IEXTEN Enable extended functions
- ECHOCTL Echo control characters as ^char and delete as ~?
- ECHOPRT Echo erased character as character erased
- ECHOKE BS-SP-BS entire line on line kill
- FLUSHO Output being flushed
- PENDIN Retype pending input at next read or input char
- TOSTOP Send SIGTTOU for background output
- Canonical input is line-oriented. Input characters are put
- into a buffer which can be edited interactively by the user
- until a CR (carriage return) or LF (line feed) character is
- received.
- Raw input is unprocessed. Input characters are passed
- through exactly as they are received, when they are
- received. Generally you'll deselect the ICANON, ECHO,
- ECHOE, and ISIG options when using raw input
- */
- /* Raw input */
- tios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
- /* C_IFLAG Input options
- Constant Description
- INPCK Enable parity check
- IGNPAR Ignore parity errors
- PARMRK Mark parity errors
- ISTRIP Strip parity bits
- IXON Enable software flow control (outgoing)
- IXOFF Enable software flow control (incoming)
- IXANY Allow any character to start flow again
- IGNBRK Ignore break condition
- BRKINT Send a SIGINT when a break condition is detected
- INLCR Map NL to CR
- IGNCR Ignore CR
- ICRNL Map CR to NL
- IUCLC Map uppercase to lowercase
- IMAXBEL Echo BEL on input line too long
- */
- if (ctx_rtu->parity == 'N') {
- /* None */
- tios.c_iflag &= ~INPCK;
- } else {
- tios.c_iflag |= INPCK;
- }
- /* Software flow control is disabled */
- tios.c_iflag &= ~(IXON | IXOFF | IXANY);
- /* C_OFLAG Output options
- OPOST Postprocess output (not set = raw output)
- ONLCR Map NL to CR-NL
- ONCLR ant others needs OPOST to be enabled
- */
- /* Raw ouput */
- tios.c_oflag &=~ OPOST;
- /* C_CC Control characters
- VMIN Minimum number of characters to read
- VTIME Time to wait for data (tenths of seconds)
- UNIX serial interface drivers provide the ability to
- specify character and packet timeouts. Two elements of the
- c_cc array are used for timeouts: VMIN and VTIME. Timeouts
- are ignored in canonical input mode or when the NDELAY
- option is set on the file via open or fcntl.
- VMIN specifies the minimum number of characters to read. If
- it is set to 0, then the VTIME value specifies the time to
- wait for every character read. Note that this does not mean
- that a read call for N bytes will wait for N characters to
- come in. Rather, the timeout will apply to the first
- character and the read call will return the number of
- characters immediately available (up to the number you
- request).
- If VMIN is non-zero, VTIME specifies the time to wait for
- the first character read. If a character is read within the
- time given, any read will block (wait) until all VMIN
- characters are read. That is, once the first character is
- read, the serial interface driver expects to receive an
- entire packet of characters (VMIN bytes total). If no
- character is read within the time allowed, then the call to
- read returns 0. This method allows you to tell the serial
- driver you need exactly N bytes and any read call will
- return 0 or N bytes. However, the timeout only applies to
- the first character read, so if for some reason the driver
- misses one character inside the N byte packet then the read
- call could block forever waiting for additional input
- characters.
- VTIME specifies the amount of time to wait for incoming
- characters in tenths of seconds. If VTIME is set to 0 (the
- default), reads will block (wait) indefinitely unless the
- NDELAY option is set on the port with open or fcntl.
- */
- /* Unused because we use open with the NDELAY option */
- tios.c_cc[VMIN] = 0;
- tios.c_cc[VTIME] = 0;
- if (tcsetattr(ctx->s, TCSANOW, &tios) < 0) {
- return -1;
- }
- /* The RS232 mode has been set by default */
- ctx_rtu->serial_mode = MODBUS_RTU_RS232;
- #endif
- return 0;
- }
- int modbus_rtu_set_serial_mode(modbus_t *ctx, int mode)
- {
- if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) {
- #if defined(HAVE_DECL_TIOCSRS485)
- modbus_rtu_t *ctx_rtu = ctx->backend_data;
- struct serial_rs485 rs485conf;
- memset(&rs485conf, 0x0, sizeof(struct serial_rs485));
- if (mode == MODBUS_RTU_RS485) {
- rs485conf.flags = SER_RS485_ENABLED;
- if (ioctl(ctx->s, TIOCSRS485, &rs485conf) < 0) {
- return -1;
- }
- ctx_rtu->serial_mode |= MODBUS_RTU_RS485;
- return 0;
- } else if (mode == MODBUS_RTU_RS232) {
- if (ioctl(ctx->s, TIOCSRS485, &rs485conf) < 0) {
- return -1;
- }
- ctx_rtu->serial_mode = MODBUS_RTU_RS232;
- return 0;
- }
- #else
- if (ctx->debug) {
- fprintf(stderr, "This function isn't supported on your platform\n");
- }
- errno = ENOTSUP;
- return -1;
- #endif
- }
- /* Wrong backend and invalid mode specified */
- errno = EINVAL;
- return -1;
- }
- int modbus_rtu_get_serial_mode(modbus_t *ctx) {
- if (ctx->backend->backend_type == _MODBUS_BACKEND_TYPE_RTU) {
- modbus_rtu_t *ctx_rtu = ctx->backend_data;
- return ctx_rtu->serial_mode;
- } else {
- errno = EINVAL;
- return -1;
- }
- }
- void _modbus_rtu_close(modbus_t *ctx)
- {
- /* Closes the file descriptor in RTU mode */
- modbus_rtu_t *ctx_rtu = ctx->backend_data;
- #if defined(_WIN32)
- /* Revert settings */
- if (!SetCommState(ctx_rtu->w_ser.fd, &ctx_rtu->old_dcb))
- fprintf(stderr, "ERROR Couldn't revert to configuration (LastError %d)\n",
- (int)GetLastError());
- if (!CloseHandle(ctx_rtu->w_ser.fd))
- fprintf(stderr, "ERROR Error while closing handle (LastError %d)\n",
- (int)GetLastError());
- #else
- tcsetattr(ctx->s, TCSANOW, &(ctx_rtu->old_tios));
- close(ctx->s);
- #endif
- }
- int _modbus_rtu_flush(modbus_t *ctx)
- {
- #if defined(_WIN32)
- modbus_rtu_t *ctx_rtu = ctx->backend_data;
- ctx_rtu->w_ser.n_bytes = 0;
- return (FlushFileBuffers(ctx_rtu->w_ser.fd) == FALSE);
- #else
- return tcflush(ctx->s, TCIOFLUSH);
- #endif
- }
- int _modbus_rtu_select(modbus_t *ctx, fd_set *rfds,
- struct timeval *tv, int length_to_read)
- {
- int s_rc;
- #if defined(_WIN32)
- s_rc = win32_ser_select(&(((modbus_rtu_t*)ctx->backend_data)->w_ser),
- length_to_read, tv);
- if (s_rc == 0) {
- errno = ETIMEDOUT;
- return -1;
- }
- if (s_rc < 0) {
- return -1;
- }
- #else
- while ((s_rc = select(ctx->s+1, rfds, NULL, NULL, tv)) == -1) {
- if (errno == EINTR) {
- if (ctx->debug) {
- fprintf(stderr, "A non blocked signal was caught\n");
- }
- /* Necessary after an error */
- FD_ZERO(rfds);
- FD_SET(ctx->s, rfds);
- } else {
- return -1;
- }
- }
- if (s_rc == 0) {
- /* Timeout */
- errno = ETIMEDOUT;
- return -1;
- }
- #endif
- return s_rc;
- }
- int _modbus_rtu_filter_request(modbus_t *ctx, int slave)
- {
- /* Filter on the Modbus unit identifier (slave) in RTU mode */
- if (slave != ctx->slave && slave != MODBUS_BROADCAST_ADDRESS) {
- /* Ignores the request (not for me) */
- if (ctx->debug) {
- printf("Request for slave %d ignored (not %d)\n",
- slave, ctx->slave);
- }
- return 1;
- } else {
- return 0;
- }
- }
- const modbus_backend_t _modbus_rtu_backend = {
- _MODBUS_BACKEND_TYPE_RTU,
- _MODBUS_RTU_HEADER_LENGTH,
- _MODBUS_RTU_CHECKSUM_LENGTH,
- MODBUS_RTU_MAX_ADU_LENGTH,
- _modbus_set_slave,
- _modbus_rtu_build_request_basis,
- _modbus_rtu_build_response_basis,
- _modbus_rtu_prepare_response_tid,
- _modbus_rtu_send_msg_pre,
- _modbus_rtu_send,
- _modbus_rtu_recv,
- _modbus_rtu_check_integrity,
- NULL,
- _modbus_rtu_connect,
- _modbus_rtu_close,
- _modbus_rtu_flush,
- _modbus_rtu_select,
- _modbus_rtu_filter_request
- };
- modbus_t* modbus_new_rtu(const char *device,
- int baud, char parity, int data_bit,
- int stop_bit)
- {
- modbus_t *ctx;
- modbus_rtu_t *ctx_rtu;
- size_t dest_size;
- size_t ret_size;
- ctx = (modbus_t *) malloc(sizeof(modbus_t));
- _modbus_init_common(ctx);
- ctx->backend = &_modbus_rtu_backend;
- ctx->backend_data = (modbus_rtu_t *) malloc(sizeof(modbus_rtu_t));
- ctx_rtu = (modbus_rtu_t *)ctx->backend_data;
- dest_size = sizeof(ctx_rtu->device);
- ret_size = strlcpy(ctx_rtu->device, device, dest_size);
- if (ret_size == 0) {
- fprintf(stderr, "The device string is empty\n");
- modbus_free(ctx);
- errno = EINVAL;
- return NULL;
- }
- if (ret_size >= dest_size) {
- fprintf(stderr, "The device string has been truncated\n");
- modbus_free(ctx);
- errno = EINVAL;
- return NULL;
- }
- ctx_rtu->baud = baud;
- if (parity == 'N' || parity == 'E' || parity == 'O') {
- ctx_rtu->parity = parity;
- } else {
- modbus_free(ctx);
- errno = EINVAL;
- return NULL;
- }
- ctx_rtu->data_bit = data_bit;
- ctx_rtu->stop_bit = stop_bit;
- return ctx;
- }
|