BMLite_Nordic/BMLite_example/src/bep_host_if.c
Andrey Perminov 06b99d7e18 Initial commit
Change-Id: I532af5014d3392fb525487a4894673405cf6fe42
2020-04-16 15:40:31 -07:00

543 lines
17 KiB
C

/*
* Copyright (c) 2020 Fingerprint Cards AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file bep_host_if.c
* @brief BEP Host Interface implementation.
*/
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include "fpc_bep_types.h"
#include "fpc_hcp_common.h"
#include "fpc_com_result.h"
#include "fpc_hcp.h"
#include "platform.h"
#include "com_common.h"
#include "bep_host_if.h"
/** Returns the number of elements in an array. */
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define RECEIVE_TIMEOUT 10
#ifdef DEBUG
#define log_debug(format, ...) printf(format, ##__VA_ARGS__)
#else
#define log_debug(format, ...)
#endif
// #define log_info(format, ...) printf(format, ##__VA_ARGS__)
// #define log_error(format, ...) printf(format, ##__VA_ARGS__)
#define log_info(format, ...)
#define log_error(format, ...)
/** Maximum attempts for capture image */
static const uint8_t MAX_CAPTURE_ATTEMPTS = 15U;
static const uint16_t CAPTURE_TIMEOUT = 3000;
/**
* @brief Helper function for sending HCP commands
*
* @param chain HCP communication chain
* @param command_id command to send
* @param arg_key1 first key to add to the command
* @param arg_data1 first argument data to add
* @param arg_data1_length first data length of argument data
* @param arg_key2 second key to add to the command
* @param arg_data2 second argument data to add
* @param arg_data2_length second data length of argument data
* @return ::fpc_bep_result_t
*/
static fpc_bep_result_t send_command_args2(fpc_com_chain_t *chain, fpc_hcp_cmd_t command_id,
fpc_hcp_arg_t arg_key1, void *arg_data1, uint16_t arg_data1_length,
fpc_hcp_arg_t arg_key2, void *arg_data2, uint16_t arg_data2_length)
{
fpc_hcp_packet_t command;
fpc_bep_result_t bep_result;
fpc_com_result_t com_result;
fpc_hcp_arg_data_t args_tx[10] = {{ 0 }};
memset(&command, 0x0, sizeof(command));
command.arguments = args_tx;
command.num_args = ARRAY_SIZE(args_tx);
command.id = command_id;
if (arg_key1 != ARG_NONE) {
if (!fpc_hcp_arg_add(&command, arg_key1, arg_data1_length, false, arg_data1)) {
log_error("%s:%u Could not add arg:%u\n", __func__, __LINE__, arg_key1);
bep_result = FPC_BEP_RESULT_NO_MEMORY;
goto exit;
}
}
if (arg_key2 != ARG_NONE) {
if (!fpc_hcp_arg_add(&command, arg_key2, arg_data2_length, false, arg_data2)) {
log_error("%s:%u Could not add arg:%u\n", __func__, __LINE__, arg_key2);
bep_result = FPC_BEP_RESULT_NO_MEMORY;
goto exit;
}
}
com_result = fpc_hcp_transmit(&command, chain);
bep_result = com_to_bep_result(com_result);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s:%u ERROR %d\n", __func__, __LINE__, bep_result);
}
exit:
fpc_hcp_free(chain, &command);
return bep_result;
}
static fpc_bep_result_t send_command_no_args(fpc_com_chain_t *chain, fpc_hcp_cmd_t command_id)
{
return send_command_args2(chain, command_id, ARG_NONE, NULL, 0, ARG_NONE, NULL, 0);
}
static fpc_bep_result_t send_command(fpc_com_chain_t *chain, fpc_hcp_cmd_t command_id,
fpc_hcp_arg_t arg_key, void *arg_data, uint16_t arg_data_length)
{
return send_command_args2(chain, command_id, arg_key, arg_data, arg_data_length,
ARG_NONE, NULL, 0);
}
/**
* @brief Helper function for receiving HCP commands
* @param command_id command to send
* @param arg_key1 first key to receive
* @param arg_data1 first argument data
* @param arg_data1_length first argument data length
* @param arg_key2 second key to receive
* @param arg_data2 second argument data
* @param arg_data2_length second argument
* @return ::fpc_bep_result_t
*/
static fpc_bep_result_t receive_result_args2(fpc_com_chain_t *chain,
fpc_hcp_arg_t arg_key1, void *arg_data1, uint16_t arg_data1_length,
fpc_hcp_arg_t arg_key2, void *arg_data2, uint16_t arg_data2_length)
{
fpc_hcp_packet_t response;
fpc_hcp_arg_data_t args_rx[10] = {{ 0 }};
fpc_bep_result_t bep_result = FPC_BEP_RESULT_GENERAL_ERROR;
fpc_hcp_arg_data_t *arg_data;
memset(&response, 0x0, sizeof(fpc_hcp_cmd_t));
response.arguments = args_rx;
response.num_args = ARRAY_SIZE(args_rx);
do {
fpc_com_result_t com_result = fpc_hcp_receive(&response, chain);
bep_result = com_to_bep_result(com_result);
} while (bep_result == FPC_BEP_RESULT_TIMEOUT);
if (bep_result != FPC_BEP_RESULT_OK) {
goto exit;
}
/* Check bep result first */
arg_data = fpc_hcp_arg_get(&response, ARG_RESULT);
if (arg_data) {
bep_result = *(int8_t *)arg_data->data;
} else {
log_error("%s Result argument missing\n", __func__);
bep_result = FPC_BEP_RESULT_INVALID_ARGUMENT;
}
if (bep_result != FPC_BEP_RESULT_OK) {
goto exit;
}
/* Get first argument */
if (arg_key1 != ARG_NONE) {
arg_data = fpc_hcp_arg_get(&response, arg_key1);
if (arg_data && arg_data->size <= arg_data1_length) {
memcpy(arg_data1, arg_data->data, arg_data->size);
} else {
log_error("%s %d argument missing\n", __func__, arg_key1);
bep_result = FPC_BEP_RESULT_INVALID_ARGUMENT;
goto exit;
}
}
/* Get second argument */
if (arg_key2 != ARG_NONE) {
arg_data = fpc_hcp_arg_get(&response, arg_key2);
if (arg_data && arg_data->size <= arg_data2_length) {
memcpy(arg_data2, arg_data->data, arg_data->size);
} else {
/* Not an error since the second argument is optional */
log_debug("%s %d argument missing\n", __func__, arg_key2);
}
}
exit:
fpc_hcp_free(chain, &response);
return bep_result;
}
static fpc_bep_result_t receive_result_no_args(fpc_com_chain_t *chain)
{
return receive_result_args2(chain, ARG_NONE, NULL, 0, ARG_NONE, NULL, 0);
}
static fpc_bep_result_t receive_result_args1(fpc_com_chain_t *chain,
fpc_hcp_arg_t arg_key, void *arg_data, uint16_t arg_data_length)
{
return receive_result_args2(chain, arg_key, arg_data, arg_data_length, ARG_NONE, NULL, 0);
}
fpc_bep_result_t bep_capture(fpc_com_chain_t *chain, uint16_t timeout)
{
fpc_bep_result_t bep_result;
log_info("Put finger on sensor\n");
/* Capture finger down */
bep_result = send_command(chain, CMD_CAPTURE, ARG_TIMEOUT, &timeout, sizeof(timeout));
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s:%u Error transmitting CMD_CAPTURE\n", __func__, __LINE__);
return bep_result;
}
platform_set_led(BMLITE_LED_STATUS_WAITTOUCH);
bep_result = receive_result_no_args(chain);
platform_set_led(BMLITE_LED_STATUS_READY);
return bep_result;
}
fpc_bep_result_t bep_enroll_finger(fpc_com_chain_t *chain)
{
uint32_t samples_remaining = 0;
fpc_bep_result_t bep_result = FPC_BEP_RESULT_OK;
bool enroll_done = false;
/* Enroll start */
bep_result = send_command(chain, CMD_ENROLL, ARG_START, NULL, 0);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s, ERROR line:%u\n", __func__, __LINE__);
goto exit;
}
bep_result = receive_result_no_args(chain);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s:%u, ERROR receiving status=%d\n", __func__, __LINE__, bep_result);
goto exit;
}
for (uint8_t i = 0; i < MAX_CAPTURE_ATTEMPTS; ++i) {
bep_result = bep_capture(chain, CAPTURE_TIMEOUT);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("Capture failed\n");
break;
}
log_info("Capture done. Remove finger from sensor\n");
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s:%u, ERROR receiving, result=%d\n", __func__, __LINE__, bep_result);
continue;
}
/* Enroll add */
bep_result = send_command(chain, CMD_ENROLL, ARG_ADD, NULL, 0);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s:%u, ERROR\n", __func__, __LINE__);
continue;
}
bep_result = receive_result_args1(chain, ARG_COUNT, &samples_remaining,
sizeof(samples_remaining));
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s:%u, ERROR receiving status=%d\n", __func__, __LINE__, bep_result);
continue;
}
log_info("Enroll samples remaining: %d\n", samples_remaining);
if (samples_remaining == 0U) {
enroll_done = true;
break;
}
bep_result = send_command(chain, CMD_WAIT, ARG_FINGER_UP, NULL, 0);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s:%u, ERROR\n", __func__, __LINE__);
continue;
}
/* Wait for finger to be lifted from sensor */
bep_result = receive_result_no_args(chain);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s:%u, ERROR receiving status=%d\n", __func__, __LINE__, bep_result);
continue;
}
}
bep_result = send_command(chain, CMD_ENROLL, ARG_FINISH, NULL, 0);
if (bep_result == FPC_BEP_RESULT_OK) {
bep_result = receive_result_no_args(chain);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s:%u, ERROR receiving status=%d\n", __func__, __LINE__, bep_result);
}
}
exit:
return (!enroll_done) ? FPC_BEP_RESULT_GENERAL_ERROR : bep_result;
}
fpc_bep_result_t bep_identify_finger(fpc_com_chain_t *chain, uint16_t *template_id, bool *match)
{
fpc_bep_result_t bep_result = FPC_BEP_RESULT_OK;
*match = false;
bep_result = bep_capture(chain, CAPTURE_TIMEOUT);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("Capture failed result=%d\n", bep_result);
return bep_result;
}
log_info("Capture done. Remove finger from sensor\n");
bep_result = bep_image_extract(chain);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("Extract failed\n");
return bep_result;
}
bep_result = send_command(chain, CMD_IDENTIFY, ARG_NONE, NULL, 0);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("Identify failed\n");
return bep_result;
}
bep_result = receive_result_args2(chain, ARG_MATCH, match, sizeof(bool),
ARG_ID, template_id, sizeof(uint16_t));
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("Identify failed\n");
return bep_result;
}
return bep_result;
}
fpc_bep_result_t bep_save_template(fpc_com_chain_t *chain, uint16_t template_id)
{
fpc_bep_result_t bep_result = FPC_BEP_RESULT_OK;
bep_result = send_command_args2(chain, CMD_TEMPLATE, ARG_SAVE, NULL, 0, ARG_ID, &template_id,
sizeof(template_id));
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s:%u, ERROR\n", __func__, __LINE__);
return bep_result;
}
return receive_result_no_args(chain);
}
fpc_bep_result_t bep_delete_template(fpc_com_chain_t *chain, uint16_t template_id)
{
fpc_bep_result_t bep_result = FPC_BEP_RESULT_OK;
if (template_id == REMOVE_ID_ALL_TEMPLATES) {
bep_result = send_command_args2(chain, CMD_STORAGE_TEMPLATE, ARG_DELETE, NULL, 0,
ARG_ALL, NULL, 0);
} else {
bep_result = send_command_args2(chain, CMD_STORAGE_TEMPLATE, ARG_DELETE, NULL, 0,
ARG_ID, &template_id, sizeof(template_id));
}
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s:%u, ERROR\n", __func__, __LINE__);
return bep_result;
}
return receive_result_no_args(chain);
}
fpc_bep_result_t bep_get_template_count(fpc_com_chain_t *chain, uint32_t *template_count)
{
fpc_bep_result_t bep_result = FPC_BEP_RESULT_OK;
bep_result = send_command(chain, CMD_STORAGE_TEMPLATE, ARG_COUNT, NULL, 0);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s:%u ERROR sending CMD_STORAGE_TEMPLATE\n", __func__, __LINE__);
return bep_result;
}
bep_result = receive_result_args1(chain, ARG_COUNT, template_count, sizeof(template_count[0]));
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s:%u, ERROR receiving status=%d\n", __func__, __LINE__, bep_result);
return bep_result;
}
return bep_result;
}
fpc_bep_result_t bep_get_template_ids(fpc_com_chain_t *chain, uint16_t *template_ids,
uint32_t nof_templates)
{
fpc_bep_result_t bep_result = FPC_BEP_RESULT_OK;
bep_result = send_command(chain, CMD_STORAGE_TEMPLATE, ARG_ID, NULL, 0);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s:%u ERROR sending CMD_STORAGE_TEMPLATE\n", __func__, __LINE__);
return bep_result;
}
bep_result = receive_result_args1(chain, ARG_DATA, template_ids, nof_templates *
sizeof(template_ids[0]));
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s:%u, ERROR receiving status=%d\n", __func__, __LINE__, bep_result);
return bep_result;
}
return bep_result;
}
fpc_bep_result_t bep_image_extract(fpc_com_chain_t *chain)
{
fpc_bep_result_t bep_result;
bep_result = send_command(chain, CMD_IMAGE, ARG_EXTRACT, NULL, 0);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("Extract failed\n");
return bep_result;
}
return receive_result_no_args(chain);
}
fpc_bep_result_t bep_image_get_size(fpc_com_chain_t *chain, uint32_t *size)
{
fpc_bep_result_t bep_result;
bep_result = send_command(chain, CMD_IMAGE, ARG_SIZE, NULL, 0);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("Extract failed\n");
return bep_result;
}
log_info("Downloading image data...\n");
return receive_result_args1(chain, ARG_SIZE, size, sizeof(size));
}
fpc_bep_result_t bep_image_get(fpc_com_chain_t *chain, uint8_t *data, uint32_t size)
{
fpc_bep_result_t bep_result;
bep_result = send_command(chain, CMD_IMAGE, ARG_UPLOAD, NULL, 0);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("Extract failed\n");
return bep_result;
}
return receive_result_args1(chain, ARG_DATA, data, size);
}
fpc_bep_result_t bep_version(fpc_com_chain_t *chain, char *version, int len)
{
fpc_bep_result_t bep_result;
bep_result = send_command_args2(chain, CMD_INFO, ARG_GET, NULL, 0, ARG_VERSION, NULL, 0);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s, ERROR line:%u\n", __func__, __LINE__);
return bep_result;
}
return receive_result_args1(chain, ARG_VERSION, version, len);
}
fpc_bep_result_t bep_reset(fpc_com_chain_t *chain)
{
fpc_bep_result_t bep_result;
bep_result = send_command_no_args(chain, CMD_RESET);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s:%u, ERROR\n", __func__, __LINE__);
return bep_result;
}
return receive_result_no_args(chain);
}
fpc_bep_result_t bep_sensor_calibrate(fpc_com_chain_t *chain)
{
fpc_bep_result_t bep_result;
bep_result = send_command_no_args(chain, CMD_STORAGE_CALIBRATION);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s:%u, ERROR\n", __func__, __LINE__);
return bep_result;
}
return receive_result_no_args(chain);
}
fpc_bep_result_t bep_sensor_calibrate_remove(fpc_com_chain_t *chain)
{
fpc_bep_result_t bep_result;
bep_result = send_command(chain, CMD_STORAGE_CALIBRATION, ARG_DELETE, NULL, 0);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s:%u, ERROR\n", __func__, __LINE__);
return bep_result;
}
return receive_result_no_args(chain);
}
fpc_bep_result_t bep_sensor_wait_for_finger(fpc_com_chain_t *chain, uint16_t timeout)
{
fpc_bep_result_t bep_result;
bep_result = send_command(chain, CMD_WAIT, ARG_FINGER_DOWN, NULL, 0);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s:%u, ERROR\n", __func__, __LINE__);
return bep_result;
}
/* Wait for finger on sensor */
return receive_result_no_args(chain);
}
fpc_bep_result_t bep_sensor_wait_finger_not_present(fpc_com_chain_t *chain, uint16_t timeout)
{
fpc_bep_result_t bep_result;
bep_result = send_command(chain, CMD_WAIT, ARG_FINGER_UP, NULL, 0);
if (bep_result != FPC_BEP_RESULT_OK) {
log_error("%s:%u, ERROR\n", __func__, __LINE__);
return bep_result;
}
/* Wait for finger to be lifted from sensor */
return receive_result_no_args(chain);
}