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