diff --git a/.gitignore b/.gitignore index a18ea62..d5f7474 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ .cproject .pydevproject /build +/build-arm build.sh \ No newline at end of file diff --git a/conf/mbusd.conf.example b/conf/mbusd.conf.example index 2cde560..22cf67b 100644 --- a/conf/mbusd.conf.example +++ b/conf/mbusd.conf.example @@ -18,12 +18,43 @@ mode = 8n1 # Enable RS-485 support for given serial port device (Linux only) # enable_rs485 = no -# RS-485 data direction control type (addc, rts, sysfs_0, sysfs_1) +# RS-485 data direction control type (addc, rts, sysfs_0, sysfs_1, ioctl) trx_control = addc # Sysfs file to use to control data direction # trx_sysfile = +# Example IOCTL configuration + +#trx_control = ioctl +#trx_sysfile = /dev/gpio_ctl + +# IOCTL request number + +#trx_ioctl_req = 0x40080004 + +# IOCTL argument type (long, struct) + +# "long" argument example + +#trx_ioctl_arg_type = long + +# 1 when RTS set, 0 when clear +# rts_inv for inverse +#trx_ioctl_val = rts + + +# "struct" argument example + +#trx_ioctl_arg_type = struct + +#trx_ioctl_val_1 = 0 + +# 1 when RTS set, 0 when clear +# rts_inv for inverse +#trx_ioctl_val_2 = rts + + ############# TCP port settings ############# # TCP server address to bind diff --git a/src/cfg.c b/src/cfg.c index deb522c..a7a8a0f 100644 --- a/src/cfg.c +++ b/src/cfg.c @@ -40,7 +40,9 @@ #define CFG_MAX_LINE_LENGTH 200 #define CFG_NAME_MATCH(n) strcmp(n, name) == 0 +#define CFG_NAME_BEGINS(n) strncmp(n, name, strlen(n)) == 0 #define CFG_VALUE_MATCH(n) strcasecmp(n, value) == 0 +#define CFG_VALUE_BEGINS(n) strncasecmp(n, value, strlen(n)) == 0 #define CFG_VALUE_BOOL() (strcasecmp("y", value) == 0 || strcasecmp("yes", value) == 0) /* Global configuration storage variable */ @@ -70,6 +72,13 @@ cfg_init(void) #ifdef TRXCTL cfg.trxcntl = TRX_ADDC; *cfg.trxcntl_file = '\0'; + cfg.trx_ioctl_req = -1; + cfg.trx_ioctl_arg_type = TRX_IOCTL_ARG_LONG; + cfg.trx_ioctl_arg_long = 0; + cfg.trx_ioctl_arg_struct = NULL; + cfg.trx_ioctl_num_values = 0; + cfg.trx_ioctl_rts_value_num = 0; + cfg.trx_ioctl_rts_value_invert = FALSE; #endif strncpy(cfg.serveraddr, DEFAULT_SERVERADDR, INTBUFSIZE); cfg.serverport = DEFAULT_SERVERPORT; @@ -212,6 +221,10 @@ cfg_handle_param(char *name, char *value) { cfg.trxcntl = TRX_SYSFS_1; } + else if (CFG_VALUE_MATCH("ioctl")) + { + cfg.trxcntl = TRX_IOCTL; + } else { /* Unknown TRX control mode */ @@ -222,12 +235,146 @@ cfg_handle_param(char *name, char *value) else if (CFG_NAME_MATCH("trx_sysfile")) { strncpy(cfg.trxcntl_file, value, INTBUFSIZE); + } + else if (CFG_NAME_MATCH("trx_ioctl_req")) + { + char *end; + cfg.trx_ioctl_req = strtoul(value, &end, 0); + if (value == end || '\0' != *end) + { + CFG_ERR("invalid IOCTL request number: %s", value); + return 0; + } + } + else if (CFG_NAME_MATCH("trx_ioctl_arg_type")) + { + if (CFG_VALUE_MATCH("long")) + { + cfg.trx_ioctl_arg_type = TRX_IOCTL_ARG_LONG; + } + else if (CFG_VALUE_MATCH("struct")) + { + cfg.trx_ioctl_arg_type = TRX_IOCTL_ARG_STRUCT; + } + else + { + /* Unknown IOCTL argument type */ + CFG_ERR("unknown IOCTL argument type: %s", value); + return 0; + } + } + else if (CFG_NAME_MATCH("trx_ioctl_val")) + { + if (cfg.trx_ioctl_arg_type == TRX_IOCTL_ARG_STRUCT) + { + CFG_ERR("cannot use \"%s\" with \"struct\" argument type", name); + return 0; + } + + if (CFG_VALUE_BEGINS("rts")) + { + cfg.trx_ioctl_rts_value_num = 1; + if (value[3] == '\0') + { + cfg.trx_ioctl_rts_value_invert = FALSE; + } + else if (strcasecmp("_inv", value + 3) == 0) + { + cfg.trx_ioctl_rts_value_invert = TRUE; + } + else + { + CFG_ERR("invalid IOCTL value: %s", value); + return 0; + } + } + else + { + char *end; + cfg.trx_ioctl_arg_long = strtoul(value, &end, 0); + if (value == end || '\0' != *end) + { + CFG_ERR("invalid IOCTL argument: %s", value); + return 0; + } + } + } + else if (CFG_NAME_BEGINS("trx_ioctl_val_")) + { + if (cfg.trx_ioctl_arg_type != TRX_IOCTL_ARG_STRUCT) + { + CFG_ERR("\"%s\" only valid with \"struct\" argument type", name); + return 0; + } + + char *end; + int ioctl_val_num = strtoul(name + 14, &end, 10); + if ((name + 14) == end || '\0' != *end) + { + CFG_ERR("invalid IOCTL value number: %s", name + 14); + return 0; + } + + if (cfg.trx_ioctl_num_values < ioctl_val_num) + { + // "reallocarray" not available in uClibc on embedded system, so had to use realloc instead + // using overflow-safe implementation like libc - could be considered overkill, but + // I really prefer to be cautious when it comes to memory allocation! + + //cfg.trx_ioctl_arg_struct = reallocarray(cfg.trx_ioctl_arg_struct, ioctl_val_num, sizeof(unsigned long)); + size_t newsize; + if(!__builtin_mul_overflow(ioctl_val_num, sizeof(unsigned long), &newsize)) + { + cfg.trx_ioctl_arg_struct = realloc(cfg.trx_ioctl_arg_struct, newsize); + } + else + { + free(cfg.trx_ioctl_arg_struct); + cfg.trx_ioctl_arg_struct = NULL; + errno = ENOMEM; + } + if (!cfg.trx_ioctl_arg_struct) + { + CFG_ERR("cannot allocate memory for cfg parameter %s, exiting", name); + exit(errno); + } + + cfg.trx_ioctl_num_values = ioctl_val_num; + } + + if (CFG_VALUE_BEGINS("rts")) + { + cfg.trx_ioctl_rts_value_num = ioctl_val_num; + if (value[3] == '\0') + { + cfg.trx_ioctl_rts_value_invert = FALSE; + } + else if (CFG_VALUE_MATCH("rts_inv") == 0) + { + cfg.trx_ioctl_rts_value_invert = TRUE; + } + else + { + CFG_ERR("invalid IOCTL value: %s", value); + return 0; + } + } + else + { + int ioctl_val = strtoul(value, &end, 0); + if (value == end || '\0' != *end) + { + CFG_ERR("invalid IOCTL value: %s", value); + return 0; + } + cfg.trx_ioctl_arg_struct[ioctl_val_num - 1] = ioctl_val; + } #endif #ifdef LOG } else if (CFG_NAME_MATCH("loglevel")) { - cfg.dbglvl = (char)strtol(optarg, NULL, 0); + cfg.dbglvl = (char)strtol(value, NULL, 0); #endif } else { diff --git a/src/cfg.h b/src/cfg.h index f4b7fbd..85c453e 100644 --- a/src/cfg.h +++ b/src/cfg.h @@ -63,6 +63,18 @@ typedef struct int trxcntl; /* trx control sysfs file */ char trxcntl_file[INTBUFSIZE + 1]; + /* trx IOCTL request number */ + unsigned long trx_ioctl_req; + /* trx IOCTL argument type */ + int trx_ioctl_arg_type; + /* trx IOCTL argument */ + unsigned long trx_ioctl_arg_long; + unsigned long *trx_ioctl_arg_struct; + int trx_ioctl_rts_value_num; + int trx_ioctl_rts_value_invert; + /* number of values in IOCTL argument (if struct type) */ + int trx_ioctl_num_values; + /* #endif /* TCP server address */ char serveraddr[INTBUFSIZE + 1]; diff --git a/src/tty.c b/src/tty.c index 92462c7..1e3d5bd 100644 --- a/src/tty.c +++ b/src/tty.c @@ -461,7 +461,51 @@ void sysfs_gpio_set(char *filename, char *value) { write(fd, value, 1); close(fd); +#ifdef LOG logw(9, "tty: sysfs_gpio_set(%s,%s)\n",filename,value); +#endif + +} + +void sysfs_gpio_ioctl(char *filename, int set) { + int fd; + +#ifdef LOG + logw(9, "tty: sysfs_gpio_ioctl(%s,%d)\n",filename,set); +#endif + + if (cfg.trx_ioctl_rts_value_invert) + set = !set; + + if (cfg.trx_ioctl_rts_value_num > 0) + { + if (cfg.trx_ioctl_arg_type == TRX_IOCTL_ARG_STRUCT) + cfg.trx_ioctl_arg_struct[cfg.trx_ioctl_rts_value_num - 1] = set; + else if (cfg.trx_ioctl_arg_type == TRX_IOCTL_ARG_LONG) + cfg.trx_ioctl_arg_long = set; + } + + fd = open(filename, O_WRONLY); + if (cfg.trx_ioctl_arg_type == TRX_IOCTL_ARG_LONG) + ioctl(fd, cfg.trx_ioctl_req, cfg.trx_ioctl_arg_long); + else if (cfg.trx_ioctl_arg_type == TRX_IOCTL_ARG_STRUCT) + ioctl(fd, cfg.trx_ioctl_req, cfg.trx_ioctl_arg_struct); + + close(fd); + +#ifdef LOG + logw(9, "tty: ioctl request = 0x%lx\n",cfg.trx_ioctl_req); + + if (cfg.trx_ioctl_arg_type == TRX_IOCTL_ARG_LONG) + logw(9, "tty: ioctl arg = 0x%lx\n",cfg.trx_ioctl_arg_long); + + if (cfg.trx_ioctl_arg_type == TRX_IOCTL_ARG_STRUCT) + { + logw(9, "tty: ioctl struct: %d values\n",cfg.trx_ioctl_num_values); + for(int i = 0; i < cfg.trx_ioctl_num_values; i++) + logw(9, "tty: ioctl struct value %d = 0x%lx\n",i,cfg.trx_ioctl_arg_struct[i]); + } +#endif } @@ -476,7 +520,10 @@ tty_set_rts(int fd) sysfs_gpio_set(cfg.trxcntl_file,"1"); } else if ( TRX_SYSFS_0 == cfg.trxcntl) { sysfs_gpio_set(cfg.trxcntl_file,"0"); + } else if ( TRX_IOCTL == cfg.trxcntl) { + sysfs_gpio_ioctl(cfg.trxcntl_file,1); } + } /* Set RTS line to passive state */ @@ -490,7 +537,10 @@ tty_clr_rts(int fd) sysfs_gpio_set(cfg.trxcntl_file,"0"); } else if ( TRX_SYSFS_0 == cfg.trxcntl) { sysfs_gpio_set(cfg.trxcntl_file,"1"); + } else if ( TRX_IOCTL == cfg.trxcntl) { + sysfs_gpio_ioctl(cfg.trxcntl_file,0); } + } #endif diff --git a/src/tty.h b/src/tty.h index fe7d82f..1edefe6 100644 --- a/src/tty.h +++ b/src/tty.h @@ -80,6 +80,13 @@ #define TRX_RTS 1 #define TRX_SYSFS_1 2 #define TRX_SYSFS_0 3 +#define TRX_IOCTL 4 + +/* + * TRX IOCTL argument types + */ +#define TRX_IOCTL_ARG_LONG 0 +#define TRX_IOCTL_ARG_STRUCT 1 #endif /*