Commit e2f59e9e54b7b1eefe023d67387adb4366ab29d6

Authored by Tobias Doerffel
Committed by Stéphane Raimbault
1 parent 31577bbb

Added support for native Win32

Added support for native Win32 based on
https://code.launchpad.net/~thepyper/libmodbus/win32-native
This mainly affects the RTU implementation as the TCP/IP socket
interface is the same thanks to the winsocks library. The interface
for using the serial port is completely different on Win32 and is now
implemented accordingly.

Signed-off-by: Stéphane Raimbault <stephane.raimbault@gmail.com>
src/modbus-rtu-private.h
... ... @@ -24,6 +24,23 @@
24 24  
25 25 #define _MODBUS_RTU_CHECKSUM_LENGTH 2
26 26  
  27 +#ifdef NATIVE_WIN32
  28 +
  29 +#define WIN32_LEAN_AND_MEAN
  30 +#include <windows.h>
  31 +
  32 +/* WIN32: struct containing serial handle and a receive buffer */
  33 +#define PY_BUF_SIZE 512
  34 +struct win32_ser {
  35 + /* File handle */
  36 + HANDLE fd;
  37 + /* Receive buffer */
  38 + uint8_t buf[PY_BUF_SIZE];
  39 + /* Received chars */
  40 + DWORD n_bytes;
  41 +};
  42 +#endif /* NATIVE_WIN32 */
  43 +
27 44 typedef struct _modbus_rtu {
28 45 /* Device: "/dev/ttyS0", "/dev/ttyUSB0" or "/dev/tty.USA19*" on Mac OS X for
29 46 KeySpan USB<->Serial adapters this string had to be made bigger on OS X
... ... @@ -43,8 +60,13 @@ typedef struct _modbus_rtu {
43 60 uint8_t stop_bit;
44 61 /* Parity: 'N', 'O', 'E' */
45 62 char parity;
  63 +#ifdef NATIVE_WIN32
  64 + struct win32_ser w_ser;
  65 + DCB old_dcb;
  66 +#else
46 67 /* Save old termios settings */
47 68 struct termios old_tios;
  69 +#endif
48 70 } modbus_rtu_t;
49 71  
50 72 #endif /* _MODBUS_RTU_PRIVATE_H_ */
... ...
src/modbus-rtu.c
... ... @@ -155,14 +155,87 @@ int _modbus_rtu_send_msg_pre(uint8_t *req, int req_length)
155 155 return req_length;
156 156 }
157 157  
  158 +#ifdef NATIVE_WIN32
  159 +/* This simple implementation is sort of a substitute of the select() call, working
  160 + * this way: the win32_ser_select() call tries to read some data from the serial port,
  161 + * setting the timeout as the select() call would. Data read is stored into the
  162 + * receive buffer, that is then consumed by the win32_ser_read() call.
  163 + * So win32_ser_select() does both the event waiting and the reading,
  164 + * while win32_ser_read() only consumes the receive buffer.
  165 + */
  166 +
  167 +static void win32_ser_init(struct win32_ser *ws) {
  168 + /* Clear everything */
  169 + memset(ws,0x00,sizeof(struct win32_ser));
  170 + /* Set file handle to invalid */
  171 + ws->fd = INVALID_HANDLE_VALUE;
  172 +}
  173 +
  174 +static int win32_ser_select(struct win32_ser *ws, int max_len, struct timeval *tv) {
  175 + COMMTIMEOUTS comm_to; unsigned int msec = 0;
  176 + /* Check if some data still in the buffer to be consumed */
  177 + if (ws->n_bytes> 0) {
  178 + return 1;
  179 + }
  180 + /* Setup timeouts like select() would do */
  181 + msec = tv->tv_sec * 1000 + tv->tv_usec / 1000;
  182 + if (msec < 1) msec = 1;
  183 + comm_to.ReadIntervalTimeout = msec;
  184 + comm_to.ReadTotalTimeoutMultiplier = 0;
  185 + comm_to.ReadTotalTimeoutConstant = msec;
  186 + comm_to.WriteTotalTimeoutMultiplier = 0;
  187 + comm_to.WriteTotalTimeoutConstant = 1000;
  188 + SetCommTimeouts(ws->fd,&comm_to);
  189 + /* Read some bytes */
  190 + if ((max_len > PY_BUF_SIZE) || (max_len < 0)) {
  191 + max_len = PY_BUF_SIZE;
  192 + }
  193 + if (ReadFile(ws->fd, &ws->buf, max_len, &ws->n_bytes, NULL)) {
  194 + /* Check if some bytes available */
  195 + if (ws->n_bytes > 0) {
  196 + /* Some bytes read */
  197 + return 1;
  198 + } else {
  199 + /* Just timed out */
  200 + return 0;
  201 + }
  202 + } else {
  203 + /* Some kind of error */
  204 + return -1;
  205 + }
  206 +}
  207 +
  208 +static int win32_ser_read(struct win32_ser *ws, uint8_t *p_msg, unsigned int max_len) {
  209 + unsigned int n = ws->n_bytes;
  210 + if (max_len < n) {
  211 + n = max_len;
  212 + }
  213 + if (n > 0) {
  214 + memcpy(p_msg,ws->buf,n);
  215 + }
  216 + ws->n_bytes -= n;
  217 + return(n);
  218 +}
  219 +#endif
  220 +
