Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement IOCTL control of RS-485 transmitter #99

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
.cproject
.pydevproject
/build
/build-arm
build.sh
33 changes: 32 additions & 1 deletion conf/mbusd.conf.example
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
149 changes: 148 additions & 1 deletion src/cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
Expand All @@ -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 {
Expand Down
12 changes: 12 additions & 0 deletions src/cfg.h
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
50 changes: 50 additions & 0 deletions src/tty.c
Original file line number Diff line number Diff line change
Expand Up @@ -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

}

Expand All @@ -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 */
Expand All @@ -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

Expand Down
7 changes: 7 additions & 0 deletions src/tty.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

/*
Expand Down