#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; }