From fa68debd94d40299dd2a69abd0a820ccfaadcbe8 Mon Sep 17 00:00:00 2001 From: Michael Heimpold Date: Wed, 22 Apr 2015 13:35:43 +0200 Subject: [PATCH] Add support for triggering LEDs during serial traffic Signed-off-by: Michael Heimpold --- Patch sent upstream: http://sourceforge.net/p/ser2net/mailman/message/34064847/ Makefile.am | 4 +-- dataxfer.c | 20 ++++++++++++ readconfig.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ser2net.conf | 13 ++++++++ sysfs-led.c | 43 +++++++++++++++++++++++++ sysfs-led.h | 11 +++++++ utils.c | 30 ++++++++++++++++++ utils.h | 9 ++++++ 8 files changed, 228 insertions(+), 2 deletions(-) create mode 100644 sysfs-led.c create mode 100644 sysfs-led.h diff --git a/Makefile.am b/Makefile.am index d56179f..866eb89 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,9 +2,9 @@ sbin_PROGRAMS = ser2net ACLOCAL_AMFLAGS = -I m4 AM_CFLAGS=-Wall ser2net_SOURCES = controller.c dataxfer.c devcfg.c readconfig.c selector.c \ - ser2net.c utils.c telnet.c buffer.c + ser2net.c utils.c telnet.c buffer.c sysfs-led.c noinst_HEADERS = controller.h dataxfer.h devio.h readconfig.h selector.h \ - utils.h telnet.h buffer.h ser2net.h + utils.h telnet.h buffer.h sysfs-led.h ser2net.h man_MANS = ser2net.8 EXTRA_DIST = $(man_MANS) ser2net.conf ser2net.spec ser2net.init diff --git a/dataxfer.c b/dataxfer.c index 646a71b..b99cabf 100644 --- a/dataxfer.c +++ b/dataxfer.c @@ -42,6 +42,7 @@ #include "telnet.h" #include "devio.h" #include "buffer.h" +#include "sysfs-led.h" #define SERIAL "term" #define NET "tcp " @@ -230,6 +231,12 @@ typedef struct port_info #ifdef USE_RS485_FEATURE struct serial_rs485 *rs485conf; #endif + + /* + * LED names to flash for serial traffic + */ + char *led_tx; + char *led_rx; } port_info_t; port_info_t *ports = NULL; /* Linked list of ports. */ @@ -311,6 +318,8 @@ init_port_data(port_info_t *port) #ifdef USE_RS485_FEATURE port->rs485conf = NULL; #endif + port->led_tx = NULL; + port->led_rx = NULL; } static void @@ -530,6 +539,9 @@ handle_dev_fd_read(struct devio *io) /* Do both tracing, ignore errors. */ do_trace(port, port->tb, port->dev_to_tcp.buf, count, SERIAL); + if (port->led_rx) + led_blink_kick(port->led_rx); + port->dev_bytes_received += count; if (port->enabled == PORT_TELNET) { @@ -759,6 +771,8 @@ handle_tcp_fd_read(int fd, void *data) return; } } else { + if (port->led_tx) + led_blink_kick(port->led_tx); port->dev_bytes_sent += count; port->tcp_to_dev.cursize -= count; if (port->tcp_to_dev.cursize != 0) { @@ -1854,6 +1868,12 @@ 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, "led-rx=", 7) == 0) { + /* LED for UART RX traffic */ + port->led_rx = find_led(pos + 7); + } else if (strncmp(pos, "led-tx=", 7) == 0) { + /* LED for UART TX traffic */ + port->led_tx = find_led(pos + 7); #ifdef USE_RS485_FEATURE } else if (strncmp(pos, "rs485=", 6) == 0) { /* get RS485 configuration. */ diff --git a/readconfig.c b/readconfig.c index d4ca0d4..62cff5c 100644 --- a/readconfig.c +++ b/readconfig.c @@ -31,6 +31,7 @@ #include "readconfig.h" #include "utils.h" #include "telnet.h" +#include "sysfs-led.h" #define MAX_LINE_SIZE 256 /* Maximum line length in the config file. */ @@ -361,6 +362,89 @@ free_rs485confs(void) } #endif +struct led_s +{ + char *name; + char *device; + unsigned int duration; + struct led_s *next; +}; + +/* all LEDs in the system. */ +struct led_s *leds = NULL; + +static void +handle_led(char *name, char *cfg) +{ + struct led_s *new_led; + char devicename[256]; + + new_led = malloc(sizeof(*new_led)); + if (!new_led) { + syslog(LOG_ERR, "Out of memory handling LED on %d", lineno); + return; + } + + new_led->name = strdup(name); + if (!new_led->name) { + syslog(LOG_ERR, "Out of memory handling LED on %d", lineno); + free(new_led); + return; + } + + if (sscanf(cfg, "%256s %u", devicename, &new_led->duration) != 2) { + syslog(LOG_ERR, "Couldn't parse LED config on %d", lineno); + free(new_led->name); + free(new_led); + return; + } + + new_led->device = strdup(devicename); + if (!new_led->device) { + syslog(LOG_ERR, "Out of memory handling LED on %d", lineno); + free(new_led->name); + free(new_led); + return; + } + + /* setup the led */ + led_blink_prepare(new_led->device, new_led->duration); + + new_led->next = leds; + leds = new_led; +} + +char * +find_led(const char *name) +{ + struct led_s *led = leds; + + while (led) { + if (strcmp(name, led->name) == 0) + return strdup(led->device); + led = led->next; + } + syslog(LOG_ERR, "LED %s not found, it will be ignored", name); + return NULL; +} + +static void +free_leds(void) +{ + struct led_s *led; + + while (leds) { + led = leds; + leds = leds->next; + + led_off(led->device); + + free(led->name); + free(led->device); + free(led); + } +} + static int startswith(char *str, const char *test, char **strtok_data) { @@ -503,6 +587,21 @@ handle_config_line(char *inbuf) return; } + if (startswith(inbuf, "LED", &strtok_data)) { + char *name = strtok_r(NULL, ":", &strtok_data); + char *str = strtok_r(NULL, "\n", &strtok_data); + if (name == NULL) { + syslog(LOG_ERR, "No LED name given on line %d", lineno); + return; + } + if ((str == NULL) || (strlen(str) == 0)) { + syslog(LOG_ERR, "No LED given on line %d", lineno); + return; + } + handle_led(name, str); + return; + } + comma = strchr(inbuf, ','); if (comma) { if (!strtok_r(comma, ":", &strtok_data)) { @@ -568,6 +667,7 @@ readconfig(char *filename) #ifdef USE_RS485_FEATURE free_rs485confs(); #endif + free_leds(); config_num++; diff --git a/ser2net.conf b/ser2net.conf index 870926c..dc2ba19 100644 --- a/ser2net.conf +++ b/ser2net.conf @@ -53,6 +53,8 @@ # specified in TRACEFILE that will take all traced data. # tw is data written to the device, tr is data read from # the device, and tb is both. +# The "led-tx" and "led-rx" options allow to specify +# a LED defined above to trigger for traffic. # # or... @@ -79,6 +81,12 @@ # This specifies a filename to trace output into, as tw=/tmp/trace1. # This takes the same escape sequences as banners. # +# LED::sysfs-filename duration +# This specifies a LED which will be configured to use linux's transient trigger. +# The LED is always kicked when traffic is detected on serial side. The duration +# is given in milliseconds. See Linux's documentation for transient trigger for +# details. +# # OPENSTR::str # This specifies a string to be transmitted to the device when the # port is opened. This takes the same escape sequences as banners. @@ -108,6 +116,9 @@ SIGNATURE:signature1:ser2net port ttyS2 RS485CONF:rs485port1:0:0:0:0 +LED:rx:duckbill:green:rs485 10 +LED:tx:duckbill:red:rs485 10 + TRACEFILE:tw1:/tmp/tw-\p-\Y-\M-\D-\H:\i:\s.\U TRACEFILE:tr1:/tmp/tr-\p-\Y-\M-\D-\H:\i:\s.\U @@ -138,3 +149,5 @@ CLOSESTR:close1:close str\r\n 3020:telnet:0:/dev/ttyUSB0:9600 banner1 remctl asdfasd 3021:telnet:0:/dev/ttyUSB1:9600 banner2 open1 close1 remctl + +5000:telnet:0:/dev/ttyAPP0:9600 NONE 1STOPBIT 8DATABITS -XONXOFF LOCAL -RTSCTS led-tx=tx led-rx=rx diff --git a/sysfs-led.c b/sysfs-led.c new file mode 100644 index 0000000..efe0b29 --- /dev/null +++ b/sysfs-led.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 I2SE GmbH + */ +#include +#include + +#include "utils.h" +#include "sysfs-led.h" + +#define SYSFS_LED_BASE "/sys/class/leds" + +static int led_write(char *led, char *property, char *buf) +{ + char fn[255]; + + snprintf(fn, sizeof(fn), "%s/%s/%s", SYSFS_LED_BASE, led, property); + + return file_store(fn, buf, strlen(buf)); +} + +int led_off(char *led) +{ + led_write(led, "trigger", "none"); + led_write(led, "brightness", "0"); + return 0; +} + +int led_blink_prepare(char *led, unsigned int duration) +{ + char buffer[10]; + + snprintf(buffer, sizeof(buffer), "%u", duration); + led_write(led, "trigger", "transient"); + msleep(10); + led_write(led, "state", "1"); + led_write(led, "duration", buffer); + return 0; +} + +int led_blink_kick(char *led) +{ + return led_write(led, "activate", "1"); +} diff --git a/sysfs-led.h b/sysfs-led.h new file mode 100644 index 0000000..00b21b6 --- /dev/null +++ b/sysfs-led.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2015 I2SE GmbH + */ +#ifndef SYSFS_LED_H +#define SYSFS_LED_H + +int led_off(char *led); +int led_blink_prepare(char *led, unsigned int duration); +int led_blink_kick(char *led); + +#endif /* SYSFS_LED_H */ diff --git a/utils.c b/utils.c index c194c4c..c96cedb 100644 --- a/utils.c +++ b/utils.c @@ -25,6 +25,9 @@ #include #include #include +#include +#include +#include #include "ser2net.h" #include "utils.h" @@ -205,3 +208,30 @@ write_ignore_fail(int fd, const char *data, size_t count) count -= written; } } + +int +msleep(int msec) +{ + struct timespec req; + + req.tv_sec = 0; + req.tv_nsec = msec * 1000000; + + return nanosleep(&req, NULL); +} + +int +file_store(const char *filename, const char *buf, size_t count) +{ + int fd; + + if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) == -1) + return -1; + + if (write(fd, buf, count) != count) { + close(fd); + return -1; + } + + return close(fd); +} diff --git a/utils.h b/utils.h index 582ea88..8af65ec 100644 --- a/utils.h +++ b/utils.h @@ -64,6 +64,9 @@ char *find_tracefile(const char *name); /* Search for RS485 configuration by name. */ struct serial_rs485 *find_rs485conf(const char *name); +/* Search for a LED by name */ +char *find_led(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. */ @@ -72,4 +75,10 @@ int write_full(int fd, char *data, size_t count); /* Write the data completely out, return without comment on error. */ void write_ignore_fail(int fd, const char *data, size_t count); +/* Helper to sleep a given amount of milli-seconds */ +int msleep(int msec); + +/* Store the given data to a file */ +int file_store(const char *filename, const char *buf, size_t count); + #endif /* UTILS */ -- 1.7.10.4