diff --git a/drivers/generic_modbus.c b/drivers/generic_modbus.c index e05d419ac2..44d99168cf 100644 --- a/drivers/generic_modbus.c +++ b/drivers/generic_modbus.c @@ -25,8 +25,8 @@ #include #include -#define DRIVER_NAME "NUT Generic Modbus driver" -#define DRIVER_VERSION "0.01" +#define DRIVER_NAME "NUT Generic Modbus driver" +#define DRIVER_VERSION "0.02" /* variables */ static modbus_t *mbctx = NULL; /* modbus memory context */ @@ -41,6 +41,11 @@ static int ser_data_bit = DATA_BIT; /* serial port data b static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ static int FSD_pulse_duration = SHTDOWN_PULSE_DURATION; /* set the FSD pulse duration */ +static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ +static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ +static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ +static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ + /* get config vars set by -x or defined in ups.conf driver section */ void get_config_vars(void); @@ -48,6 +53,9 @@ void get_config_vars(void); /* create a new modbus context based on connection type (serial or TCP) */ modbus_t *modbus_new(const char *port); +/* reconnect upon communication error */ +void modbus_reconnect(void); + /* modbus register read function */ int register_read(modbus_t *mb, int addr, regtype_t type, void *data); @@ -100,24 +108,69 @@ void upsdrv_initups(void) get_config_vars(); - /* open serial port */ + /* open communication port */ mbctx = modbus_new(device_path); if (mbctx == NULL) { - fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open serial port context"); + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); } /* set slave ID */ - rval = modbus_set_slave(mbctx, rio_slave_id); /* slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); if (rval < 0) { modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d\n", rio_slave_id); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); } /* connect to modbus device */ if (modbus_connect(mbctx) == -1) { modbus_free(mbctx); - fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s\n", modbus_strerror(errno)); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); } + + /* set modbus response timeout */ +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } +#elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) + { + /* Older libmodbus API (with timeval), and we have + * checked at configure time that we can put uint32_t + * into its fields. They are probably "long" on many + * systems as respectively time_t and suseconds_t - + * but that is not guaranteed; for more details see + * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_time.h.html + */ + struct timeval to; + memset(&to, 0, sizeof(struct timeval)); + to.tv_sec = mod_resp_to_s; + to.tv_usec = mod_resp_to_us; + /* void */ modbus_set_response_timeout(mbctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#else +# error "Can not use libmodbus API for timeouts" +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ + + /* set modbus byte time out */ +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +#elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) + { /* see comments above */ + struct timeval to; + memset(&to, 0, sizeof(struct timeval)); + to.tv_sec = mod_byte_to_s; + to.tv_usec = mod_byte_to_us; + /* void */ modbus_set_byte_timeout(mbctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ } /* update UPS signal state */ @@ -146,6 +199,7 @@ void upsdrv_updateinfo(void) } else { status_set("OB"); online = 0; + /* if DISCHRG state is not mapped to a contact and UPS is on * batteries set status to DISCHRG state */ if (sigar[DISCHRG_T].addr == NOTUSED) { @@ -173,9 +227,9 @@ void upsdrv_updateinfo(void) } /* - * update UPS status regarding CHARGING state via HB. HB is usually - * mapped to "ready" contact when closed indicates a charging state > 85% - */ + * update UPS status regarding CHARGING state via HB. HB is usually + * mapped to "ready" contact when closed indicates a charging state > 85% + */ if (sigar[HB_T].addr != NOTUSED) { rval = get_signal_state(HB_T); upsdebugx(2, "HB value: %d", rval); @@ -305,6 +359,10 @@ void upsdrv_makevartable(void) addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); + addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); + addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); + addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); + addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); addvar(VAR_VALUE, "OL_addr", "modbus address for OL state"); addvar(VAR_VALUE, "OB_addr", "modbus address for OB state"); addvar(VAR_VALUE, "LB_addr", "modbus address for LB state"); @@ -410,6 +468,12 @@ int register_read(modbus_t *mb, int addr, regtype_t type, void *data) (type == INPUT_R) ? "INPUT_R" : "HOLDING", device_path ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(2, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } } upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); return rval; @@ -460,6 +524,12 @@ int register_write(modbus_t *mb, int addr, regtype_t type, void *data) (type == INPUT_R) ? "INPUT_R" : "HOLDING", device_path ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(2, "register_write: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } } upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); return rval; @@ -639,7 +709,7 @@ void get_config_vars() /* initialize sigar table */ for (i = 0; i < NUMOF_SIG_STATES; i++) { sigar[i].addr = NOTUSED; - sigar[i].noro = 0; /* ON corresponds to 1 (closed contact) */ + sigar[i].noro = 0; /* ON corresponds to 1 (closed contact) */ } /* check if device manufacturer is set ang get the value */ @@ -691,6 +761,36 @@ void get_config_vars() } upsdebugx(2, "rio_slave_id %d", rio_slave_id); + /* check if response time out (s) is set ang get the value */ + if (testvar("mod_resp_to_s")) { + mod_resp_to_s = (uint32_t)strtol(getval("mod_resp_to_s"), NULL, 10); + } + upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); + + /* check if response time out (us) is set ang get the value */ + if (testvar("mod_resp_to_us")) { + mod_resp_to_us = (uint32_t) strtol(getval("mod_resp_to_us"), NULL, 10); + if (mod_resp_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); + } + } + upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); + + /* check if byte time out (s) is set ang get the value */ + if (testvar("mod_byte_to_s")) { + mod_byte_to_s = (uint32_t)strtol(getval("mod_byte_to_s"), NULL, 10); + } + upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); + + /* check if byte time out (us) is set ang get the value */ + if (testvar("mod_byte_to_us")) { + mod_byte_to_us = (uint32_t) strtol(getval("mod_byte_to_us"), NULL, 10); + if (mod_byte_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); + } + } + upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); + /* check if OL address is set and get the value */ if (testvar("OL_addr")) { sigar[OL_T].addr = (int)strtol(getval("OL_addr"), NULL, 0); @@ -701,6 +801,7 @@ void get_config_vars() } } } + /* check if OL register type is set and get the value otherwise set to INPUT_B */ if (testvar("OL_regtype")) { sigar[OL_T].type = (unsigned int)strtol(getval("OL_regtype"), NULL, 10); @@ -721,6 +822,7 @@ void get_config_vars() sigar[OB_T].noro = 0; } } + /* check if OB register type is set and get the value otherwise set to INPUT_B */ if (testvar("OB_regtype")) { sigar[OB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); @@ -741,6 +843,7 @@ void get_config_vars() } } } + /* check if LB register type is set and get the value otherwise set to INPUT_B */ if (testvar("LB_regtype")) { sigar[LB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); @@ -761,6 +864,7 @@ void get_config_vars() } } } + /* check if HB register type is set and get the value otherwise set to INPUT_B */ if (testvar("HB_regtype")) { sigar[HB_T].type = (unsigned int)strtol(getval("HB_regtype"), NULL, 10); @@ -781,6 +885,7 @@ void get_config_vars() } } } + /* check if RB register type is set and get the value otherwise set to INPUT_B */ if (testvar("RB_regtype")) { sigar[RB_T].type = (unsigned int)strtol(getval("RB_regtype"), NULL, 10); @@ -801,6 +906,7 @@ void get_config_vars() } } } + /* check if CHRG register type is set and get the value otherwise set to INPUT_B */ if (testvar("CHRG_regtype")) { sigar[CHRG_T].type = (unsigned int)strtol(getval("CHRG_regtype"), NULL, 10); @@ -821,6 +927,7 @@ void get_config_vars() } } } + /* check if DISCHRG register type is set and get the value otherwise set to INPUT_B */ if (testvar("DISCHRG_regtype")) { sigar[DISCHRG_T].type = (unsigned int)strtol(getval("DISCHRG_regtype"), NULL, 10); @@ -841,6 +948,7 @@ void get_config_vars() } } } + /* check if FSD register type is set and get the value otherwise set to COIL */ if (testvar("FSD_regtype")) { sigar[FSD_T].type = (unsigned int)strtol(getval("FSD_regtype"), NULL, 10); @@ -922,3 +1030,70 @@ modbus_t *modbus_new(const char *port) } return mb; } + +/* reconnect to modbus server upon connection error */ +void modbus_reconnect(void) +{ + int rval; + + upsdebugx(2, "modbus_reconnect, trying to reconnect to modbus server"); + + /* clear current modbus context */ + modbus_close(mbctx); + modbus_free(mbctx); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); + } + + /* set modbus response timeout */ +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } +#elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) + { /* see comments above */ + struct timeval to; + memset(&to, 0, sizeof(struct timeval)); + to.tv_sec = mod_resp_to_s; + to.tv_usec = mod_resp_to_us; + /* void */ modbus_set_response_timeout(mbctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ + + /* set modbus byte timeout */ +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +#elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) + { /* see comments above */ + struct timeval to; + memset(&to, 0, sizeof(struct timeval)); + to.tv_sec = mod_byte_to_s; + to.tv_usec = mod_byte_to_us; + /* void */ modbus_set_byte_timeout(mbctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ +} diff --git a/drivers/generic_modbus.h b/drivers/generic_modbus.h index 3d60b61f10..3a6b8a50ce 100644 --- a/drivers/generic_modbus.h +++ b/drivers/generic_modbus.h @@ -33,6 +33,16 @@ #define DATA_BIT 8 #define STOP_BIT 1 +/* + * modbus response and byte timeouts + * us: 1 - 999999 + */ +#define MODRESP_TIMEOUT_s 0 +#define MODRESP_TIMEOUT_us 200000 +#define MODBYTE_TIMEOUT_s 0 +#define MODBYTE_TIMEOUT_us 50000 + + /* modbus access parameters */ #define MODBUS_SLAVE_ID 5 diff --git a/m4/ax_c_pragmas.m4 b/m4/ax_c_pragmas.m4 index 4b2ad5f820..28b39bbb07 100644 --- a/m4/ax_c_pragmas.m4 +++ b/m4/ax_c_pragmas.m4 @@ -2,6 +2,9 @@ dnl Check for current compiler support of specific pragmas we use, dnl e.g. diagnostics management to keep warnings quiet sometimes AC_DEFUN([AX_C_PRAGMAS], [ +if test -z "${nut_have_ax_c_pragmas_seen}"; then + nut_have_ax_c_pragmas_seen="yes" + CFLAGS_SAVED="${CFLAGS}" CXXFLAGS_SAVED="${CXXFLAGS}" @@ -428,6 +431,33 @@ dnl ### [CFLAGS="${CFLAGS_SAVED} -Werror=pragmas -Werror=unknown-warning" AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_COMPARE_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wsign-compare" (outside functions)]) ]) + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wsign-conversion"], + [ax_cv__pragma__gcc__diags_ignored_sign_conversion], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wsign-conversion" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_sign_conversion=yes], + [ax_cv__pragma__gcc__diags_ignored_sign_conversion=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_sign_conversion" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_CONVERSION], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wsign-conversion"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wsign-conversion" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_sign_conversion_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wsign-conversion"]], [])], + [ax_cv__pragma__gcc__diags_ignored_sign_conversion_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_sign_conversion_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_sign_conversion_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_CONVERSION_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wsign-conversion" (outside functions)]) + ]) + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wunreachable-code-break"], [ax_cv__pragma__gcc__diags_ignored_unreachable_code_break], [AC_COMPILE_IFELSE( @@ -875,9 +905,13 @@ dnl ### [CFLAGS="${CFLAGS_SAVED} -Werror=pragmas -Werror=unknown-warning" CFLAGS="${CFLAGS_SAVED}" CXXFLAGS="${CXXFLAGS_SAVED}" +fi ]) AC_DEFUN([AX_C_PRINTF_STRING_NULL], [ +if test -z "${nut_have_ax_c_printf_string_null_seen}"; then + nut_have_ax_c_printf_string_null_seen="yes" + dnl ### To be sure, bolt the language AC_LANG_PUSH([C]) @@ -917,4 +951,5 @@ exit 0; ]) AC_LANG_POP([C]) +fi ]) diff --git a/m4/nut_check_libmodbus.m4 b/m4/nut_check_libmodbus.m4 index 9405a5988c..ea08dd3659 100644 --- a/m4/nut_check_libmodbus.m4 +++ b/m4/nut_check_libmodbus.m4 @@ -69,11 +69,116 @@ if test -z "${nut_have_libmodbus_seen}"; then AC_CHECK_HEADERS(modbus.h, [nut_have_libmodbus=yes], [nut_have_libmodbus=no], [AC_INCLUDES_DEFAULT]) AC_CHECK_FUNCS(modbus_new_rtu, [], [nut_have_libmodbus=no]) AC_CHECK_FUNCS(modbus_new_tcp, [], [nut_have_libmodbus=no]) + AC_CHECK_FUNCS(modbus_set_byte_timeout, [], [nut_have_libmodbus=no]) + AC_CHECK_FUNCS(modbus_set_response_timeout, [], [nut_have_libmodbus=no]) - if test "${nut_have_libmodbus}" = "yes"; then - LIBMODBUS_CFLAGS="${CFLAGS}" - LIBMODBUS_LIBS="${LIBS}" - fi + dnl modbus_set_byte_timeout() and modbus_set_response_timeout() + dnl in 3.0.x and 3.1.x have different args (since ~2013): the + dnl older version used to accept timeout as a struct timeval + dnl instead of seconds and microseconds. Detect which we use?.. + AS_IF([test x"$nut_have_libmodbus" = xyes], + [dnl Do not rely on versions if we can test actual API + AX_C_PRAGMAS + AC_LANG_PUSH([C]) + AC_CACHE_CHECK([types of arguments for modbus_set_byte_timeout], + [nut_cv_func_modbus_set_byte_timeout_args], + [nut_cv_func_modbus_set_byte_timeout_args="unknown" + AC_COMPILE_IFELSE( + [dnl Try purely the old API (timeval) + AC_LANG_PROGRAM([ +#include +#include +], [modbus_t *ctx; struct timeval to = (struct timeval){0}; +modbus_set_byte_timeout(ctx, &to);]) + ], [nut_cv_func_modbus_set_byte_timeout_args="timeval" + dnl Try the old API in more detail: check + dnl if we can just assign uint32's for new + dnl code into timeval fields (exist+numeric)? + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([ +#include +#include +#include +], [modbus_t *ctx; uint32_t to_sec = 10, to_usec = 50; +struct timeval to = (struct timeval){0}; +/* TODO: Clarify and detect warning names and + * so pragmas for signed/unsigned assignment (e.g. + * for timeval definitions that have "long" fields) + */ +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_COMPARE +# pragma GCC diagnostic ignored "-Wsign-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_CONVERSION +# pragma GCC diagnostic ignored "-Wsign-conversion" +#endif +to.tv_sec = to_sec; +to.tv_usec = to_usec; +modbus_set_byte_timeout(ctx, &to); +]) + ], [nut_cv_func_modbus_set_byte_timeout_args="timeval_numeric_fields"]) + ], + + [dnl Try another API variant: new API with + dnl fields of struct timeval as numbers + dnl (checks they exist, and are compatible + dnl numeric types so compiler can convert) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([ +#include +#include +#include +], [modbus_t *ctx; struct timeval to = (struct timeval){0}; +/* TODO: Clarify and detect warning names and + * so pragmas for signed/unsigned assignment (e.g. + * for timeval definitions that have "long" fields) + */ +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_COMPARE +# pragma GCC diagnostic ignored "-Wsign-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_CONVERSION +# pragma GCC diagnostic ignored "-Wsign-conversion" +#endif +uint32_t to_sec = to.tv_sec, to_usec = to.tv_usec; +modbus_set_byte_timeout(ctx, to_sec, to_usec); +]) + ], [nut_cv_func_modbus_set_byte_timeout_args="sec_usec_uint32_cast_timeval_fields"], + [dnl Try another API variant: new API purely (two uint32's) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([ +#include +#include +], [modbus_t *ctx; uint32_t to_sec = 0, to_usec = 0; +modbus_set_byte_timeout(ctx, to_sec, to_usec);]) + ], [nut_cv_func_modbus_set_byte_timeout_args="sec_usec_uint32"]) + ]) + ]) + ]) + + dnl NOTE: We could add similar tests to above for + dnl other time-related methods, but for now keep + dnl it simple -- and assume some same approach + dnl applies to the same generation of the library. + AC_LANG_POP([C]) + AC_MSG_RESULT([Found types to use for modbus_set_byte_timeout: ${nut_cv_func_modbus_set_byte_timeout_args}]) + dnl NOTE: code should check for having a token name defined e.g.: + dnl #ifdef NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32 + dnl Alas, we can't pass variables as macro name to AC_DEFINE + COMMENT="Define to specify timeout method args approach for libmodbus" + AS_CASE(["${nut_cv_func_modbus_set_byte_timeout_args}"], + [timeval_numeric_fields], [AC_DEFINE([NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields], 1, [${COMMENT}])], + [timeval], [AC_DEFINE([NUT_MODBUS_TIMEOUT_ARG_timeval], 1, [${COMMENT}])], + [sec_usec_uint32_cast_timeval_fields], [AC_DEFINE([NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields], 1, [${COMMENT}])], + [sec_usec_uint32], [AC_DEFINE([NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32], 1, [${COMMENT}])], + [dnl default + AC_MSG_WARN([Cannot find proper types to use for modbus_set_byte_timeout]) + nut_have_libmodbus=no] + ) + ]) + + AS_IF([test x"${nut_have_libmodbus}" = x"yes"], + [LIBMODBUS_CFLAGS="${CFLAGS}" + LIBMODBUS_LIBS="${LIBS}"] + ) dnl restore original CFLAGS and LIBS CFLAGS="${CFLAGS_ORIG}"