diff -uNr libmodbus-3.0.2/src/Makefile.am libmodbus-3.0.2-xuart/src/Makefile.am --- libmodbus-3.0.2/src/Makefile.am 2011-09-09 13:29:57.000000000 -0700 +++ libmodbus-3.0.2-xuart/src/Makefile.am 2012-05-15 15:21:48.000000000 -0700 @@ -10,7 +10,9 @@ modbus-tcp.c \ modbus-tcp.h \ modbus-tcp-private.h \ - modbus-version.h + modbus-version.h \ + modbus-xuart.h \ + modbus-xuart.c if OS_WIN32 libmodbus_la_LIBADD = -lwsock32 diff -uNr libmodbus-3.0.2/src/Makefile.in libmodbus-3.0.2-xuart/src/Makefile.in --- libmodbus-3.0.2/src/Makefile.in 2012-01-24 21:30:09.000000000 -0700 +++ libmodbus-3.0.2-xuart/src/Makefile.in 2012-05-15 16:00:19.000000000 -0700 @@ -76,7 +76,7 @@ LTLIBRARIES = $(lib_LTLIBRARIES) libmodbus_la_DEPENDENCIES = am_libmodbus_la_OBJECTS = modbus.lo modbus-data.lo modbus-rtu.lo \ - modbus-tcp.lo + modbus-tcp.lo modbus-xuart.lo libmodbus_la_OBJECTS = $(am_libmodbus_la_OBJECTS) AM_V_lt = $(am__v_lt_$(V)) am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) @@ -255,7 +255,9 @@ modbus-tcp.c \ modbus-tcp.h \ modbus-tcp-private.h \ - modbus-version.h + modbus-version.h \ + modbus-xuart.h \ + modbus-xuart.c @OS_WIN32_TRUE@libmodbus_la_LIBADD = -lwsock32 libmodbus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined -version-info $(LIBMODBUS_LT_VERSION_INFO) @@ -345,6 +347,7 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/modbus-data.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/modbus-rtu.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/modbus-tcp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/modbus-xuart.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/modbus.Plo@am__quote@ .c.o: diff -uNr libmodbus-3.0.2/src/modbus-rtu-private.h libmodbus-3.0.2-xuart/src/modbus-rtu-private.h --- libmodbus-3.0.2/src/modbus-rtu-private.h 2012-01-24 21:29:36.000000000 -0700 +++ libmodbus-3.0.2-xuart/src/modbus-rtu-private.h 2012-05-16 11:55:56.000000000 -0700 @@ -81,6 +81,12 @@ #if HAVE_DECL_TIOCSRS485 int serial_mode; #endif + uint8_t is_xuart; + uint8_t nbits; /* For XUARTs */ + uint8_t rmsk; /* For XUARTs */ + int fput, fget; /* For XUARTs */ + unsigned short *xtbl; /* For XUARTs */ + uint8_t fifo[512]; /* For XUARTs */ } modbus_rtu_t; #endif /* _MODBUS_RTU_PRIVATE_H_ */ diff -uNr libmodbus-3.0.2/src/modbus-rtu.c libmodbus-3.0.2-xuart/src/modbus-rtu.c --- libmodbus-3.0.2/src/modbus-rtu.c 2012-01-24 21:29:36.000000000 -0700 +++ libmodbus-3.0.2-xuart/src/modbus-rtu.c 2012-05-16 11:57:56.000000000 -0700 @@ -16,6 +16,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include #include #include @@ -27,7 +28,7 @@ #include #include "modbus-private.h" - +#include "modbus-xuart.h" #include "modbus-rtu.h" #include "modbus-rtu-private.h" @@ -264,7 +265,9 @@ 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); + modbus_rtu_t *ctx_rtu = ctx->backend_data; + if (ctx_rtu->is_xuart) return _modbus_xuart_write(ctx, req, req_length); + else return write(ctx->s, req, req_length); #endif } @@ -273,7 +276,9 @@ #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); + modbus_rtu_t *ctx_rtu = ctx->backend_data; + if (ctx_rtu->is_xuart) return _modbus_xuart_read(ctx, rsp, rsp_length); + else return read(ctx->s, rsp, rsp_length); #endif } @@ -460,6 +465,8 @@ return -1; } #else + if (ctx_rtu->is_xuart) return _modbus_xuart_connect(ctx); + /* 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 @@ -799,13 +806,16 @@ ctx_rtu->w_ser.n_bytes = 0; return (FlushFileBuffers(ctx_rtu->w_ser.fd) == FALSE); #else - return tcflush(ctx->s, TCIOFLUSH); + modbus_rtu_t *ctx_rtu = ctx->backend_data; + if (ctx_rtu->is_xuart) return _modbus_xuart_flush(ctx); + else return tcflush(ctx->s, TCIOFLUSH); #endif } int _modbus_rtu_select(modbus_t *ctx, fd_set *rfds, struct timeval *tv, int length_to_read) { + modbus_rtu_t *ctx_rtu = ctx->backend_data; int s_rc; #if defined(_WIN32) s_rc = win32_ser_select(&(((modbus_rtu_t*)ctx->backend_data)->w_ser), @@ -819,6 +829,8 @@ return -1; } #else + if (ctx_rtu->is_xuart) + return _modbus_xuart_select(ctx, rfds, tv, length_to_read); while ((s_rc = select(ctx->s+1, rfds, NULL, NULL, tv)) == -1) { if (errno == EINTR) { if (ctx->debug) { @@ -878,6 +890,32 @@ _modbus_rtu_filter_request }; +/* Looks for string in the form of "NAME:PORT" where NAME is a valid DNS + * or dotted quad IP number and PORT is a numeric port number. + */ +int is_xuart(const char *d) { + int state; + + for (state = 0; *d; d++) switch (state) { + case 0: + if (!isalnum(*d)) return 0; + else state = 1; + break; + case 1: + if (*d == ':') state = 2; + else if (!isalnum(*d) && *d != '-' && *d != '.') return 0; + break; + case 2: + if (!isdigit(*d)) return 0; + else state = 3; + break; + case 3: + if (!isdigit(*d)) return 0; + break; + } + if (state != 3) return 0; else return 1; +} + modbus_t* modbus_new_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit) @@ -893,6 +931,7 @@ ctx->backend = &_modbus_rtu_backend; ctx->backend_data = (modbus_rtu_t *) malloc(sizeof(modbus_rtu_t)); ctx_rtu = (modbus_rtu_t *)ctx->backend_data; + ctx_rtu->is_xuart = is_xuart(device); dest_size = sizeof(ctx_rtu->device); ret_size = strlcpy(ctx_rtu->device, device, dest_size); diff -uNr libmodbus-3.0.2/src/modbus-xuart.c libmodbus-3.0.2-xuart/src/modbus-xuart.c --- libmodbus-3.0.2/src/modbus-xuart.c 1969-12-31 17:00:00.000000000 -0700 +++ libmodbus-3.0.2-xuart/src/modbus-xuart.c 2012-05-16 15:50:26.000000000 -0700 @@ -0,0 +1,369 @@ +/* Copyright 2012 Technologic Systems + * All Rights Reserved. + * + * This 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 2.1 of the License, or (at your option) any later version. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "modbus-private.h" +#include "modbus-xuart.h" +#include "modbus-rtu.h" +#include "modbus-rtu-private.h" + +#ifndef TEMP_FAILURE_RETRY +# define TEMP_FAILURE_RETRY(expression) \ + (__extension__ \ + ({ long int __result; \ + do __result = (long int) (expression); \ + while (__result == -1L && errno == EINTR); \ + __result; })) +#endif + +/* This function is used to connect to a socket and get a socket pointer in + * return. It takes two arguments, hostspec and args. The hostspec must be + * in the form of hostname:port, if it is NULL it will default to + * localhost:7350 (the first XUART port). The args argument is any additional + * arguments. In the case of this library, it is required to use at least + * --mode=raw since the read/write functions provided by this library utilize + * the raw mode of the XUARTs. All other options will default to the xuartctl + * server defaults. + * + * This function will return a socket FD + */ + +static int xuart_connect(char *hostspec, char *args) { + struct sockaddr_in sa; + struct hostent *he; + int sk, r, x, tos = IPTOS_LOWDELAY; + char *p; + char buf[1024]; + char cmd[1024]; + struct linger lx; + + /* hostspec must be in form hostname:port */ + if (hostspec) strncpy(buf, hostspec, sizeof(buf)); + else strcpy(buf, "127.0.0.1"); + buf[sizeof(buf) - 1] = 0; + + if (args) { + snprintf(cmd, sizeof(cmd), + "xuartctl --server --port=%s %s >/dev/null 2>&1", buf, args); + system(cmd); + } + p = buf; + while (*p != ':' && *p != 0) p++; + if (*p == 0) p = "7350"; + else { + *p = 0; + p++; + } + + sa.sin_family = AF_INET; + he = gethostbyname(buf); + assert(he != NULL); + memcpy(&sa.sin_addr, he->h_addr_list[0], he->h_length); + sa.sin_port = htons(strtoul(p, NULL, 0)); + assert(sa.sin_port != 0); + sk = socket(PF_INET, SOCK_STREAM, 0); + assert(sk != -1); + lx.l_onoff = 1; + lx.l_linger = 15; + setsockopt(sk, SOL_SOCKET, SO_LINGER, &lx, sizeof(lx)); + r = connect(sk, (struct sockaddr *)&sa, sizeof(sa)); + setsockopt(sk, IPPROTO_IP, IP_TOS, &tos, 4); + x = 1; + setsockopt(sk, IPPROTO_TCP, TCP_NODELAY, &x, 4); + fcntl(sk, F_SETFL, O_NONBLOCK); + + if (r) { + close(sk); + return -1; + } else return sk; +} + + +static unsigned short xtbl_8n2[256]; +static unsigned short xtbl_8e2[256]; +static unsigned short xtbl_8o2[256]; +static unsigned short xtbl_8e1[256]; +static unsigned short xtbl_8o1[256]; +static unsigned short xtbl_7n2[256]; +static unsigned short xtbl_7e2[256]; +static unsigned short xtbl_7o2[256]; +static unsigned short xtbl_7e1[256]; +static unsigned short xtbl_7o1[256]; +static void fill_xtbl(void) { + int i; + static int filled = 0; + if (filled) return; + filled = 1; + + for (i = 0; i < 256; i++) { + int j = i; + int p = 0; + while (j) { + if (j & 0x1) p++; + j >>= 1; + } + + xtbl_7n2[i] = 0x80 | i; + xtbl_8n2[i] = 0x100 | i; + if (p & 0x1) { + xtbl_8e1[i] = 0x100 | i; + xtbl_8o1[i] = i; + xtbl_8e2[i] = 0x300 | i; + xtbl_8o2[i] = 0x200 | i; + } else { + xtbl_8o1[i] = 0x100 | i; + xtbl_8e1[i] = i; + xtbl_8o2[i] = 0x300 | i; + xtbl_8e2[i] = 0x200 | i; + } + + if (i >= 128) { + xtbl_7o1[i] = xtbl_7o1[i-128]; + xtbl_7e1[i] = xtbl_7e1[i-128]; + xtbl_7o2[i] = xtbl_7o2[i-128]; + xtbl_7e2[i] = xtbl_7e2[i-128]; + } else if (p & 0x1) { + xtbl_7e1[i] = 0x80 | i; + xtbl_7o1[i] = i & 0x7f; + xtbl_7o2[i] = 0x100 | (i & 0x7f); + xtbl_7e2[i] = 0x180 | i; + } else { + xtbl_7o1[i] = 0x80 | i; + xtbl_7e1[i] = i & 0x7f; + xtbl_7e2[i] = 0x100 | (i & 0x7f); + xtbl_7o2[i] = 0x180 | i; + } + } +} + + +int _modbus_xuart_connect(modbus_t *ctx) { + modbus_rtu_t *ctx_rtu = ctx->backend_data; + char buf[128]; + char mode[4]; + int i, match; + static struct { + char *mode; + unsigned short *xtbl; + char nbits; + char rmsk; + } modes[] = { + {"8n1", NULL, 8, 0xff}, + {"8n2", xtbl_8n2, 9, 0xff}, + {"8e1", xtbl_8e1, 9, 0xff}, + {"8o1", xtbl_8o1, 9, 0xff}, + {"8e2", xtbl_8e2, 10, 0xff}, + {"8o2", xtbl_8o2, 10, 0xff}, + {"7n1", NULL, 7, 0x7f}, + {"7n2", xtbl_7n2, 8, 0x7f}, + {"7e1", xtbl_7e1, 8, 0x7f}, + {"7o1", xtbl_7o1, 8, 0x7f}, + {"7e2", xtbl_7e2, 9, 0x7f}, + {"7o2", xtbl_7o2, 9, 0x7f}, + {NULL, NULL, 0, 0}, + }; + + fill_xtbl(); + + snprintf(mode, sizeof(mode), "%d%c%d", ctx_rtu->data_bit, + ctx_rtu->parity, ctx_rtu->stop_bit); + snprintf(buf, sizeof(buf), "--mode=%s,raw --speed=%d", + mode, ctx_rtu->baud); + + for (i = 0, match = -1; NULL != modes[i].mode; i++) { + if (strcasecmp(mode, modes[i].mode) == 0) { + match = i; + break; + } + } + if (match == -1) { + fprintf(stderr, "ERROR Unsupported mode '%s'\n", mode); + return -1; + } + ctx_rtu->nbits = modes[match].nbits; + ctx_rtu->xtbl = modes[match].xtbl; + ctx_rtu->rmsk = modes[match].rmsk; + ctx_rtu->fput = ctx_rtu->fget = 0; + + ctx->s = xuart_connect(ctx_rtu->device, buf); + if (ctx->s == -1) { + fprintf(stderr, "ERROR Can't open the device %s (%s)\n", + ctx_rtu->device, strerror(errno)); + return -1; + } + + return 0; +} + +/* The benefit of the XUART here is that the MODBUS packet is sent atomically + * with no inter-character gaps. It also includes the required 3.5 character idle + * gap before and after the packet. + */ +ssize_t _modbus_xuart_write(modbus_t *ctx, const uint8_t *req, int req_length) { + unsigned short buf[512], *b = buf; + unsigned char *c; + modbus_rtu_t *ctx_rtu = ctx->backend_data; + unsigned short *xtbl; + int i, written, sk = ctx->s, len = (req_length + 3) * 2; + + xtbl = ctx_rtu->xtbl; + + *b++ = 0x5f00 | (req_length+2); /* Atomic packet of req_length+2 */ + *b++ = 40 | (2<<11); /* 40 bit times of idle */ + for (i = 0; i < req_length; i++, b++) { + if (xtbl) *b = xtbl[req[i]]; + else *b = req[i]; + } + *b = 40 | (2<<11); + + c = (unsigned char *)buf; + while (len) { + written = TEMP_FAILURE_RETRY(write(sk, c, len)); + if (written == -1 && (errno == EAGAIN)) { + fd_set fds; + FD_ZERO(&fds); + FD_SET(sk, &fds); + select(sk + 1, NULL, &fds, NULL, NULL); + } else if (written >= 0) { + len -= written; + c += written; + } else return -1; + } + return req_length; +} + +/* XUARTs get 16-bit opcodes instead of just bytes. These opcodes contain the + * length of idle inbetween characters so those should be stripped of the result + * passed to upper layers. If parity is enabled, we ignore parity errors. The + * CRC check should be sufficient for modbus. + */ +ssize_t _modbus_xuart_read(modbus_t *ctx, uint8_t *rsp, int rsp_length) { + modbus_rtu_t *ctx_rtu = ctx->backend_data; + unsigned short buf[512], *b = buf; + int nread, rshift = 11 - ctx_rtu->nbits; + unsigned char rmsk = ctx_rtu->rmsk; + uint8_t *orig_rsp = rsp; + + + while (ctx_rtu->fput != ctx_rtu->fget && rsp_length) { + *rsp++ = ctx_rtu->fifo[ctx_rtu->fget]; + ctx_rtu->fget = (ctx_rtu->fget + 1) & 0x1ff; + rsp_length--; + } + + if (rsp_length == 0 && rsp != orig_rsp) return rsp - orig_rsp; + + nread = TEMP_FAILURE_RETRY(read(ctx->s, buf, 1024)); + if (nread == 0 || nread == -1) { + if (rsp != orig_rsp) return rsp - orig_rsp; + else return nread; + } + + assert((nread & 0x1) == 0); + + while (rsp_length && nread) { + unsigned short v = *b++; + if (!(v & 0x1800)) { + v >>= rshift; + v &= rmsk; + *rsp++ = v; + rsp_length--; + } + nread -= 2; + } + + while (nread) { + unsigned short v = *b++; + if (!(v & 0x1800)) { + v >>= rshift; + v &= rmsk; + ctx_rtu->fifo[ctx_rtu->fput] = v; + ctx_rtu->fput = (ctx_rtu->fput + 1) & 0x1ff; + } + nread -= 2; + } + + return rsp - orig_rsp; +} + +int _modbus_xuart_flush(modbus_t *ctx) { + int nread; + modbus_rtu_t *ctx_rtu = ctx->backend_data; + unsigned char buf[1024]; + + ctx_rtu->fput = ctx_rtu->fget = 0; + do nread = TEMP_FAILURE_RETRY(read(ctx->s, buf, 1024)); + while (nread > 0); + + return 0; +} + +int _modbus_xuart_select(modbus_t *ctx, fd_set *rfds, struct timeval *tv, + int length_to_read) { + modbus_rtu_t *ctx_rtu = ctx->backend_data; + int ret; + + while (ctx_rtu->fput == ctx_rtu->fget) { + _modbus_xuart_read(ctx, NULL, 0); + if (ctx_rtu->fput != ctx_rtu->fget) return 1; +intr: + ret = select(ctx->s+1, rfds, NULL, NULL, tv); + if (ret == -1 && errno == EINTR) { + FD_ZERO(rfds); + FD_SET(ctx->s, rfds); + goto intr; + } else if (ret == -1) return -1; + else if (ret == 0) { + errno = ETIMEDOUT; + return -1; + } + } + + return 1; +} diff -uNr libmodbus-3.0.2/src/modbus-xuart.h libmodbus-3.0.2-xuart/src/modbus-xuart.h --- libmodbus-3.0.2/src/modbus-xuart.h 1969-12-31 17:00:00.000000000 -0700 +++ libmodbus-3.0.2-xuart/src/modbus-xuart.h 2012-05-16 11:57:20.000000000 -0700 @@ -0,0 +1,36 @@ +/* + * Copyright © 2012 Technologic Systems + * + * This 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 2.1 of the License, or (at your option) any later version. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _MODBUS_XUART_H_ +#define _MODBUS_XUART_H_ + +#include "modbus.h" +#include "modbus-rtu-private.h" + +MODBUS_BEGIN_DECLS + +int _modbus_xuart_connect(modbus_t *ctx); +ssize_t _modbus_xuart_write(modbus_t *ctx, const uint8_t *req, int req_length); +ssize_t _modbus_xuart_read(modbus_t *ctx, uint8_t *rsp, int rsp_length); +int _modbus_xuart_flush(modbus_t *ctx); +int _modbus_xuart_select(modbus_t *ctx, fd_set *rfds, struct timeval *tv, + int length_to_read); + +MODBUS_END_DECLS + +#endif /* _MODBUS_XUART_H_ */