[Ser2net-devel] [PATCH] Add support for RS485 mode
Brought to you by:
cminyard
From: <yeg...@go...> - 2014-08-15 09:47:20
|
From: Yegor Yefremov <yeg...@go...> Add options to fill and set the serial_rs485 structure, that configures RS485 behavior of the serial driver. Signed-off-by: Yegor Yefremov <yeg...@go...> --- dataxfer.c | 180 +-------------------------------------------------------- dataxfer.h | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ devcfg.c | 17 ++++++ readconfig.c | 111 +++++++++++++++++++++++++++++++++++- ser2net.conf | 13 +++- utils.h | 3 + 6 files changed, 323 insertions(+), 180 deletions(-) diff --git a/dataxfer.c b/dataxfer.c index 5ad1684..1fef285 100644 --- a/dataxfer.c +++ b/dataxfer.c @@ -20,27 +20,8 @@ /* This code handles the actual transfer of data between the serial ports and the TCP ports. */ -#include <sys/time.h> -#include <arpa/inet.h> -#include <stdlib.h> -#include <unistd.h> -#include <stdio.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <errno.h> -#include <syslog.h> -#include <string.h> -#include <ctype.h> -#include <time.h> -#include <fcntl.h> -#include "ser2net.h" #include "dataxfer.h" -#include "selector.h" -#include "utils.h" -#include "telnet.h" -#include "devio.h" -#include "buffer.h" #define SERIAL "term" #define NET "tcp " @@ -70,163 +51,6 @@ char *state_str[] = { "unconnected", "waiting input", "waiting output", #define PORT_TELNET 3 /* Port will do telnet negotiation. */ char *enabled_str[] = { "off", "raw", "rawlp", "telnet" }; -#define PORT_BUFSIZE 64 - -typedef struct trace_info_s -{ - int hexdump; /* output each block as a hexdump */ - int timestamp; /* preceed each line with a timestamp */ - char *filename; /* open file. NULL if not used */ - int fd; /* open file. -1 if not used */ -} trace_info_t; - -typedef struct port_info -{ - int enabled; /* If PORT_DISABLED, the port - is disabled and the TCP - accept port is not - operational. If PORT_RAW, - the port is enabled and - will not do any telnet - negotiations. If - PORT_RAWLP, the port is enabled - only for output without any - termios setting - it allows - to redirect /dev/lpX devices If - PORT_TELNET, the port is - enabled and it will do - telnet negotiations. */ - - int timeout; /* The number of seconds to - wait without any I/O before - we shut the port down. */ - - int timeout_left; /* The amount of time left (in - seconds) before the timeout - goes off. */ - - sel_timer_t *timer; /* Used to timeout when the no - I/O has been seen for a - certain period of time. */ - - - /* Information about the TCP port. */ - char *portname; /* The name given for the port. */ - int is_stdio; /* Do stdio on the port? */ - struct addrinfo *ai; /* The address list for the portname. */ - int *acceptfds; /* The file descriptor used to - accept connections on the - TCP port. */ - unsigned int nr_acceptfds; - int tcpfd; /* When connected, the file - descriptor for the TCP - port used for I/O. */ - struct sockaddr_storage remote; /* The socket address of who - is connected to this port. */ - unsigned int tcp_bytes_received; /* Number of bytes read from the - TCP port. */ - unsigned int tcp_bytes_sent; /* Number of bytes written to the - TCP port. */ - struct sbuf *banner; /* Outgoing banner */ - - unsigned int dev_bytes_received; /* Number of bytes read from the - device. */ - unsigned int dev_bytes_sent; /* Number of bytes written to the - device. */ - - - /* Information use when transferring information from the TCP port - to the terminal device. */ - int tcp_to_dev_state; /* State of transferring - data from the TCP port - to the device. */ - struct sbuf tcp_to_dev; /* Buffer struct for - TCP to device - transfers. */ - unsigned char tcp_to_devbuf[PORT_BUFSIZE]; /* Buffer used for - TCP to device - transfers. */ - struct controller_info *tcp_monitor; /* If non-null, send any input - received from the TCP port - to this controller port. */ - struct sbuf *devstr; /* Outgoing string */ - - /* Information use when transferring information from the terminal - device to the TCP port. */ - int dev_to_tcp_state; /* State of transferring - data from the device to - the TCP port. */ - struct sbuf dev_to_tcp; /* Buffer struct for - device to TCP - transfers. */ - unsigned char dev_to_tcpbuf[PORT_BUFSIZE]; /* Buffer used for - device to TCP - transfers. */ - struct controller_info *dev_monitor; /* If non-null, send any input - received from the device - to this controller port. */ - - struct port_info *next; /* Used to keep a linked list - of these. */ - - int config_num; /* Keep track of what configuration this was last - updated under. Setting to -1 means to delete - the port when the current session is done. */ - - struct port_info *new_config; /* If the port is reconfigged while - open, this will hold the new - configuration that should be - loaded when the current session - is done. */ - - /* Data for the telnet processing */ - telnet_data_t tn_data; - - /* Is RFC 2217 mode enabled? */ - int is_2217; - - /* Masks for RFC 2217 */ - unsigned char linestate_mask; - unsigned char modemstate_mask; - unsigned char last_modemstate; - - /* Allow RFC 2217 mode */ - int allow_2217; - - /* kickolduser mode */ - int kickolduser_mode; - - /* Banner to display at startup, or NULL if none. */ - char *bannerstr; - - /* RFC 2217 signature. */ - char *signaturestr; - - /* String to send to device at startup, or NULL if none. */ - char *openstr; - - /* String to send to device at close, or NULL if none. */ - char *closestr; - - /* - * File to read/write trace, NULL if none. If the same, then - * trace information is in the same file, only one open is done. - */ - trace_info_t trace_read; - trace_info_t trace_write; - trace_info_t trace_both; - - /* - * Pointers to the above, that way if two are the same file we can just - * set up one and point both to it. - */ - trace_info_t *tr; - trace_info_t *tw; - trace_info_t *tb; - - struct devio io; /* For handling I/O operation to the device */ -} port_info_t; - port_info_t *ports = NULL; /* Linked list of ports. */ static void shutdown_port(port_info_t *port, char *reason); @@ -303,6 +127,7 @@ init_port_data(port_info_t *port) port->trace_read.fd = -1; port->trace_write.fd = -1; port->trace_both.fd = -1; + port->rs485conf = NULL; } static void @@ -1846,6 +1671,9 @@ myconfig(void *data, struct absout *eout, const char *pos) } else if (strncmp(pos, "tb=", 3) == 0) { /* trace both directions. */ port->trace_both.filename = find_tracefile(pos + 3); + } else if (strncmp(pos, "rs485=", 6) == 0) { + /* get RS485 configuration. */ + port->rs485conf = find_rs485conf(pos + 6); } else if ((s = find_str(pos, &stype))) { /* It's a startup banner, signature or open/close string, it's already set. */ diff --git a/dataxfer.h b/dataxfer.h index e1c23ec..32f80aa 100644 --- a/dataxfer.h +++ b/dataxfer.h @@ -20,12 +20,191 @@ #ifndef DATAXFER #define DATAXFER +#include <sys/time.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <errno.h> +#include <syslog.h> +#include <string.h> +#include <ctype.h> +#include <time.h> +#include <fcntl.h> + +#include "ser2net.h" +#include "selector.h" +#include "utils.h" +#include "telnet.h" +#include "devio.h" +#include "buffer.h" #include "controller.h" #ifdef USE_UUCP_LOCKING extern int uucp_locking_enabled; #endif +#define PORT_BUFSIZE 64 + +typedef struct trace_info_s +{ + int hexdump; /* output each block as a hexdump */ + int timestamp; /* preceed each line with a timestamp */ + char *filename; /* open file. NULL if not used */ + int fd; /* open file. -1 if not used */ +} trace_info_t; + +typedef struct port_info +{ + int enabled; /* If PORT_DISABLED, the port + is disabled and the TCP + accept port is not + operational. If PORT_RAW, + the port is enabled and + will not do any telnet + negotiations. If + PORT_RAWLP, the port is enabled + only for output without any + termios setting - it allows + to redirect /dev/lpX devices If + PORT_TELNET, the port is + enabled and it will do + telnet negotiations. */ + + int timeout; /* The number of seconds to + wait without any I/O before + we shut the port down. */ + + int timeout_left; /* The amount of time left (in + seconds) before the timeout + goes off. */ + + sel_timer_t *timer; /* Used to timeout when the no + I/O has been seen for a + certain period of time. */ + + + /* Information about the TCP port. */ + char *portname; /* The name given for the port. */ + int is_stdio; /* Do stdio on the port? */ + struct addrinfo *ai; /* The address list for the portname. */ + int *acceptfds; /* The file descriptor used to + accept connections on the + TCP port. */ + unsigned int nr_acceptfds; + int tcpfd; /* When connected, the file + descriptor for the TCP + port used for I/O. */ + struct sockaddr_storage remote; /* The socket address of who + is connected to this port. */ + unsigned int tcp_bytes_received; /* Number of bytes read from the + TCP port. */ + unsigned int tcp_bytes_sent; /* Number of bytes written to the + TCP port. */ + struct sbuf *banner; /* Outgoing banner */ + + unsigned int dev_bytes_received; /* Number of bytes read from the + device. */ + unsigned int dev_bytes_sent; /* Number of bytes written to the + device. */ + + + /* Information use when transferring information from the TCP port + to the terminal device. */ + int tcp_to_dev_state; /* State of transferring + data from the TCP port + to the device. */ + struct sbuf tcp_to_dev; /* Buffer struct for + TCP to device + transfers. */ + unsigned char tcp_to_devbuf[PORT_BUFSIZE]; /* Buffer used for + TCP to device + transfers. */ + struct controller_info *tcp_monitor; /* If non-null, send any input + received from the TCP port + to this controller port. */ + struct sbuf *devstr; /* Outgoing string */ + + /* Information use when transferring information from the terminal + device to the TCP port. */ + int dev_to_tcp_state; /* State of transferring + data from the device to + the TCP port. */ + struct sbuf dev_to_tcp; /* Buffer struct for + device to TCP + transfers. */ + unsigned char dev_to_tcpbuf[PORT_BUFSIZE]; /* Buffer used for + device to TCP + transfers. */ + struct controller_info *dev_monitor; /* If non-null, send any input + received from the device + to this controller port. */ + + struct port_info *next; /* Used to keep a linked list + of these. */ + + int config_num; /* Keep track of what configuration this was last + updated under. Setting to -1 means to delete + the port when the current session is done. */ + + struct port_info *new_config; /* If the port is reconfigged while + open, this will hold the new + configuration that should be + loaded when the current session + is done. */ + + /* Data for the telnet processing */ + telnet_data_t tn_data; + + /* Is RFC 2217 mode enabled? */ + int is_2217; + + /* Masks for RFC 2217 */ + unsigned char linestate_mask; + unsigned char modemstate_mask; + unsigned char last_modemstate; + + /* Allow RFC 2217 mode */ + int allow_2217; + + /* kickolduser mode */ + int kickolduser_mode; + + /* Banner to display at startup, or NULL if none. */ + char *bannerstr; + + /* RFC 2217 signature. */ + char *signaturestr; + + /* String to send to device at startup, or NULL if none. */ + char *openstr; + + /* String to send to device at close, or NULL if none. */ + char *closestr; + + /* + * File to read/write trace, NULL if none. If the same, then + * trace information is in the same file, only one open is done. + */ + trace_info_t trace_read; + trace_info_t trace_write; + trace_info_t trace_both; + + /* + * Pointers to the above, that way if two are the same file we can just + * set up one and point both to it. + */ + trace_info_t *tr; + trace_info_t *tw; + trace_info_t *tb; + + struct devio io; /* For handling I/O operation to the device */ + + struct serial_rs485 *rs485conf; +} port_info_t; + struct absout { int (*out)(struct absout *e, const char *str, ...); void *data; diff --git a/devcfg.c b/devcfg.c index 95ad936..77715ab 100644 --- a/devcfg.c +++ b/devcfg.c @@ -31,6 +31,7 @@ #include <signal.h> #include <errno.h> #include <syslog.h> +#include <linux/serial.h> #include "ser2net.h" #include "selector.h" @@ -53,6 +54,8 @@ struct devcfg_data { /* Disable break-commands */ int disablebreak; + + struct serial_rs485 *conf; }; static struct baud_rates_s { @@ -294,6 +297,8 @@ devconfig(struct devcfg_data *d, struct absout *eout, const char *instr, char *pos; char *strtok_data; int rv = 0; + port_info_t *port = data; + devinit(termctl); @@ -444,6 +449,7 @@ devconfig(struct devcfg_data *d, struct absout *eout, const char *instr, pos = strtok_r(NULL, ", \t", &strtok_data); } + d->conf = port->rs485conf; out: free(str); return rv; @@ -766,6 +772,17 @@ static int devcfg_setup(struct devio *io, const char *name, const char **errstr) name); } + if (d->conf) { + if (d->conf->flags & SER_RS485_ENABLED) { + if ( ioctl (d->devfd , TIOCSRS485, d->conf ) < 0) { + syslog(LOG_ERR, "Could not set RS485 config for device %s port %s: %m", + io->devname, + name); + return -1; + } + } + } + sel_set_fd_handlers(ser2net_sel, d->devfd, io, io->read_disabled ? NULL : do_read, do_write, do_except); diff --git a/readconfig.c b/readconfig.c index 0cb4846..5f46d59 100644 --- a/readconfig.c +++ b/readconfig.c @@ -26,6 +26,7 @@ #include <stdio.h> #include <syslog.h> #include <stdarg.h> +#include <linux/serial.h> #include "dataxfer.h" #include "readconfig.h" @@ -204,7 +205,14 @@ struct tracefile_s struct tracefile_s *next; }; -/* All the tracefiles in the system. */ +struct rs485conf_s +{ + char *name; + struct serial_rs485 conf; + struct rs485conf_s *next; +}; + +/* All the RS485 configs in the system. */ struct tracefile_s *tracefiles = NULL; static void @@ -265,6 +273,91 @@ free_tracefiles(void) } } +/* All the tracefiles in the system. */ +struct rs485conf_s *rs485confs = NULL; + +static void +handle_rs485conf(char *name, char *str) +{ + struct rs485conf_s *new_rs485conf; + uint8_t rts_on_send, rx_during_tx; + + new_rs485conf = malloc(sizeof(*new_rs485conf)); + if (!new_rs485conf) { + syslog(LOG_ERR, "Out of memory handling rs485 config on %d", lineno); + return; + } + + new_rs485conf->name = strdup(name); + if (!new_rs485conf->name) { + syslog(LOG_ERR, "Out of memory handling rs485 config on %d", lineno); + free(new_rs485conf); + return; + } + + if (sscanf(str, "%u:%u:%1hhu:%1hhu", + &new_rs485conf->conf.delay_rts_before_send, + &new_rs485conf->conf.delay_rts_after_send, + &rts_on_send, + &rx_during_tx) != 4) { + syslog(LOG_ERR, "Couldn't parse RS485 config on %d", lineno); + return; + } + + /* check, if flags have values 0 or 1 */ + if (rts_on_send > 1) { + syslog(LOG_ERR, "RTS_ON_SEND parameter can be 0 or 1 on %d", lineno); + return; + } + + if (rx_during_tx > 1) { + syslog(LOG_ERR, "RX_DURING_TX parameter can be 0 or 1 on %d", lineno); + return; + } + + new_rs485conf->conf.flags = SER_RS485_ENABLED; + + if (rts_on_send) { + new_rs485conf->conf.flags |= SER_RS485_RTS_ON_SEND; + } else { + new_rs485conf->conf.flags |= SER_RS485_RTS_AFTER_SEND; + } + + if (rx_during_tx) { + new_rs485conf->conf.flags |= SER_RS485_RX_DURING_TX; + } + + new_rs485conf->next = rs485confs; + rs485confs = new_rs485conf; +} + +struct serial_rs485 * +find_rs485conf(const char *name) +{ + struct rs485conf_s *new_rs485conf = rs485confs; + + while (new_rs485conf) { + if (strcmp(name, new_rs485conf->name) == 0) + return &new_rs485conf->conf; + new_rs485conf = new_rs485conf->next; + } + syslog(LOG_ERR, "RS485 configuration %s not found, it will be ignored", name); + return NULL; +} + +static void +free_rs485confs(void) +{ + struct rs485conf_s *rs485conf; + + while (rs485confs) { + rs485conf = rs485confs; + rs485confs = rs485confs->next; + free(rs485conf->name); + free(rs485conf); + } +} + static int startswith(char *str, const char *test, char **strtok_data) { @@ -353,6 +446,21 @@ handle_config_line(char *inbuf) return; } + if (startswith(inbuf, "RS485CONF", &strtok_data)) { + char *name = strtok_r(NULL, ":", &strtok_data); + char *str = strtok_r(NULL, "\n", &strtok_data); + if (name == NULL) { + syslog(LOG_ERR, "No signature given on line %d", lineno); + return; + } + if ((str == NULL) || (strlen(str) == 0)) { + syslog(LOG_ERR, "No RS485 configuration given on line %d", lineno); + return; + } + handle_rs485conf(name, str); + return; + } + if (startswith(inbuf, "OPENSTR", &strtok_data)) { char *name = strtok_r(NULL, ":", &strtok_data); char *str = strtok_r(NULL, "\n", &strtok_data); @@ -452,6 +560,7 @@ readconfig(char *filename) free_longstrs(); free_tracefiles(); + free_rs485confs(); config_num++; diff --git a/ser2net.conf b/ser2net.conf index d8abb11..6de18c3 100644 --- a/ser2net.conf +++ b/ser2net.conf @@ -46,8 +46,9 @@ # setting of the serial port. # The "remctl" option allow remote control (ala RFC # 2217) of serial-port configuration. -# A banner name, signature, open string, and/or close -# string may also be specified. +# A banner name, signature, open string and/or close +# stringmay also be specified. +# The rs485 option takes RS485 related configuration. # The tw, tr, and tb options take a tracefile name ( # specified in TRACEFILE that will take all traced data. # tw is data written to the device, tr is data read from @@ -70,6 +71,10 @@ # This will create a RFC 2217 signature, that will be sent on clients # request. # +# RS485CONF:<RS485 configuration name>:<RTS delay before send>:<RTS delay after send>:<RTS_ON_SEND>:<RX_DURING_TX> +# This will create a RS485 configuration set, i.e. RTS delays before and after send as +# also RTS logical level and receive configuration. +# # TRACEFILE:<name>:filename # This specifies a filename to trace output into, as tw=/tmp/trace1. # This takes the same escape sequences as banners. @@ -101,6 +106,8 @@ BANNER:banner3:this is ser2net TCP port \p device \d serial parms \s\r\n SIGNATURE:signature1:ser2net port ttyS2 +RS485CONF:rs485port1:0:0:0:0 + TRACEFILE:tw1:/tmp/tw-\p-\Y-\M-\D-\H:\i:\s.\U TRACEFILE:tr1:/tmp/tr-\p-\Y-\M-\D-\H:\i:\s.\U @@ -122,7 +129,7 @@ CLOSESTR:close1:close str\r\n 3011:telnet:3:/dev/ttyS0:19200 banner2 #3002:telnet:0:/dev/ttyS1:9600 3003:telnet:0:/dev/ttyS2:9600 banner3 -3003:telnet:0:/dev/ttyS2:9600 signature1 +3003:telnet:0:/dev/ttyS2:9600 signature1 rs485=rs485port1 3004:telnet:0:/dev/ttyS3:115200 3005:telnet:0:/dev/ttyS4:9600 3006:telnet:0:/dev/ttyS5:9600 open1 diff --git a/utils.h b/utils.h index 6a33afb..582ea88 100644 --- a/utils.h +++ b/utils.h @@ -61,6 +61,9 @@ char *find_str(const char *name, enum str_type *type); */ char *find_tracefile(const char *name); +/* Search for RS485 configuration by name. */ +struct serial_rs485 *find_rs485conf(const char *name); + void check_ipv6_only(int family, struct sockaddr *addr, int fd); /* Make sure the full contents get written, return an error if it occurs. */ -- 1.7.7 |