1 /*
2 * Copyright (c) 2020 Siddharth Chandrasekaran <siddharth@embedjournal.com>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdlib.h>
8 #include <zephyr/logging/log.h>
9 LOG_MODULE_DECLARE(osdp, CONFIG_OSDP_LOG_LEVEL);
10
11 #include "osdp_common.h"
12
13 #define OSDP_PD_POLL_TIMEOUT_MS (1000 / CONFIG_OSDP_PD_POLL_RATE)
14 #define OSDP_CMD_RETRY_WAIT_MS (CONFIG_OSDP_CMD_RETRY_WAIT_SEC * 1000)
15
16 #ifdef CONFIG_OSDP_SC_ENABLED
17 #define OSDP_PD_SC_RETRY_MS (CONFIG_OSDP_SC_RETRY_WAIT_SEC * 1000)
18 #endif
19
20 #define CMD_POLL_LEN 1
21 #define CMD_LSTAT_LEN 1
22 #define CMD_ISTAT_LEN 1
23 #define CMD_OSTAT_LEN 1
24 #define CMD_RSTAT_LEN 1
25 #define CMD_ID_LEN 2
26 #define CMD_CAP_LEN 2
27 #define CMD_DIAG_LEN 2
28 #define CMD_OUT_LEN 5
29 #define CMD_LED_LEN 15
30 #define CMD_BUZ_LEN 6
31 #define CMD_TEXT_LEN 7 /* variable length command */
32 #define CMD_COMSET_LEN 6
33 #define CMD_KEYSET_LEN 19
34 #define CMD_CHLNG_LEN 9
35 #define CMD_SCRYPT_LEN 17
36
37 #define REPLY_ACK_DATA_LEN 0
38 #define REPLY_PDID_DATA_LEN 12
39 #define REPLY_PDCAP_ENTITY_LEN 3
40 #define REPLY_LSTATR_DATA_LEN 2
41 #define REPLY_RSTATR_DATA_LEN 1
42 #define REPLY_COM_DATA_LEN 5
43 #define REPLY_NAK_DATA_LEN 1
44 #define REPLY_CCRYPT_DATA_LEN 32
45 #define REPLY_RMAC_I_DATA_LEN 16
46 #define REPLY_KEYPPAD_DATA_LEN 2 /* variable length command */
47 #define REPLY_RAW_DATA_LEN 4 /* variable length command */
48 #define REPLY_FMT_DATA_LEN 3 /* variable length command */
49 #define REPLY_BUSY_DATA_LEN 0
50
51 enum osdp_cp_error_e {
52 OSDP_CP_ERR_NONE = 0,
53 OSDP_CP_ERR_GENERIC = -1,
54 OSDP_CP_ERR_NO_DATA = -2,
55 OSDP_CP_ERR_RETRY_CMD = -3,
56 OSDP_CP_ERR_CAN_YIELD = -4,
57 OSDP_CP_ERR_INPROG = -5,
58 OSDP_CP_ERR_UNKNOWN = -6,
59 };
60
61
cp_cmd_alloc(struct osdp_pd * pd)62 static struct osdp_cmd *cp_cmd_alloc(struct osdp_pd *pd)
63 {
64 struct osdp_cmd *cmd = NULL;
65
66 if (k_mem_slab_alloc(&pd->cmd.slab, (void **)&cmd, K_MSEC(100))) {
67 LOG_ERR("Memory allocation time-out");
68 return NULL;
69 }
70 return cmd;
71 }
72
cp_cmd_free(struct osdp_pd * pd,struct osdp_cmd * cmd)73 static void cp_cmd_free(struct osdp_pd *pd, struct osdp_cmd *cmd)
74 {
75 k_mem_slab_free(&pd->cmd.slab, (void *)cmd);
76 }
77
cp_cmd_enqueue(struct osdp_pd * pd,struct osdp_cmd * cmd)78 static void cp_cmd_enqueue(struct osdp_pd *pd, struct osdp_cmd *cmd)
79 {
80 sys_slist_append(&pd->cmd.queue, &cmd->node);
81 }
82
cp_cmd_dequeue(struct osdp_pd * pd,struct osdp_cmd ** cmd)83 static int cp_cmd_dequeue(struct osdp_pd *pd, struct osdp_cmd **cmd)
84 {
85 sys_snode_t *node;
86
87 node = sys_slist_peek_head(&pd->cmd.queue);
88 if (node == NULL) {
89 return -1;
90 }
91 sys_slist_remove(&pd->cmd.queue, NULL, node);
92 *cmd = CONTAINER_OF(node, struct osdp_cmd, node);
93 return 0;
94 }
95
osdp_extract_address(int * address)96 int osdp_extract_address(int *address)
97 {
98 int pd_offset = 0;
99 unsigned long addr;
100 char *tok, *s1, *s2, addr_buf[32 * CONFIG_OSDP_NUM_CONNECTED_PD];
101
102 strncpy(addr_buf, CONFIG_OSDP_PD_ADDRESS_LIST, sizeof(addr_buf) - 1);
103 addr_buf[sizeof(addr_buf) - 1] = '\0';
104 tok = strtok_r(addr_buf, ", ", &s1);
105 while (tok && pd_offset < CONFIG_OSDP_NUM_CONNECTED_PD) {
106 addr = strtoul(tok, &s2, 10);
107 if (*s2 != '\0') { /* tok must be number-ish */
108 return -1;
109 }
110 address[pd_offset] = addr;
111 pd_offset++;
112 tok = strtok_r(NULL, ", ", &s1);
113 }
114 return (pd_offset == CONFIG_OSDP_NUM_CONNECTED_PD) ? 0 : -1;
115 }
116
assert_buf_len(int need,int have)117 static inline void assert_buf_len(int need, int have)
118 {
119 __ASSERT(need < have, "OOM at build command: need:%d have:%d",
120 need, have);
121 }
122
cp_build_command(struct osdp_pd * pd,uint8_t * buf,int max_len)123 static int cp_build_command(struct osdp_pd *pd, uint8_t *buf, int max_len)
124 {
125 struct osdp_cmd *cmd = NULL;
126 int len = 0;
127 int data_off = osdp_phy_packet_get_data_offset(pd, buf);
128 #ifdef CONFIG_OSDP_SC_ENABLED
129 uint8_t *smb = osdp_phy_packet_get_smb(pd, buf);
130 #endif
131
132 buf += data_off;
133 max_len -= data_off;
134 if (max_len <= 0) {
135 return OSDP_CP_ERR_GENERIC;
136 }
137
138 switch (pd->cmd_id) {
139 case CMD_POLL:
140 assert_buf_len(CMD_POLL_LEN, max_len);
141 buf[len++] = pd->cmd_id;
142 break;
143 case CMD_LSTAT:
144 assert_buf_len(CMD_LSTAT_LEN, max_len);
145 buf[len++] = pd->cmd_id;
146 break;
147 case CMD_ISTAT:
148 assert_buf_len(CMD_ISTAT_LEN, max_len);
149 buf[len++] = pd->cmd_id;
150 break;
151 case CMD_OSTAT:
152 assert_buf_len(CMD_OSTAT_LEN, max_len);
153 buf[len++] = pd->cmd_id;
154 break;
155 case CMD_RSTAT:
156 assert_buf_len(CMD_RSTAT_LEN, max_len);
157 buf[len++] = pd->cmd_id;
158 break;
159 case CMD_ID:
160 assert_buf_len(CMD_ID_LEN, max_len);
161 buf[len++] = pd->cmd_id;
162 buf[len++] = 0x00;
163 break;
164 case CMD_CAP:
165 assert_buf_len(CMD_CAP_LEN, max_len);
166 buf[len++] = pd->cmd_id;
167 buf[len++] = 0x00;
168 break;
169 case CMD_DIAG:
170 assert_buf_len(CMD_DIAG_LEN, max_len);
171 buf[len++] = pd->cmd_id;
172 buf[len++] = 0x00;
173 break;
174 case CMD_OUT:
175 assert_buf_len(CMD_OUT_LEN, max_len);
176 cmd = (struct osdp_cmd *)pd->ephemeral_data;
177 buf[len++] = pd->cmd_id;
178 buf[len++] = cmd->output.output_no;
179 buf[len++] = cmd->output.control_code;
180 buf[len++] = BYTE_0(cmd->output.timer_count);
181 buf[len++] = BYTE_1(cmd->output.timer_count);
182 break;
183 case CMD_LED:
184 assert_buf_len(CMD_LED_LEN, max_len);
185 cmd = (struct osdp_cmd *)pd->ephemeral_data;
186 buf[len++] = pd->cmd_id;
187 buf[len++] = cmd->led.reader;
188 buf[len++] = cmd->led.led_number;
189
190 buf[len++] = cmd->led.temporary.control_code;
191 buf[len++] = cmd->led.temporary.on_count;
192 buf[len++] = cmd->led.temporary.off_count;
193 buf[len++] = cmd->led.temporary.on_color;
194 buf[len++] = cmd->led.temporary.off_color;
195 buf[len++] = BYTE_0(cmd->led.temporary.timer_count);
196 buf[len++] = BYTE_1(cmd->led.temporary.timer_count);
197
198 buf[len++] = cmd->led.permanent.control_code;
199 buf[len++] = cmd->led.permanent.on_count;
200 buf[len++] = cmd->led.permanent.off_count;
201 buf[len++] = cmd->led.permanent.on_color;
202 buf[len++] = cmd->led.permanent.off_color;
203 break;
204 case CMD_BUZ:
205 assert_buf_len(CMD_BUZ_LEN, max_len);
206 cmd = (struct osdp_cmd *)pd->ephemeral_data;
207 buf[len++] = pd->cmd_id;
208 buf[len++] = cmd->buzzer.reader;
209 buf[len++] = cmd->buzzer.control_code;
210 buf[len++] = cmd->buzzer.on_count;
211 buf[len++] = cmd->buzzer.off_count;
212 buf[len++] = cmd->buzzer.rep_count;
213 break;
214 case CMD_TEXT:
215 cmd = (struct osdp_cmd *)pd->ephemeral_data;
216 assert_buf_len(CMD_TEXT_LEN + cmd->text.length, max_len);
217 buf[len++] = pd->cmd_id;
218 buf[len++] = cmd->text.reader;
219 buf[len++] = cmd->text.control_code;
220 buf[len++] = cmd->text.temp_time;
221 buf[len++] = cmd->text.offset_row;
222 buf[len++] = cmd->text.offset_col;
223 buf[len++] = cmd->text.length;
224 memcpy(buf + len, cmd->text.data, cmd->text.length);
225 len += cmd->text.length;
226 break;
227 case CMD_COMSET:
228 assert_buf_len(CMD_COMSET_LEN, max_len);
229 cmd = (struct osdp_cmd *)pd->ephemeral_data;
230 buf[len++] = pd->cmd_id;
231 buf[len++] = cmd->comset.address;
232 buf[len++] = BYTE_0(cmd->comset.baud_rate);
233 buf[len++] = BYTE_1(cmd->comset.baud_rate);
234 buf[len++] = BYTE_2(cmd->comset.baud_rate);
235 buf[len++] = BYTE_3(cmd->comset.baud_rate);
236 break;
237 #ifdef CONFIG_OSDP_SC_ENABLED
238 case CMD_KEYSET:
239 if (!sc_is_active(pd)) {
240 LOG_ERR("Cannot perform KEYSET without SC!");
241 return OSDP_CP_ERR_GENERIC;
242 }
243 assert_buf_len(CMD_KEYSET_LEN, max_len);
244 cmd = (struct osdp_cmd *)pd->ephemeral_data;
245 if (cmd->keyset.length != 16) {
246 LOG_ERR("Invalid key length");
247 return OSDP_CP_ERR_GENERIC;
248 }
249 buf[len++] = pd->cmd_id;
250 buf[len++] = 1; /* key type (1: SCBK) */
251 buf[len++] = 16; /* key length in bytes */
252 if (cmd->keyset.type == 1) { /* SCBK */
253 memcpy(buf + len, cmd->keyset.data, 16);
254 } else if (cmd->keyset.type == 0) { /* master_key */
255 osdp_compute_scbk(pd, cmd->keyset.data, buf + len);
256 } else {
257 LOG_ERR("Unknown key type (%d)", cmd->keyset.type);
258 return OSDP_CP_ERR_GENERIC;
259 }
260 len += 16;
261 break;
262 case CMD_CHLNG:
263 assert_buf_len(CMD_CHLNG_LEN, max_len);
264 if (smb == NULL) {
265 LOG_ERR("Invalid secure message block!");
266 return -1;
267 }
268 smb[0] = 3; /* length */
269 smb[1] = SCS_11; /* type */
270 smb[2] = ISSET_FLAG(pd, PD_FLAG_SC_USE_SCBKD) ? 0 : 1;
271 buf[len++] = pd->cmd_id;
272 memcpy(buf + len, pd->sc.cp_random, 8);
273 len += 8;
274 break;
275 case CMD_SCRYPT:
276 assert_buf_len(CMD_SCRYPT_LEN, max_len);
277 if (smb == NULL) {
278 LOG_ERR("Invalid secure message block!");
279 return -1;
280 }
281 osdp_compute_cp_cryptogram(pd);
282 smb[0] = 3; /* length */
283 smb[1] = SCS_13; /* type */
284 smb[2] = ISSET_FLAG(pd, PD_FLAG_SC_USE_SCBKD) ? 0 : 1;
285 buf[len++] = pd->cmd_id;
286 memcpy(buf + len, pd->sc.cp_cryptogram, 16);
287 len += 16;
288 break;
289 #endif /* CONFIG_OSDP_SC_ENABLED */
290 default:
291 LOG_ERR("Unknown/Unsupported CMD(%02x)", pd->cmd_id);
292 return OSDP_CP_ERR_GENERIC;
293 }
294
295 #ifdef CONFIG_OSDP_SC_ENABLED
296 if (smb && (smb[1] > SCS_14) && sc_is_active(pd)) {
297 /**
298 * When SC active and current cmd is not a handshake (<= SCS_14)
299 * then we must set SCS type to 17 if this message has data
300 * bytes and 15 otherwise.
301 */
302 smb[0] = 2;
303 smb[1] = (len > 1) ? SCS_17 : SCS_15;
304 }
305 #endif /* CONFIG_OSDP_SC_ENABLED */
306
307 return len;
308 }
309
cp_decode_response(struct osdp_pd * pd,uint8_t * buf,int len)310 static int cp_decode_response(struct osdp_pd *pd, uint8_t *buf, int len)
311 {
312 uint32_t temp32;
313 struct osdp *ctx = pd_to_osdp(pd);
314 int ret = OSDP_CP_ERR_GENERIC, pos = 0, t1, t2;
315 struct osdp_event event;
316
317 pd->reply_id = buf[pos++];
318 len--; /* consume reply id from the head */
319
320 switch (pd->reply_id) {
321 case REPLY_ACK:
322 if (len != REPLY_ACK_DATA_LEN) {
323 break;
324 }
325 ret = OSDP_CP_ERR_NONE;
326 break;
327 case REPLY_NAK:
328 if (len != REPLY_NAK_DATA_LEN) {
329 break;
330 }
331 LOG_WRN("PD replied with NAK(%d) for CMD(%02x)",
332 buf[pos], pd->cmd_id);
333 ret = OSDP_CP_ERR_NONE;
334 break;
335 case REPLY_PDID:
336 if (len != REPLY_PDID_DATA_LEN) {
337 break;
338 }
339 pd->id.vendor_code = buf[pos++];
340 pd->id.vendor_code |= buf[pos++] << 8;
341 pd->id.vendor_code |= buf[pos++] << 16;
342
343 pd->id.model = buf[pos++];
344 pd->id.version = buf[pos++];
345
346 pd->id.serial_number = buf[pos++];
347 pd->id.serial_number |= buf[pos++] << 8;
348 pd->id.serial_number |= buf[pos++] << 16;
349 pd->id.serial_number |= buf[pos++] << 24;
350
351 pd->id.firmware_version = buf[pos++] << 16;
352 pd->id.firmware_version |= buf[pos++] << 8;
353 pd->id.firmware_version |= buf[pos++];
354 ret = OSDP_CP_ERR_NONE;
355 break;
356 case REPLY_PDCAP:
357 if ((len % REPLY_PDCAP_ENTITY_LEN) != 0) {
358 LOG_ERR("PDCAP response length is not a multiple of 3");
359 return OSDP_CP_ERR_GENERIC;
360 }
361 while (pos < len) {
362 t1 = buf[pos++]; /* func_code */
363 if (t1 >= OSDP_PD_CAP_SENTINEL) {
364 break;
365 }
366 pd->cap[t1].function_code = t1;
367 pd->cap[t1].compliance_level = buf[pos++];
368 pd->cap[t1].num_items = buf[pos++];
369 }
370
371 /* post-capabilities hooks */
372 t2 = OSDP_PD_CAP_COMMUNICATION_SECURITY;
373 if (pd->cap[t2].compliance_level & 0x01) {
374 SET_FLAG(pd, PD_FLAG_SC_CAPABLE);
375 } else {
376 CLEAR_FLAG(pd, PD_FLAG_SC_CAPABLE);
377 }
378 ret = OSDP_CP_ERR_NONE;
379 break;
380 case REPLY_LSTATR:
381 if (len != REPLY_LSTATR_DATA_LEN) {
382 break;
383 }
384 if (buf[pos++]) {
385 SET_FLAG(pd, PD_FLAG_TAMPER);
386 } else {
387 CLEAR_FLAG(pd, PD_FLAG_TAMPER);
388 }
389 if (buf[pos++]) {
390 SET_FLAG(pd, PD_FLAG_POWER);
391 } else {
392 CLEAR_FLAG(pd, PD_FLAG_POWER);
393 }
394 ret = OSDP_CP_ERR_NONE;
395 break;
396 case REPLY_RSTATR:
397 if (len != REPLY_RSTATR_DATA_LEN) {
398 break;
399 }
400 if (buf[pos++]) {
401 SET_FLAG(pd, PD_FLAG_R_TAMPER);
402 } else {
403 CLEAR_FLAG(pd, PD_FLAG_R_TAMPER);
404 }
405 ret = OSDP_CP_ERR_NONE;
406 break;
407 case REPLY_COM:
408 if (len != REPLY_COM_DATA_LEN) {
409 break;
410 }
411 t1 = buf[pos++];
412 temp32 = buf[pos++];
413 temp32 |= buf[pos++] << 8;
414 temp32 |= buf[pos++] << 16;
415 temp32 |= buf[pos++] << 24;
416 LOG_WRN("COMSET responded with ID:%d Baud:%d", t1, temp32);
417 pd->address = t1;
418 pd->baud_rate = temp32;
419 ret = OSDP_CP_ERR_NONE;
420 break;
421 case REPLY_KEYPPAD:
422 if (len < REPLY_KEYPPAD_DATA_LEN || !ctx->event_callback) {
423 break;
424 }
425 event.type = OSDP_EVENT_KEYPRESS;
426 event.keypress.reader_no = buf[pos++];
427 event.keypress.length = buf[pos++];
428 if ((len - REPLY_KEYPPAD_DATA_LEN) != event.keypress.length) {
429 break;
430 }
431 memcpy(event.keypress.data, buf + pos, event.keypress.length);
432 ctx->event_callback(ctx->event_callback_arg, pd->idx, &event);
433 ret = OSDP_CP_ERR_NONE;
434 break;
435 case REPLY_RAW:
436 if (len < REPLY_RAW_DATA_LEN || !ctx->event_callback) {
437 break;
438 }
439 event.type = OSDP_EVENT_CARDREAD;
440 event.cardread.reader_no = buf[pos++];
441 event.cardread.format = buf[pos++];
442 event.cardread.length = buf[pos++]; /* bits LSB */
443 event.cardread.length |= buf[pos++] << 8; /* bits MSB */
444 event.cardread.direction = 0; /* un-specified */
445 t1 = (event.cardread.length + 7) / 8; /* len: bytes */
446 if (t1 != (len - REPLY_RAW_DATA_LEN)) {
447 break;
448 }
449 memcpy(event.cardread.data, buf + pos, t1);
450 ctx->event_callback(ctx->event_callback_arg, pd->idx, &event);
451 ret = OSDP_CP_ERR_NONE;
452 break;
453 case REPLY_FMT:
454 if (len < REPLY_FMT_DATA_LEN || !ctx->event_callback) {
455 break;
456 }
457 event.type = OSDP_EVENT_CARDREAD;
458 event.cardread.reader_no = buf[pos++];
459 event.cardread.direction = buf[pos++];
460 event.cardread.length = buf[pos++];
461 event.cardread.format = OSDP_CARD_FMT_ASCII;
462 if (event.cardread.length != (len - REPLY_FMT_DATA_LEN) ||
463 event.cardread.length > OSDP_EVENT_MAX_DATALEN) {
464 break;
465 }
466 memcpy(event.cardread.data, buf + pos, event.cardread.length);
467 ctx->event_callback(ctx->event_callback_arg, pd->idx, &event);
468 ret = OSDP_CP_ERR_NONE;
469 break;
470 case REPLY_BUSY:
471 /* PD busy; signal upper layer to retry command */
472 if (len != REPLY_BUSY_DATA_LEN) {
473 break;
474 }
475 ret = OSDP_CP_ERR_RETRY_CMD;
476 break;
477 #ifdef CONFIG_OSDP_SC_ENABLED
478 case REPLY_CCRYPT:
479 if (len != REPLY_CCRYPT_DATA_LEN) {
480 break;
481 }
482 memcpy(pd->sc.pd_client_uid, buf + pos, 8);
483 memcpy(pd->sc.pd_random, buf + pos + 8, 8);
484 memcpy(pd->sc.pd_cryptogram, buf + pos + 16, 16);
485 pos += 32;
486 osdp_compute_session_keys(pd);
487 if (osdp_verify_pd_cryptogram(pd) != 0) {
488 LOG_ERR("Failed to verify PD cryptogram");
489 return OSDP_CP_ERR_GENERIC;
490 }
491 ret = OSDP_CP_ERR_NONE;
492 break;
493 case REPLY_RMAC_I:
494 if (len != REPLY_RMAC_I_DATA_LEN) {
495 break;
496 }
497 memcpy(pd->sc.r_mac, buf + pos, 16);
498 sc_activate(pd);
499 ret = OSDP_CP_ERR_NONE;
500 break;
501 #endif /* CONFIG_OSDP_SC_ENABLED */
502 default:
503 LOG_WRN("Unexpected REPLY(%02x) for CMD(%02x)",
504 pd->cmd_id, pd->reply_id);
505 return OSDP_CP_ERR_UNKNOWN;
506 }
507
508 if (ret != OSDP_CP_ERR_NONE) {
509 LOG_ERR("Failed to decode response: REPLY(%02x) for CMD(%02x)",
510 pd->cmd_id, pd->reply_id);
511 }
512
513 if (pd->cmd_id != CMD_POLL) {
514 LOG_DBG("CMD(%02x) REPLY(%02x)", pd->cmd_id, pd->reply_id);
515 }
516
517 return ret;
518 }
519
cp_build_packet(struct osdp_pd * pd)520 static int cp_build_packet(struct osdp_pd *pd)
521 {
522 int ret, len;
523
524 /* init packet buf with header */
525 len = osdp_phy_packet_init(pd, pd->rx_buf, sizeof(pd->rx_buf));
526 if (len < 0) {
527 return OSDP_CP_ERR_GENERIC;
528 }
529
530 /* fill command data */
531 ret = cp_build_command(pd, pd->rx_buf, sizeof(pd->rx_buf));
532 if (ret < 0) {
533 return OSDP_CP_ERR_GENERIC;
534 }
535 len += ret;
536
537 /* finalize packet */
538 len = osdp_phy_packet_finalize(pd, pd->rx_buf, len, sizeof(pd->rx_buf));
539 if (len < 0) {
540 return OSDP_CP_ERR_GENERIC;
541 }
542
543 pd->rx_buf_len = len;
544
545 return OSDP_CP_ERR_NONE;
546 }
547
cp_send_command(struct osdp_pd * pd)548 static int cp_send_command(struct osdp_pd *pd)
549 {
550 int ret;
551
552 /* flush rx to remove any invalid data. */
553 if (pd->channel.flush) {
554 pd->channel.flush(pd->channel.data);
555 }
556
557 ret = pd->channel.send(pd->channel.data, pd->rx_buf, pd->rx_buf_len);
558 if (ret != pd->rx_buf_len) {
559 LOG_ERR("Channel send for %d bytes failed! ret: %d",
560 pd->rx_buf_len, ret);
561 return OSDP_CP_ERR_GENERIC;
562 }
563
564 if (IS_ENABLED(CONFIG_OSDP_PACKET_TRACE)) {
565 if (pd->cmd_id != CMD_POLL) {
566 LOG_DBG("bytes sent");
567 osdp_dump(NULL, pd->rx_buf, pd->rx_buf_len);
568 }
569 }
570
571 return OSDP_CP_ERR_NONE;
572 }
573
cp_process_reply(struct osdp_pd * pd)574 static int cp_process_reply(struct osdp_pd *pd)
575 {
576 uint8_t *buf;
577 int err, len, one_pkt_len, remaining;
578
579 buf = pd->rx_buf + pd->rx_buf_len;
580 remaining = sizeof(pd->rx_buf) - pd->rx_buf_len;
581
582 len = pd->channel.recv(pd->channel.data, buf, remaining);
583 if (len <= 0) { /* No data received */
584 return OSDP_CP_ERR_NO_DATA;
585 }
586 pd->rx_buf_len += len;
587
588 if (IS_ENABLED(CONFIG_OSDP_PACKET_TRACE)) {
589 if (pd->cmd_id != CMD_POLL) {
590 LOG_DBG("bytes received");
591 osdp_dump(NULL, pd->rx_buf, pd->rx_buf_len);
592 }
593 }
594
595 err = osdp_phy_check_packet(pd, pd->rx_buf, pd->rx_buf_len, &len);
596
597 /* Translate phy error codes to CP errors */
598 switch (err) {
599 case OSDP_ERR_PKT_NONE:
600 break;
601 case OSDP_ERR_PKT_WAIT:
602 return OSDP_CP_ERR_NO_DATA;
603 case OSDP_ERR_PKT_BUSY:
604 return OSDP_CP_ERR_RETRY_CMD;
605 default:
606 return OSDP_CP_ERR_GENERIC;
607 }
608
609 one_pkt_len = len;
610
611 /* Valid OSDP packet in buffer */
612 len = osdp_phy_decode_packet(pd, pd->rx_buf, len, &buf);
613 if (len <= 0) {
614 return OSDP_CP_ERR_GENERIC;
615 }
616 err = cp_decode_response(pd, buf, len);
617
618 /* We are done with the packet (error or not). Remove processed bytes */
619 len = pd->rx_buf_len - one_pkt_len;
620 if (len) {
621 memmove(pd->rx_buf, pd->rx_buf + one_pkt_len, len);
622 }
623 pd->rx_buf_len = len;
624
625 return err;
626 }
627
cp_flush_command_queue(struct osdp_pd * pd)628 static void cp_flush_command_queue(struct osdp_pd *pd)
629 {
630 struct osdp_cmd *cmd;
631
632 while (cp_cmd_dequeue(pd, &cmd) == 0) {
633 cp_cmd_free(pd, cmd);
634 }
635 }
636
cp_set_state(struct osdp_pd * pd,enum osdp_cp_state_e state)637 static inline void cp_set_state(struct osdp_pd *pd, enum osdp_cp_state_e state)
638 {
639 pd->state = state;
640 CLEAR_FLAG(pd, PD_FLAG_AWAIT_RESP);
641 }
642
cp_set_online(struct osdp_pd * pd)643 static inline void cp_set_online(struct osdp_pd *pd)
644 {
645 cp_set_state(pd, OSDP_CP_STATE_ONLINE);
646 pd->wait_ms = 0;
647 pd->tstamp = 0;
648 }
649
cp_set_offline(struct osdp_pd * pd)650 static inline void cp_set_offline(struct osdp_pd *pd)
651 {
652 sc_deactivate(pd);
653 pd->state = OSDP_CP_STATE_OFFLINE;
654 pd->tstamp = osdp_millis_now();
655 if (pd->wait_ms == 0) {
656 pd->wait_ms = 1000; /* retry after 1 second initially */
657 } else {
658 pd->wait_ms <<= 1;
659 if (pd->wait_ms > OSDP_ONLINE_RETRY_WAIT_MAX_MS) {
660 pd->wait_ms = OSDP_ONLINE_RETRY_WAIT_MAX_MS;
661 }
662 }
663 }
664
665 #ifdef CONFIG_OSDP_SC_ENABLED
cp_sc_should_retry(struct osdp_pd * pd)666 static inline bool cp_sc_should_retry(struct osdp_pd *pd)
667 {
668 return (sc_is_capable(pd) && !sc_is_active(pd) &&
669 osdp_millis_since(pd->sc_tstamp) > OSDP_PD_SC_RETRY_MS);
670 }
671 #endif
672
cp_phy_state_update(struct osdp_pd * pd)673 static int cp_phy_state_update(struct osdp_pd *pd)
674 {
675 int64_t elapsed;
676 int rc, ret = OSDP_CP_ERR_CAN_YIELD;
677 struct osdp_cmd *cmd = NULL;
678
679 switch (pd->phy_state) {
680 case OSDP_CP_PHY_STATE_WAIT:
681 elapsed = osdp_millis_since(pd->phy_tstamp);
682 if (elapsed < OSDP_CMD_RETRY_WAIT_MS) {
683 break;
684 }
685 pd->phy_state = OSDP_CP_PHY_STATE_SEND_CMD;
686 break;
687 case OSDP_CP_PHY_STATE_ERR:
688 ret = OSDP_CP_ERR_GENERIC;
689 break;
690 case OSDP_CP_PHY_STATE_IDLE:
691 if (cp_cmd_dequeue(pd, &cmd)) {
692 ret = OSDP_CP_ERR_NONE; /* command queue is empty */
693 break;
694 }
695 pd->cmd_id = cmd->id;
696 memcpy(pd->ephemeral_data, cmd, sizeof(struct osdp_cmd));
697 cp_cmd_free(pd, cmd);
698 /* fall-thru */
699 case OSDP_CP_PHY_STATE_SEND_CMD:
700 if (cp_build_packet(pd)) {
701 LOG_ERR("Failed to build packet for CMD(%d)",
702 pd->cmd_id);
703 ret = OSDP_CP_ERR_GENERIC;
704 break;
705 }
706 if (cp_send_command(pd)) {
707 LOG_ERR("Failed to send CMD(%d)", pd->cmd_id);
708 pd->phy_state = OSDP_CP_PHY_STATE_ERR;
709 ret = OSDP_CP_ERR_GENERIC;
710 break;
711 }
712 ret = OSDP_CP_ERR_INPROG;
713 pd->phy_state = OSDP_CP_PHY_STATE_REPLY_WAIT;
714 pd->rx_buf_len = 0; /* reset buf_len for next use */
715 pd->phy_tstamp = osdp_millis_now();
716 break;
717 case OSDP_CP_PHY_STATE_REPLY_WAIT:
718 rc = cp_process_reply(pd);
719 if (rc == OSDP_CP_ERR_NONE) {
720 #ifdef CONFIG_OSDP_SC_ENABLED
721 if (sc_is_active(pd)) {
722 pd->sc_tstamp = osdp_millis_now();
723 }
724 #endif
725 pd->phy_state = OSDP_CP_PHY_STATE_IDLE;
726 break;
727 }
728 if (rc == OSDP_CP_ERR_RETRY_CMD) {
729 LOG_INF("PD busy; retry last command");
730 pd->phy_tstamp = osdp_millis_now();
731 pd->phy_state = OSDP_CP_PHY_STATE_WAIT;
732 break;
733 }
734 if (rc == OSDP_CP_ERR_GENERIC || rc == OSDP_CP_ERR_UNKNOWN ||
735 osdp_millis_since(pd->phy_tstamp) > OSDP_RESP_TOUT_MS) {
736 if (rc != OSDP_CP_ERR_GENERIC) {
737 LOG_ERR("Response timeout for CMD(%02x)",
738 pd->cmd_id);
739 }
740 pd->rx_buf_len = 0;
741 if (pd->channel.flush) {
742 pd->channel.flush(pd->channel.data);
743 }
744 cp_flush_command_queue(pd);
745 pd->phy_state = OSDP_CP_PHY_STATE_ERR;
746 ret = OSDP_CP_ERR_GENERIC;
747 break;
748 }
749 ret = OSDP_CP_ERR_INPROG;
750 break;
751 }
752
753 return ret;
754 }
755
cp_cmd_dispatcher(struct osdp_pd * pd,int cmd)756 static int cp_cmd_dispatcher(struct osdp_pd *pd, int cmd)
757 {
758 struct osdp_cmd *c;
759
760 if (ISSET_FLAG(pd, PD_FLAG_AWAIT_RESP)) {
761 CLEAR_FLAG(pd, PD_FLAG_AWAIT_RESP);
762 return OSDP_CP_ERR_NONE; /* nothing to be done here */
763 }
764
765 c = cp_cmd_alloc(pd);
766 if (c == NULL) {
767 return OSDP_CP_ERR_GENERIC;
768 }
769
770 c->id = cmd;
771
772 if (c->id == CMD_KEYSET) {
773 memcpy(&c->keyset, pd->ephemeral_data, sizeof(c->keyset));
774 }
775
776 cp_cmd_enqueue(pd, c);
777 SET_FLAG(pd, PD_FLAG_AWAIT_RESP);
778 return OSDP_CP_ERR_INPROG;
779 }
780
state_update(struct osdp_pd * pd)781 static int state_update(struct osdp_pd *pd)
782 {
783 bool soft_fail;
784 int phy_state;
785 #ifdef CONFIG_OSDP_SC_ENABLED
786 struct osdp *ctx = pd_to_osdp(pd);
787 struct osdp_cmd_keyset *keyset;
788 #endif
789
790 phy_state = cp_phy_state_update(pd);
791 if (phy_state == OSDP_CP_ERR_INPROG ||
792 phy_state == OSDP_CP_ERR_CAN_YIELD) {
793 return phy_state;
794 }
795
796 /* Certain states can fail without causing PD offline */
797 soft_fail = (pd->state == OSDP_CP_STATE_SC_CHLNG);
798
799 /* phy state error -- cleanup */
800 if (pd->state != OSDP_CP_STATE_OFFLINE &&
801 phy_state == OSDP_CP_ERR_GENERIC && !soft_fail) {
802 cp_set_offline(pd);
803 return OSDP_CP_ERR_CAN_YIELD;
804 }
805
806 /* command queue is empty and last command was successful */
807
808 switch (pd->state) {
809 case OSDP_CP_STATE_ONLINE:
810 #ifdef CONFIG_OSDP_SC_ENABLED
811 if (cp_sc_should_retry(pd)) {
812 LOG_INF("Retry SC after retry timeout");
813 cp_set_state(pd, OSDP_CP_STATE_SC_INIT);
814 break;
815 }
816 #endif
817 if (osdp_millis_since(pd->tstamp) < OSDP_PD_POLL_TIMEOUT_MS) {
818 break;
819 }
820 if (cp_cmd_dispatcher(pd, CMD_POLL) == 0) {
821 pd->tstamp = osdp_millis_now();
822 }
823 break;
824 case OSDP_CP_STATE_OFFLINE:
825 if (osdp_millis_since(pd->tstamp) > pd->wait_ms) {
826 cp_set_state(pd, OSDP_CP_STATE_INIT);
827 osdp_phy_state_reset(pd);
828 }
829 break;
830 case OSDP_CP_STATE_INIT:
831 if (cp_cmd_dispatcher(pd, CMD_POLL) != 0) {
832 break;
833 }
834 cp_set_state(pd, OSDP_CP_STATE_IDREQ);
835 __fallthrough;
836 case OSDP_CP_STATE_IDREQ:
837 if (cp_cmd_dispatcher(pd, CMD_ID) != 0) {
838 break;
839 }
840 if (pd->reply_id != REPLY_PDID) {
841 LOG_ERR("Unexpected REPLY(%02x) for cmd "
842 STRINGIFY(CMD_ID), pd->reply_id);
843 cp_set_offline(pd);
844 break;
845 }
846 cp_set_state(pd, OSDP_CP_STATE_CAPDET);
847 __fallthrough;
848 case OSDP_CP_STATE_CAPDET:
849 if (cp_cmd_dispatcher(pd, CMD_CAP) != 0) {
850 break;
851 }
852 if (pd->reply_id != REPLY_PDCAP) {
853 LOG_ERR("Unexpected REPLY(%02x) for cmd "
854 STRINGIFY(CMD_CAP), pd->reply_id);
855 cp_set_offline(pd);
856 break;
857 }
858 #ifdef CONFIG_OSDP_SC_ENABLED
859 if (sc_is_capable(pd)) {
860 CLEAR_FLAG(pd, PD_FLAG_SC_SCBKD_DONE);
861 CLEAR_FLAG(pd, PD_FLAG_SC_USE_SCBKD);
862 cp_set_state(pd, OSDP_CP_STATE_SC_INIT);
863 break;
864 }
865 #endif /* CONFIG_OSDP_SC_ENABLED */
866 cp_set_online(pd);
867 break;
868 #ifdef CONFIG_OSDP_SC_ENABLED
869 case OSDP_CP_STATE_SC_INIT:
870 osdp_sc_setup(pd);
871 cp_set_state(pd, OSDP_CP_STATE_SC_CHLNG);
872 __fallthrough;
873 case OSDP_CP_STATE_SC_CHLNG:
874 if (cp_cmd_dispatcher(pd, CMD_CHLNG) != 0) {
875 break;
876 }
877 if (phy_state < 0) {
878 if (ISSET_FLAG(pd, PD_FLAG_SC_USE_SCBKD)) {
879 LOG_INF("SC Failed. Online without SC");
880 pd->sc_tstamp = osdp_millis_now();
881 osdp_phy_state_reset(pd);
882 cp_set_online(pd);
883 break;
884 }
885 SET_FLAG(pd, PD_FLAG_SC_USE_SCBKD);
886 cp_set_state(pd, OSDP_CP_STATE_SC_INIT);
887 pd->phy_state = 0; /* soft reset phy state */
888 LOG_WRN("SC Failed. Retry with SCBK-D");
889 break;
890 }
891 if (pd->reply_id != REPLY_CCRYPT) {
892 LOG_ERR("CHLNG failed. Online without SC");
893 pd->sc_tstamp = osdp_millis_now();
894 osdp_phy_state_reset(pd);
895 cp_set_online(pd);
896 break;
897 }
898 cp_set_state(pd, OSDP_CP_STATE_SC_SCRYPT);
899 __fallthrough;
900 case OSDP_CP_STATE_SC_SCRYPT:
901 if (cp_cmd_dispatcher(pd, CMD_SCRYPT) != 0) {
902 break;
903 }
904 if (pd->reply_id != REPLY_RMAC_I) {
905 LOG_ERR("SCRYPT failed. Online without SC");
906 osdp_phy_state_reset(pd);
907 pd->sc_tstamp = osdp_millis_now();
908 cp_set_online(pd);
909 break;
910 }
911 if (ISSET_FLAG(pd, PD_FLAG_SC_USE_SCBKD)) {
912 LOG_WRN("SC ACtive with SCBK-D. Set SCBK");
913 cp_set_state(pd, OSDP_CP_STATE_SET_SCBK);
914 break;
915 }
916 LOG_INF("SC Active");
917 pd->sc_tstamp = osdp_millis_now();
918 cp_set_online(pd);
919 break;
920 case OSDP_CP_STATE_SET_SCBK:
921 if (!ISSET_FLAG(pd, PD_FLAG_AWAIT_RESP)) {
922 keyset = (struct osdp_cmd_keyset *)pd->ephemeral_data;
923 if (ISSET_FLAG(pd, PD_FLAG_HAS_SCBK)) {
924 memcpy(keyset->data, pd->sc.scbk, 16);
925 keyset->type = 1;
926 } else {
927 keyset->type = 0;
928 memcpy(keyset->data, ctx->sc_master_key, 16);
929 }
930 keyset->length = 16;
931 }
932 if (cp_cmd_dispatcher(pd, CMD_KEYSET) != 0) {
933 break;
934 }
935 if (pd->reply_id == REPLY_NAK) {
936 LOG_WRN("Failed to set SCBK; continue with SCBK-D");
937 cp_set_online(pd);
938 break;
939 }
940 cp_keyset_complete(pd);
941 pd->seq_number = -1;
942 break;
943 #endif /* CONFIG_OSDP_SC_ENABLED */
944 default:
945 break;
946 }
947
948 return OSDP_CP_ERR_CAN_YIELD;
949 }
950
951 #ifdef CONFIG_OSDP_SC_ENABLED
osdp_cp_send_command_keyset(struct osdp_cmd_keyset * p)952 static int osdp_cp_send_command_keyset(struct osdp_cmd_keyset *p)
953 {
954 int i, res = 0;
955 struct osdp_cmd *cmd[OSDP_PD_MAX] = { 0 };
956 struct osdp_pd *pd;
957 struct osdp *ctx = osdp_get_ctx();
958
959 for (i = 0; i < NUM_PD(ctx); i++) {
960 pd = osdp_to_pd(ctx, i);
961 if (!ISSET_FLAG(pd, PD_FLAG_SC_ACTIVE)) {
962 LOG_WRN("master_key based key set can be performed only"
963 " when all PDs are ONLINE, SC_ACTIVE");
964 return -1;
965 }
966 }
967
968 LOG_INF("master_key based key set is a global command; "
969 "all connected PDs will be affected.");
970
971 for (i = 0; i < NUM_PD(ctx); i++) {
972 pd = osdp_to_pd(ctx, i);
973 cmd[i] = cp_cmd_alloc(pd);
974 if (cmd[i] == NULL) {
975 res = -1;
976 break;
977 }
978 cmd[i]->id = CMD_KEYSET;
979 memcpy(&cmd[i]->keyset, p, sizeof(struct osdp_cmd_keyset));
980 }
981
982 for (i = 0; i < NUM_PD(ctx); i++) {
983 pd = osdp_to_pd(ctx, i);
984 if (res == 0) {
985 cp_cmd_enqueue(pd, cmd[i]);
986 } else if (cmd[i]) {
987 cp_cmd_free(pd, cmd[i]);
988 }
989 }
990
991 return res;
992 }
993
cp_keyset_complete(struct osdp_pd * pd)994 void cp_keyset_complete(struct osdp_pd *pd)
995 {
996 struct osdp_cmd *c = (struct osdp_cmd *)pd->ephemeral_data;
997
998 sc_deactivate(pd);
999 CLEAR_FLAG(pd, PD_FLAG_SC_USE_SCBKD);
1000 memcpy(pd->sc.scbk, c->keyset.data, 16);
1001 cp_set_state(pd, OSDP_CP_STATE_SC_INIT);
1002 LOG_INF("SCBK set; restarting SC to verify new SCBK");
1003 }
1004 #endif /* CONFIG_OSDP_SC_ENABLED */
1005
osdp_update(struct osdp * ctx)1006 void osdp_update(struct osdp *ctx)
1007 {
1008 int i;
1009
1010 for (i = 0; i < NUM_PD(ctx); i++) {
1011 SET_CURRENT_PD(ctx, i);
1012 state_update(GET_CURRENT_PD(ctx));
1013 }
1014 }
1015
osdp_setup(struct osdp * ctx,uint8_t * key)1016 int osdp_setup(struct osdp *ctx, uint8_t *key)
1017 {
1018 ARG_UNUSED(ctx);
1019 ARG_UNUSED(key);
1020
1021 #ifdef CONFIG_OSDP_SC_ENABLED
1022 if (key == NULL) {
1023 LOG_ERR("Master key cannot be null");
1024 return -1;
1025 }
1026 memcpy(ctx->sc_master_key, key, 16);
1027 #endif
1028 return 0;
1029 }
1030
1031 /* --- Exported Methods --- */
1032
osdp_cp_set_event_callback(cp_event_callback_t cb,void * arg)1033 void osdp_cp_set_event_callback(cp_event_callback_t cb, void *arg)
1034 {
1035 struct osdp *ctx = osdp_get_ctx();
1036
1037 ctx->event_callback = cb;
1038 ctx->event_callback_arg = arg;
1039 }
1040
osdp_cp_send_command(int pd,struct osdp_cmd * cmd)1041 int osdp_cp_send_command(int pd, struct osdp_cmd *cmd)
1042 {
1043 struct osdp *ctx = osdp_get_ctx();
1044 struct osdp_cmd *p;
1045 int cmd_id;
1046
1047 if (pd < 0 || pd >= NUM_PD(ctx)) {
1048 LOG_ERR("Invalid PD number");
1049 return -1;
1050 }
1051 if (osdp_to_pd(ctx, pd)->state != OSDP_CP_STATE_ONLINE) {
1052 LOG_WRN("PD not online");
1053 return -1;
1054 }
1055
1056 switch (cmd->id) {
1057 case OSDP_CMD_OUTPUT:
1058 cmd_id = CMD_OUT;
1059 break;
1060 case OSDP_CMD_LED:
1061 cmd_id = CMD_LED;
1062 break;
1063 case OSDP_CMD_BUZZER:
1064 cmd_id = CMD_BUZ;
1065 break;
1066 case OSDP_CMD_TEXT:
1067 cmd_id = CMD_TEXT;
1068 break;
1069 case OSDP_CMD_COMSET:
1070 cmd_id = CMD_COMSET;
1071 break;
1072 #ifdef CONFIG_OSDP_SC_ENABLED
1073 case OSDP_CMD_KEYSET:
1074 return osdp_cp_send_command_keyset(&cmd->keyset);
1075 #endif
1076 default:
1077 LOG_ERR("Invalid CMD_ID:%d", cmd->id);
1078 return -1;
1079 }
1080
1081 p = cp_cmd_alloc(osdp_to_pd(ctx, pd));
1082 if (p == NULL) {
1083 return -1;
1084 }
1085 memcpy(p, cmd, sizeof(struct osdp_cmd));
1086 p->id = cmd_id; /* translate to internal */
1087 cp_cmd_enqueue(osdp_to_pd(ctx, pd), p);
1088 return 0;
1089 }
1090