158 221 ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_length)
159 222 {
  223 +#ifdef NATIVE_WIN32
  224 + modbus_rtu_t *ctx_rtu = ctx->backend_data;
  225 + DWORD n_bytes = 0;
  226 + return (WriteFile(ctx_rtu->w_ser.fd, req, req_length, &n_bytes, NULL)) ? n_bytes : -1;
  227 +#else
160 228 return write(ctx->s, req, req_length);
  229 +#endif
161 230 }
162 231  
163 232 ssize_t _modbus_rtu_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length)
164 233 {
  234 +#ifdef NATIVE_WIN32
  235 + return win32_ser_read(&((modbus_rtu_t *)ctx->backend_data)->w_ser, rsp, rsp_length);
  236 +#else
165 237 return read(ctx->s, rsp, rsp_length);
  238 +#endif
166 239 }
167 240  
168 241 /* The check_crc16 function shall return the message length if the CRC is
... ... @@ -195,8 +268,12 @@ int _modbus_rtu_check_integrity(modbus_t *ctx, uint8_t *msg,
195 268 /* Sets up a serial port for RTU communications */
196 269 static int _modbus_rtu_connect(modbus_t *ctx)
197 270 {
  271 +#ifdef NATIVE_WIN32
  272 + DCB dcb;
  273 +#else
198 274 struct termios tios;
199 275 speed_t speed;
  276 +#endif
200 277 modbus_rtu_t *ctx_rtu = ctx->backend_data;
201 278  
202 279 if (ctx->debug) {
... ... @@ -205,6 +282,138 @@ static int _modbus_rtu_connect(modbus_t *ctx)
205 282 ctx_rtu->data_bit, ctx_rtu->stop_bit);
206 283 }
207 284  
  285 +#ifdef NATIVE_WIN32
  286 + /* Some references here:
  287 + * http://msdn.microsoft.com/en-us/library/aa450602.aspx
  288 + */
  289 + win32_ser_init(&ctx_rtu->w_ser);
  290 +
  291 + /* ctx_rtu->device should contain a string like "COMxx:" xx being a decimal number */
  292 + ctx_rtu->w_ser.fd = CreateFileA(ctx_rtu->device,
  293 + GENERIC_READ | GENERIC_WRITE,
  294 + 0,
  295 + NULL,
  296 + OPEN_EXISTING,
  297 + 0,
  298 + NULL);
  299 +
  300 + /* Error checking */
  301 + if (ctx_rtu->w_ser.fd == INVALID_HANDLE_VALUE) {
  302 + fprintf(stderr, "ERROR Can't open the device %s (%s)\n",
  303 + ctx_rtu->device, strerror(errno));
  304 + return -1;
  305 + }
  306 +
  307 + /* Save params */
  308 + ctx_rtu->old_dcb.DCBlength = sizeof(DCB);
  309 + if (!GetCommState(ctx_rtu->w_ser.fd, &ctx_rtu->old_dcb)) {
  310 + fprintf(stderr, "ERROR Error getting configuration (LastError %d)\n",
  311 + (int)GetLastError());
  312 + return -1;
  313 + }
  314 +
  315 + /* Build new configuration (starting from current settings) */
  316 + dcb = ctx_rtu->old_dcb;
  317 +
  318 + /* Speed setting */
  319 + switch (ctx_rtu->baud) {
  320 + case 110:
  321 + dcb.BaudRate = CBR_110;
  322 + break;
  323 + case 300:
  324 + dcb.BaudRate = CBR_300;
  325 + break;
  326 + case 600:
  327 + dcb.BaudRate = CBR_600;
  328 + break;
  329 + case 1200:
  330 + dcb.BaudRate = CBR_1200;
  331 + break;
  332 + case 2400:
  333 + dcb.BaudRate = CBR_2400;
  334 + break;
  335 + case 4800:
  336 + dcb.BaudRate = CBR_4800;
  337 + break;
  338 + case 9600:
  339 + dcb.BaudRate = CBR_9600;
  340 + break;
  341 + case 19200:
  342 + dcb.BaudRate = CBR_19200;
  343 + break;
  344 + case 38400:
  345 + dcb.BaudRate = CBR_38400;
  346 + break;
  347 + case 57600:
  348 + dcb.BaudRate = CBR_57600;
  349 + break;
  350 + case 115200:
  351 + dcb.BaudRate = CBR_115200;
  352 + break;
  353 + default:
  354 + dcb.BaudRate = CBR_9600;
  355 + printf("WARNING Unknown baud rate %d for %s (B9600 used)\n",
  356 + ctx_rtu->baud, ctx_rtu->device);
  357 + }
  358 +
  359 + /* Data bits */
  360 + switch (ctx_rtu->data_bit) {
  361 + case 5:
  362 + dcb.ByteSize = 5;
  363 + break;
  364 + case 6:
  365 + dcb.ByteSize = 6;
  366 + break;
  367 + case 7:
  368 + dcb.ByteSize = 7;
  369 + break;
  370 + case 8:
  371 + default:
  372 + dcb.ByteSize = 8;
  373 + break;
  374 + }
  375 +
  376 + /* Stop bits */
  377 + if (ctx_rtu->stop_bit == 1)
  378 + dcb.StopBits = ONESTOPBIT;
  379 + else /* 2 */
  380 + dcb.StopBits = TWOSTOPBITS;
  381 +
  382 + /* Parity */
  383 + if (ctx_rtu->parity == 'N') {
  384 + dcb.Parity = NOPARITY;
  385 + dcb.fParity = FALSE;
  386 + } else if (ctx_rtu->parity == 'E') {
  387 + dcb.Parity = EVENPARITY;
  388 + dcb.fParity = TRUE;
  389 + } else {
  390 + /* odd */
  391 + dcb.Parity = ODDPARITY;
  392 + dcb.fParity = TRUE;
  393 + }
  394 +
  395 + /* Hardware handshaking left as default settings retrieved */
  396 +
  397 + /* No software handshaking */
  398 + dcb.fTXContinueOnXoff = TRUE;
  399 + dcb.fOutX = FALSE;
  400 + dcb.fInX = FALSE;
  401 +
  402 + /* Binary mode (it's the only supported on Windows anyway) */
  403 + dcb.fBinary = TRUE;
  404 +
  405 + /* Don't want errors to be blocking */
  406 + dcb.fAbortOnError = FALSE;
  407 +
  408 + /* TODO: any other flags !? */
  409 +
  410 + /* Setup port */
  411 + if (!SetCommState(ctx_rtu->w_ser.fd, &dcb)) {
  412 + fprintf(stderr, "ERROR Error setting new configuration (LastError %d)\n",
  413 + (int)GetLastError());
  414 + return -1;
  415 + }
  416 +#else
