diff --git a/include/zephyr/mgmt/osdp.h b/include/zephyr/mgmt/osdp.h index f6cacf7d3b9..14080821239 100644 --- a/include/zephyr/mgmt/osdp.h +++ b/include/zephyr/mgmt/osdp.h @@ -18,17 +18,7 @@ extern "C" { #define OSDP_CMD_TEXT_MAX_LEN 32 #define OSDP_CMD_KEYSET_KEY_MAX_LEN 32 - -/** - * @brief Various card formats that a PD can support. This is sent to CP - * when a PD must report a card read. - */ -enum osdp_card_formats_e { - OSDP_CARD_FMT_RAW_UNSPECIFIED, - OSDP_CARD_FMT_RAW_WIEGAND, - OSDP_CARD_FMT_ASCII, - OSDP_CARD_FMT_SENTINEL -}; +#define OSDP_EVENT_MAX_DATALEN 64 /** * @brief Command sent from CP to Control digital output of PD. @@ -211,24 +201,162 @@ struct osdp_cmd { }; }; +/** + * @brief Various card formats that a PD can support. This is sent to CP + * when a PD must report a card read. + */ +enum osdp_event_cardread_format_e { + OSDP_CARD_FMT_RAW_UNSPECIFIED, + OSDP_CARD_FMT_RAW_WIEGAND, + OSDP_CARD_FMT_ASCII, + OSDP_CARD_FMT_SENTINEL +}; + +/** + * @brief OSDP event cardread + * + * @param reader_no In context of readers attached to current PD, this number + * indicated this number. This is not supported by LibOSDP. + * @param format Format of the card being read. + * see `enum osdp_event_cardread_format_e` + * @param direction Card read direction of PD 0 - Forward; 1 - Backward + * @param length Length of card data in bytes or bits depending on `format` + * (see note). + * @param data Card data of `length` bytes or bits bits depending on `format` + * (see note). + * + * @note When `format` is set to OSDP_CARD_FMT_RAW_UNSPECIFIED or + * OSDP_CARD_FMT_RAW_WIEGAND, the length is expressed in bits. OTOH, when it is + * set to OSDP_CARD_FMT_ASCII, the length is in bytes. The number of bytes to + * read from the `data` field must be interpreted accordingly. + */ +struct osdp_event_cardread { + int reader_no; + enum osdp_event_cardread_format_e format; + int direction; + int length; + uint8_t data[OSDP_EVENT_MAX_DATALEN]; +}; + +/** + * @brief OSDP Event Keypad + * + * @param reader_no In context of readers attached to current PD, this number + * indicated this number. This is not supported by LibOSDP. + * @param length Length of keypress data in bytes + * @param data keypress data of `length` bytes + */ +struct osdp_event_keypress { + int reader_no; + int length; + uint8_t data[OSDP_EVENT_MAX_DATALEN]; +}; + +/** + * @brief OSDP PD Events + */ +enum osdp_event_type { + OSDP_EVENT_CARDREAD, + OSDP_EVENT_KEYPRESS, + OSDP_EVENT_SENTINEL +}; + +/** + * @brief OSDP Event structure. + * + * @param type used to select specific event in union. See: enum osdp_event_type + * @param keypress keypress event structure + * @param cardread cardread event structure + */ +struct osdp_event { + sys_snode_t node; + enum osdp_event_type type; + union { + struct osdp_event_keypress keypress; + struct osdp_event_cardread cardread; + }; +}; + +/** + * @brief Callback for PD command notifications. After it has been registered + * with `osdp_pd_set_command_callback`, this method is invoked when the PD + * receives a command from the CP. + * + * @param arg pointer that will was passed to the arg param of + * `osdp_pd_set_command_callback`. + * @param cmd pointer to the received command. + * + * @retval 0 if LibOSDP must send a `osdp_ACK` response + * @retval -ve if LibOSDP must send a `osdp_NAK` response + * @retval +ve and modify the passed `struct osdp_cmd *cmd` if LibOSDP must send + * a specific response. This is useful for sending manufacturer specific + * reply ``osdp_MFGREP``. + */ +typedef int (*pd_command_callback_t)(void *arg, struct osdp_cmd *cmd); + +/** + * @brief Callback for CP event notifications. After it has been registered with + * `osdp_cp_set_event_callback`, this method is invoked when the CP receives an + * event from the PD. + * + * @param arg Opaque pointer provided by the application during callback + * registration. + * @param pd the offset (0-indexed) of this PD in kconfig `OSDP_PD_ADDRESS_LIST` + * @param ev pointer to osdp_event struct (filled by libosdp). + * + * @retval 0 on handling the event successfully. + * @retval -ve on errors. + */ +typedef int (*cp_event_callback_t)(void *arg, int pd, struct osdp_event *ev); + #ifdef CONFIG_OSDP_MODE_PD /** - * @param cmd pointer to a command structure that was received by the driver. + * @brief Set callback method for PD command notification. This callback is + * invoked when the PD receives a command from the CP. * - * @retval 0 on success. - * @retval -1 on failure. + * @param cb The callback function's pointer + * @param arg A pointer that will be passed as the first argument of `cb` */ -int osdp_pd_get_cmd(struct osdp_cmd *cmd); +void osdp_pd_set_command_callback(pd_command_callback_t cb, void *arg); + +/** + * @brief API to notify PD events to CP. These events are sent to the CP as an + * alternate response to a POLL command. + * + * @param event pointer to event struct. Must be filled by application. + * + * @retval 0 on success + * @retval -1 on failure + */ +int osdp_pd_notify_event(const struct osdp_event *event); #else /* CONFIG_OSDP_MODE_PD */ -int osdp_cp_set_callback_key_press( - int (*cb)(int address, uint8_t key)); -int osdp_cp_set_callback_card_read( - int (*cb)(int address, int format, uint8_t *data, int len)); +/** + * @brief Generic command enqueue API. + * + * @param pd the offset (0-indexed) of this PD in kconfig `OSDP_PD_ADDRESS_LIST` + * @param cmd command pointer. Must be filled by application. + * + * @retval 0 on success + * @retval -1 on failure + * + * @note This method only adds the command on to a particular PD's command + * queue. The command itself can fail due to various reasons. + */ int osdp_cp_send_command(int pd, struct osdp_cmd *cmd); + +/** + * @brief Set callback method for CP event notification. This callback is + * invoked when the CP receives an event from the PD. + * + * @param cb The callback function's pointer + * @param arg A pointer that will be passed as the first argument of `cb` + */ +void osdp_cp_set_event_callback(cp_event_callback_t cb, void *arg); + #endif /* CONFIG_OSDP_MODE_PD */ #ifdef CONFIG_OSDP_SC_ENABLED diff --git a/samples/subsys/mgmt/osdp/control_panel/src/main.c b/samples/subsys/mgmt/osdp/control_panel/src/main.c index 560663992a9..5ccd3ec1aa4 100644 --- a/samples/subsys/mgmt/osdp/control_panel/src/main.c +++ b/samples/subsys/mgmt/osdp/control_panel/src/main.c @@ -28,9 +28,9 @@ enum osdp_pd_e { OSDP_PD_SENTINEL, }; -int key_press_callback(int pd, uint8_t key) +int key_press_callback(int pd, uint8_t *data, int len) { - printk("CP PD[%d] key press - data: 0x%02x\n", pd, key); + printk("CP PD[%d] key press - data: 0x%02x\n", pd, data[0]); return 0; } @@ -49,6 +49,22 @@ int card_read_callback(int pd, int format, uint8_t *data, int len) return 0; } +int event_handler(void *unused, int pd, struct osdp_event *e) +{ + switch (e->type) { + case OSDP_EVENT_CARDREAD: + card_read_callback(pd, e->cardread.format, + e->cardread.data, e->cardread.length); + break; + case OSDP_EVENT_KEYPRESS: + key_press_callback(pd, e->keypress.data, e->keypress.length); + break; + default: + break; + } + return 0; +} + void main(void) { int ret, led_state; @@ -71,8 +87,7 @@ void main(void) return; } - osdp_cp_set_callback_key_press(key_press_callback); - osdp_cp_set_callback_card_read(card_read_callback); + osdp_cp_set_event_callback(event_handler, NULL); led_state = 0; while (1) { diff --git a/samples/subsys/mgmt/osdp/peripheral_device/src/main.c b/samples/subsys/mgmt/osdp/peripheral_device/src/main.c index 985d9a4b82d..3a1055342ac 100644 --- a/samples/subsys/mgmt/osdp/peripheral_device/src/main.c +++ b/samples/subsys/mgmt/osdp/peripheral_device/src/main.c @@ -21,7 +21,7 @@ static const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET_OR(LED0_NODE, gpios, {0 #define SLEEP_TIME_MS (20) #define CNT_PER_SEC (1000 / SLEEP_TIME_MS) -int cmd_handler(struct osdp_cmd *p) +int cmd_handler(void *unused, struct osdp_cmd *p) { printk("App received command %d\n", p->id); return 0; @@ -31,7 +31,6 @@ void main(void) { int ret, led_state; uint32_t cnt = 0; - struct osdp_cmd cmd; if (!device_is_ready(led0.port)) { printk("LED0 GPIO port %s is not ready\n", led0.port->name); @@ -45,15 +44,14 @@ void main(void) return; } + osdp_pd_set_command_callback(cmd_handler, NULL); + led_state = 0; while (1) { if ((cnt & 0x7f) == 0x7f) { /* show a sign of life */ led_state = !led_state; } - if (osdp_pd_get_cmd(&cmd) == 0) { - cmd_handler(&cmd); - } gpio_pin_set(led0.port, led0.pin, led_state); k_msleep(SLEEP_TIME_MS); cnt++; diff --git a/subsys/mgmt/osdp/src/osdp_common.c b/subsys/mgmt/osdp/src/osdp_common.c index 9600714fda1..0fb21d086d3 100644 --- a/subsys/mgmt/osdp/src/osdp_common.c +++ b/subsys/mgmt/osdp/src/osdp_common.c @@ -41,45 +41,6 @@ int64_t osdp_millis_since(int64_t last) return (int64_t) k_uptime_delta(&tmp); } -struct osdp_cmd *osdp_cmd_alloc(struct osdp_pd *pd) -{ - struct osdp_cmd *cmd = NULL; - - if (k_mem_slab_alloc(&pd->cmd.slab, (void **)&cmd, K_MSEC(100))) { - LOG_ERR("Memory allocation time-out"); - return NULL; - } - return cmd; -} - -void osdp_cmd_free(struct osdp_pd *pd, struct osdp_cmd *cmd) -{ - k_mem_slab_free(&pd->cmd.slab, (void **)&cmd); -} - -void osdp_cmd_enqueue(struct osdp_pd *pd, struct osdp_cmd *cmd) -{ - sys_slist_append(&pd->cmd.queue, &cmd->node); -} - -int osdp_cmd_dequeue(struct osdp_pd *pd, struct osdp_cmd **cmd) -{ - sys_snode_t *node; - - node = sys_slist_peek_head(&pd->cmd.queue); - if (node == NULL) { - return -1; - } - sys_slist_remove(&pd->cmd.queue, NULL, node); - *cmd = CONTAINER_OF(node, struct osdp_cmd, node); - return 0; -} - -struct osdp_cmd *osdp_cmd_get_last(struct osdp_pd *pd) -{ - return (struct osdp_cmd *)sys_slist_peek_tail(&pd->cmd.queue); -} - #ifdef CONFIG_OSDP_SC_ENABLED void osdp_encrypt(uint8_t *key, uint8_t *iv, uint8_t *data, int len) diff --git a/subsys/mgmt/osdp/src/osdp_common.h b/subsys/mgmt/osdp/src/osdp_common.h index 163b6a342ec..548dd514c9a 100644 --- a/subsys/mgmt/osdp/src/osdp_common.h +++ b/subsys/mgmt/osdp/src/osdp_common.h @@ -10,10 +10,12 @@ #include #include +#define STR(x) #x + #define OSDP_RESP_TOUT_MS (200) -#define OSDP_CMD_SLAB_BUF_SIZE \ - (sizeof(struct osdp_cmd) * CONFIG_OSDP_PD_COMMAND_QUEUE_SIZE) +#define OSDP_QUEUE_SLAB_SIZE \ + (sizeof(union osdp_ephemeral_data) * CONFIG_OSDP_PD_COMMAND_QUEUE_SIZE) #define ISSET_FLAG(p, f) (((p)->flags & (f)) == (f)) #define SET_FLAG(p, f) ((p)->flags |= (f)) @@ -311,6 +313,12 @@ enum osdp_pd_cap_function_code_e { OSDP_PD_CAP_SENTINEL }; +/* Unused type only to estimate ephemeral_data size */ +union osdp_ephemeral_data { + struct osdp_cmd cmd; + struct osdp_event event; +}; + /** * @brief PD capability structure. Each PD capability has a 3 byte * representation. @@ -379,15 +387,10 @@ struct osdp_channel { void (*flush)(void *data); }; -struct osdp_cmd_queue { +struct osdp_queue { sys_slist_t queue; struct k_mem_slab slab; - uint8_t slab_buf[OSDP_CMD_SLAB_BUF_SIZE]; -}; - -struct osdp_notifiers { - int (*keypress)(int address, uint8_t key); - int (*cardread)(int address, int format, uint8_t *data, int len); + uint8_t slab_buf[OSDP_QUEUE_SLAB_SIZE]; }; #ifdef CONFIG_OSDP_SC_ENABLED @@ -435,7 +438,16 @@ struct osdp_pd { uint8_t cmd_data[OSDP_COMMAND_DATA_MAX_LEN]; struct osdp_channel channel; - struct osdp_cmd_queue cmd; + + union { + struct osdp_queue cmd; /* Command queue (CP Mode only) */ + struct osdp_queue event; /* Command queue (PD Mode only) */ + }; + + /* PD command callback to app with opaque arg pointer as passed by app */ + void *command_callback_arg; + pd_command_callback_t command_callback; + #ifdef CONFIG_OSDP_SC_ENABLED int64_t sc_tstamp; struct osdp_secure_channel sc; @@ -451,7 +463,9 @@ struct osdp { #ifdef CONFIG_OSDP_SC_ENABLED uint8_t sc_master_key[16]; #endif - struct osdp_notifiers notifier; + /* CP event callback to app with opaque arg pointer as passed by app */ + void *event_callback_arg; + cp_event_callback_t event_callback; }; /* from osdp_phy.c */ @@ -468,11 +482,6 @@ int64_t osdp_millis_now(void); int64_t osdp_millis_since(int64_t last); void osdp_dump(const char *head, uint8_t *buf, int len); uint16_t osdp_compute_crc16(const uint8_t *buf, size_t len); -struct osdp_cmd *osdp_cmd_alloc(struct osdp_pd *pd); -void osdp_cmd_free(struct osdp_pd *pd, struct osdp_cmd *cmd); -void osdp_cmd_enqueue(struct osdp_pd *pd, struct osdp_cmd *cmd); -int osdp_cmd_dequeue(struct osdp_pd *pd, struct osdp_cmd **cmd); -struct osdp_cmd *osdp_cmd_get_last(struct osdp_pd *pd); /* from osdp.c */ struct osdp *osdp_get_ctx(); diff --git a/subsys/mgmt/osdp/src/osdp_cp.c b/subsys/mgmt/osdp/src/osdp_cp.c index e21747821de..3fd57035f0f 100644 --- a/subsys/mgmt/osdp/src/osdp_cp.c +++ b/subsys/mgmt/osdp/src/osdp_cp.c @@ -59,6 +59,40 @@ enum osdp_cp_error_e { }; +static struct osdp_cmd *cp_cmd_alloc(struct osdp_pd *pd) +{ + struct osdp_cmd *cmd = NULL; + + if (k_mem_slab_alloc(&pd->cmd.slab, (void **)&cmd, K_MSEC(100))) { + LOG_ERR("Memory allocation time-out"); + return NULL; + } + return cmd; +} + +static void cp_cmd_free(struct osdp_pd *pd, struct osdp_cmd *cmd) +{ + k_mem_slab_free(&pd->cmd.slab, (void **)&cmd); +} + +static void cp_cmd_enqueue(struct osdp_pd *pd, struct osdp_cmd *cmd) +{ + sys_slist_append(&pd->cmd.queue, &cmd->node); +} + +static int cp_cmd_dequeue(struct osdp_pd *pd, struct osdp_cmd **cmd) +{ + sys_snode_t *node; + + node = sys_slist_peek_head(&pd->cmd.queue); + if (node == NULL) { + return -1; + } + sys_slist_remove(&pd->cmd.queue, NULL, node); + *cmd = CONTAINER_OF(node, struct osdp_cmd, node); + return 0; +} + int osdp_extract_address(int *address) { int pd_offset = 0; @@ -288,6 +322,7 @@ static int cp_decode_response(struct osdp_pd *pd, uint8_t *buf, int len) uint32_t temp32; struct osdp *ctx = pd_to_osdp(pd); int i, ret = OSDP_CP_ERR_GENERIC, pos = 0, t1, t2; + struct osdp_event event; if (len < 1) { LOG_ERR("response must have at least one byte"); @@ -399,52 +434,58 @@ static int cp_decode_response(struct osdp_pd *pd, uint8_t *buf, int len) ret = OSDP_CP_ERR_NONE; break; case REPLY_KEYPPAD: - if (len < REPLY_KEYPPAD_DATA_LEN) { + if (len < REPLY_KEYPPAD_DATA_LEN || !ctx->event_callback) { break; } - pos++; /* reader number; skip */ - t1 = buf[pos++]; /* key length */ - if ((len - REPLY_KEYPPAD_DATA_LEN) != t1) { + event.type = OSDP_EVENT_KEYPRESS; + event.keypress.reader_no = buf[pos++]; + event.keypress.length = buf[pos++]; + if ((len - REPLY_KEYPPAD_DATA_LEN) != event.keypress.length) { break; } - if (ctx->notifier.keypress) { - for (i = 0; i < t1; i++) { - t2 = buf[pos + i]; /* key data */ - ctx->notifier.keypress(pd->idx, t2); - } + for (i = 0; i < event.keypress.length; i++) { + event.keypress.data[i] = buf[pos + i]; } + ctx->event_callback(ctx->event_callback_arg, pd->idx, &event); ret = OSDP_CP_ERR_NONE; break; case REPLY_RAW: - if (len < REPLY_RAW_DATA_LEN) { + if (len < REPLY_RAW_DATA_LEN || !ctx->event_callback) { break; } - pos++; /* reader number; skip */ - t1 = buf[pos++]; /* format */ - t2 = buf[pos++]; /* length LSB */ - t2 |= buf[pos++] << 8; /* length MSB */ - if ((len - REPLY_RAW_DATA_LEN) != t2) { + event.type = OSDP_EVENT_CARDREAD; + event.cardread.reader_no = buf[pos++]; + event.cardread.format = buf[pos++]; + event.cardread.length = buf[pos++]; /* bits LSB */ + event.cardread.length |= buf[pos++] << 8; /* bits MSB */ + event.cardread.direction = 0; /* un-specified */ + t1 = (event.cardread.length + 7) / 8; /* len: bytes */ + if (t1 != (len - REPLY_RAW_DATA_LEN)) { break; } - if (ctx->notifier.cardread) { - ctx->notifier.cardread(pd->idx, t1, buf + pos, t2); + for (i = 0; i < t1; i++) { + event.cardread.data[i] = buf[pos + i]; } + ctx->event_callback(ctx->event_callback_arg, pd->idx, &event); ret = OSDP_CP_ERR_NONE; break; case REPLY_FMT: - if (len < REPLY_FMT_DATA_LEN) { + if (len < REPLY_FMT_DATA_LEN || !ctx->event_callback) { break; } - pos++; /* reader number; skip */ - pos++; /* skip one byte -- TODO: handle reader direction */ - t1 = buf[pos++]; /* Key length */ - if ((len - REPLY_FMT_DATA_LEN) != t1) { + event.type = OSDP_EVENT_CARDREAD; + event.cardread.reader_no = buf[pos++]; + event.cardread.direction = buf[pos++]; + event.cardread.length = buf[pos++]; + event.cardread.format = OSDP_CARD_FMT_ASCII; + if (event.cardread.length != (len - REPLY_FMT_DATA_LEN) || + event.cardread.length > OSDP_EVENT_MAX_DATALEN) { break; } - if (ctx->notifier.cardread) { - ctx->notifier.cardread(pd->idx, OSDP_CARD_FMT_ASCII, - buf + pos, t1); + for (i = 0; i < event.cardread.length; i++) { + event.cardread.data[i] = buf[pos + i]; } + ctx->event_callback(ctx->event_callback_arg, pd->idx, &event); ret = OSDP_CP_ERR_NONE; break; case REPLY_BUSY: @@ -593,8 +634,8 @@ static void cp_flush_command_queue(struct osdp_pd *pd) { struct osdp_cmd *cmd; - while (osdp_cmd_dequeue(pd, &cmd) == 0) { - osdp_cmd_free(pd, cmd); + while (cp_cmd_dequeue(pd, &cmd) == 0) { + cp_cmd_free(pd, cmd); } } @@ -638,13 +679,13 @@ static int cp_phy_state_update(struct osdp_pd *pd) ret = OSDP_CP_ERR_GENERIC; break; case OSDP_CP_PHY_STATE_IDLE: - if (osdp_cmd_dequeue(pd, &cmd)) { + if (cp_cmd_dequeue(pd, &cmd)) { ret = OSDP_CP_ERR_NONE; /* command queue is empty */ break; } pd->cmd_id = cmd->id; memcpy(pd->cmd_data, cmd, sizeof(struct osdp_cmd)); - osdp_cmd_free(pd, cmd); + cp_cmd_free(pd, cmd); /* fall-thru */ case OSDP_CP_PHY_STATE_SEND_CMD: if ((cp_send_command(pd)) < 0) { @@ -708,13 +749,13 @@ static int cp_cmd_dispatcher(struct osdp_pd *pd, int cmd) return OSDP_CP_ERR_NONE; /* nothing to be done here */ } - c = osdp_cmd_alloc(pd); + c = cp_cmd_alloc(pd); if (c == NULL) { return OSDP_CP_ERR_GENERIC; } c->id = cmd; - osdp_cmd_enqueue(pd, c); + cp_cmd_enqueue(pd, c); SET_FLAG(pd, PD_FLAG_AWAIT_RESP); return OSDP_CP_ERR_INPROG; } @@ -885,13 +926,13 @@ static int osdp_cp_send_command_keyset(struct osdp_cmd_keyset *cmd) for (i = 0; i < NUM_PD(ctx); i++) { pd = osdp_to_pd(ctx, i); - p = osdp_cmd_alloc(pd); + p = cp_cmd_alloc(pd); if (p == NULL) { return -1; } p->id = CMD_KEYSET; memcpy(&p->keyset, &cmd, sizeof(struct osdp_cmd_keyset)); - osdp_cmd_enqueue(pd, p); + cp_cmd_enqueue(pd, p); } return 0; @@ -925,23 +966,12 @@ int osdp_setup(struct osdp *ctx, uint8_t *key) /* --- Exported Methods --- */ -int osdp_cp_set_callback_key_press(int (*cb)(int address, uint8_t key)) +void osdp_cp_set_event_callback(cp_event_callback_t cb, void *arg) { struct osdp *ctx = osdp_get_ctx(); - ctx->notifier.keypress = cb; - - return 0; -} - -int osdp_cp_set_callback_card_read( - int (*cb)(int address, int format, uint8_t *data, int len)) -{ - struct osdp *ctx = osdp_get_ctx(); - - ctx->notifier.cardread = cb; - - return 0; + ctx->event_callback = cb; + ctx->event_callback_arg = arg; } int osdp_cp_send_command(int pd, struct osdp_cmd *cmd) @@ -984,12 +1014,12 @@ int osdp_cp_send_command(int pd, struct osdp_cmd *cmd) return -1; } - p = osdp_cmd_alloc(osdp_to_pd(ctx, pd)); + p = cp_cmd_alloc(osdp_to_pd(ctx, pd)); if (p == NULL) { return -1; } memcpy(p, cmd, sizeof(struct osdp_cmd)); p->id = cmd_id; /* translate to internal */ - osdp_cmd_enqueue(osdp_to_pd(ctx, pd), p); + cp_cmd_enqueue(osdp_to_pd(ctx, pd), p); return 0; } diff --git a/subsys/mgmt/osdp/src/osdp_pd.c b/subsys/mgmt/osdp/src/osdp_pd.c index f28f345ea19..cb4ad77ae92 100644 --- a/subsys/mgmt/osdp/src/osdp_pd.c +++ b/subsys/mgmt/osdp/src/osdp_pd.c @@ -111,14 +111,93 @@ static struct osdp_pd_cap osdp_pd_cap[] = { { -1, 0, 0 } /* Sentinel */ }; +static struct osdp_event *pd_event_alloc(struct osdp_pd *pd) +{ + struct osdp_event *event = NULL; + + if (k_mem_slab_alloc(&pd->event.slab, (void **)&event, K_MSEC(100))) { + LOG_ERR("Memory allocation time-out"); + return NULL; + } + return event; +} + +static void pd_event_free(struct osdp_pd *pd, struct osdp_event *event) +{ + k_mem_slab_free(&pd->event.slab, (void **)&event); +} + +static void pd_event_enqueue(struct osdp_pd *pd, struct osdp_event *event) +{ + sys_slist_append(&pd->event.queue, &event->node); +} + +static int pd_event_dequeue(struct osdp_pd *pd, struct osdp_event **event) +{ + sys_snode_t *node; + + node = sys_slist_peek_head(&pd->event.queue); + if (node == NULL) { + return -1; + } + sys_slist_remove(&pd->event.queue, NULL, node); + *event = CONTAINER_OF(node, struct osdp_event, node); + return 0; +} + +static int pd_translate_event(struct osdp_pd *pd, struct osdp_event *event) +{ + int reply_code = 0; + + switch (event->type) { + case OSDP_EVENT_CARDREAD: + if (event->cardread.format == OSDP_CARD_FMT_RAW_UNSPECIFIED || + event->cardread.format == OSDP_CARD_FMT_RAW_WIEGAND) { + reply_code = REPLY_RAW; + } else if (event->cardread.format == OSDP_CARD_FMT_ASCII) { + reply_code = REPLY_FMT; + } else { + LOG_ERR("Event: cardread; Error: unknown format"); + break; + } + break; + case OSDP_EVENT_KEYPRESS: + reply_code = REPLY_KEYPPAD; + break; + default: + LOG_ERR("Unknown event type %d", event->type); + break; + } + if (reply_code == 0) { + /* POLL command cannot fail even when there are errors here */ + return REPLY_ACK; + } + memcpy(pd->cmd_data, event, sizeof(struct osdp_event)); + return reply_code; +} + +static bool do_command_callback(struct osdp_pd *pd, struct osdp_cmd *cmd) +{ + int ret; + + ret = pd->command_callback(pd->command_callback_arg, cmd); + if (ret != 0) { + pd->reply_id = REPLY_NAK; + pd->cmd_data[0] = OSDP_PD_NAK_RECORD; + return false; + } + return true; +} + static int pd_decode_command(struct osdp_pd *pd, uint8_t *buf, int len) { int ret = OSDP_PD_ERR_GENERIC; int i, pos = 0; - struct osdp_cmd *cmd; + struct osdp_cmd cmd; + struct osdp_event *event; pd->reply_id = 0; - pd->cmd_id = buf[pos++]; + pd->cmd_id = cmd.id = buf[pos++]; len--; switch (pd->cmd_id) { @@ -126,7 +205,14 @@ static int pd_decode_command(struct osdp_pd *pd, uint8_t *buf, int len) if (len != CMD_POLL_DATA_LEN) { break; } - pd->reply_id = REPLY_ACK; + /* Check if we have external events in the queue */ + if (pd_event_dequeue(pd, &event) == 0) { + ret = pd_translate_event(pd, event); + pd->reply_id = ret; + pd_event_free(pd, event); + } else { + pd->reply_id = REPLY_ACK; + } ret = OSDP_PD_ERR_NONE; break; case CMD_LSTAT: @@ -174,125 +260,118 @@ static int pd_decode_command(struct osdp_pd *pd, uint8_t *buf, int len) ret = OSDP_PD_ERR_NONE; break; case CMD_OUT: - if (len != CMD_OUT_DATA_LEN) { + if (len != CMD_OUT_DATA_LEN || !pd->command_callback) { break; } - cmd = osdp_cmd_alloc(pd); - if (cmd == NULL) { - LOG_ERR("cmd alloc error"); + cmd.id = OSDP_CMD_OUTPUT; + cmd.output.output_no = buf[pos++]; + cmd.output.control_code = buf[pos++]; + cmd.output.timer_count = buf[pos++]; + cmd.output.timer_count |= buf[pos++] << 8; + ret = OSDP_PD_ERR_REPLY; + if (!do_command_callback(pd, &cmd)) { break; } - cmd->id = OSDP_CMD_OUTPUT; - cmd->output.output_no = buf[pos++]; - cmd->output.control_code = buf[pos++]; - cmd->output.timer_count = buf[pos++]; - cmd->output.timer_count |= buf[pos++] << 8; - osdp_cmd_enqueue(pd, cmd); pd->reply_id = REPLY_ACK; ret = OSDP_PD_ERR_NONE; break; case CMD_LED: - if (len != CMD_LED_DATA_LEN) { + if (len != CMD_LED_DATA_LEN || !pd->command_callback) { break; } - cmd = osdp_cmd_alloc(pd); - if (cmd == NULL) { - LOG_ERR("cmd alloc error"); + cmd.id = OSDP_CMD_LED; + cmd.led.reader = buf[pos++]; + cmd.led.led_number = buf[pos++]; + + cmd.led.temporary.control_code = buf[pos++]; + cmd.led.temporary.on_count = buf[pos++]; + cmd.led.temporary.off_count = buf[pos++]; + cmd.led.temporary.on_color = buf[pos++]; + cmd.led.temporary.off_color = buf[pos++]; + cmd.led.temporary.timer_count = buf[pos++]; + cmd.led.temporary.timer_count |= buf[pos++] << 8; + + cmd.led.permanent.control_code = buf[pos++]; + cmd.led.permanent.on_count = buf[pos++]; + cmd.led.permanent.off_count = buf[pos++]; + cmd.led.permanent.on_color = buf[pos++]; + cmd.led.permanent.off_color = buf[pos++]; + ret = OSDP_PD_ERR_REPLY; + if (!do_command_callback(pd, &cmd)) { break; } - cmd->id = OSDP_CMD_LED; - cmd->led.reader = buf[pos++]; - cmd->led.led_number = buf[pos++]; - - cmd->led.temporary.control_code = buf[pos++]; - cmd->led.temporary.on_count = buf[pos++]; - cmd->led.temporary.off_count = buf[pos++]; - cmd->led.temporary.on_color = buf[pos++]; - cmd->led.temporary.off_color = buf[pos++]; - cmd->led.temporary.timer_count = buf[pos++]; - cmd->led.temporary.timer_count |= buf[pos++] << 8; - - cmd->led.permanent.control_code = buf[pos++]; - cmd->led.permanent.on_count = buf[pos++]; - cmd->led.permanent.off_count = buf[pos++]; - cmd->led.permanent.on_color = buf[pos++]; - cmd->led.permanent.off_color = buf[pos++]; - osdp_cmd_enqueue(pd, cmd); pd->reply_id = REPLY_ACK; ret = OSDP_PD_ERR_NONE; break; case CMD_BUZ: - if (len != CMD_BUZ_DATA_LEN) { + if (len != CMD_BUZ_DATA_LEN || !pd->command_callback) { break; } - cmd = osdp_cmd_alloc(pd); - if (cmd == NULL) { - LOG_ERR("cmd alloc error"); + cmd.id = OSDP_CMD_BUZZER; + cmd.buzzer.reader = buf[pos++]; + cmd.buzzer.control_code = buf[pos++]; + cmd.buzzer.on_count = buf[pos++]; + cmd.buzzer.off_count = buf[pos++]; + cmd.buzzer.rep_count = buf[pos++]; + ret = OSDP_PD_ERR_REPLY; + if (!do_command_callback(pd, &cmd)) { break; } - cmd->id = OSDP_CMD_BUZZER; - cmd->buzzer.reader = buf[pos++]; - cmd->buzzer.control_code = buf[pos++]; - cmd->buzzer.on_count = buf[pos++]; - cmd->buzzer.off_count = buf[pos++]; - cmd->buzzer.rep_count = buf[pos++]; - osdp_cmd_enqueue(pd, cmd); pd->reply_id = REPLY_ACK; ret = OSDP_PD_ERR_NONE; break; case CMD_TEXT: - if (len < CMD_TEXT_DATA_LEN) { + if (len < CMD_TEXT_DATA_LEN || !pd->command_callback) { break; } - cmd = osdp_cmd_alloc(pd); - if (cmd == NULL) { - LOG_ERR("cmd alloc error"); + cmd.id = OSDP_CMD_TEXT; + cmd.text.reader = buf[pos++]; + cmd.text.control_code = buf[pos++]; + cmd.text.temp_time = buf[pos++]; + cmd.text.offset_row = buf[pos++]; + cmd.text.offset_col = buf[pos++]; + cmd.text.length = buf[pos++]; + if (cmd.text.length > OSDP_CMD_TEXT_MAX_LEN || + ((len - CMD_TEXT_DATA_LEN) < cmd.text.length) || + cmd.text.length > OSDP_CMD_TEXT_MAX_LEN) { break; } - cmd->id = OSDP_CMD_TEXT; - cmd->text.reader = buf[pos++]; - cmd->text.control_code = buf[pos++]; - cmd->text.temp_time = buf[pos++]; - cmd->text.offset_row = buf[pos++]; - cmd->text.offset_col = buf[pos++]; - cmd->text.length = buf[pos++]; - if (cmd->text.length > OSDP_CMD_TEXT_MAX_LEN || - ((len - CMD_TEXT_DATA_LEN) < cmd->text.length) || - cmd->text.length > OSDP_CMD_TEXT_MAX_LEN) { - osdp_cmd_free(pd, cmd); + for (i = 0; i < cmd.text.length; i++) { + cmd.text.data[i] = buf[pos++]; + } + ret = OSDP_PD_ERR_REPLY; + if (!do_command_callback(pd, &cmd)) { break; } - for (i = 0; i < cmd->text.length; i++) { - cmd->text.data[i] = buf[pos++]; - } - osdp_cmd_enqueue(pd, cmd); pd->reply_id = REPLY_ACK; ret = OSDP_PD_ERR_NONE; break; case CMD_COMSET: - if (len != CMD_COMSET_DATA_LEN) { + if (len != CMD_COMSET_DATA_LEN || !pd->command_callback) { break; } - cmd = osdp_cmd_alloc(pd); - if (cmd == NULL) { - LOG_ERR("cmd alloc error"); - break; - } - cmd->id = OSDP_CMD_COMSET; - cmd->comset.address = buf[pos++]; - cmd->comset.baud_rate = buf[pos++]; - cmd->comset.baud_rate |= buf[pos++] << 8; - cmd->comset.baud_rate |= buf[pos++] << 16; - cmd->comset.baud_rate |= buf[pos++] << 24; - if (cmd->comset.address >= 0x7F || - (cmd->comset.baud_rate != 9600 && - cmd->comset.baud_rate != 38400 && - cmd->comset.baud_rate != 115200)) { + cmd.id = OSDP_CMD_COMSET; + cmd.comset.address = buf[pos++]; + cmd.comset.baud_rate = buf[pos++]; + cmd.comset.baud_rate |= buf[pos++] << 8; + cmd.comset.baud_rate |= buf[pos++] << 16; + cmd.comset.baud_rate |= buf[pos++] << 24; + if (cmd.comset.address >= 0x7F || + (cmd.comset.baud_rate != 9600 && + cmd.comset.baud_rate != 19200 && + cmd.comset.baud_rate != 38400 && + cmd.comset.baud_rate != 115200 && + cmd.comset.baud_rate != 230400)) { LOG_ERR("COMSET Failed! command discarded"); - cmd->comset.address = pd->address; - cmd->comset.baud_rate = pd->baud_rate; + cmd.comset.address = pd->address; + cmd.comset.baud_rate = pd->baud_rate; + break; } - osdp_cmd_enqueue(pd, cmd); + if (!do_command_callback(pd, &cmd)) { + ret = OSDP_PD_ERR_REPLY; + break; + } + memcpy(pd->cmd_data, &cmd, sizeof(struct osdp_cmd)); pd->reply_id = REPLY_COM; ret = OSDP_PD_ERR_NONE; break; @@ -317,17 +396,18 @@ static int pd_decode_command(struct osdp_pd *pd, uint8_t *buf, int len) buf[pos], buf[pos + 1]); break; } - cmd = osdp_cmd_alloc(pd); - if (cmd == NULL) { - LOG_ERR("cmd alloc error"); + cmd.id = OSDP_CMD_KEYSET; + cmd.keyset.type = buf[pos++]; + cmd.keyset.length = buf[pos++]; + memcpy(cmd.keyset.data, buf + pos, 16); + if (!pd->command_callback) { + LOG_ERR("Keyset without a command callback! The SC new " + "SCBK will be lost when the PD reboots."); + } else if (!do_command_callback(pd, &cmd)) { + ret = OSDP_PD_ERR_REPLY; break; } - cmd->id = OSDP_CMD_KEYSET; - cmd->keyset.type = buf[pos++]; - cmd->keyset.length = buf[pos++]; - memcpy(cmd->keyset.data, buf + pos, 16); - memcpy(pd->sc.scbk, buf + pos, 16); - osdp_cmd_enqueue(pd, cmd); + memcpy(pd->sc.scbk, cmd.keyset.data, 16); CLEAR_FLAG(pd, PD_FLAG_SC_USE_SCBKD); CLEAR_FLAG(pd, PD_FLAG_INSTALL_MODE); pd->reply_id = REPLY_ACK; @@ -479,12 +559,7 @@ static int pd_build_reply(struct osdp_pd *pd, uint8_t *buf, int max_len) * TODO: Persist pd->address and pd->baud_rate via * subsys/settings */ - cmd = osdp_cmd_get_last(pd); - if (cmd == NULL || cmd->id != OSDP_CMD_COMSET) { - LOG_ERR("Failed to fetch queue tail for COMSET"); - break; - } - + cmd = (struct osdp_cmd *)pd->cmd_data; buf[len++] = pd->reply_id; buf[len++] = cmd->comset.address; buf[len++] = BYTE_0(cmd->comset.baud_rate); @@ -771,15 +846,25 @@ int osdp_setup(struct osdp *ctx, uint8_t *key) /* --- Exported Methods --- */ -int osdp_pd_get_cmd(struct osdp_cmd *cmd) +void osdp_pd_set_command_callback(pd_command_callback_t cb, void *arg) { - struct osdp_cmd *c; struct osdp_pd *pd = osdp_to_pd(osdp_get_ctx(), 0); - if (osdp_cmd_dequeue(pd, &c)) { + pd->command_callback_arg = arg; + pd->command_callback = cb; +} + +int osdp_pd_notify_event(const struct osdp_event *event) +{ + struct osdp_event *ev; + struct osdp_pd *pd = osdp_to_pd(osdp_get_ctx(), 0); + + ev = pd_event_alloc(pd); + if (ev == NULL) { return -1; } - memcpy(cmd, c, sizeof(struct osdp_cmd)); - osdp_cmd_free(pd, c); + + memcpy(ev, event, sizeof(struct osdp_event)); + pd_event_enqueue(pd, ev); return 0; }