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