208 417 /* The O_NOCTTY flag tells UNIX that this program doesn't want
209 418 to be the "controlling terminal" for that port. If you
210 419 don't specify this then any input (such as keyboard abort
... ... @@ -447,6 +656,7 @@ static int _modbus_rtu_connect(modbus_t *ctx)
447 656 if (tcsetattr(ctx->s, TCSANOW, &tios) < 0) {
448 657 return -1;
449 658 }
  659 +#endif
450 660  
451 661 return 0;
452 662 }
... ... @@ -456,13 +666,30 @@ void _modbus_rtu_close(modbus_t *ctx)
456 666 /* Closes the file descriptor in RTU mode */
457 667 modbus_rtu_t *ctx_rtu = ctx->backend_data;
458 668  
  669 +#ifdef NATIVE_WIN32
  670 + /* Revert settings */
  671 + if (!SetCommState(ctx_rtu->w_ser.fd, &ctx_rtu->old_dcb))
  672 + fprintf(stderr, "ERROR Couldn't revert to configuration (LastError %d)\n",
  673 + (int)GetLastError());
  674 +
  675 + if (!CloseHandle(ctx_rtu->w_ser.fd))
  676 + fprintf(stderr, "ERROR Error while closing handle (LastError %d)\n",
  677 + (int)GetLastError());
  678 +#else
