summaryrefslogtreecommitdiffstats
path: root/sia.c
blob: 60471172093c46d61791f52b748c6e6a4fc6f838 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
#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;
}