You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

418 lines
10 KiB

  1. From fa68debd94d40299dd2a69abd0a820ccfaadcbe8 Mon Sep 17 00:00:00 2001
  2. From: Michael Heimpold <michael.heimpold@i2se.com>
  3. Date: Wed, 22 Apr 2015 13:35:43 +0200
  4. Subject: [PATCH] Add support for triggering LEDs during serial traffic
  5. Signed-off-by: Michael Heimpold <michael.heimpold@i2se.com>
  6. ---
  7. Patch sent upstream:
  8. http://sourceforge.net/p/ser2net/mailman/message/34064847/
  9. Makefile.am | 4 +--
  10. dataxfer.c | 20 ++++++++++++
  11. readconfig.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  12. ser2net.conf | 13 ++++++++
  13. sysfs-led.c | 43 +++++++++++++++++++++++++
  14. sysfs-led.h | 11 +++++++
  15. utils.c | 30 ++++++++++++++++++
  16. utils.h | 9 ++++++
  17. 8 files changed, 228 insertions(+), 2 deletions(-)
  18. create mode 100644 sysfs-led.c
  19. create mode 100644 sysfs-led.h
  20. diff --git a/Makefile.am b/Makefile.am
  21. index d56179f..866eb89 100644
  22. --- a/Makefile.am
  23. +++ b/Makefile.am
  24. @@ -2,9 +2,9 @@ sbin_PROGRAMS = ser2net
  25. ACLOCAL_AMFLAGS = -I m4
  26. AM_CFLAGS=-Wall
  27. ser2net_SOURCES = controller.c dataxfer.c devcfg.c readconfig.c selector.c \
  28. - ser2net.c utils.c telnet.c buffer.c
  29. + ser2net.c utils.c telnet.c buffer.c sysfs-led.c
  30. noinst_HEADERS = controller.h dataxfer.h devio.h readconfig.h selector.h \
  31. - utils.h telnet.h buffer.h ser2net.h
  32. + utils.h telnet.h buffer.h sysfs-led.h ser2net.h
  33. man_MANS = ser2net.8
  34. EXTRA_DIST = $(man_MANS) ser2net.conf ser2net.spec ser2net.init
  35. diff --git a/dataxfer.c b/dataxfer.c
  36. index 646a71b..b99cabf 100644
  37. --- a/dataxfer.c
  38. +++ b/dataxfer.c
  39. @@ -42,6 +42,7 @@
  40. #include "telnet.h"
  41. #include "devio.h"
  42. #include "buffer.h"
  43. +#include "sysfs-led.h"
  44. #define SERIAL "term"
  45. #define NET "tcp "
  46. @@ -230,6 +231,12 @@ typedef struct port_info
  47. #ifdef USE_RS485_FEATURE
  48. struct serial_rs485 *rs485conf;
  49. #endif
  50. +
  51. + /*
  52. + * LED names to flash for serial traffic
  53. + */
  54. + char *led_tx;
  55. + char *led_rx;
  56. } port_info_t;
  57. port_info_t *ports = NULL; /* Linked list of ports. */
  58. @@ -311,6 +318,8 @@ init_port_data(port_info_t *port)
  59. #ifdef USE_RS485_FEATURE
  60. port->rs485conf = NULL;
  61. #endif
  62. + port->led_tx = NULL;
  63. + port->led_rx = NULL;
  64. }
  65. static void
  66. @@ -530,6 +539,9 @@ handle_dev_fd_read(struct devio *io)
  67. /* Do both tracing, ignore errors. */
  68. do_trace(port, port->tb, port->dev_to_tcp.buf, count, SERIAL);
  69. + if (port->led_rx)
  70. + led_blink_kick(port->led_rx);
  71. +
  72. port->dev_bytes_received += count;
  73. if (port->enabled == PORT_TELNET) {
  74. @@ -759,6 +771,8 @@ handle_tcp_fd_read(int fd, void *data)
  75. return;
  76. }
  77. } else {
  78. + if (port->led_tx)
  79. + led_blink_kick(port->led_tx);
  80. port->dev_bytes_sent += count;
  81. port->tcp_to_dev.cursize -= count;
  82. if (port->tcp_to_dev.cursize != 0) {
  83. @@ -1854,6 +1868,12 @@ myconfig(void *data, struct absout *eout, const char *pos)
  84. } else if (strncmp(pos, "tb=", 3) == 0) {
  85. /* trace both directions. */
  86. port->trace_both.filename = find_tracefile(pos + 3);
  87. + } else if (strncmp(pos, "led-rx=", 7) == 0) {
  88. + /* LED for UART RX traffic */
  89. + port->led_rx = find_led(pos + 7);
  90. + } else if (strncmp(pos, "led-tx=", 7) == 0) {
  91. + /* LED for UART TX traffic */
  92. + port->led_tx = find_led(pos + 7);
  93. #ifdef USE_RS485_FEATURE
  94. } else if (strncmp(pos, "rs485=", 6) == 0) {
  95. /* get RS485 configuration. */
  96. diff --git a/readconfig.c b/readconfig.c
  97. index d4ca0d4..62cff5c 100644
  98. --- a/readconfig.c
  99. +++ b/readconfig.c
  100. @@ -31,6 +31,7 @@
  101. #include "readconfig.h"
  102. #include "utils.h"
  103. #include "telnet.h"
  104. +#include "sysfs-led.h"
  105. #define MAX_LINE_SIZE 256 /* Maximum line length in the config file. */
  106. @@ -361,6 +362,89 @@ free_rs485confs(void)
  107. }
  108. #endif
  109. +struct led_s
  110. +{
  111. + char *name;
  112. + char *device;
  113. + unsigned int duration;
  114. + struct led_s *next;
  115. +};
  116. +
  117. +/* all LEDs in the system. */
  118. +struct led_s *leds = NULL;
  119. +
  120. +static void
  121. +handle_led(char *name, char *cfg)
  122. +{
  123. + struct led_s *new_led;
  124. + char devicename[256];
  125. +
  126. + new_led = malloc(sizeof(*new_led));
  127. + if (!new_led) {
  128. + syslog(LOG_ERR, "Out of memory handling LED on %d", lineno);
  129. + return;
  130. + }
  131. +
  132. + new_led->name = strdup(name);
  133. + if (!new_led->name) {
  134. + syslog(LOG_ERR, "Out of memory handling LED on %d", lineno);
  135. + free(new_led);
  136. + return;
  137. + }
  138. +
  139. + if (sscanf(cfg, "%256s %u", devicename, &new_led->duration) != 2) {
  140. + syslog(LOG_ERR, "Couldn't parse LED config on %d", lineno);
  141. + free(new_led->name);
  142. + free(new_led);
  143. + return;
  144. + }
  145. +
  146. + new_led->device = strdup(devicename);
  147. + if (!new_led->device) {
  148. + syslog(LOG_ERR, "Out of memory handling LED on %d", lineno);
  149. + free(new_led->name);
  150. + free(new_led);
  151. + return;
  152. + }
  153. +
  154. + /* setup the led */
  155. + led_blink_prepare(new_led->device, new_led->duration);
  156. +
  157. + new_led->next = leds;
  158. + leds = new_led;
  159. +}
  160. +
  161. +char *
  162. +find_led(const char *name)
  163. +{
  164. + struct led_s *led = leds;
  165. +
  166. + while (led) {
  167. + if (strcmp(name, led->name) == 0)
  168. + return strdup(led->device);
  169. + led = led->next;
  170. + }
  171. + syslog(LOG_ERR, "LED %s not found, it will be ignored", name);
  172. + return NULL;
  173. +}
  174. +
  175. +static void
  176. +free_leds(void)
  177. +{
  178. + struct led_s *led;
  179. +
  180. + while (leds) {
  181. + led = leds;
  182. + leds = leds->next;
  183. +
  184. + led_off(led->device);
  185. +
  186. + free(led->name);
  187. + free(led->device);
  188. + free(led);
  189. + }
  190. +}
  191. +
  192. static int
  193. startswith(char *str, const char *test, char **strtok_data)
  194. {
  195. @@ -503,6 +587,21 @@ handle_config_line(char *inbuf)
  196. return;
  197. }
  198. + if (startswith(inbuf, "LED", &strtok_data)) {
  199. + char *name = strtok_r(NULL, ":", &strtok_data);
  200. + char *str = strtok_r(NULL, "\n", &strtok_data);
  201. + if (name == NULL) {
  202. + syslog(LOG_ERR, "No LED name given on line %d", lineno);
  203. + return;
  204. + }
  205. + if ((str == NULL) || (strlen(str) == 0)) {
  206. + syslog(LOG_ERR, "No LED given on line %d", lineno);
  207. + return;
  208. + }
  209. + handle_led(name, str);
  210. + return;
  211. + }
  212. +
  213. comma = strchr(inbuf, ',');
  214. if (comma) {
  215. if (!strtok_r(comma, ":", &strtok_data)) {
  216. @@ -568,6 +667,7 @@ readconfig(char *filename)
  217. #ifdef USE_RS485_FEATURE
  218. free_rs485confs();
  219. #endif
  220. + free_leds();
  221. config_num++;
  222. diff --git a/ser2net.conf b/ser2net.conf
  223. index 870926c..dc2ba19 100644
  224. --- a/ser2net.conf
  225. +++ b/ser2net.conf
  226. @@ -53,6 +53,8 @@
  227. # specified in TRACEFILE that will take all traced data.
  228. # tw is data written to the device, tr is data read from
  229. # the device, and tb is both.
  230. +# The "led-tx" and "led-rx" options allow to specify
  231. +# a LED defined above to trigger for traffic.
  232. #
  233. # or...
  234. @@ -79,6 +81,12 @@
  235. # This specifies a filename to trace output into, as tw=/tmp/trace1.
  236. # This takes the same escape sequences as banners.
  237. #
  238. +# LED:<name>:sysfs-filename duration
  239. +# This specifies a LED which will be configured to use linux's transient trigger.
  240. +# The LED is always kicked when traffic is detected on serial side. The duration
  241. +# is given in milliseconds. See Linux's documentation for transient trigger for
  242. +# details.
  243. +#
  244. # OPENSTR:<name>:str
  245. # This specifies a string to be transmitted to the device when the
  246. # port is opened. This takes the same escape sequences as banners.
  247. @@ -108,6 +116,9 @@ SIGNATURE:signature1:ser2net port ttyS2
  248. RS485CONF:rs485port1:0:0:0:0
  249. +LED:rx:duckbill:green:rs485 10
  250. +LED:tx:duckbill:red:rs485 10
  251. +
  252. TRACEFILE:tw1:/tmp/tw-\p-\Y-\M-\D-\H:\i:\s.\U
  253. TRACEFILE:tr1:/tmp/tr-\p-\Y-\M-\D-\H:\i:\s.\U
  254. @@ -138,3 +149,5 @@ CLOSESTR:close1:close str\r\n
  255. 3020:telnet:0:/dev/ttyUSB0:9600 banner1 remctl asdfasd
  256. 3021:telnet:0:/dev/ttyUSB1:9600 banner2 open1 close1 remctl
  257. +
  258. +5000:telnet:0:/dev/ttyAPP0:9600 NONE 1STOPBIT 8DATABITS -XONXOFF LOCAL -RTSCTS led-tx=tx led-rx=rx
  259. diff --git a/sysfs-led.c b/sysfs-led.c
  260. new file mode 100644
  261. index 0000000..efe0b29
  262. --- /dev/null
  263. +++ b/sysfs-led.c
  264. @@ -0,0 +1,43 @@
  265. +/*
  266. + * Copyright (C) 2015 I2SE GmbH
  267. + */
  268. +#include <stdio.h>
  269. +#include <string.h>
  270. +
  271. +#include "utils.h"
  272. +#include "sysfs-led.h"
  273. +
  274. +#define SYSFS_LED_BASE "/sys/class/leds"
  275. +
  276. +static int led_write(char *led, char *property, char *buf)
  277. +{
  278. + char fn[255];
  279. +
  280. + snprintf(fn, sizeof(fn), "%s/%s/%s", SYSFS_LED_BASE, led, property);
  281. +
  282. + return file_store(fn, buf, strlen(buf));
  283. +}
  284. +
  285. +int led_off(char *led)
  286. +{
  287. + led_write(led, "trigger", "none");
  288. + led_write(led, "brightness", "0");
  289. + return 0;
  290. +}
  291. +
  292. +int led_blink_prepare(char *led, unsigned int duration)
  293. +{
  294. + char buffer[10];
  295. +
  296. + snprintf(buffer, sizeof(buffer), "%u", duration);
  297. + led_write(led, "trigger", "transient");
  298. + msleep(10);
  299. + led_write(led, "state", "1");
  300. + led_write(led, "duration", buffer);
  301. + return 0;
  302. +}
  303. +
  304. +int led_blink_kick(char *led)
  305. +{
  306. + return led_write(led, "activate", "1");
  307. +}
  308. diff --git a/sysfs-led.h b/sysfs-led.h
  309. new file mode 100644
  310. index 0000000..00b21b6
  311. --- /dev/null
  312. +++ b/sysfs-led.h
  313. @@ -0,0 +1,11 @@
  314. +/*
  315. + * Copyright (C) 2015 I2SE GmbH
  316. + */
  317. +#ifndef SYSFS_LED_H
  318. +#define SYSFS_LED_H
  319. +
  320. +int led_off(char *led);
  321. +int led_blink_prepare(char *led, unsigned int duration);
  322. +int led_blink_kick(char *led);
  323. +
  324. +#endif /* SYSFS_LED_H */
  325. diff --git a/utils.c b/utils.c
  326. index c194c4c..c96cedb 100644
  327. --- a/utils.c
  328. +++ b/utils.c
  329. @@ -25,6 +25,9 @@
  330. #include <errno.h>
  331. #include <unistd.h>
  332. #include <fcntl.h>
  333. +#include <time.h>
  334. +#include <sys/types.h>
  335. +#include <sys/stat.h>
  336. #include "ser2net.h"
  337. #include "utils.h"
  338. @@ -205,3 +208,30 @@ write_ignore_fail(int fd, const char *data, size_t count)
  339. count -= written;
  340. }
  341. }
  342. +
  343. +int
  344. +msleep(int msec)
  345. +{
  346. + struct timespec req;
  347. +
  348. + req.tv_sec = 0;
  349. + req.tv_nsec = msec * 1000000;
  350. +
  351. + return nanosleep(&req, NULL);
  352. +}
  353. +
  354. +int
  355. +file_store(const char *filename, const char *buf, size_t count)
  356. +{
  357. + int fd;
  358. +
  359. + if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) == -1)
  360. + return -1;
  361. +
  362. + if (write(fd, buf, count) != count) {
  363. + close(fd);
  364. + return -1;
  365. + }
  366. +
  367. + return close(fd);
  368. +}
  369. diff --git a/utils.h b/utils.h
  370. index 582ea88..8af65ec 100644
  371. --- a/utils.h
  372. +++ b/utils.h
  373. @@ -64,6 +64,9 @@ char *find_tracefile(const char *name);
  374. /* Search for RS485 configuration by name. */
  375. struct serial_rs485 *find_rs485conf(const char *name);
  376. +/* Search for a LED by name */
  377. +char *find_led(const char *name);
  378. +
  379. void check_ipv6_only(int family, struct sockaddr *addr, int fd);
  380. /* Make sure the full contents get written, return an error if it occurs. */
  381. @@ -72,4 +75,10 @@ int write_full(int fd, char *data, size_t count);
  382. /* Write the data completely out, return without comment on error. */
  383. void write_ignore_fail(int fd, const char *data, size_t count);
  384. +/* Helper to sleep a given amount of milli-seconds */
  385. +int msleep(int msec);
  386. +
  387. +/* Store the given data to a file */
  388. +int file_store(const char *filename, const char *buf, size_t count);
  389. +
  390. #endif /* UTILS */
  391. --
  392. 1.7.10.4