459 679 tcsetattr(ctx->s, TCSANOW, &(ctx_rtu->old_tios));
460 680 close(ctx->s);
  681 +#endif
461 682 }
462 683  
463 684 int _modbus_rtu_flush(modbus_t *ctx)
464 685 {
  686 +#ifdef NATIVE_WIN32
  687 + modbus_rtu_t *ctx_rtu = ctx->backend_data;
  688 + ctx_rtu->w_ser.n_bytes = 0;
  689 + return ( FlushFileBuffers(ctx_rtu->w_ser.fd) == FALSE );
  690 +#else
465 691 return tcflush(ctx->s, TCIOFLUSH);
  692 +#endif
466 693 }
467 694  
468 695 int _modbus_rtu_listen(modbus_t *ctx, int nb_connection)
... ... @@ -488,6 +715,25 @@ int _modbus_rtu_accept(modbus_t *ctx, int *socket)
488 715 int _modbus_rtu_select(modbus_t *ctx, fd_set *rfds, struct timeval *tv, int msg_length_computed, int msg_length)
489 716 {
490 717 int s_rc;
  718 +#ifdef NATIVE_WIN32
  719 + s_rc = win32_ser_select(&(((modbus_rtu_t*)ctx->backend_data)->w_ser), msg_length_computed, tv);
  720 + if (s_rc == 0) {
  721 + errno = ETIMEDOUT;
  722 + return -1;
  723 + }
  724 +
  725 + if (s_rc < 0) {
  726 + _error_print(ctx, "select");
  727 + if (ctx->error_recovery && (errno == EBADF)) {
  728 + modbus_close(ctx);
  729 + modbus_connect(ctx);
  730 + errno = EBADF;
  731 + return -1;
  732 + } else {
  733 + return -1;
  734 + }
  735 + }
  736 +#else
491 737 while ((s_rc = select(ctx->s+1, rfds, NULL, NULL, tv)) == -1) {
492 738 if (errno == EINTR) {
493 739 if (ctx->debug) {
... ... @@ -524,6 +770,7 @@ int _modbus_rtu_select(modbus_t *ctx, fd_set *rfds, struct timeval *tv, int msg_
524 770 }
525 771 return -1;
526 772 }
  773 +#endif
527 774  
528 775 return s_rc;
529 776 }
... ...
src/modbus-tcp.c
... ... @@ -15,17 +15,21 @@
15 15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 */
17 17  
  18 +#include "modbus.h"
  19 +#include "modbus-private.h"
  20 +
18 21 #include <stdio.h>
19 22 #include <stdlib.h>
20 23 #include <string.h>
21 24 #include <errno.h>
22 25  
23 26 #include <sys/types.h>
  27 +#ifdef NATIVE_WIN32
  28 +#include <ws2tcpip.h>
  29 +#else
24 30 #include <sys/socket.h>
25 31 #include <sys/ioctl.h>
26   -
27   -#include "modbus.h"
28   -#include "modbus-private.h"
  32 +#endif
29 33  
30 34 #include "modbus-tcp.h"
31 35 #include "modbus-tcp-private.h"
... ... @@ -164,7 +168,7 @@ static int _modbus_tcp_connect(modbus_t *ctx)
164 168 return -1;
165 169 }
166 170  
167   -#if (!HAVE_DECL___CYGWIN__)
  171 +#ifndef NATIVE_WIN32
168 172 /**
169 173 * Cygwin defines IPTOS_LOWDELAY but can't handle that flag so it's
170 174 * necessary to workaround that problem.
... ... @@ -210,10 +214,10 @@ int _modbus_tcp_flush(modbus_t *ctx)
210 214 do {
211 215 /* Extract the garbage from the socket */
212 216 char devnull[MODBUS_TCP_MAX_ADU_LENGTH];
213   -#if (!HAVE_DECL___CYGWIN__)
  217 +#ifndef NATIVE_WIN32
214 218 rc = recv(ctx->s, devnull, MODBUS_TCP_MAX_ADU_LENGTH, MSG_DONTWAIT);
215 219 #else
216   - /* On Cygwin, it's a bit more complicated to not wait */
  220 + /* On Win32, it's a bit more complicated to not wait */
217 221 fd_set rfds;
218 222 struct timeval tv;
219 223  
... ...
src/modbus.h
... ... @@ -18,11 +18,26 @@
18 18 #ifndef _MODBUS_H_
19 19 #define _MODBUS_H_
20 20  
  21 +#include <config.h>
  22 +
  23 +/* If win32 and no cygwin, suppose it's MinGW or any other native windows compiler. */
  24 +#if defined(WIN32) && !defined(__CYGWIN__)
  25 +#define NATIVE_WIN32
  26 +#define MSG_NOSIGNAL 0
  27 +#define ECONNRESET WSAECONNRESET
  28 +#define ECONNREFUSED WSAECONNREFUSED
  29 +#define ETIMEDOUT WSAETIMEDOUT
  30 +#define ENOPROTOOPT WSAENOPROTOOPT
  31 +#define SHUT_RDWR 2
  32 +#include <winsock2.h>
  33 +#endif /* win32 and no cygwin */
  34 +
21 35 /* Add this for macros that defined unix flavor */
22 36 #if (defined(__unix__) || defined(unix)) && !defined(USG)
23 37 #include <sys/param.h>
24 38 #endif
25 39 #include <stdint.h>
  40 +#ifndef NATIVE_WIN32
26 41 #include <termios.h>
27 42 #if defined(OpenBSD) || (defined(__FreeBSD__) && __FreeBSD__ < 5)
28 43 #include <netinet/in_systm.h>
... ... @@ -31,6 +46,7 @@
31 46 #include <netinet/ip.h>
32 47 #include <netinet/tcp.h>
33 48 #include <arpa/inet.h>
  49 +#endif
34 50 #include <sys/time.h>
35 51  
36 52 #include "modbus-version.h"
... ...
tests/bandwidth-server-many-up.c
... ... @@ -24,6 +24,10 @@
24 24  
25 25 #include <modbus.h>
26 26  
  27 +#ifdef NATIVE_WIN32
  28 +#include <ws2tcpip.h>
  29 +#endif
  30 +
27 31 #define NB_CONNECTION 5
28 32  
29 33 modbus_t *ctx = NULL;
... ...