From 62bc1af6c6a1201db551e1ec523e757415464fd5 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 25 Oct 2020 13:26:29 +0000 Subject: tidy up, make less awful --- .gitignore | 6 ++ Makefile | 14 +++ arm.c | 263 ++++++++++++++++++++------------------------------ arm.h | 6 ++ net_arm.c | 144 ++++++++++++++++++++++++++++ net_rx.c | 176 ++++++++++++++++++++++++++++++++++ rx.c | 208 ---------------------------------------- serial_arm.c | 147 ++++++++++++++++++++++++++++ serial_rx.c | 142 +++++++++++++++++++++++++++ sia.c | 253 ++++++++++++++++++++++++++++++++++++++++++++++++ sia.h | 66 +++++++++++++ util.c | 306 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ util.h | 13 +++ 13 files changed, 1374 insertions(+), 370 deletions(-) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 arm.h create mode 100644 net_arm.c create mode 100644 net_rx.c delete mode 100644 rx.c create mode 100644 serial_arm.c create mode 100644 serial_rx.c create mode 100644 sia.c create mode 100644 sia.h create mode 100644 util.c create mode 100644 util.h 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 #include #include -#include - +#include #include #include -#include -#include -#include - +#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 +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include + +#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 -#include -#include -#include -#include -#include -#include -#include - - -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 +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#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_ */ -- cgit v1.2.3