summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorroot <root@nolonger-other.tetra.james.local>2020-10-25 13:26:29 +0000
committerroot <root@nolonger-other.tetra.james.local>2020-10-25 13:26:29 +0000
commit62bc1af6c6a1201db551e1ec523e757415464fd5 (patch)
tree49c2eadf8a51e7e43a059f1aff59997c3c169c5e
parent4b4e4972088d82cac99941398834d8e6d661682c (diff)
downloadgalaxy_tools-62bc1af6c6a1201db551e1ec523e757415464fd5.tar.gz
galaxy_tools-62bc1af6c6a1201db551e1ec523e757415464fd5.tar.bz2
galaxy_tools-62bc1af6c6a1201db551e1ec523e757415464fd5.zip
tidy up, make less awful
-rw-r--r--.gitignore6
-rw-r--r--Makefile14
-rw-r--r--arm.c263
-rw-r--r--arm.h6
-rw-r--r--net_arm.c144
-rw-r--r--net_rx.c176
-rw-r--r--rx.c208
-rw-r--r--serial_arm.c147
-rw-r--r--serial_rx.c142
-rw-r--r--sia.c253
-rw-r--r--sia.h66
-rw-r--r--util.c306
-rw-r--r--util.h13
13 files changed, 1374 insertions, 370 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..fa6e66e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+*.o
+serial_rx
+net_rx
+serial_arm
+net_arm
+*.orig
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..3264523
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,14 @@
+CFLAGS=-Wall
+
+all: net_rx net_arm serial_rx serial_arm
+
+net_rx:util.o sia.o net_rx.o
+net_arm:util.o sia.o net_arm.o arm.c
+serial_rx:util.o sia.o serial_rx.o
+serial_arm:util.o sia.o serial_arm.o arm.c
+
+tidy:
+ astyle -A3 -s2 --attach-extern-c -L -c -w -Y -m0 -f -p -H -U -k3 -xj -xd sia.c sia.h util.c util.h serial_rx.c net_rx.c serial_arm.c net_arm.c arm.c
+
+clean:
+ /bin/rm -f *.o
diff --git a/arm.c b/arm.c
index 3e6510b..9f994cc 100644
--- a/arm.c
+++ b/arm.c
@@ -2,233 +2,172 @@
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
-#include <strings.h>
-
+#include <termios.h>
#include <sys/types.h>
#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netdb.h>
-#include <arpa/inet.h>
-
+#include "util.h"
+#include "sia.h"
+#include "arm.h"
-int happy = 0;
-
-int valid_fn (unsigned char fn)
+int auth_cmd_with_ack (int fd, unsigned char fn, const void *data, size_t len, SIA_Block *b)
{
- switch (fn) {
- case 0x30:
- case 0x31:
- case 0x32:
- case 0x33:
- case 0x34:
- case 0x35:
- case 0x36:
- case 0x37:
- case 0x38:
- case 0x08:
- case 0x39:
- case 0x09:
- case 0x43:
- case 0x45:
- case 0x4E:
- case 0x4F:
- case 0x50:
- case 0x40:
- case 0x3F:
- case 0x23:
- case 0x26:
- case 0x41:
- case 0x58:
- case 0x4C:
- case 0x56:
- case 0x76:
- case 0x49:
- return 1;
- }
- return 0;
-}
-
-int ackable (unsigned char fn)
-{
- switch (fn) {
-
-
- case 0x09:
- case 0x39:
- case 0x08:
- case 0x38:
- case 0x40:
- case 0x43:
- case 0x58:
- return 0;
+ if (sia_login (fd, b)) {
+ printf ("Login failed\n");
+ return -1;
}
- return 1;
-}
+ b->reverse_channel = 0;
+ b->request_ack = 1;
+ b->function = fn;
+ memcpy (b->data, data, len);
+ b->length = len;
-void ack (int fd)
-{
- char buf[3];
+ if (fd_drain (fd))
+ return -1;
- buf[0] = 0;
- buf[1] = 0x38;
- buf[2] = 0xff ^ buf[0] ^ buf[1];
+ if (sia_write (fd, b) != 1)
+ return -1;
- write (fd, buf, sizeof (buf));
+ if (sia_rx_with_timeout_ignore_log (fd, b, SIA_TIMEOUT))
+ return -1;
+ return 0;
}
-int rx_pkt (int fd, unsigned char *pkt, unsigned len)
+static int get_thing (int fd, const char *thing, unsigned char *buf)
{
- unsigned char parity = 0xff;
- unsigned i;
+ SIA_Block b = {0};
+ unsigned len = strlen (thing);
- for (i = 0; i < len; ++i)
- parity ^= pkt[i];
+ memset (buf, 0, SIA_MAX_DATA_LENGTH);
- if (parity) return -1;
+ if (auth_cmd_with_ack (fd, SIA_FN_CONTROL, thing, len, &b))
+ return -1;
- happy++;
+ if (b.length < len + 2)
+ return -1;
- for (i = 2; i < len - 1; ++i) {
- if ((pkt[i] > 31) && (pkt[i] < 127))
- putchar (pkt[i]);
- else
- printf ("\\x%02x", pkt[i]);
- }
+ if (memcmp (b.data, thing, len))
+ return -1;
- printf ("\n");
+ if (b.data[len] != '*')
+ return -1;
- if ((pkt[0] & 0x40) && ackable (pkt[1])) ack (fd);
+ memcpy (buf, &b.data[len + 1], b.length - 3);
- return 0;
-}
+ return b.length - 3;
+}
-int rx (int fd)
+static const char *armed_to_str (int i)
{
- static unsigned char pkt[67];
- static unsigned ptr;
- unsigned char byte;
- unsigned len;
-
- if (read (fd, &byte, 1) != 1) {
- ptr = 0;
- return -1;
- }
+ switch (i) {
+ case '0':
+ return "unset";
- pkt[ptr++] = byte;
+ case '1':
+ return "SET";
- if (ptr > 2 && !valid_fn (pkt[1])) {
- ptr = 0;
- return 0;
+ case '2':
+ return "Part SET";
}
- len = pkt[0] & 0x3f;
-
- if (ptr == (len + 3)) {
- ptr = 0;
- return rx_pkt (fd, pkt, len + 3);
- }
-
- return 0;
+ return "?";
}
-int transact (int fd, int ack, unsigned char fn, void *data, unsigned len, int timeout)
+static const char *state_to_str (int i)
{
- struct timeval tv = {0};
- int rc;
- fd_set rfds;
- unsigned char buf[67], parity;
- unsigned i;
-
- happy = 0;
-
- tv.tv_sec = timeout;
-
- buf[0] = len;
-
- if (ack) buf[0] |= 0x40;
+ switch (i) {
+ case '0':
+ return "normal";
- buf[1] = fn;
+ case '1':
+ return "ALARM";
- memcpy (&buf[2], data, len);
-
- parity = 0xff;
-
- for (i = 0; i < (len + 2); ++i)
- parity ^= buf[i];
+ case '2':
+ return "reset req.";
+ }
- buf[i] = parity;
- i++;
+ return "?";
+}
- write (fd, buf, i);
+static const char *ready_to_str (int i)
+{
+ switch (i) {
+ case '0':
+ return "unset";
- fwrite (data, 1, len, stdout);
+ case '1':
+ return "set";
- printf (" => ");
- fflush (stdout);
+ case '2':
+ return "part set";
+ case '3':
+ return "ready to set";
+ case '4':
+ return "time locked";
+ }
- while (!happy) {
+ return "?";
+}
- FD_ZERO (&rfds);
- FD_SET (fd, &rfds);
- rc = select (fd + 1, &rfds, NULL, NULL, &tv);
+int show_state (int fd)
+{
+ int i, l = SIA_MAX_DATA_LENGTH;
+ unsigned char armed[SIA_MAX_DATA_LENGTH];
+ unsigned char state[SIA_MAX_DATA_LENGTH];
+ unsigned char ready[SIA_MAX_DATA_LENGTH];
- if (!rc) break;
- rx (fd);
+ i = get_thing (fd, "SA", armed);
+ if (i < 0) {
+ printf ("Get armed failed\n");
+ return -1;
}
- return happy;
-}
+ if (i < l) l = i;
+ i = get_thing (fd, "SA91", state);
-int main (int argc, char *argv[])
-{
- struct sockaddr_in sin;
- int fd;
-
- unsigned char buf[128];
+ if (i < 0) {
+ printf ("Get state failed\n");
+ return -1;
+ }
- fd = socket (PF_INET, SOCK_STREAM, 0);
+ if (i < l) l = i;
- sin.sin_family = AF_INET;
- sin.sin_port = htons (10005);
- inet_aton ("10.32.52.66", &sin.sin_addr);
+ i = get_thing (fd, "SA92", ready);
- connect (fd, (struct sockaddr *)&sin, sizeof (sin));
+ if (i < 0) {
+ printf ("Get ready failed\n");
+ return -1;
+ }
+ if (i < l) l = i;
+ printf ("%-6s | %-15s | %-15s | %-15s\n", "Zone", "Armed", "State", "Ready");
+ printf ("-------+-----------------+-----------------+----------------\n");
- transact (fd, 1, 0x3f, "543210", 6, 4);
- transact (fd, 1, 0x43, "SA", 2, 4);
+ for (i = 0; i < l; ++i)
+ printf ("%6d | %-15s | %-15s | %-15s\n", i + 1, armed_to_str (armed[i]), state_to_str (state[i]), ready_to_str (ready[i]));
- transact (fd, 1, 0x3f, "543210", 6, 4);
- transact (fd, 1, 0x43, "SA91", 4, 4);
+ printf ("\n");
+ return 0;
+}
- transact (fd, 1, 0x3f, "543210", 6, 4);
- transact (fd, 1, 0x43, "SA92", 4, 4);
- if (argc == 2) {
- sprintf (buf, "SA1*%s", argv[1]);
- transact (fd, 1, 0x3f, "543210", 6, 4);
- transact (fd, 1, 0x43, buf, strlen (buf), 4);
- }
- close (fd);
-}
diff --git a/arm.h b/arm.h
new file mode 100644
index 0000000..3d4de83
--- /dev/null
+++ b/arm.h
@@ -0,0 +1,6 @@
+#ifndef _arm_h_
+#define _arm_h_
+extern int auth_cmd_with_ack(int fd, unsigned char fn, const void *data, size_t len,SIA_Block *b);
+extern int show_state(int fd);
+#endif /* _arm_h_ */
+
diff --git a/net_arm.c b/net_arm.c
new file mode 100644
index 0000000..e2cd6db
--- /dev/null
+++ b/net_arm.c
@@ -0,0 +1,144 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include "util.h"
+#include "sia.h"
+#include "arm.h"
+
+
+
+
+static int usage (const char *name)
+{
+ fprintf (stderr, "Usage:\n");
+ fprintf (stderr, "%s -h host [-p port] [-z zone] [-U|-S|-P|-R|-B|-F|-T|-A|-D]\n", name);
+ fprintf (stderr, "\n");
+ fprintf (stderr, " -U Unset system/zone\n");
+ fprintf (stderr, " -S Set system/zone\n");
+ fprintf (stderr, " -P Part set system/zone\n");
+ fprintf (stderr, " -R Reset system/zone\n");
+ fprintf (stderr, " -B aBort set system/zone\n");
+ fprintf (stderr, " -F Force set system/zone\n");
+ fprintf (stderr, " -T sTate system/zone\n");
+ fprintf (stderr, " -A Alarm system/zone\n");
+ fprintf (stderr, " -D reaDy system/zone\n");
+
+
+ return -1;
+}
+
+
+int main (int argc, char *argv[])
+{
+ SIA_Block b;
+ char cmd[SIA_MAX_DATA_LENGTH];
+ unsigned opt;
+ int zone = -1;
+ int action = -1;
+ const char *host = NULL;
+ unsigned port = 10005;
+ int fd;
+
+ while ((opt = getopt (argc, argv, "h:p:z:USPRBFTAD")) != -1) {
+ switch (opt) {
+ case 'h':
+ host = optarg;
+ break;
+
+ case 'p':
+ port = atoi (optarg);
+ break;
+
+ case 'z':
+ zone = atoi (optarg);
+ break;
+
+ case 'U':
+ action = 0;
+ break;
+
+ case 'S':
+ action = 1;
+ break;
+
+ case 'P':
+ action = 2;
+ break;
+
+ case 'R':
+ action = 3;
+ break;
+
+ case 'B':
+ action = 4;
+ break;
+
+ case 'F':
+ action = 5;
+ break;
+
+ case 'T':
+ action = 6;
+ break;
+
+ case 'A':
+ action = 7;
+ break;
+
+ case 'D':
+ action = 8;
+ break;
+
+ default: /* '?' */
+ return usage (argv[0]);
+ }
+ }
+
+ if (!host) return (usage (argv[0]));
+
+ fd = open_tcp_client (host, port);
+
+ if (fd < 0) {
+ perror ("open tcp port");
+ return -1;
+ }
+
+
+
+ if (action == -1) {
+ if (show_state (fd))
+ return -1;
+
+ return 0;
+ }
+
+
+ if (zone != -1)
+ sprintf (cmd, "SA%d*%d", zone, action);
+ else
+ sprintf (cmd, "SA*%d", action);
+
+
+ if (auth_cmd_with_ack (fd, SIA_FN_CONTROL, cmd, strlen (cmd), &b)) {
+ printf ("Command failed\n");
+ return -1;
+ }
+
+ switch (b.function) {
+ case SIA_FN_ACKNOLEDGE:
+ printf ("Panel acks command\n");
+ return 0;
+
+ case SIA_FN_REJECT:
+ printf ("Panel nacks command\n");
+ /*fall through */
+ }
+
+ return -1;
+}
+
+
diff --git a/net_rx.c b/net_rx.c
new file mode 100644
index 0000000..a3d95e4
--- /dev/null
+++ b/net_rx.c
@@ -0,0 +1,176 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "util.h"
+#include "sia.h"
+
+
+
+
+
+void msg (char *account, char *event, char *ascii)
+{
+ printf ("%s %64s %s\n", account, event, ascii);
+}
+
+
+static int new_block (int fd, SIA_Block *b)
+{
+ static int have_ascii_messages = 0; /*SIA level 3 doesn't have ascii, SIA level 4 does */
+
+ static char account[SIA_MAX_DATA_LENGTH + 1];
+ static char event[SIA_MAX_DATA_LENGTH + 1];
+ static char ascii[SIA_MAX_DATA_LENGTH + 1];
+
+ unsigned len = sia_data_length (b);
+
+ if (sia_ack_if_needed (fd, b))
+ return -1;
+
+ switch (b->function) {
+ case SIA_FN_ACCOUNT_ID:
+ memcpy (account, b->data, len);
+ account[len] = 0;
+ break;
+
+ case SIA_FN_NEW_EVENT:
+ case SIA_FN_OLD_EVENT:
+ memcpy (event, b->data, len);
+ event[len] = 0;
+
+ if (!have_ascii_messages) msg (account, event, "");
+
+ break;
+
+ case SIA_FN_ASCII:
+ have_ascii_messages = 1;
+ memcpy (ascii, b->data, len);
+ ascii[len] = 0;
+
+ msg (account, event, ascii);
+ break;
+ }
+
+ return 0;
+}
+
+static void periodic_task (void)
+{
+}
+
+static int usage (const char *name)
+{
+ fprintf (stderr, "Usage:\n");
+ fprintf (stderr, "%s [ -p listen_port ]\n\n", name);
+ fprintf (stderr, "listen_port defaults to 10002\n");
+
+ return 1;
+}
+
+
+#define TICK 5
+
+int main (int argc, char *argv[])
+{
+ int port = 10002;
+ unsigned opt;
+ int fd;
+ int afd = -1;
+ SIA_Block b;
+ struct timeval tv = {0};
+
+ unsigned char buf[SIA_MAX_BLOCK_LENGTH];
+ unsigned ptr = 0;
+
+
+ while ((opt = getopt (argc, argv, "p:")) != -1) {
+ switch (opt) {
+ case 'p':
+ port = atoi (optarg);
+ break;
+
+ default: /* '?' */
+ return usage (argv[0]);
+ }
+ }
+
+ fd = open_tcp_server (port);
+
+ if (fd < 0) {
+ perror ("open socket");
+ return -1;
+ }
+
+ set_blocking (fd);
+
+
+ // Epically budget TCP server
+
+ tv.tv_sec = TICK;
+
+ for (;;) {
+ fd_set rfds;
+ int rc;
+
+ FD_ZERO (&rfds);
+ FD_SET (fd, &rfds);
+
+ if (afd > 0)
+ FD_SET (afd, &rfds);
+
+ rc = select (afd > fd ? afd + 1 : fd + 1, &rfds, NULL, NULL, &tv);
+
+ if (!rc) periodic_task();
+
+ if (!tv.tv_sec && !tv.tv_usec)
+ tv.tv_sec = TICK;
+
+ if (FD_ISSET (fd, &rfds)) {
+ if (afd > 0) close (afd);
+
+ afd = accept (fd, NULL, NULL);
+ ptr = 0;
+ }
+
+ if ((afd > 0) && (FD_ISSET (afd, &rfds))) {
+
+ if (read (afd, &buf[ptr], 1) != 1) {
+ close (afd);
+ afd = -1;
+ ptr = 0;
+ continue;
+ }
+
+ ptr++;
+
+
+ switch (sia_parse (buf, ptr, &b)) {
+ case -1: /*Parse errror, form here is top drop connexion*/
+ close (afd);
+ afd = -1;
+ break;
+
+ case 0: /*More data needed*/
+ break;
+
+ default: /*Valid block */
+ new_block (afd, &b);
+ ptr = 0;
+ break;
+ }
+
+ if (ptr == SIA_MAX_BLOCK_LENGTH) {
+ close (afd);
+ afd = -1;
+ }
+
+ }
+
+ }
+}
+
diff --git a/rx.c b/rx.c
deleted file mode 100644
index a02c964..0000000
--- a/rx.c
+++ /dev/null
@@ -1,208 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netdb.h>
-
-
-void poll (int fd)
-{
-}
-
-int valid_fn (unsigned char fn)
-{
- switch (fn) {
- case 0x30:
- case 0x31:
- case 0x32:
- case 0x33:
- case 0x34:
- case 0x35:
- case 0x36:
- case 0x37:
- case 0x38:
- case 0x08:
- case 0x39:
- case 0x09:
- case 0x43:
- case 0x45:
- case 0x4E:
- case 0x4F:
- case 0x50:
- case 0x40:
- case 0x3F:
- case 0x23:
- case 0x26:
- case 0x41:
- case 0x58:
- case 0x4C:
- case 0x56:
- case 0x76:
- case 0x49:
- return 1;
- }
-
- return 0;
-}
-
-
-void ack (int fd)
-{
- char buf[3];
-
- buf[0] = 0;
- buf[1] = 0x38;
- buf[2] = 0xff ^ buf[0] ^ buf[1];
-
- write (fd, buf, sizeof (buf));
-
-}
-
-
-void msg (char *account, char *event, char *ascii)
-{
- printf ("%s %64s %s\n", account, event, ascii);
-}
-
-
-int rx_pkt (int fd, unsigned char *pkt, unsigned len)
-{
- unsigned char parity = 0xff;
- unsigned i;
-
- static char account[65];
- static char event[65];
- static char ascii[65];
-
-
-
- for (i = 0; i < len; ++i)
- parity ^= pkt[i];
-
- if (parity) return -1;
-
- len--;
-
-
- switch (pkt[1]) {
- case 0x23:
- memcpy (account, &pkt[2], len - 2);
- account[len - 2] = 0;
- break;
-
- case 0x4e:
- case 0x4f:
- memcpy (event, &pkt[2], len - 2);
- event[len - 2] = 0;
- break;
-
- case 0x41:
- memcpy (ascii, &pkt[2], len - 2);
- ascii[len - 2] = 0;
-
- msg (account, event, ascii);
- break;
- }
-
-
- if (pkt[0] & 0x40) ack (fd);
-
- return 0;
-}
-
-
-
-int rx (int fd)
-{
- static unsigned char pkt[67];
- static unsigned ptr;
- unsigned char byte;
- unsigned len;
-
- if (read (fd, &byte, 1) != 1) {
- ptr = 0;
- return -1;
- }
-
- pkt[ptr++] = byte;
-
- if (ptr > 2 && !valid_fn (pkt[1])) {
- ptr = 0;
- return 0;
- }
-
- len = pkt[0] & 0x3f;
-
- if (ptr == (len + 3)) {
- ptr = 0;
- return rx_pkt (fd, pkt, len + 3);
- }
-
- return 0;
-}
-
-
-int main (int argc, char *argv)
-{
- struct sockaddr_in sin;
- struct timeval tv = {0};
- int rc;
- int fd;
- int afd = -1;
- fd_set rfds;
- fd_set wfds;
-
-
- fd = socket (PF_INET, SOCK_STREAM, 0);
-
- sin.sin_family = AF_INET;
- sin.sin_port = htons (10002);
- sin.sin_addr.s_addr = INADDR_ANY;
-
- bind (fd, (struct sockaddr *)&sin, sizeof (sin));
-
- listen (fd, 5);
-
- tv.tv_sec = 5;
-
- FD_ZERO (&wfds);
-
- for (;;) {
-
- FD_ZERO (&rfds);
-
- FD_SET (fd, &rfds);
-
- if (afd > 0)
- FD_SET (afd, &rfds);
-
- rc = select (afd > fd ? afd + 1 : fd + 1, &rfds, &wfds, NULL, &tv);
-
- if ((!rc) && (afd > 0)) poll (afd);
-
-
- if (FD_ISSET (fd, &rfds)) {
- if (afd > 0) close (afd);
-
- afd = accept (fd, NULL, NULL);
- }
-
- if ((afd > 0) && (FD_ISSET (afd, &rfds))) {
- if (rx (afd)) {
- close (afd);
- afd = -1;
- }
- }
-
- if (!tv.tv_sec && !tv.tv_usec)
- tv.tv_sec = 5;
-
-
-
-
- }
-}
-
diff --git a/serial_arm.c b/serial_arm.c
new file mode 100644
index 0000000..d1a5149
--- /dev/null
+++ b/serial_arm.c
@@ -0,0 +1,147 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include "util.h"
+#include "sia.h"
+#include "arm.h"
+
+
+
+
+static int usage (const char *name)
+{
+ fprintf (stderr, "Usage:\n");
+ fprintf (stderr, "%s -h host [-p port] [-z zone] [-U|-S|-P|-R|-B|-F|-T|-A|-D]\n", name);
+ fprintf (stderr, "\n");
+ fprintf (stderr, " -U Unset system/zone\n");
+ fprintf (stderr, " -S Set system/zone\n");
+ fprintf (stderr, " -P Part set system/zone\n");
+ fprintf (stderr, " -R Reset system/zone\n");
+ fprintf (stderr, " -B aBort set system/zone\n");
+ fprintf (stderr, " -F Force set system/zone\n");
+ fprintf (stderr, " -T sTate system/zone\n");
+ fprintf (stderr, " -A Alarm system/zone\n");
+ fprintf (stderr, " -D reaDy system/zone\n");
+
+
+ return -1;
+}
+
+
+int main (int argc, char *argv[])
+{
+ SIA_Block b;
+ char cmd[SIA_MAX_DATA_LENGTH];
+ unsigned opt;
+ int zone = -1;
+ int action = -1;
+ const char *port = NULL;
+ unsigned baud = 9600;
+ int fd;
+
+ while ((opt = getopt (argc, argv, "h:p:z:USPRBFTAD")) != -1) {
+ switch (opt) {
+ case 'p':
+ port = optarg;
+ break;
+
+ case 'b':
+ baud = atoi (optarg);
+ break;
+
+ case 'z':
+ zone = atoi (optarg);
+ break;
+
+ case 'U':
+ action = 0;
+ break;
+
+ case 'S':
+ action = 1;
+ break;
+
+ case 'P':
+ action = 2;
+ break;
+
+ case 'R':
+ action = 3;
+ break;
+
+ case 'B':
+ action = 4;
+ break;
+
+ case 'F':
+ action = 5;
+ break;
+
+ case 'T':
+ action = 6;
+ break;
+
+ case 'A':
+ action = 7;
+ break;
+
+ case 'D':
+ action = 8;
+ break;
+
+ default: /* '?' */
+ return usage (argv[0]);
+ }
+ }
+
+ if (!port) return (usage (argv[0]));
+
+ fd = open_tty (port, baud);
+
+ set_blocking (fd);
+
+
+ if (fd < 0) {
+ perror ("open tcp port");
+ return -1;
+ }
+
+
+
+ if (action == -1) {
+ if (show_state (fd))
+ return -1;
+
+ return 0;
+ }
+
+
+ if (zone != -1)
+ sprintf (cmd, "SA%d*%d", zone, action);
+ else
+ sprintf (cmd, "SA*%d", action);
+
+
+ if (auth_cmd_with_ack (fd, SIA_FN_CONTROL, cmd, strlen (cmd), &b)) {
+ printf ("Command failed\n");
+ return -1;
+ }
+
+ switch (b.function) {
+ case SIA_FN_ACKNOLEDGE:
+ printf ("Panel acks command\n");
+ return 0;
+
+ case SIA_FN_REJECT:
+ printf ("Panel nacks command\n");
+ /*fall through */
+ }
+
+ return -1;
+}
+
+
diff --git a/serial_rx.c b/serial_rx.c
new file mode 100644
index 0000000..d481417
--- /dev/null
+++ b/serial_rx.c
@@ -0,0 +1,142 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+
+#include "util.h"
+#include "sia.h"
+
+
+
+
+
+void msg (char *account, char *event, char *ascii)
+{
+ printf ("%s %64s %s\n", account, event, ascii);
+}
+
+
+static int new_block (int fd, SIA_Block *b)
+{
+ static int have_ascii_messages = 0; /*SIA level 3 doesn't have ascii, SIA level 4 does */
+
+ static char account[SIA_MAX_DATA_LENGTH + 1];
+ static char event[SIA_MAX_DATA_LENGTH + 1];
+ static char ascii[SIA_MAX_DATA_LENGTH + 1];
+
+ unsigned len = sia_data_length (b);
+
+ if (sia_ack_if_needed (fd, b))
+ return -1;
+
+ switch (b->function) {
+ case SIA_FN_ACCOUNT_ID:
+ memcpy (account, b->data, len);
+ account[len] = 0;
+ break;
+
+ case SIA_FN_NEW_EVENT:
+ case SIA_FN_OLD_EVENT:
+ memcpy (event, b->data, len);
+ event[len] = 0;
+
+ if (!have_ascii_messages) msg (account, event, "");
+
+ break;
+
+ case SIA_FN_ASCII:
+ have_ascii_messages = 1;
+ memcpy (ascii, b->data, len);
+ ascii[len] = 0;
+
+ msg (account, event, ascii);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int usage (const char *name)
+{
+ fprintf (stderr, "Usage:\n");
+ fprintf (stderr, "%s [ -b baud ] -p serial_device\n\n", name);
+ fprintf (stderr, "baud defaults to 9600\n");
+
+ return 1;
+}
+
+
+int main (int argc, char *argv[])
+{
+ const char *port;
+ int baud = 9600;
+ unsigned opt;
+ int fd;
+ SIA_Block b;
+ ssize_t red;
+
+ unsigned char buf[SIA_MAX_BLOCK_LENGTH];
+ unsigned ptr = 0;
+
+
+ while ((opt = getopt (argc, argv, "p:b:")) != -1) {
+ switch (opt) {
+ case 'p':
+ port = optarg;
+ break;
+
+ case 'b':
+ baud = atoi (optarg);
+ break;
+
+ default: /* '?' */
+ return usage (argv[0]);
+ }
+ }
+
+
+
+ if (!port)
+ return usage (argv[0]);
+
+ fd = open_tty (port, baud);
+
+ set_blocking (fd);
+
+ for (;;) {
+ red = read (fd, &buf[ptr], 1);
+
+ if ((red < 0) || (red > 1)) {
+ perror ("read");
+ return 1;
+ }
+
+ if (!red) continue;
+
+ ptr++;
+
+ switch (sia_parse (buf, ptr, &b)) {
+ case -1: /*Parse errror, flush and carry on */
+ tcflush (fd, TCIFLUSH);
+ ptr = 0;
+ break;
+
+ case 0: /*More data needed*/
+ break;
+
+ default: /*Valid block */
+ new_block (fd, &b);
+ ptr = 0;
+ break;
+ }
+
+ if (ptr == SIA_MAX_BLOCK_LENGTH) {
+ tcflush (fd, TCIFLUSH);
+ ptr = 0;
+ }
+
+ }
+
+}
diff --git a/sia.c b/sia.c
new file mode 100644
index 0000000..3c47c8b
--- /dev/null
+++ b/sia.c
@@ -0,0 +1,253 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include "util.h"
+
+
+#include "sia.h"
+
+
+
+int sia_fn_valid (SIA_Block *s)
+{
+ switch (s->function) {
+
+ case SIA_FN_ACCOUNT_ID:
+ case SIA_FN_ORIGIN_ID:
+ case SIA_FN_END_OF_DATA:
+ case SIA_FN_WAIT:
+ case SIA_FN_ABORT:
+ case SIA_FN_RES_3:
+ case SIA_FN_RES_4:
+ case SIA_FN_RES_5:
+ case SIA_FN_ACK_AND_STANDBY:
+ case SIA_FN_ACK_AND_DISCONNECT:
+ case SIA_FN_ACKNOLEDGE:
+ case SIA_FN_ALT_ACKNOLEDGE:
+ case SIA_FN_REJECT:
+ case SIA_FN_ALT_REJECT:
+ case SIA_FN_REMOTE_LOGIN:
+ case SIA_FN_CONFIGURATION:
+ case SIA_FN_ASCII:
+ case SIA_FN_CONTROL:
+ case SIA_FN_ENVIRONMENTAL:
+ case SIA_FN_VIDEO:
+ case SIA_FN_LISTEN_IN:
+ case SIA_FN_NEW_EVENT:
+ case SIA_FN_OLD_EVENT:
+ case SIA_FN_PROGRAM:
+ case SIA_FN_VCHN_REQUEST:
+ case SIA_FN_EXTENDED:
+ case SIA_FN_VCHN_FRAME:
+ return 1;
+ }
+
+ return 0;
+}
+
+int sia_fn_permits_ack (unsigned char fn)
+{
+ switch (fn) {
+ case SIA_FN_ALT_REJECT:
+ case SIA_FN_REJECT:
+ case SIA_FN_ALT_ACKNOLEDGE:
+ case SIA_FN_ACKNOLEDGE:
+ case SIA_FN_CONFIGURATION:
+ case SIA_FN_CONTROL:
+ case SIA_FN_EXTENDED:
+ return 0;
+ }
+
+ return 1;
+}
+
+
+unsigned int sia_data_length (SIA_Block *s)
+{
+ return s->length;
+}
+
+unsigned int sia_length (SIA_Block *s)
+{
+ return sia_data_length (s) + 3;
+}
+
+int sia_parity_valid (SIA_Block *s)
+{
+ unsigned char parity = 0xff;
+
+ unsigned i, l = sia_length (s);
+
+ for (i = 0; i < l; ++i)
+ parity ^= s->block[i];
+
+
+ return !parity;
+}
+
+
+void sia_set_parity (SIA_Block *s)
+{
+ unsigned char parity = 0xff;
+ unsigned i, l = sia_length (s) - 1;
+
+ for (i = 0; i < l; ++i)
+ parity ^= s->block[i];
+
+ s->block[l] = parity;
+}
+
+
+ssize_t sia_write (int fd, SIA_Block *s)
+{
+ ssize_t rc, l = sia_length (s);
+
+ sia_set_parity (s);
+
+ rc = write (fd, s->block, l);
+
+ if (rc == l) return 1;
+
+ if (rc > 0) rc = 0;
+
+ return rc;
+}
+
+
+ssize_t sia_acknoledge (int fd)
+{
+ SIA_Block b;
+
+ b.length = 0;
+ b.request_ack = 0;
+ b.reverse_channel = 0;
+ b.function = SIA_FN_ACKNOLEDGE;
+
+ return sia_write (fd, &b);
+}
+
+/* Zero on success, 1 on error */
+int sia_ack_if_needed (int fd, SIA_Block *s)
+{
+ if (!s->request_ack) return 0;
+
+ if (!sia_fn_permits_ack (s->function))
+ return 0;
+
+ return ! (sia_acknoledge (fd) == 1);
+}
+
+
+
+/* Returns 0 for need more data, number of bytes consumed for success, -1 for parse error */
+/* On tcp/ip the form appears to be to drop the connexion on parse error*/
+/* On serial drain the buffer as comms is bursty */
+
+int sia_parse (unsigned char *buf, unsigned len, SIA_Block *b)
+{
+ unsigned calc_len;
+
+ if (len < 2) return 0;
+
+ if (len > SIA_MAX_BLOCK_LENGTH) len = SIA_MAX_BLOCK_LENGTH;
+
+ memcpy (b->block, buf, len);
+
+ if (!sia_fn_valid (b)) return -1;
+
+ calc_len = sia_length (b);
+
+ if (len < calc_len) return 0;
+
+
+ if (!sia_parity_valid (b)) return -1;
+
+ return calc_len;
+}
+
+
+/*Returns 0 for success, -1 for error */
+int sia_rx_with_timeout (int fd, SIA_Block *b, int timeout)
+{
+ unsigned char buf[SIA_MAX_BLOCK_LENGTH];
+ unsigned ptr = 0;
+ int rc;
+
+ struct timeval tv = {0};
+
+ tv.tv_sec = timeout;
+
+ while (tv.tv_sec || tv.tv_usec) {
+ fd_set rfds;
+ FD_ZERO (&rfds);
+ FD_SET (fd, &rfds);
+
+ rc = select (fd + 1, &rfds, NULL, NULL, &tv);
+
+ if (rc <= 0) return -1;
+
+ rc = read (fd, &buf[ptr], 1);
+
+ if (rc <= 0)
+ return -1;
+
+ ptr++;
+
+ switch (sia_parse (buf, ptr, b)) {
+ case -1:
+ return -1;
+
+ case 0:
+ break;
+
+ default:
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+int sia_rx_with_timeout_ignore_log (int fd, SIA_Block *b, int timeout)
+{
+ int ret;
+
+ do {
+ ret = sia_rx_with_timeout (fd, b, timeout);
+
+ if (ret < 0) return ret;
+ } while ((b->function == SIA_FN_ACCOUNT_ID) || (b->function == SIA_FN_NEW_EVENT) || (b->function == SIA_FN_OLD_EVENT) || (b->function == SIA_FN_ASCII));
+
+ return ret;
+}
+
+
+int sia_login (int fd, SIA_Block *b)
+{
+ const char *pin = "543210"; //JMM: Oh yes! and there's no way to change it from the panel, you need the £3k bit of software to do that
+
+ b->reverse_channel = 0;
+ b->request_ack = 1;
+ b->function = SIA_FN_REMOTE_LOGIN;
+ b->length = strlen (pin);
+ memcpy (b->data, pin, b->length);
+
+ if (fd_drain (fd))
+ return -1;
+
+ if (sia_write (fd, b) != 1)
+ return -1;
+
+ if (sia_rx_with_timeout_ignore_log (fd, b, SIA_TIMEOUT))
+ return -1;
+
+
+ if (b->function != SIA_FN_CONFIGURATION)
+ return -1;
+
+ return 0;
+}
+
+
diff --git a/sia.h b/sia.h
new file mode 100644
index 0000000..71a8c9d
--- /dev/null
+++ b/sia.h
@@ -0,0 +1,66 @@
+#ifndef _sia_h_
+#define _sia_h_
+
+#define SIA_TIMEOUT 3 // standard says 2.5s
+
+#define SIA_FN_ACCOUNT_ID 0x23
+#define SIA_FN_ORIGIN_ID 0x26
+#define SIA_FN_END_OF_DATA 0x30
+#define SIA_FN_WAIT 0x31
+#define SIA_FN_ABORT 0x32
+#define SIA_FN_RES_3 0x33
+#define SIA_FN_RES_4 0x34
+#define SIA_FN_RES_5 0x35
+#define SIA_FN_ACK_AND_STANDBY 0x36
+#define SIA_FN_ACK_AND_DISCONNECT 0x37
+#define SIA_FN_ACKNOLEDGE 0x38
+#define SIA_FN_ALT_ACKNOLEDGE 0x08 //JMM I can't imagine how someone made that mistake :)
+#define SIA_FN_REJECT 0x39
+#define SIA_FN_ALT_REJECT 0x09
+#define SIA_FN_REMOTE_LOGIN 0x3f
+#define SIA_FN_CONFIGURATION 0x40
+#define SIA_FN_ASCII 0x41
+#define SIA_FN_CONTROL 0x43
+#define SIA_FN_ENVIRONMENTAL 0x45
+#define SIA_FN_VIDEO 0x49
+#define SIA_FN_LISTEN_IN 0x4c
+#define SIA_FN_NEW_EVENT 0x4e
+#define SIA_FN_OLD_EVENT 0x4f
+#define SIA_FN_PROGRAM 0x50
+#define SIA_FN_VCHN_REQUEST 0x56
+#define SIA_FN_EXTENDED 0x58
+#define SIA_FN_VCHN_FRAME 0x76
+
+
+#define SIA_MAX_DATA_LENGTH 0x3f
+#define SIA_MAX_BLOCK_LENGTH (2 + SIA_MAX_DATA_LENGTH + 1)
+
+typedef union {
+ unsigned char block[SIA_MAX_BLOCK_LENGTH];
+ struct {
+ unsigned char length : 6;
+ unsigned char request_ack: 1;
+ unsigned char reverse_channel: 1;
+ unsigned char function;
+ unsigned char data[];
+ };
+} SIA_Block;
+
+
+/* sia.c */
+extern int sia_fn_valid (SIA_Block *s);
+extern int sia_fn_permits_ack (unsigned char fn);
+extern unsigned int sia_data_length (SIA_Block *s);
+extern unsigned int sia_length (SIA_Block *s);
+extern int sia_parity_valid (SIA_Block *s);
+extern void sia_set_parity (SIA_Block *s);
+extern ssize_t sia_write (int fd, SIA_Block *s);
+extern ssize_t sia_acknoledge (int fd);
+extern int sia_ack_if_needed (int fd, SIA_Block *s);
+extern int sia_parse (unsigned char *buf, unsigned len, SIA_Block *b);
+extern int sia_rx_with_timeout (int fd, SIA_Block *b, int timeout);
+extern int sia_rx_with_timeout_ignore_log (int fd, SIA_Block *b, int timeout);
+extern int sia_login (int fd, SIA_Block *b);
+
+
+#endif /* _sia_h_ */
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..95268b4
--- /dev/null
+++ b/util.c
@@ -0,0 +1,306 @@
+#include <string.h>
+#include <unistd.h>
+#include <termio.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+
+
+#include "util.h"
+
+
+void
+set_nonblocking (int fd)
+{
+ long arg = 0;
+ arg = fcntl (fd, F_GETFL, arg);
+ arg |= O_NONBLOCK;
+ fcntl (fd, F_SETFL, arg);
+}
+
+void
+set_blocking (int fd)
+{
+ long arg = 0;
+ arg = fcntl (fd, F_GETFL, arg);
+ arg &= ~O_NONBLOCK;
+ fcntl (fd, F_SETFL, arg);
+}
+
+int fd_can_read (int fd)
+{
+ struct timeval tv = {0};
+ fd_set rfds;
+ FD_ZERO (&rfds);
+ FD_SET (fd, &rfds);
+
+ if (select (fd + 1, &rfds, NULL, NULL, &tv) == 1)
+ return 1;
+
+ return 0;
+}
+
+int fd_drain (int fd)
+{
+ char c;
+
+ while (fd_can_read (fd))
+ if (read (fd, &c, 1) != 1) return -1;
+
+
+ return 0;
+}
+
+
+static speed_t
+baud_to_speed_t (int baud)
+{
+ switch (baud) {
+#ifdef B0
+
+ case 0:
+ return B0;
+#endif
+#ifdef B50
+
+ case 50:
+ return B50;
+#endif
+#ifdef B75
+
+ case 75:
+ return B75;
+#endif
+#ifdef B110
+
+ case 110:
+ return B110;
+#endif
+#ifdef B134
+
+ case 134:
+ return B134;
+#endif
+#ifdef B150
+
+ case 150:
+ return B150;
+#endif
+#ifdef B200
+
+ case 200:
+ return B200;
+#endif
+#ifdef B300
+
+ case 300:
+ return B300;
+#endif
+#ifdef B600
+
+ case 600:
+ return B600;
+#endif
+#ifdef B1200
+
+ case 1200:
+ return B1200;
+#endif
+#ifdef B1800
+
+ case 1800:
+ return B1800;
+#endif
+#ifdef B2400
+
+ case 2400:
+ return B2400;
+#endif
+#ifdef B4800
+
+ case 4800:
+ return B4800;
+#endif
+#ifdef B9600
+
+ case 9600:
+ return B9600;
+#endif
+#ifdef B19200
+
+ case 19200:
+ return B19200;
+#endif
+#ifdef B38400
+
+ case 38400:
+ return B38400;
+#endif
+#ifdef B57600
+
+ case 57600:
+ return B57600;
+#endif
+#ifdef B115200
+
+ case 115200:
+ return B115200;
+#endif
+#ifdef B230400
+
+ case 230400:
+ return B230400;
+#endif
+ }
+
+ return -1;
+}
+
+
+
+static void
+default_termios (struct termios *termios)
+{
+ termios->c_iflag = 0;
+ termios->c_oflag = NL0 | CR0 | TAB0 | BS0 | VT0 | FF0;
+ termios->c_lflag = 0;
+ termios->c_cflag = CS8 | CREAD | CLOCAL;
+
+ termios->c_cc[VINTR] = 003;
+ termios->c_cc[VQUIT] = 034;
+ termios->c_cc[VERASE] = 0177;
+ termios->c_cc[VKILL] = 025;
+ termios->c_cc[VEOF] = 004;
+ termios->c_cc[VEOL] = 0;
+ termios->c_cc[VEOL2] = 0;
+ termios->c_cc[VSTART] = 021;
+ termios->c_cc[VSTOP] = 023;
+ termios->c_cc[VSUSP] = 032;
+ termios->c_cc[VLNEXT] = 026;
+ termios->c_cc[VWERASE] = 027;
+ termios->c_cc[VREPRINT] = 022;
+ termios->c_cc[VDISCARD] = 017;
+
+
+}
+
+
+//XXX: no locks
+
+int open_tty (const char *path, int baud)
+{
+ int fd;
+ struct termios termios;
+ speed_t s = baud_to_speed_t (baud);
+
+ if (s == (speed_t) -1) return -1;
+
+
+ fd = open (path, O_RDWR | O_NOCTTY | O_NONBLOCK);
+
+
+ if (tcgetattr (fd, &termios)) {
+ close (fd);
+ return -1;
+ }
+
+ default_termios (&termios);
+
+ if (tcsetattr (fd, TCSANOW, &termios)) {
+ close (fd);
+ return -1;
+ }
+
+ cfsetispeed (&termios, s);
+ cfsetospeed (&termios, s);
+
+ return fd;
+}
+
+
+
+static int host_to_sin (const char *host, struct sockaddr_in *sin)
+{
+
+ struct hostent *he = gethostbyname2 (host, AF_INET);
+
+ if (!he) {
+ if (!inet_aton (host, &sin->sin_addr))
+ return -1;
+
+ return 0;
+ }
+
+ if (he->h_addrtype != AF_INET)
+ return -1;
+
+ sin->sin_family = AF_INET;
+
+ memcpy (&sin->sin_addr, he->h_addr_list[0], he->h_length);
+
+ return 0;
+}
+
+
+int open_tcp_client (const char *host, unsigned port)
+{
+ struct sockaddr_in sin = {0};
+ int fd;
+
+
+ if (host_to_sin (host, &sin))
+ return -1;
+
+
+ fd = socket (PF_INET, SOCK_STREAM, 0);
+
+ if (fd < 0) return -1;
+
+ sin.sin_port = htons (port);
+
+ if (connect (fd, (struct sockaddr *)&sin, sizeof (sin))) {
+ close (fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+int open_tcp_server (unsigned port)
+{
+ int fd;
+ int enable = 1;
+ struct sockaddr_in sin = {0};
+
+ fd = socket (PF_INET, SOCK_STREAM, 0);
+
+ if (fd < 0) return -1;
+
+ if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof (enable)))
+ return -1;
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons (port);
+ sin.sin_addr.s_addr = INADDR_ANY;
+
+ if (bind (fd, (struct sockaddr *)&sin, sizeof (sin))) {
+ close (fd);
+ return -1;
+ }
+
+ if (listen (fd, 5)) {
+ close (fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+
+
+
diff --git a/util.h b/util.h
new file mode 100644
index 0000000..9bee282
--- /dev/null
+++ b/util.h
@@ -0,0 +1,13 @@
+#ifndef _util_h_
+#define _util_h_
+
+/* util.c */
+extern void set_nonblocking (int fd);
+extern void set_blocking (int fd);
+extern int fd_can_read (int fd);
+extern int fd_drain (int fd);
+extern int open_tty (const char *path, int baud);
+extern int open_tcp_client (const char *host, unsigned port);
+extern int open_tcp_server (unsigned port);
+
+#endif /* _util_h_ */