From ac65cf455e26a898b1d93f0c55ab876dae329f02 Mon Sep 17 00:00:00 2001 From: Vincent Huang Date: Mon, 29 Jul 2019 15:55:27 +0200 Subject: [PATCH] synaptics: Add synaptics driver Heavily modified by Benjamin Berg to port it to the new libfprint API and adjust the coding style to follow more closely other drivers. --- libfprint/drivers/synaptics/bmkt.h | 236 +++++ libfprint/drivers/synaptics/bmkt_message.c | 411 ++++++++ libfprint/drivers/synaptics/bmkt_message.h | 86 ++ libfprint/drivers/synaptics/bmkt_response.h | 489 +++++++++ libfprint/drivers/synaptics/sensor.h | 80 ++ libfprint/drivers/synaptics/synaptics.c | 1040 +++++++++++++++++++ libfprint/drivers/synaptics/synaptics.h | 130 +++ libfprint/meson.build | 6 + meson.build | 2 +- 9 files changed, 2479 insertions(+), 1 deletion(-) create mode 100644 libfprint/drivers/synaptics/bmkt.h create mode 100644 libfprint/drivers/synaptics/bmkt_message.c create mode 100644 libfprint/drivers/synaptics/bmkt_message.h create mode 100644 libfprint/drivers/synaptics/bmkt_response.h create mode 100644 libfprint/drivers/synaptics/sensor.h create mode 100644 libfprint/drivers/synaptics/synaptics.c create mode 100644 libfprint/drivers/synaptics/synaptics.h diff --git a/libfprint/drivers/synaptics/bmkt.h b/libfprint/drivers/synaptics/bmkt.h new file mode 100644 index 0000000..de7f875 --- /dev/null +++ b/libfprint/drivers/synaptics/bmkt.h @@ -0,0 +1,236 @@ +/* + * Synaptics MiS Fingerprint Sensor Interface + * Copyright (C) 2019 Synaptics Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _BMKT_H_ +#define _BMKT_H_ + +/**< User ID maximum length allowed */ +#define BMKT_MAX_USER_ID_LEN 100 +/**< Software Part Number length */ +#define BMKT_PART_NUM_LEN 10 +/**< Software supplier identification length */ +#define BMKT_SUPPLIER_ID_LEN 2 + +/**< Maximum namber of templates for storing in internal flash of the fingerprint sensor */ +#define BMKT_MAX_NUM_TEMPLATES_INTERNAL_FLASH 15 + +#include +#include "bmkt_response.h" + +/*! +******************************************************************************* +** Type definition for result +*/ +/** No error; Operation successfully completed. */ +#define BMKT_SUCCESS 0 +/** Fingerprint system not initialized */ +#define BMKT_FP_SYSTEM_NOT_INITIALIZED 101 +/** Fingerprint system busy performing another operation */ +#define BMKT_FP_SYSTEM_BUSY 102 +/** Operation not allowed */ +#define BMKT_OPERATION_DENIED 103 +/** System ran out of memory while performing operation */ +#define BMKT_OUT_OF_MEMORY 104 +/** Corrupt message, CRC check fail or truncated message */ +#define BMKT_CORRUPT_MESSAGE 110 +/** One of the command parameters is outside the range of valid values */ +#define BMKT_INVALID_PARAM 111 +/** Unrecognized message or message with invalid message ID */ +#define BMKT_UNRECOGNIZED_MESSAGE 112 +/** Operation time out */ +#define BMKT_OP_TIME_OUT 113 +/** General error – cause of error cannot be determined */ +#define BMKT_GENERAL_ERROR 114 + +#define BMKT_SET_SECURITY_LEVEL_FAIL 120 +#define BMKT_GET_SECURITY_LEVEL_FAIL 121 + +/** Fingerprint sensor reset while operation was being performed */ +#define BMKT_SENSOR_RESET 201 +/** Fingerprint sensor malfunctioned */ +#define BMKT_SENSOR_MALFUNCTION 202 +/** Fingerprint sensor cannot be accessed despite repeated attempts */ +#define BMKT_SENSOR_TAMPERED 203 +/** +* BMKT_SENSOR_NOT_INIT: +* Fingerprint sensor module not initialized yet – not ready for use +* (different from error code 101 which indicates that the entire system +* has not been initialized) +*/ +#define BMKT_SENSOR_NOT_INIT 204 +/** Number of re-pairing operations exceeded limit or re-pairing has been disabled */ +#define BMKT_OWNERSHIP_RESET_MAX_EXCEEDED 205 +/** +* BMKT_SENSOR_STIMULUS_ERROR: +* There is a finger or debris on the sensor that needs to be removed +* before issuing this command +*/ +#define BMKT_SENSOR_STIMULUS_ERROR 213 +/** +* BMKT_CORRUPT_TEMPLATE_DATA: +* One of the fingerprint templates stored on flash is corrupt. +* This error code is returned in case of failure in finding a fingerprint match +* during identify or verify operations while also detecting that one or more +* fingerprint templates stored on the flash has become corrupted +*/ +#define BMKT_CORRUPT_TEMPLATE_DATA 300 +/** Failed to extract features from fingerprint image acquired by sensor */ +#define BMKT_FEATURE_EXTRACT_FAIL 301 +/** Failed to generate fingerprint template */ +#define BMKT_ENROLL_FAIL 302 +/** Specified finger already enrolled for this user */ +#define BMKT_ENROLLMENT_EXISTS 303 +/** Invalid fingerprint image */ +#define BMKT_INVALID_FP_IMAGE 304 +/** No matching user fingerprint template found in database */ +#define BMKT_FP_NO_MATCH 404 +/** Fingerprint database is full */ +#define BMKT_FP_DATABASE_FULL 501 +/** Fingerprint database is empty */ +#define BMKT_FP_DATABASE_EMPTY 502 +/** Cannot access fingerprint database */ +#define BMKT_FP_DATABASE_ACCESS_FAIL 503 +/** Fingerprint template record does not exist */ +#define BMKT_FP_DATABASE_NO_RECORD_EXISTS 504 +/** Failed to read/write system parameters stored on flash */ +#define BMKT_FP_PARAM_ACCESS_FAIL 505 +/** Fingerprint is a spoof */ +#define BMKT_FP_SPOOF_ALERT 801 +/** Anti-spoof module failure */ +#define BMKT_ANTI_SPOOF_MODULE_FAIL 802 + +#define BMKT_CORRUPT_UPDATE_IMAGE 901 +#define BMKT_SYSTEM_UPDATE_FAIL 902 + +#define BMKT_EVENT_NOT_SET 1000 +#define BMKT_SENSOR_NOT_READY 1001 +#define BMKT_TIMEOUT 1002 +#define BMKT_SENSOR_RESPONSE_PENDING 1003 + + +#ifdef __cplusplus +extern "C" { +#endif + +/** +* bmkt_mode: +* Fingerprint system operational mode values level 1 +*/ +typedef enum bmkt_mode +{ + BMKT_STATE_UNINIT = 0xFF, + BMKT_STATE_IDLE = 0x00, + BMKT_STATE_ENROLL = 0x10, + BMKT_STATE_IDENTIFY = 0x20, + BMKT_STATE_VERIFY = 0x30, + BMKT_STATE_DB_OPS = 0x40, + BMKT_STATE_SYS_TEST = 0x50, + BMKT_STATE_SYS_OPS = 0x60, +} bmkt_mode_t; + +/** +* bmkt_mode_level2: +* Fingerprint system operational mode values level 2 +*/ +typedef enum bmkt_mode_level2 +{ + BMKT_STATE_L2_IDLE = 0x00, + BMKT_STATE_L2_STARTING = 0x11, + BMKT_STATE_L2_WAITING_FOR_FINGER = 0x12, + BMKT_STATE_L2_CAPTURE_IMAGE = 0x13, + BMKT_STATE_L2_CAPTURE_COMPLETE = 0x14, + BMKT_STATE_L2_EXTRACT_FEATURE = 0x15, + BMKT_STATE_L2_CREATE_TEMPLATE = 0x16, + BMKT_STATE_L2_READING_FROM_FLASH = 0x17, + BMKT_STATE_L2_WRITING_TO_FLASH = 0x18, + BMKT_STATE_L2_FINISHING = 0x19, + BMKT_STATE_L2_CANCELING_OP = 0x20, + BMKT_STATE_L2_MATCHING = 0x21, + BMKT_STATE_L2_TRANSMITTING_RESPONSE = 0x22, + BMKT_STATE_L2_READY_POWER_DOWN = 0xF0, +} bmkt_mode_level2_t; + +/** +* bmkt_transport_type: +* Fingerprint system transport types +*/ +typedef enum bmkt_transport_type +{ + BMKT_TRANSPORT_TYPE_USB = 0, +} bmkt_transport_type_t; + +/** +* bmkt_usb_config: +* Structure represcontainingenting USB configuration details +*/ +typedef struct bmkt_usb_config +{ + int product_id; /**< USB device product ID */ +} bmkt_usb_config_t; + +/** +* bmkt_transport_config_t: +* Union containing transport configuration details +*/ +typedef union +{ + bmkt_usb_config_t usb_config; +} bmkt_transport_config_t; + +/** +* bmkt_sensor_desc_t: +* Structure containing fingerprint system description +*/ +typedef struct bmkt_sensor_desc +{ + int product_id; + int flags; +} bmkt_sensor_desc_t; + +/** +* bmkt_finger_state_t: +* Finger state representation values. +*/ +typedef enum +{ + BMKT_FINGER_STATE_UNKNOWN = 0, + BMKT_FINGER_STATE_ON_SENSOR, + BMKT_FINGER_STATE_NOT_ON_SENSOR, +} bmkt_finger_state_t; + +/** +* bmkt_finger_event_t: +* Structure containing finger state +*/ +typedef struct bmkt_finger_event +{ + bmkt_finger_state_t finger_state; +} bmkt_finger_event_t; + +typedef struct bmkt_user_id +{ + uint8_t user_id_len; + uint8_t user_id[BMKT_MAX_USER_ID_LEN]; +} bmkt_user_id_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _BMKT_H_ */ diff --git a/libfprint/drivers/synaptics/bmkt_message.c b/libfprint/drivers/synaptics/bmkt_message.c new file mode 100644 index 0000000..fe0bebe --- /dev/null +++ b/libfprint/drivers/synaptics/bmkt_message.c @@ -0,0 +1,411 @@ +/* + * Copyright (C) 2019 Synaptics Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "bmkt_response.h" +#include "bmkt_message.h" + +static uint8_t extract8(const uint8_t *buf, int *offset) +{ + uint8_t ret = 0; + int off = 0; + if (offset) + off = *offset; + + ret = *(buf + off); + + if (offset) + *offset += 1; + + return ret; +} + + +static int parse_error_response(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp) +{ + if (msg_resp->payload_len != 2) + { + return BMKT_UNRECOGNIZED_MESSAGE; + } + + resp->result = (msg_resp->payload[0] << 8) | msg_resp->payload[1]; + + return BMKT_SUCCESS; +} + +static int parse_init_ok(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp) +{ + bmkt_init_resp_t *init_resp = &resp->response.init_resp; + + if (msg_resp->payload_len != 1) + { + return BMKT_UNRECOGNIZED_MESSAGE; + } + + init_resp->finger_presence = extract8(msg_resp->payload, NULL); + + return BMKT_SUCCESS; +} + + +static int parse_fps_mode_report(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp) +{ + int offset = 0; + bmkt_fps_mode_resp_t *fps_mode_resp = &resp->response.fps_mode_resp; + + if (msg_resp->payload_len != sizeof(bmkt_fps_mode_resp_t)) + { + return BMKT_UNRECOGNIZED_MESSAGE; + } + + fps_mode_resp->mode = extract8(msg_resp->payload, &offset); + fps_mode_resp->level2_mode = extract8(msg_resp->payload, &offset); + fps_mode_resp->cmd_id = extract8(msg_resp->payload, &offset); + fps_mode_resp->finger_presence = extract8(msg_resp->payload, &offset); + + return BMKT_SUCCESS; +} + +static int parse_enroll_report(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp) +{ + bmkt_enroll_resp_t *enroll_resp = &resp->response.enroll_resp; + + if (msg_resp->payload_len != 1) + { + return BMKT_UNRECOGNIZED_MESSAGE; + } + + enroll_resp->progress = extract8(msg_resp->payload, NULL); + + return BMKT_SUCCESS; +} + +static int parse_enroll_ok(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp) +{ + bmkt_enroll_resp_t *enroll_resp = &resp->response.enroll_resp; + + if (msg_resp->payload_len < 1 || msg_resp->payload_len > (BMKT_MAX_USER_ID_LEN + 1)) + { + return BMKT_UNRECOGNIZED_MESSAGE; + } + + enroll_resp->finger_id = msg_resp->payload[0]; + memcpy(enroll_resp->user_id, &msg_resp->payload[1], msg_resp->payload_len - 1); + + return BMKT_SUCCESS; +} + +static int parse_auth_ok(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp) +{ + bmkt_identify_resp_t *id_resp = &resp->response.id_resp; + + if (msg_resp->payload_len < 3 || msg_resp->payload_len > (BMKT_MAX_USER_ID_LEN + 3)) + { + return BMKT_UNRECOGNIZED_MESSAGE; + } + + id_resp->match_result = (double)msg_resp->payload[0] + 0.01 * (double)msg_resp->payload[1]; + id_resp->finger_id = msg_resp->payload[2]; + memcpy(id_resp->user_id, &msg_resp->payload[3], msg_resp->payload_len - 3); + + return BMKT_SUCCESS; +} + +static int parse_security_level_report(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp) +{ + bmkt_set_sec_level_resp_t *sec_level_resp = &resp->response.sec_level_resp; + + if (msg_resp->payload_len != 1) + { + return BMKT_UNRECOGNIZED_MESSAGE; + } + + sec_level_resp->sec_level = extract8(msg_resp->payload, NULL); + + return BMKT_SUCCESS; +} + +static int parse_del_all_users_progress_report(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp) +{ + bmkt_del_all_users_resp_t *del_all_users_resp = &resp->response.del_all_users_resp; + + if (msg_resp->payload_len != 1) + { + return BMKT_UNRECOGNIZED_MESSAGE; + } + + del_all_users_resp->progress = extract8(msg_resp->payload, NULL); + + return BMKT_SUCCESS; +} + +static int parse_db_cap_report(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp) +{ + bmkt_get_db_capacity_resp_t *db_cap_resp = &resp->response.db_cap_resp; + int offset = 0; + + if (msg_resp->payload_len < 2 || msg_resp->payload_len > 4) + { + return BMKT_UNRECOGNIZED_MESSAGE; + } + + db_cap_resp->total = extract8(msg_resp->payload, &offset); + db_cap_resp->empty = extract8(msg_resp->payload, &offset); + + if (msg_resp->payload_len == 4) + { + db_cap_resp->bad_slots = extract8(msg_resp->payload, &offset); + db_cap_resp->corrupt_templates = extract8(msg_resp->payload, &offset); + } + + return BMKT_SUCCESS; +} + +static int parse_get_enrolled_fingers_report(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp) +{ + int offset = 0; + int i = 0; + + if (msg_resp->payload_len < 2) + { + return BMKT_UNRECOGNIZED_MESSAGE; + } + /* 2 bytes per finger so calculate the total number of fingers to process*/ + int num_fingers = (msg_resp->payload_len) / 2; + + bmkt_enrolled_fingers_resp_t *get_enrolled_fingers_resp = &resp->response.enrolled_fingers_resp; + + for (i = 0; i < num_fingers; i++) + { + get_enrolled_fingers_resp->fingers[i].finger_id = extract8(msg_resp->payload, &offset); + get_enrolled_fingers_resp->fingers[i].template_status = extract8(msg_resp->payload, &offset); + + } + return BMKT_SUCCESS; +} +static int parse_get_enrolled_users_report(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp) +{ + int offset = 0; + int i = 0; + + /* the payload is 2 bytes + template data */ + if (msg_resp->payload_len < 2) + { + return BMKT_UNRECOGNIZED_MESSAGE; + } + + bmkt_enroll_templates_resp_t *get_enroll_templates_resp = &resp->response.enroll_templates_resp; + + get_enroll_templates_resp->total_query_messages = extract8(msg_resp->payload, &offset); + get_enroll_templates_resp->query_sequence = extract8(msg_resp->payload, &offset); + + int n = 0; + for (n = 0; n < BMKT_MAX_NUM_TEMPLATES_INTERNAL_FLASH; n++) + { + if (offset >= msg_resp->payload_len) + break; + get_enroll_templates_resp->templates[n].user_id_len = extract8(msg_resp->payload, &offset) - 2; + if(get_enroll_templates_resp->templates[n].user_id_len > BMKT_MAX_USER_ID_LEN) + { + return BMKT_UNRECOGNIZED_MESSAGE; + } + get_enroll_templates_resp->templates[n].template_status = extract8(msg_resp->payload, &offset); + get_enroll_templates_resp->templates[n].finger_id = extract8(msg_resp->payload, &offset); + for (i = 0; i < get_enroll_templates_resp->templates[n].user_id_len; i++) + { + get_enroll_templates_resp->templates[n].user_id[i] = extract8(msg_resp->payload, &offset); + } + get_enroll_templates_resp->templates[n].user_id[i] = '\0'; + } + + return BMKT_SUCCESS; +} + +static int parse_get_version_report(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp) +{ + bmkt_get_version_resp_t *get_version_resp = &resp->response.get_version_resp; + int offset = 0; + + if (msg_resp->payload_len != 15) + { + return BMKT_UNRECOGNIZED_MESSAGE; + } + + memcpy(get_version_resp->part, msg_resp->payload, BMKT_PART_NUM_LEN); + offset += BMKT_PART_NUM_LEN; + get_version_resp->year = extract8(msg_resp->payload, &offset); + get_version_resp->week = extract8(msg_resp->payload, &offset); + get_version_resp->patch = extract8(msg_resp->payload, &offset); + memcpy(get_version_resp->supplier_id, msg_resp->payload + offset, BMKT_SUPPLIER_ID_LEN); + + return BMKT_SUCCESS; +} + +int bmkt_compose_message(uint8_t *cmd, int *cmd_len, uint8_t msg_id, uint8_t seq_num, + uint8_t payload_size, const uint8_t *payload) +{ + int message_len = BMKT_MESSAGE_HEADER_LEN + payload_size; + + if (*cmd_len < message_len) + { + return BMKT_OUT_OF_MEMORY; + } + + cmd[BMKT_MESSAGE_HEADER_ID_FIELD] = BMKT_MESSAGE_HEADER_ID; + cmd[BMKT_MESSAGE_SEQ_NUM_FIELD] = seq_num; + cmd[BMKT_MESSAGE_ID_FIELD] = msg_id; + cmd[BMKT_MESSAGE_PAYLOAD_LEN_FIELD] = payload_size; + memcpy(&cmd[BMKT_MESSAGE_PAYLOAD_FIELD], payload, payload_size); + + *cmd_len = message_len; + + return BMKT_SUCCESS; +} + +int bmkt_parse_message_header(uint8_t *resp_buf, int resp_len, bmkt_msg_resp_t *msg_resp) +{ + if (resp_buf[BMKT_MESSAGE_HEADER_ID_FIELD] != BMKT_MESSAGE_HEADER_ID) + { + return BMKT_CORRUPT_MESSAGE; + } + + msg_resp->seq_num = resp_buf[BMKT_MESSAGE_SEQ_NUM_FIELD]; + msg_resp->msg_id = resp_buf[BMKT_MESSAGE_ID_FIELD]; + msg_resp->payload_len = resp_buf[BMKT_MESSAGE_PAYLOAD_LEN_FIELD]; + if (msg_resp->payload_len > 0) + { + msg_resp->payload = &resp_buf[BMKT_MESSAGE_PAYLOAD_FIELD]; + } + else + { + msg_resp->payload = NULL; + } + + return BMKT_SUCCESS; +} + +int bmkt_parse_message_payload(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp) +{ + int ret = BMKT_SUCCESS; + + memset(resp, 0, sizeof(bmkt_response_t)); + + resp->response_id = msg_resp->msg_id; + + switch(msg_resp->msg_id) + { + case BMKT_RSP_CONTINUOUS_IMAGE_CAPTURE_FAIL: + case BMKT_RSP_SENSOR_MODULE_TEST_FAIL: + case BMKT_RSP_FPS_INIT_FAIL: + case BMKT_RSP_FPS_MODE_FAIL: + case BMKT_RSP_SET_SECURITY_LEVEL_FAIL: + case BMKT_RSP_GET_SECURITY_LEVEL_FAIL: + case BMKT_RSP_CANCEL_OP_FAIL: + case BMKT_RSP_ENROLL_FAIL: + case BMKT_RSP_ID_FAIL: + case BMKT_RSP_VERIFY_FAIL: + case BMKT_RSP_QUERY_FAIL: + case BMKT_RSP_DEL_USER_FP_FAIL: + case BMKT_RSP_DEL_FULL_DB_FAIL: + case BMKT_RSP_REPEAT_LAST_BMKT_RSP_FAIL: + case BMKT_RSP_POWER_DOWN_FAIL: + case BMKT_RSP_GET_VERSION_FAIL: + case BMKT_RSP_DISABLE_PAIRING_FAIL: + case BMKT_RSP_QUERY_PAIRING_FAIL: + case BMKT_RSP_SENSOR_STATUS_FAIL: + case BMKT_RSP_RETRIEVE_FINAL_RESULT_FAIL: + ret = parse_error_response(msg_resp, resp); + resp->complete = 1; + break; + + case BMKT_RSP_FPS_INIT_OK: + ret = parse_init_ok(msg_resp, resp); + resp->complete = 1; + break; + + case BMKT_RSP_CANCEL_OP_OK: + case BMKT_RSP_DEL_FULL_DB_OK: + case BMKT_RSP_DEL_USER_FP_OK: + /* responses with a payload of 0 + so the response indicates success */ + resp->result = BMKT_SUCCESS; + resp->complete = 1; + break; + + case BMKT_RSP_FPS_MODE_REPORT: + // parse_fps_mode + ret = parse_fps_mode_report(msg_resp, resp); + resp->complete = 1; + break; + + case BMKT_RSP_GET_SECURITY_LEVEL_REPORT: + case BMKT_RSP_SET_SECURITY_LEVEL_REPORT: + /* parse security level result */ + ret = parse_security_level_report(msg_resp, resp); + resp->complete = 1; + break; + + case BMKT_RSP_DELETE_PROGRESS: + ret = parse_del_all_users_progress_report(msg_resp, resp); + break; + + case BMKT_RSP_CAPTURE_COMPLETE: + resp->result = BMKT_SUCCESS; + break; + + case BMKT_RSP_ENROLL_READY: + resp->result = BMKT_SUCCESS; + break; + + case BMKT_RSP_ENROLL_REPORT: + ret = parse_enroll_report(msg_resp, resp); + break; + + case BMKT_RSP_ENROLL_OK: + resp->complete = 1; + ret = parse_enroll_ok(msg_resp, resp); + break; + + case BMKT_RSP_ID_OK: + case BMKT_RSP_VERIFY_OK: + ret = parse_auth_ok(msg_resp, resp); + resp->complete = 1; + break; + case BMKT_RSP_GET_ENROLLED_FINGERS_REPORT: + ret = parse_get_enrolled_fingers_report(msg_resp, resp); + resp->complete = 1; + break; + case BMKT_RSP_DATABASE_CAPACITY_REPORT: + resp->complete = 1; + ret = parse_db_cap_report(msg_resp, resp); + break; + case BMKT_RSP_TEMPLATE_RECORDS_REPORT: + ret = parse_get_enrolled_users_report(msg_resp, resp); + break; + case BMKT_RSP_QUERY_RESPONSE_COMPLETE: + resp->complete = 1; + break; + + case BMKT_RSP_VERSION_INFO: + ret = parse_get_version_report(msg_resp, resp); + resp->complete = 1; + break; + } + + return ret; +} diff --git a/libfprint/drivers/synaptics/bmkt_message.h b/libfprint/drivers/synaptics/bmkt_message.h new file mode 100644 index 0000000..9366771 --- /dev/null +++ b/libfprint/drivers/synaptics/bmkt_message.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 Synaptics Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef BMKT_MESSAGE_H_ +#define BMKT_MESSAGE_H_ + + +#define BMKT_MESSAGE_HEADER_ID 0xFE +#define BMKT_MESSAGE_HEADER_LEN (4) +#define BMKT_MESSAGE_CRC32_LEN (4) +#define BMKT_MESSAGE_HEADER_ID_FIELD 0 +#define BMKT_MESSAGE_SEQ_NUM_FIELD 1 +#define BMKT_MESSAGE_ID_FIELD 2 +#define BMKT_MESSAGE_PAYLOAD_LEN_FIELD 3 +#define BMKT_MESSAGE_PAYLOAD_FIELD 4 + +// Command messages +#define BMKT_CMD_CONTINUOUS_IMAGE_CAPTURE 0x01 +#define BMKT_CMD_CONTINUOUS_IMAGE_CAPTURE_STOP 0x04 +#define BMKT_CMD_SENSOR_MODULE_TEST 0x06 +#define BMKT_CMD_SENSOR_MODULE_TEST_START 0x08 +#define BMKT_CMD_NEXT_TEST_REPORT_CHUNK 0x0B +#define BMKT_CMD_FPS_INIT 0x11 +#define BMKT_CMD_GET_FPS_MODE 0x21 +#define BMKT_CMD_SET_SECURITY_LEVEL 0x31 +#define BMKT_CMD_GET_SECURITY_LEVEL 0x34 +#define BMKT_CMD_CANCEL_OP 0x41 +#define BMKT_CMD_ENROLL_USER 0x51 +#define BMKT_CMD_ENROLL_PAUSE 0x52 +#define BMKT_CMD_ENROLL_RESUME 0x53 +#define BMKT_CMD_ID_USER 0x61 +#define BMKT_CMD_VERIFY_USER 0x65 +#define BMKT_CMD_GET_TEMPLATE_RECORDS 0x71 +#define BMKT_CMD_GET_NEXT_QUERY_RESPONSE 0x72 +#define BMKT_CMD_GET_ENROLLED_FINGERS 0x73 +#define BMKT_CMD_GET_DATABASE_CAPACITY 0x74 +#define BMKT_CMD_DEL_USER_FP 0x81 +#define BMKT_CMD_DEL_FULL_DB 0x84 +#define BMKT_CMD_REPEAT_LAST_RSP 0x92 +#define BMKT_CMD_POWER_DOWN_NOTIFY 0xA1 +#define BMKT_CMD_GET_VERSION 0xB1 +#define BMKT_CMD_DISABLE_PAIRING 0xC2 +#define BMKT_CMD_QUERY_PAIRING 0xC5 +#define BMKT_CMD_SENSOR_STATUS 0xD1 +#define BMKT_CMD_ID_USER_IN_ORDER 0xE1 +#define BMKT_CMD_ID_NEXT_USER 0xE3 +#define BMKT_CMD_VERIFY_USER_IN_ORDER 0xF1 +#define BMKT_CMD_VERIFY_FINGERS_IN_ORDER 0xF2 +#define BMKT_CMD_GET_FINAL_RESULT 0xE4 + +#define BMKT_EVT_FINGER_REPORT 0x91 + +#define BMKT_EVT_FINGER_STATE_NOT_ON_SENSOR 0x00 +#define BMKT_EVT_FINGER_STATE_ON_SENSOR 0x01 + +typedef struct bmkt_msg_resp +{ + uint8_t msg_id; + uint8_t seq_num; + uint8_t payload_len; + uint8_t *payload; + int result; +} bmkt_msg_resp_t; + +int bmkt_compose_message(uint8_t *cmd, int *cmd_len, uint8_t msg_id, uint8_t seq_num, + uint8_t payload_size, const uint8_t *payload); + +int bmkt_parse_message_header(uint8_t *resp_buf, int resp_len, bmkt_msg_resp_t *msg_resp); +int bmkt_parse_message_payload(bmkt_msg_resp_t *msg_resp, bmkt_response_t *resp); +#endif /* BMKT_MESSAGE_H_ */ diff --git a/libfprint/drivers/synaptics/bmkt_response.h b/libfprint/drivers/synaptics/bmkt_response.h new file mode 100644 index 0000000..874bf97 --- /dev/null +++ b/libfprint/drivers/synaptics/bmkt_response.h @@ -0,0 +1,489 @@ +/* + * Synaptics MiS Fingerprint Sensor Response Data Interface + * Copyright (C) 2019 Synaptics Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef _BMKT_RESPONSE_H_ +#define _BMKT_RESPONSE_H_ + +#include "bmkt.h" + +/** List of response message IDs */ +#define BMKT_RSP_CONTINUOUS_IMAGE_CAPTURE_FAIL 0x02 +#define BMKT_RSP_CONTINUOUS_IMAGE_CAPTURE_READY 0x03 +#define BMKT_RSP_CONTINUOUS_IMAGE_CAPTURE_STOPPED 0x05 +#define BMKT_RSP_SENSOR_MODULE_TEST_READY 0x07 +#define BMKT_RSP_SENSOR_MODULE_TEST_FAIL 0x09 +#define BMKT_RSP_SENSOR_MODULE_TEST_REPORT 0x0A +#define BMKT_RSP_NEXT_TEST_REPORT_CHUNK 0x0C + +/*! \addtogroup init +* Response IDs returned by fingerprint initialization operation +* @{ +*/ +/** Failed to initialize fingerprint sensor module */ +#define BMKT_RSP_FPS_INIT_FAIL 0x12 +/** Successfully initialized fingerprint sensor module */ +#define BMKT_RSP_FPS_INIT_OK 0x13 +/*! @} */ + +/*! \addtogroup mode +* Response IDs returned by get fingerprint mode operation +* @{ +*/ +/** Failed to get fingerprint sensor module’s current operational mode */ +#define BMKT_RSP_FPS_MODE_FAIL 0x22 +/** +* BMKT_RSP_FPS_MODE_REPORT: +* Response containing the current operational mode of the fingerprint sensor module +*
Payload data represented in \ref bmkt_fps_mode_resp_t struct +*/ +#define BMKT_RSP_FPS_MODE_REPORT 0x23 +/*! @} */ + +/*! \addtogroup setseclevel +* Response IDs returned by set security level operation +* @{ +*/ +/** Failed to set fingerprint sensor module security level */ +#define BMKT_RSP_SET_SECURITY_LEVEL_FAIL 0x32 +/** +* BMKT_RSP_SET_SECURITY_LEVEL_REPORT: +* Security level of the fingerprint sensor module was set successfully +*
Contains payload data represented in \ref bmkt_set_sec_level_resp_t struct +*/ +#define BMKT_RSP_SET_SECURITY_LEVEL_REPORT 0x33 +/*! @} */ + +/*! \addtogroup getseclevel +* Response IDs returned by get security level operation +* @{ +*/ +/** Failed to get fingerprint sensor module security level */ +#define BMKT_RSP_GET_SECURITY_LEVEL_FAIL 0x35 +/** +* BMKT_RSP_GET_SECURITY_LEVEL_REPORT: +* Returns the current security level of the fingerprint sensor module +*
Contains payload data represented in \ref bmkt_set_sec_level_resp_t struct +*/ +#define BMKT_RSP_GET_SECURITY_LEVEL_REPORT 0x36 +/*! @} */ + +/*! \addtogroup cancelop +* Response IDs returned by cancel_operation operation +* @{ +*/ +/** +* BMKT_RSP_CANCEL_OP_OK: +* Successfully canceled the current operation and returned +* fingerprint sensor module to idle mode +*/ +#define BMKT_RSP_CANCEL_OP_OK 0x42 +/** Failed to cancel the current operation */ +#define BMKT_RSP_CANCEL_OP_FAIL 0x43 +/*! @} */ + +/*! \addtogroup enrollment +* Response IDs returned by enrollment operation +* @{ +*/ +/** +* BMKT_RSP_ENROLL_READY: +* Fingerprint enrollment session has begun and the user can place +* their finger on the sensor +*/ +#define BMKT_RSP_ENROLL_READY 0x54 +/** Progress of the currently on-going fingerprint enrollment session */ +#define BMKT_RSP_ENROLL_REPORT 0x55 +/** Enrollment has been paused */ +#define BMKT_RSP_ENROLL_PAUSED 0x56 +/** Enrollment has been resume */ +#define BMKT_RSP_ENROLL_RESUMED 0x57 +/** The current enrollment session has encountered an error */ +#define BMKT_RSP_ENROLL_FAIL 0x58 +/** +* BMKT_RSP_ENROLL_OK: +* User has been successfully enrolled into the fingerprint sensor module +*
Contains payload data represented in \ref bmkt_enroll_resp_t struct +*/ +#define BMKT_RSP_ENROLL_OK 0x59 + +/** +* BMKT_RSP_CAPTURE_COMPLETE: +* Fingerprint image capture is complete and it is safe for the user +* to lift their finger off the sensor +*/ +#define BMKT_RSP_CAPTURE_COMPLETE 0x60 +/*! @} */ + +/*! \addtogroup identify +* Response IDs returned by identify operation. +* @{ +*/ +/* Fingerprint identification session has begun */ +#define BMKT_RSP_ID_READY 0x62 +/* Identification has failed */ +#define BMKT_RSP_ID_FAIL 0x63 +/** +* BMKT_RSP_ID_OK: +* User has been successfully identified +*
Contains payload data represented in \ref bmkt_auth_resp struct +*/ +#define BMKT_RSP_ID_OK 0x64 +/*! @} */ + +/*! \addtogroup verify +* Response IDs returned by identify operation. +* @{ +*/ +/** Fingerprint verification session has begun */ +#define BMKT_RSP_VERIFY_READY 0x66 +/** Verification has failed */ +#define BMKT_RSP_VERIFY_FAIL 0x67 +/** +* BMKT_RSP_VERIFY_OK: +* User’s identity has been successfully verified +*
Contains payload data represented in \ref bmkt_auth_resp struct +*/ +#define BMKT_RSP_VERIFY_OK 0x68 +/*! @} */ + +/** +* BMKT_RSP_TEMPLATE_RECORDS_REPORT: +* Response ID returned by get enrolled users templates record operation +*
Returns list of template records containing user IDs and corresponding finger IDs +*
Payload data represented in \ref bmkt_enroll_templates_resp_t struct +*/ +#define BMKT_RSP_TEMPLATE_RECORDS_REPORT 0x75 + +/** +* BMKT_RSP_QUERY_RESPONSE_COMPLETE: +* Response ID returned by get next query response operation +*
Complete sequence of messages containing the template records query response has been sent +*/ +#define BMKT_RSP_QUERY_RESPONSE_COMPLETE 0x76 + +/** +* BMKT_RSP_GET_ENROLLED_FINGERS_REPORT: +* Response ID returned by get enrolled fingers operation +*
Returns list of IDs of enrolled fingers for a specific user, +* along with template record status corresponding to each enrolled finger +*
Contains payload data represented in \ref bmkt_enrolled_fingers_resp_t struct +*/ +#define BMKT_RSP_GET_ENROLLED_FINGERS_REPORT 0x77 + +/*! \addtogroup dbcapacity +* Response IDs returned by get database capacity operation +* @{ +*/ +/** +* BMKT_RSP_DATABASE_CAPACITY_REPORT: +* Response specifying total capacity of fingerprint template database and +* how much free capacity is remaining along with how many templates are corrupted and +* how many bad (permanently unusable) storage slots are there. +*
Payload data represented in \ref bmkt_get_db_capacity_resp_t struct +*/ +#define BMKT_RSP_DATABASE_CAPACITY_REPORT 0x78 +/** Failed to execute database query */ +#define BMKT_RSP_QUERY_FAIL 0x79 +/*! @} */ + +/*! \addtogroup deluser +* Response IDs returned by delete fingerprint of specific user operation +* @{ +*/ +/** Failed to delete a user’s fingerprint template from the database */ +#define BMKT_RSP_DEL_USER_FP_FAIL 0x82 +/** +* BMKT_RSP_DEL_USER_FP_OK: +* Fingerprint template successfully deleted from the database. +* Returns the user ID and finger ID deleted. If value of finger ID is set equal to 0, +* then all fingerprint templates for that user have been deleted from the database +*
Payload data represented in \ref bmkt_del_user_resp_t struct +*/ +#define BMKT_RSP_DEL_USER_FP_OK 0x83 +/*! @} */ + +/*! \addtogroup delfulldb +* Response IDs returned by delete entire fingerprint template DB operation +* @{ +*/ +/** Failed to erase entire fingerprint template database */ +#define BMKT_RSP_DEL_FULL_DB_FAIL 0x85 +/** Successfully erased entire fingerprint template database */ +#define BMKT_RSP_DEL_FULL_DB_OK 0x86 +/** +* BMKT_RSP_DELETE_PROGRESS: +* Notify progress made during the on-going deletion of the full template database +*
Payload data represented in \ref bmkt_del_all_users_resp_t struct +*/ +#define BMKT_RSP_DELETE_PROGRESS 0x87 +/*! @} */ + +/** +* BMKT_RSP_REPEAT_LAST_BMKT_RSP_FAIL: +* Response ID returned by repeate last response operation +*
Failed to retrieve and re-send last response +*/ +#define BMKT_RSP_REPEAT_LAST_BMKT_RSP_FAIL 0x93 + +/*! \addtogroup pwrdwn +* Response IDs returned by power down notify operation +* @{ +*/ +/** Fingerprint sensor module is ready to be powered down */ +#define BMKT_RSP_POWER_DOWN_READY 0xA2 +/** Failed to go into power down mode */ +#define BMKT_RSP_POWER_DOWN_FAIL 0xA3 +/*! @} */ + +/*! \addtogroup versioninfo +* Response IDs returned by get version operation +* @{ +*/ +/** +* BMKT_RSP_VERSION_INFO: +* System version information of the fingerprint sensor module +*
Payload data represented in \ref bmkt_get_version_resp_t struct +*/ +#define BMKT_RSP_VERSION_INFO 0xB2 +/* Failed to retrieve and send last response */ +#define BMKT_RSP_GET_VERSION_FAIL 0xB3 +/*! @} */ + +/** +* BMKT_RSP_GENERAL_ERROR: +* Not tied to a specific command-response session. +*
Could be caused by corrupt or truncated command message +*/ +#define BMKT_RSP_GENERAL_ERROR 0xC1 +#define BMKT_RSP_DISABLE_PAIRING_FAIL 0xC3 +#define BMKT_RSP_DISABLE_PAIRING_OK 0xC4 +#define BMKT_RSP_QUERY_PAIRING_FAIL 0xC6 +#define BMKT_RSP_SENSOR_PAIRING_REPORT 0xC7 + +/*! \addtogroup versioninfo +* Response IDs returned by get sensor module status operation +* @{ +*/ +/** +* BMKT_RSP_SENSOR_STATUS_REPORT: +* Response returning the current status of the sensor module +*
Payload data represented in bmkt_XXX struct +*/ +#define BMKT_RSP_SENSOR_STATUS_REPORT 0xD2 +/** Failed to retrieve sensor status */ +#define BMKT_RSP_SENSOR_STATUS_FAIL 0xD3 +/*! @} */ + +/** +* BMKT_RSP_SEND_NEXT_USER_ID: +* Response ID returned by identify user in order operation +*
Notify to send the next batch of user IDs in the priority list +*/ +#define BMKT_RSP_SEND_NEXT_USER_ID 0xE2 +/** +* BMKT_RSP_RETRIEVE_FINAL_RESULT_FAIL: +* Response IDs returned by retrieve final result operation +*
Failed to retrieve and re-send cached final result +*/ +#define BMKT_RSP_RETRIEVE_FINAL_RESULT_FAIL 0xE5 + +/** +* Response payload data structure returned by sensor initialization operation. +*/ +typedef struct bmkt_init_resp +{ + uint8_t finger_presence; /**< Indicates finger existence on the sensor during startup */ +} bmkt_init_resp_t; + +/** +* bmkt_enroll_resp: +* Response payload data structure returned by enrollment operation. +*/ +typedef struct bmkt_enroll_resp +{ + int progress; /**< Shows current progress stutus [0-100] */ + uint8_t finger_id; /**< User's finger id [1-10] */ + uint8_t user_id[BMKT_MAX_USER_ID_LEN]; /**< User name to be enrolled */ +} bmkt_enroll_resp_t; + +/** +* bmkt_auth_resp: +* Response payload data structure returned by identify and verify operations. +*/ +struct bmkt_auth_resp +{ + double match_result; /**< match result returned by matcher */ + uint8_t finger_id; /**< Matched templates's finger id */ + uint8_t user_id[BMKT_MAX_USER_ID_LEN]; /**< Matched template's user id */ +}; + +typedef struct bmkt_auth_resp bmkt_verify_resp_t; /**< Returned by verify */ +typedef struct bmkt_auth_resp bmkt_identify_resp_t; /**< Returned by identify */ + +/** +* bmkt_fps_mode_resp: +* Response payload data structure returned by get fingerprint mode operation. +*/ +typedef struct bmkt_fps_mode_resp +{ + uint8_t mode; /**< One of the Level I bmkt_mode_t values */ + uint8_t level2_mode; /**< One of the Level II bmkt_mode_level2_t values */ + uint8_t cmd_id; /**< Message ID of command being executed when bmkt_get_fps_mode was called */ + uint8_t finger_presence; /**< Finger presence status value finger on sensor 1 / finger not on sensor 0 */ +} bmkt_fps_mode_resp_t; + +/** +* bmkt_get_version_resp: +* Response payload data structure returned by get version operation. +*/ +typedef struct bmkt_get_version_resp +{ + uint8_t part[BMKT_PART_NUM_LEN]; /**< Software Part Number */ + uint8_t year; /**< Software Version Year */ + uint8_t week; /**< Software Version Week */ + uint8_t patch; /**< Software Version Patch Level */ + uint8_t supplier_id[BMKT_SUPPLIER_ID_LEN]; /**< Software Supplier Identification */ +} bmkt_get_version_resp_t; + +/** +* bmkt_get_db_capacity_resp: +* Response payload data structure returned by get DB capacity operation. +*/ +typedef struct bmkt_get_db_capacity_resp +{ + uint8_t total; /**< Total Available Capacity: Total number of template records that can be stored */ + uint8_t empty; /**< Free Capacity: Number of template records that can still be stored */ + uint8_t bad_slots; /**< Number of bad template storage slots */ + uint8_t corrupt_templates; /**< Number of corrupt templates */ +} bmkt_get_db_capacity_resp_t; + +/** +* bmkt_sec_level: +* Security level values. +*/ +typedef enum bmkt_sec_level +{ + BMKT_SECURITY_LEVEL_LOW = 0x10, + BMKT_SECURITY_LEVEL_MEDIUM = 0x40, + BMKT_SECURITY_LEVEL_HIGH = 0x60, +} bmkt_sec_level_t; + +/** +* bmkt_set_sec_level_resp: +* Response payload data structure returned by get/set security level operations. +*/ +typedef struct bmkt_set_sec_level_resp +{ + bmkt_sec_level_t sec_level; /**< One of the bmkt_sec_level_t values */ +} bmkt_set_sec_level_resp_t; + +/** +* bmkt_del_all_users_resp: +* Response payload data structure returned by delete all enrolled users operation. +*/ +typedef struct bmkt_del_all_users_resp +{ + int progress; /**< Progress indicator as a percentage */ +} bmkt_del_all_users_resp_t; + +/** +* bmkt_del_user_resp: +* Response payload data structure returned by delete enrolled user operation. +*/ +typedef struct bmkt_del_user_resp +{ + int progress; /**< Progress indicator as a percentage */ +} bmkt_del_user_resp_t; + +/** +* bmkt_enroll_template: +* Structure of enrolled users template record data. +*/ +typedef struct bmkt_enroll_template +{ + uint8_t user_id_len; /**< Length of user_id string */ + uint8_t template_status; /**< Template record status */ + uint8_t finger_id; /**< ID of enrolled finger */ + uint8_t user_id[BMKT_MAX_USER_ID_LEN + 1]; /**< Name of the enrolled user */ +} bmkt_enroll_template_t; + +/** +* bmkt_enroll_templates_resp: +* Response payload data structure returned by get enrolled user list operation. +*/ +typedef struct bmkt_enroll_templates_resp +{ + uint8_t total_query_messages; /**< Total query response messages */ + uint8_t query_sequence; /**< Query response sequence number */ + bmkt_enroll_template_t templates[BMKT_MAX_NUM_TEMPLATES_INTERNAL_FLASH]; /**< Enrolled user template records list */ +} bmkt_enroll_templates_resp_t; + +/** +* bmkt_enrolled_fingers: +* Structure of template record status corresponding to each enrolled finger. +*/ +typedef struct bmkt_enrolled_fingers +{ + uint8_t finger_id; /**< ID of enrolled finger */ + uint8_t template_status; /**< Template record status of finger_id */ +} bmkt_enrolled_fingers_t; + +/** +* bmkt_enrolled_fingers_resp: +* Response payload data structure returned by get enrolled fingers operation. +*/ +typedef struct bmkt_enrolled_fingers_resp +{ + bmkt_enrolled_fingers_t fingers[10]; /**< List of enroled fingers, max number of supported fingers per user is 10 */ +} bmkt_enrolled_fingers_resp_t; + +/** +* bmkt_response_data_t: +* Union combining all response payload data types. +*/ +typedef union { + bmkt_init_resp_t init_resp; + bmkt_enroll_resp_t enroll_resp; + bmkt_verify_resp_t verify_resp; + bmkt_identify_resp_t id_resp; + bmkt_fps_mode_resp_t fps_mode_resp; + bmkt_get_version_resp_t get_version_resp; + bmkt_get_db_capacity_resp_t db_cap_resp; + bmkt_set_sec_level_resp_t sec_level_resp; + bmkt_del_all_users_resp_t del_all_users_resp; + bmkt_enroll_templates_resp_t enroll_templates_resp; + bmkt_del_user_resp_t del_user_resp; + bmkt_enrolled_fingers_resp_t enrolled_fingers_resp; +} bmkt_response_data_t; + +/** +* bmkt_response: +* Structure to abstract different response structure types in one API +* to be used in bmkt_resp_cb_t callback function. +*/ +typedef struct bmkt_response +{ + int response_id; /**< Response message ID, one of th BMKT_RSP_XXX */ + int result; /**< Operation execution result code */ + int complete; /**< Operation completion status 1: complete / 0: not completed */ + bmkt_response_data_t response; /**< Operation specific response union */ +} bmkt_response_t; + +#endif /* _BMKT_RESPONSE_H_ */ diff --git a/libfprint/drivers/synaptics/sensor.h b/libfprint/drivers/synaptics/sensor.h new file mode 100644 index 0000000..b8ebdfb --- /dev/null +++ b/libfprint/drivers/synaptics/sensor.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2019 Synaptics Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _SENSOR_H_ +#define _SENSOR_H_ + +#include "usb_transport.h" +#define BMKT_MAX_PENDING_SESSIONS 2 + +typedef enum bmkt_sensor_state +{ + BMKT_SENSOR_STATE_UNINIT = 0, + BMKT_SENSOR_STATE_IDLE, + BMKT_SENSOR_STATE_INIT, + BMKT_SENSOR_STATE_EXIT, +} bmkt_sensor_state_t; + +typedef struct bmkt_sensor_drv bmkt_sensor_drv_t; + +typedef struct bmkt_sensor_version +{ + uint32_t build_time; + uint32_t build_num; + uint8_t version_major; + uint8_t version_minor; + uint8_t target; + uint8_t product; + uint8_t silicon_rev; + uint8_t formal_release; + uint8_t platform; + uint8_t patch; + uint8_t serial_number[6]; + uint16_t security; + uint8_t iface; + uint8_t device_type; +} bmkt_sensor_version_t; + +typedef struct bmkt_sensor +{ + bmkt_usb_transport_t usb_xport; + bmkt_sensor_version_t version; + bmkt_session_ctx_t pending_sessions[BMKT_MAX_PENDING_SESSIONS]; + int empty_session_idx; + int flags; + int seq_num; + bmkt_sensor_state_t sensor_state; + bmkt_event_cb_t finger_event_cb; + void *finger_cb_ctx; + bmkt_general_error_cb_t gen_err_cb; + void *gen_err_cb_ctx; + bmkt_op_state_t op_state; +} bmkt_sensor_t; + +int bmkt_sensor_open(bmkt_sensor_t *sensor, + bmkt_general_error_cb_t err_cb, void *err_cb_ctx); +int bmkt_sensor_close(bmkt_sensor_t *sensor); + +int bmkt_sensor_init_fps(bmkt_sensor_t *sensor); + +int bmkt_sensor_send_message(bmkt_sensor_t *sensor, uint8_t msg_id, uint8_t payload_size, + uint8_t *payload, bmkt_resp_cb_t resp_cb, void *resp_data); +int bmkt_sensor_handle_response(bmkt_sensor_t *sensor, uint8_t *resp_buf, int resp_len, bmkt_msg_resp_t *msg_resp); + +int bmkt_sensor_send_async_read_command(bmkt_sensor_t *sensor); +#endif /* _SENSOR_H_ */ diff --git a/libfprint/drivers/synaptics/synaptics.c b/libfprint/drivers/synaptics/synaptics.c new file mode 100644 index 0000000..ca99f64 --- /dev/null +++ b/libfprint/drivers/synaptics/synaptics.c @@ -0,0 +1,1040 @@ +/* + * Copyright (C) 2019 Synaptics Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define FP_COMPONENT "synaptics" + +#include "drivers_api.h" + +#include "fpi-byte-reader.h" + +#include "synaptics.h" +#include "bmkt_message.h" + +G_DEFINE_TYPE(FpiDeviceSynaptics, fpi_device_synaptics, FP_TYPE_DEVICE) + +static const FpIdEntry id_table [ ] = { + { .vid = SYNAPTICS_VENDOR_ID, .pid = 0xBD, }, + + { .vid = 0, .pid = 0, .driver_data = 0 }, /* terminating entry */ +}; + + +static gboolean rand_string(char *str, size_t size) +{ + const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + srand(time(NULL)); + if (size) { + --size; + for (size_t n = 0; n < size; n++) { + int key = rand() % (int) (sizeof charset - 1); + str[n] = charset[key]; + } + str[size] = '\0'; + } + else + return FALSE; + return TRUE; +} + + + +static void +cmd_recieve_cb (FpiUsbTransfer *transfer, + FpDevice *device, + gpointer user_data, + GError *error) +{ + FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (device); + SynCmdMsgCallback callback = user_data; + int res; + bmkt_msg_resp_t msg_resp; + bmkt_response_t resp; + + if (error) { + /* NOTE: assumes timeout should never happen for receiving. */ + fpi_ssm_mark_failed (transfer->ssm, error); + return; + } + + res = bmkt_parse_message_header(&transfer->buffer[SENSOR_FW_REPLY_HEADER_LEN], + transfer->actual_length - SENSOR_FW_REPLY_HEADER_LEN, + &msg_resp); + if (res != BMKT_SUCCESS) { + g_warning ("Corrupted message received"); + fpi_ssm_mark_failed (transfer->ssm, + fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); + return; + } + + /* Special case events */ + if (msg_resp.msg_id == BMKT_EVT_FINGER_REPORT) { + if (msg_resp.payload_len != 1) { + g_warning ("Corrupted finger report received"); + fpi_ssm_mark_failed (transfer->ssm, + fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); + return; + } + + if (msg_resp.payload[0] == 0x01) { + self->finger_on_sensor = TRUE; + } else { + self->finger_on_sensor = FALSE; + if (self->cmd_complete_on_removal) { + fpi_ssm_mark_completed (transfer->ssm); + return; + } + } + + fp_dbg ("Finger is now %s the sensor", self->finger_on_sensor ? "on" : "off"); + + /* XXX: Call callback!?! */ + } + + res = bmkt_parse_message_payload (&msg_resp, &resp); + if (res != BMKT_SUCCESS) { + g_warning ("Could not parse message payload: %i", res); + fpi_ssm_mark_failed (transfer->ssm, + fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); + return; + } + + /* Special cancellation handling */ + if (resp.response_id == BMKT_RSP_CANCEL_OP_OK || resp.response_id == BMKT_RSP_CANCEL_OP_FAIL) { + if (resp.response_id == BMKT_RSP_CANCEL_OP_OK) { + fp_dbg ("Received cancellation success resonse"); + fpi_ssm_mark_failed (transfer->ssm, + g_error_new_literal (G_IO_ERROR, + G_IO_ERROR_CANCELLED, + "Device reported cancellation of operation")); + } else { + fp_dbg ("Cancellation failed, this should not happen"); + fpi_ssm_mark_failed (transfer->ssm, + fpi_device_error_new (FP_DEVICE_ERROR_PROTO)); + } + return; + } + + if (msg_resp.seq_num == 0) { + /* XXX: Should we really abort the command on general error? + * The original code did not! */ + if (msg_resp.msg_id == BMKT_RSP_GENERAL_ERROR) { + guint16 err; + + /* XXX: It is weird that this is big endian. */ + err = FP_READ_UINT16_BE(msg_resp.payload); + + fp_warn("Received General Error %d from the sensor", (guint) err); + fpi_ssm_mark_failed (transfer->ssm, + fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO, + "Received general error from device")); + //fpi_ssm_jump_to_state (transfer->ssm, fpi_ssm_get_cur_state (transfer->ssm)); + return; + } else { + fp_dbg ("Received message with 0 sequence number 0x%02x, ignoring!", + msg_resp.msg_id); + fpi_ssm_next_state (transfer->ssm); + return; + } + } + + /* We should only ever have one command running, and the sequence num needs + * to match. */ + if (msg_resp.seq_num != self->cmd_seq_num) { + fp_warn ("Got unexpected sequence number from device, %d instead of %d", + msg_resp.seq_num, + self->cmd_seq_num); + } + + if (callback) + callback (self, &resp, NULL); + + /* Callback may have queued a follow up command, then we need + * to restart the SSM. If not, we'll finish/wait for interrupt + * depending on resp.complete. */ + if (self->cmd_pending_transfer) + fpi_ssm_jump_to_state (transfer->ssm, SYNAPTICS_CMD_SEND_PENDING); + else if (!resp.complete) + fpi_ssm_next_state (transfer->ssm); /* SYNAPTICS_CMD_WAIT_INTERRUPT */ + else + fpi_ssm_mark_completed (transfer->ssm); +} + +static void +cmd_interrupt_cb (FpiUsbTransfer *transfer, + FpDevice *device, + gpointer user_data, + GError *error) +{ + g_debug ("interrupt transfer done"); + if (error) { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + g_error_free (error); + fpi_ssm_jump_to_state (transfer->ssm, SYNAPTICS_CMD_GET_RESP); + return; + } + + fpi_ssm_mark_failed (transfer->ssm, error); + return; + } + g_clear_pointer (&error, g_error_free); + + if (transfer->buffer[0] & USB_ASYNC_MESSAGE_PENDING || error) + fpi_ssm_next_state (transfer->ssm); + else + fpi_usb_transfer_submit (transfer, 1000, NULL, cmd_interrupt_cb, NULL); +} + +static void +synaptics_cmd_run_state(FpiSsm *ssm, + FpDevice *dev, + void *user_data) +{ + g_autoptr(FpiUsbTransfer) transfer = NULL; + FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (dev); + + switch (fpi_ssm_get_cur_state(ssm)) { + case SYNAPTICS_CMD_SEND_PENDING: + if (self->cmd_pending_transfer) { + self->cmd_pending_transfer->ssm = ssm; + fpi_usb_transfer_submit (self->cmd_pending_transfer, + 1000, + NULL, + fpi_ssm_usb_transfer_cb, + NULL); + g_clear_pointer (&self->cmd_pending_transfer, fpi_usb_transfer_unref); + } else { + fpi_ssm_next_state (ssm); + } + break; + + case SYNAPTICS_CMD_GET_RESP: + transfer = fpi_usb_transfer_new (dev); + transfer->ssm = ssm; + fpi_usb_transfer_fill_bulk (transfer, USB_EP_REPLY, MAX_TRANSFER_LEN); + fpi_usb_transfer_submit (transfer, + 5000, + NULL, + cmd_recieve_cb, + user_data); + + break; + + case SYNAPTICS_CMD_WAIT_INTERRUPT: + transfer = fpi_usb_transfer_new (dev); + transfer->ssm = ssm; + fpi_usb_transfer_fill_interrupt (transfer, USB_EP_INTERRUPT, USB_INTERRUPT_DATA_SIZE); + fpi_usb_transfer_submit (transfer, + 0, + self->interrupt_cancellable, + cmd_interrupt_cb, + NULL); + + break; + + case SYNAPTICS_CMD_SEND_ASYNC: + transfer = fpi_usb_transfer_new (dev); + transfer->ssm = ssm; + fpi_usb_transfer_fill_bulk (transfer, USB_EP_REQUEST, SENSOR_FW_CMD_HEADER_LEN); + transfer->buffer[0] = SENSOR_CMD_ASYNCMSG_READ; + fpi_usb_transfer_submit (transfer, + 1000, + NULL, + fpi_ssm_usb_transfer_cb, + NULL); + + break; + + case SYNAPTICS_CMD_RESTART: + fpi_ssm_jump_to_state (ssm, SYNAPTICS_CMD_SEND_PENDING); + break; + } +} + +static void +cmd_ssm_done (FpiSsm *ssm, + FpDevice *dev, + void *user_data, + GError *error) +{ + FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (dev); + SynCmdMsgCallback callback = user_data; + + self->cmd_ssm = NULL; + + /* Notify about the SSM failure from here instead. */ + if (error) { + callback (self, NULL, error); + } else if (self->cmd_complete_on_removal) { + callback (self, NULL, self->cmd_complete_error); + self->cmd_complete_error = NULL; + } + self->cmd_complete_on_removal = FALSE; + g_clear_pointer (&self->cmd_complete_error, g_error_free); + fpi_ssm_free (ssm); +} + +static void +cmd_forget_cb (FpiUsbTransfer *transfer, + FpDevice *device, + gpointer user_data, + GError *error) +{ + if (error) { + g_warning ("Async command sending failed: %s", error->message); + g_error_free (error); + } else { + g_debug ("Async command sent successfully"); + } +} + +static void +synaptics_sensor_cmd (FpiDeviceSynaptics *self, + gint seq_num, + guint8 msg_id, + const guint8* payload, + gssize payload_len, + SynCmdMsgCallback callback) +{ + g_autoptr(FpiUsbTransfer) transfer = NULL; + guint8 real_seq_num; + gint msg_len; + gint res; + + /* callback may be NULL in two cases: + * - seq_num == -1 + * - a state machine is already running, continued command */ + g_assert (payload || payload_len == 0); + + /* seq_num of 0 means a normal command, -1 means the current commands + * sequence number should not be udpated (i.e. second async command which + * may only be a cancellation currently). */ + if (seq_num <= 0) { + self->last_seq_num = MAX(1, self->last_seq_num + 1); + real_seq_num = self->last_seq_num; + if (seq_num == 0) + self->cmd_seq_num = self->last_seq_num; + } else { + real_seq_num = seq_num; + self->last_seq_num = real_seq_num; + } + g_debug ("sequence number is %d", real_seq_num); + + /* We calculate the exact length here (we could also just create a larger + * buffer instead and check the result of bmkt_compose_message. */ + msg_len = BMKT_MESSAGE_HEADER_LEN + payload_len; + + /* Send out the command */ + transfer = fpi_usb_transfer_new (FP_DEVICE (self)); + transfer->short_is_error = TRUE; + fpi_usb_transfer_fill_bulk (transfer, + USB_EP_REQUEST, + msg_len + SENSOR_FW_CMD_HEADER_LEN); + + /* MIS sensors send ACE commands encapsulated in FW commands*/ + transfer->buffer[0] = SENSOR_CMD_ACE_COMMAND; + res = bmkt_compose_message (&transfer->buffer[1], + &msg_len, msg_id, + real_seq_num, + payload_len, + payload); + g_assert (res == BMKT_SUCCESS); + g_assert (msg_len + SENSOR_FW_CMD_HEADER_LEN == transfer->length); + + /* Special case for async command sending (should only be used for + * cancellation). */ + if (seq_num == -1) { + g_assert(callback == NULL); + + /* We just send and forget here. */ + fpi_usb_transfer_submit (transfer, 1000, NULL, cmd_forget_cb, NULL); + } else { + /* Command should be send using the state machine. */ + g_assert (self->cmd_pending_transfer == NULL); + + self->cmd_pending_transfer = g_steal_pointer (&transfer); + + if (self->cmd_ssm) { + /* Continued command, we already have an SSM with a callback. + * There is nothing to do in this case, the command will be + * sent automatically. */ + g_assert (callback == NULL); + } else { + /* Start of a new command, create the state machine. */ + g_assert (callback != NULL); + + self->cmd_ssm = fpi_ssm_new (FP_DEVICE (self), + synaptics_cmd_run_state, + SYNAPTICS_CMD_NUM_STATES, + callback); + + fpi_ssm_start (self->cmd_ssm, cmd_ssm_done); + } + } +} + +static gboolean +parse_print_data (GVariant *data, + guint8 *finger, + const guint8 **user_id, + gssize *user_id_len) +{ + g_autoptr(GVariant) user_id_var = NULL; + + g_return_val_if_fail (data != NULL, FALSE); + g_return_val_if_fail (finger != NULL, FALSE); + g_return_val_if_fail (user_id != NULL, FALSE); + g_return_val_if_fail (user_id_len != NULL, FALSE); + + *user_id = NULL; + *user_id_len = 0; + + if (!g_variant_check_format_string (data, "(y@ay)", FALSE)) + return FALSE; + + g_variant_get(data, + "(y@ay)", + finger, + &user_id_var); + + *user_id = g_variant_get_fixed_array (user_id_var, user_id_len, 1); + + if (*user_id_len == 0 || *user_id_len > BMKT_MAX_USER_ID_LEN) + return FALSE; + + if (*user_id_len <= 0 || *user_id[0] == ' ') + return FALSE; + + return TRUE; +} + +static void +list_msg_cb(FpiDeviceSynaptics *self, + bmkt_response_t *resp, + GError *error) +{ + bmkt_enroll_templates_resp_t *get_enroll_templates_resp; + + if (error) { + g_clear_pointer (&self->list_result, g_ptr_array_free); + fpi_device_list_complete (FP_DEVICE (self), NULL, error); + return; + } + + get_enroll_templates_resp = &resp->response.enroll_templates_resp; + + switch (resp->response_id) + { + case BMKT_RSP_QUERY_FAIL: + if (resp->result == BMKT_FP_DATABASE_EMPTY) { + fp_info("Database is empty"); + + fpi_device_list_complete (FP_DEVICE (self), + g_steal_pointer (&self->list_result), + NULL); + } else { + fp_info("Failed to query enrolled users: %d", resp->result); + g_clear_pointer (&self->list_result, g_ptr_array_free); + fpi_device_list_complete (FP_DEVICE (self), + NULL, + fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, + "Failed to query enrolled users")); + } + break; + case BMKT_RSP_QUERY_RESPONSE_COMPLETE: + fp_info("Query complete!"); + + fpi_device_list_complete (FP_DEVICE (self), + g_steal_pointer (&self->list_result), + NULL); + + break; + case BMKT_RSP_TEMPLATE_RECORDS_REPORT: + + for (int n = 0; n < BMKT_MAX_NUM_TEMPLATES_INTERNAL_FLASH; n++) + { + GVariant *data = NULL; + GVariant *uid = NULL; + FpPrint *print; + + if (get_enroll_templates_resp->templates[n].user_id_len == 0) + continue; + + fp_info("![query %d of %d] template %d: status=0x%x, userId=%s, fingerId=%d", + get_enroll_templates_resp->query_sequence, + get_enroll_templates_resp->total_query_messages, + n, + get_enroll_templates_resp->templates[n].template_status, + get_enroll_templates_resp->templates[n].user_id, + get_enroll_templates_resp->templates[n].finger_id); + + print = fp_print_new (FP_DEVICE (self)); + uid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, + get_enroll_templates_resp->templates[n].user_id, + get_enroll_templates_resp->templates[n].user_id_len, + 1); + data = g_variant_new ("(y@ay)", + get_enroll_templates_resp->templates[n].finger_id, + uid); + + fpi_print_set_type (print, FP_PRINT_RAW); + g_object_set (print, "fp-data", data, NULL); + g_object_set (print, "description", get_enroll_templates_resp->templates[n].user_id, NULL); + g_ptr_array_add (self->list_result, g_object_ref_sink (print)); + } + + synaptics_sensor_cmd (self, + self->cmd_seq_num, + BMKT_CMD_GET_NEXT_QUERY_RESPONSE, + NULL, + 0, + NULL); + + break; + } +} + +static void +list(FpDevice *device) +{ + FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (device); + + G_DEBUG_HERE(); + + self->list_result = g_ptr_array_new_with_free_func (g_object_unref); + synaptics_sensor_cmd (self, 0, BMKT_CMD_GET_TEMPLATE_RECORDS, NULL, 0, list_msg_cb); +} + +static void +verify_msg_cb(FpiDeviceSynaptics *self, + bmkt_response_t *resp, + GError *error) +{ + FpDevice *device = FP_DEVICE (self); + bmkt_verify_resp_t *verify_resp; + + if (error) { + fpi_device_verify_complete (device, FPI_MATCH_ERROR, NULL, error); + return; + } + + if (resp == NULL && self->cmd_complete_on_removal) { + fpi_device_verify_complete (device, + GPOINTER_TO_INT (self->cmd_complete_data), + NULL, + error); + return; + } + + verify_resp = &resp->response.verify_resp; + + switch (resp->response_id) + { + case BMKT_RSP_VERIFY_READY: + fp_info("Place Finger on the Sensor!"); + break; + + case BMKT_RSP_CAPTURE_COMPLETE: + fp_info("Fingerprint image capture complete!"); + break; + + case BMKT_RSP_VERIFY_FAIL: + if(resp->result == BMKT_SENSOR_STIMULUS_ERROR) { + fp_dbg ("delaying retry error until after finger removal!"); + self->cmd_complete_on_removal = TRUE; + self->cmd_complete_data = GINT_TO_POINTER (FPI_MATCH_ERROR); + self->cmd_complete_error = fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL); + } else if (resp->result == BMKT_FP_NO_MATCH) { + fp_dbg ("delaying match failure until after finger removal!"); + self->cmd_complete_on_removal = TRUE; + self->cmd_complete_data = GINT_TO_POINTER (FPI_MATCH_FAIL); + self->cmd_complete_error = NULL; + } else if (BMKT_FP_DATABASE_NO_RECORD_EXISTS) { + fp_info("Print is not in database"); + fpi_device_verify_complete (device, + FPI_MATCH_ERROR, + NULL, + fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND)); + } else { + fp_warn("Verify has failed: %d", resp->result); + fpi_device_verify_complete (device, FPI_MATCH_FAIL, NULL, NULL); + } + break; + + case BMKT_RSP_VERIFY_OK: + fp_info("Verify was successful! for user: %s finger: %d score: %f", + verify_resp->user_id, verify_resp->finger_id, verify_resp->match_result); + fpi_device_verify_complete (device, FPI_MATCH_SUCCESS, NULL, NULL); + break; + } +} + +static void +verify(FpDevice *device) +{ + FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (device); + FpPrint *print = NULL; + g_autoptr(GVariant) data = NULL; + guint8 finger; + const guint8 *user_id; + gsize user_id_len = 0; + + fpi_device_get_verify_data (device, &print); + + g_object_get (print, "fp-data", &data, NULL); + g_debug ("data is %p", data); + if (!parse_print_data (data, &finger, &user_id, &user_id_len)) { + fpi_device_verify_complete (device, + FPI_MATCH_ERROR, + NULL, + fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID)); + return; + } + + G_DEBUG_HERE(); + + synaptics_sensor_cmd (self, 0, BMKT_CMD_VERIFY_USER, user_id, user_id_len, verify_msg_cb); +} + +static void +enroll_msg_cb(FpiDeviceSynaptics *self, + bmkt_response_t *resp, + GError *error) +{ + FpDevice *device = FP_DEVICE (self); + bmkt_enroll_resp_t *enroll_resp; + + if (error) { + fpi_device_enroll_complete (device, NULL, error); + return; + } + + enroll_resp = &resp->response.enroll_resp; + + switch (resp->response_id) + { + case BMKT_RSP_ENROLL_READY: + { + self->enroll_stage = 0; + fp_info("Place Finger on the Sensor!"); + break; + } + case BMKT_RSP_CAPTURE_COMPLETE: + { + fp_info("Fingerprint image capture complete!"); + break; + } + case BMKT_RSP_ENROLL_REPORT: + { + gint done_stages; + fp_info("Enrollment is %d %% ", enroll_resp->progress); + + done_stages = (enroll_resp->progress * ENROLL_SAMPLES + 99) / 100; + if (enroll_resp->progress < 100) + done_stages = MIN(done_stages, ENROLL_SAMPLES - 1); + + /* Emit a retry error if there has been no discernable + * progress. Some firmware revisions report more required + * touches. */ + if (self->enroll_stage == done_stages) + fpi_device_enroll_progress (device, + done_stages, + NULL, + fpi_device_retry_new (FP_DEVICE_RETRY_GENERAL)); + + while (self->enroll_stage < done_stages) { + self->enroll_stage += 1; + fpi_device_enroll_progress (device, self->enroll_stage, NULL, NULL); + } + break; + } + case BMKT_RSP_ENROLL_PAUSED: + { + fp_info("Enrollment has been paused!"); + break; + } + case BMKT_RSP_ENROLL_RESUMED: + { + fp_info("Enrollment has been resumed!"); + break; + } + case BMKT_RSP_ENROLL_FAIL: + { + fp_info("Enrollment has failed!: %d", resp->result); + if (resp->result == BMKT_FP_DATABASE_FULL) + fpi_device_enroll_complete (device, + NULL, + fpi_device_error_new (FP_DEVICE_ERROR_DATA_FULL)); + else + fpi_device_enroll_complete (device, + NULL, + fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, + "Enrollment failed")); + break; + } + case BMKT_RSP_ENROLL_OK: + { + FpPrint *print = NULL; + + fp_info("Enrollment was successful!"); + + fpi_device_get_enroll_data (device, &print); + + fpi_device_enroll_complete (device, g_object_ref (print), NULL); + break; + } + } +} + +#define TEMPLATE_ID_SIZE 20 + +static void +enroll(FpDevice *device) +{ + FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (device); + FpPrint *print = NULL; + GVariant *data = NULL; + GVariant *uid = NULL; + guint8 finger; + char user_id[TEMPLATE_ID_SIZE + 1]; + gssize user_id_len; + guint8 payload[TEMPLATE_ID_SIZE + 1 + 2]; + + fpi_device_get_enroll_data (device, &print); + + G_DEBUG_HERE(); + + finger = 1; + rand_string (user_id, TEMPLATE_ID_SIZE); + user_id_len = strlen (user_id); + + uid = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, + user_id, + user_id_len, + 1); + data = g_variant_new ("(y@ay)", + finger, + uid); + + fpi_print_set_type (print, FP_PRINT_RAW); + g_object_set (print, "fp-data", data, NULL); + g_object_set (print, "description", user_id, NULL); + + g_debug("user_id: %s, finger: %d", user_id, finger); + + /* Backup options are not supported for Prometheus */ + payload[0] = 0; + payload[1] = finger; + memcpy (payload + 2, user_id, user_id_len); + + synaptics_sensor_cmd (self, 0, BMKT_CMD_ENROLL_USER, payload, user_id_len + 2, enroll_msg_cb); +} + +static void +delete_msg_cb(FpiDeviceSynaptics *self, + bmkt_response_t *resp, + GError *error) +{ + FpDevice *device = FP_DEVICE (self); + bmkt_del_user_resp_t *del_user_resp; + + if (error) { + fpi_device_delete_complete (device, error); + return; + } + + del_user_resp = &resp->response.del_user_resp; + + switch (resp->response_id) + { + case BMKT_RSP_DELETE_PROGRESS: + fp_info("Deleting Enrolled Users is %d%% complete", + del_user_resp->progress); + break; + case BMKT_RSP_DEL_USER_FP_FAIL: + fp_info("Failed to delete enrolled user: %d", resp->result); + if (resp->result == BMKT_FP_DATABASE_NO_RECORD_EXISTS) + fpi_device_delete_complete (device, + fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND)); + else + fpi_device_delete_complete (device, + fpi_device_error_new (FP_DEVICE_ERROR_GENERAL)); + break; + case BMKT_RSP_DEL_USER_FP_OK: + fp_info("Successfully deleted enrolled user"); + fpi_device_delete_complete (device, NULL); + break; + } +} + +static void +delete_print(FpDevice *device) +{ + FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (device); + FpPrint *print = NULL; + g_autoptr(GVariant) data = NULL; + guint8 finger; + const guint8 *user_id; + gsize user_id_len = 0; + g_autofree guint8 *payload = NULL; + + fpi_device_get_delete_data (device, &print); + + g_object_get (print, "fp-data", &data, NULL); + g_debug ("data is %p", data); + if (!parse_print_data (data, &finger, &user_id, &user_id_len)) { + fpi_device_delete_complete (device, + fpi_device_error_new (FP_DEVICE_ERROR_DATA_INVALID)); + return; + } + + G_DEBUG_HERE(); + + payload = g_malloc0 (1 + user_id_len); + payload[0] = finger; + memcpy (payload + 1, user_id, user_id_len); + + synaptics_sensor_cmd (self, 0, BMKT_CMD_DEL_USER_FP, payload, user_id_len + 1, delete_msg_cb); +} + +static void +dev_probe(FpDevice *device) +{ + FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (device); + GUsbDevice *usb_dev; + FpiUsbTransfer *transfer; + FpiByteReader reader; + GError *error = NULL; + guint16 status; + const guint8 *data; + gboolean read_ok = TRUE; + g_autofree gchar *serial = NULL; + + G_DEBUG_HERE(); + + /* Claim usb interface */ + usb_dev = fpi_device_get_usb_device(device); + if (!g_usb_device_open (usb_dev, &error)) { + fpi_device_probe_complete (device, NULL, NULL, error); + return; + } + + if (!g_usb_device_reset (fpi_device_get_usb_device (device), &error)) { + fpi_device_probe_complete (device, NULL, NULL, error); + return; + } + + if (!g_usb_device_claim_interface (fpi_device_get_usb_device(device), 0, 0, &error)) + goto err_close; + + /* TODO: Do not do this synchronous. */ + transfer = fpi_usb_transfer_new (device); + fpi_usb_transfer_fill_bulk (transfer, USB_EP_REQUEST, SENSOR_FW_CMD_HEADER_LEN); + transfer->short_is_error = TRUE; + transfer->buffer[0] = SENSOR_CMD_GET_VERSION; + if (!fpi_usb_transfer_submit_sync (transfer, 1000, &error)) + goto err_close; + fpi_usb_transfer_unref (transfer); + + + transfer = fpi_usb_transfer_new (device); + fpi_usb_transfer_fill_bulk (transfer, USB_EP_REPLY, 40); + if (!fpi_usb_transfer_submit_sync (transfer, 1000, &error)) + goto err_close; + + fpi_byte_reader_init (&reader, transfer->buffer, transfer->actual_length); + + if (!fpi_byte_reader_get_uint16_le (&reader, &status)) { + g_warning ("Transfer in response to version query was too short"); + error = fpi_device_error_new (FP_DEVICE_ERROR_PROTO); + goto err_close; + } + if (status != 0) { + g_warning ("Device responded with error: %d", status); + error = fpi_device_error_new (FP_DEVICE_ERROR_PROTO); + goto err_close; + } + + read_ok &= fpi_byte_reader_get_uint32_le (&reader, &self->mis_version.build_time); + read_ok &= fpi_byte_reader_get_uint32_le (&reader, &self->mis_version.build_num); + read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.version_major); + read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.version_minor); + read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.target); + read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.product); + + read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.silicon_rev); + read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.formal_release); + read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.platform); + read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.patch); + if (fpi_byte_reader_get_data (&reader, sizeof(self->mis_version.serial_number), &data)) + memcpy (self->mis_version.serial_number, data, sizeof(self->mis_version.serial_number)); + else + read_ok = FALSE; + read_ok &= fpi_byte_reader_get_uint16_le (&reader, &self->mis_version.security); + read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.iface); + read_ok &= fpi_byte_reader_get_uint8 (&reader, &self->mis_version.device_type); + + if (!read_ok) { + g_warning ("Transfer in response to verison query was too short"); + error = fpi_device_error_new (FP_DEVICE_ERROR_PROTO); + goto err_close; + } + + fp_dbg("Build Time: %d", self->mis_version.build_time); + fp_dbg("Build Num: %d", self->mis_version.build_num); + fp_dbg("Version: %d.%d", self->mis_version.version_major, self->mis_version.version_minor); + fp_dbg("Target: %d", self->mis_version.target); + fp_dbg("Product: %d", self->mis_version.product); + + fpi_usb_transfer_unref (transfer); + + /* We need at least firmware version 10.1, and for 10.1 build 2989158 */ + if (self->mis_version.version_major < 10 || + self->mis_version.version_minor < 1 || + (self->mis_version.version_major == 10 && + self->mis_version.version_minor == 1 && + self->mis_version.build_num < 2989158)) + { + fp_warn ("Firmware version %d.%d with build number %d is unsupported", + self->mis_version.version_major, + self->mis_version.version_minor, + self->mis_version.build_num); + + error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL, + "Unsupported firmware version"); + goto err_close; + } + + /* This is the same as the serial_number from above, hex encoded and somewhat reordered */ + /* Should we add in more, e.g. the chip revision? */ + serial = g_usb_device_get_string_descriptor (usb_dev, + g_usb_device_get_serial_number_index (usb_dev), + &error); + + g_usb_device_close (usb_dev, NULL); + + fpi_device_probe_complete (device, serial, NULL, error); + + return; + +err_close: + g_usb_device_close (usb_dev, NULL); + fpi_device_probe_complete (device, NULL, NULL, error); +} + +static void +fps_init_msg_cb(FpiDeviceSynaptics *self, + bmkt_response_t *resp, + GError *error) +{ + if (error) { + fpi_device_open_complete (FP_DEVICE (self), error); + return; + } + + /* BMKT_OPERATION_DENIED is returned if the sensor is already initialized */ + if (resp->result == BMKT_SUCCESS || resp->result == BMKT_OPERATION_DENIED) { + fpi_device_open_complete (FP_DEVICE (self), NULL); + } else { + g_warning ("Initializing fingerprint sensor failed with %d!", resp->result); + fpi_device_open_complete (FP_DEVICE (self), + fpi_device_error_new (FP_DEVICE_ERROR_GENERAL)); + } +} + +static void +dev_init (FpDevice *device) +{ + FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (device); + GError *error = NULL; + + G_DEBUG_HERE(); + + self->interrupt_cancellable = g_cancellable_new (); + + if (!g_usb_device_reset (fpi_device_get_usb_device (device), &error)) + goto error; + + /* Claim usb interface */ + if (!g_usb_device_claim_interface (fpi_device_get_usb_device (device), 0, 0, &error)) + goto error; + + synaptics_sensor_cmd (self, 0, BMKT_CMD_FPS_INIT, NULL, 0, fps_init_msg_cb); + + return; + +error: + fpi_device_open_complete (FP_DEVICE (self), error); +} + +static void +dev_exit(FpDevice *device) +{ + FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS (device); + GError *error = NULL; + + G_DEBUG_HERE(); + + /* Release usb interface */ + g_usb_device_release_interface(fpi_device_get_usb_device(device), 0, 0, &error); + + g_clear_object (&self->interrupt_cancellable); + + fpi_device_close_complete(device, error); +} + +static void +cancel(FpDevice *dev) +{ + FpiDeviceSynaptics *self = FPI_DEVICE_SYNAPTICS(dev); + + /* We just send out a cancel command and hope for the best. */ + synaptics_sensor_cmd (self, -1, BMKT_CMD_CANCEL_OP, NULL, 0, NULL); + + /* Cancel any current interrupt transfer (resulting us to go into + * response reading mode again); then create a new cancellable + * for the next transfers. */ + g_cancellable_cancel (self->interrupt_cancellable); + g_clear_object (&self->interrupt_cancellable); + self->interrupt_cancellable = g_cancellable_new (); +} + +static void +fpi_device_synaptics_init(FpiDeviceSynaptics *self) { +} + +static void +fpi_device_synaptics_class_init(FpiDeviceSynapticsClass *klass) { + FpDeviceClass *dev_class = FP_DEVICE_CLASS(klass); + + dev_class->id = FP_COMPONENT; + dev_class->full_name = SYNAPTICS_DRIVER_FULLNAME; + + dev_class->type = FP_DEVICE_TYPE_USB; + dev_class->scan_type = FP_SCAN_TYPE_PRESS; + dev_class->id_table = id_table; + dev_class->nr_enroll_stages = ENROLL_SAMPLES; + + dev_class->open = dev_init; + dev_class->close = dev_exit; + dev_class->probe = dev_probe; + dev_class->verify = verify; + dev_class->enroll = enroll; + dev_class->delete = delete_print; + dev_class->cancel = cancel; + dev_class->list = list; +} diff --git a/libfprint/drivers/synaptics/synaptics.h b/libfprint/drivers/synaptics/synaptics.h new file mode 100644 index 0000000..a0bc0b8 --- /dev/null +++ b/libfprint/drivers/synaptics/synaptics.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2019 Synaptics Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __synaptics_h__ +#define __synaptics_h__ + +#include "fpi-device.h" +#include "fpi-ssm.h" + +#define SYNAPTICS_VENDOR_ID 0x06cb + +G_DECLARE_FINAL_TYPE(FpiDeviceSynaptics, fpi_device_synaptics, FPI, DEVICE_SYNAPTICS, FpDevice) + + +#define MAX_TRANSFER_LEN 263 + 1 /* SPI Header */ + 2 /* VCSFW header */ + +#define USB_EP_REQUEST 0x01 +#define USB_EP_REPLY 0x81 +#define USB_EP_FINGERPRINT 0x82 +#define USB_EP_INTERRUPT 0x83 + +#define USB_ASYNC_MESSAGE_PENDING 0x4 +#define USB_INTERRUPT_DATA_SIZE 7 + +#define SENSOR_CMD_GET_VERSION 1 +#define SENSOR_CMD_ACE_COMMAND 167 +#define SENSOR_CMD_ASYNCMSG_READ 168 + +#define SENSOR_FW_CMD_HEADER_LEN 1 +#define SENSOR_FW_REPLY_HEADER_LEN 2 + + +/* Number of enroll stages */ +#define ENROLL_SAMPLES 8 + + +#define SYNAPTICS_DRIVER_FULLNAME "Synaptics Sensors" +#include "bmkt.h" +#include "bmkt_response.h" + + +typedef struct bmkt_sensor_version +{ + uint32_t build_time; + uint32_t build_num; + uint8_t version_major; + uint8_t version_minor; + uint8_t target; + uint8_t product; + uint8_t silicon_rev; + uint8_t formal_release; + uint8_t platform; + uint8_t patch; + uint8_t serial_number[6]; + uint16_t security; + uint8_t iface; + uint8_t device_type; +} bmkt_sensor_version_t; + + +struct syna_enroll_resp_data +{ + int progress; +}; +typedef enum syna_state +{ + SYNA_STATE_UNINIT = 0, + SYNA_STATE_IDLE , + SYNA_STATE_ENROLL , + SYNA_STATE_IDENTIFY , + SYNA_STATE_IDENTIFY_DELAY_RESULT , + SYNA_STATE_VERIFY , + SYNA_STATE_VERIFY_DELAY_RESULT , + SYNA_STATE_DELETE , +} syna_state_t; + +typedef enum +{ + SYNAPTICS_CMD_SEND_PENDING = 0, + SYNAPTICS_CMD_GET_RESP, + SYNAPTICS_CMD_WAIT_INTERRUPT, + SYNAPTICS_CMD_SEND_ASYNC, + SYNAPTICS_CMD_RESTART, + SYNAPTICS_CMD_NUM_STATES, +} SynapticsCmdState; + + +typedef void (*SynCmdMsgCallback) (FpiDeviceSynaptics *self, bmkt_response_t *resp, GError *error); + +struct _FpiDeviceSynaptics +{ + FpDevice parent; + + guint8 cmd_seq_num; + guint8 last_seq_num; + FpiSsm *cmd_ssm; + FpiUsbTransfer *cmd_pending_transfer; + gboolean cmd_complete_on_removal; + GError *cmd_complete_error; + void *cmd_complete_data; + + bmkt_sensor_version_t mis_version; + + GCancellable *interrupt_cancellable; + + gint enroll_stage; + gboolean finger_on_sensor; + GPtrArray *list_result; + + + struct syna_enroll_resp_data enroll_resp_data; + syna_state_t state; +}; + +#endif //__synaptics_h__ diff --git a/libfprint/meson.build b/libfprint/meson.build index f2a2e4d..af2fe84 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -141,6 +141,12 @@ foreach driver: drivers if driver == 'virtual_image' drivers_sources += [ 'drivers/virtual-image.c' ] endif + if driver == 'synaptics' + drivers_sources += [ + 'drivers/synaptics/synaptics.c', + 'drivers/synaptics/bmkt_message.c', + ] + endif endforeach if aeslib diff --git a/meson.build b/meson.build index 8481073..5552e20 100644 --- a/meson.build +++ b/meson.build @@ -51,7 +51,7 @@ mathlib_dep = cc.find_library('m', required: false) drivers = get_option('drivers').split(',') virtual_drivers = [ 'virtual_image' ] #default_drivers = [ 'upekts', 'upektc', 'upeksonly', 'vcom5s', 'uru4000', 'aes1610', 'aes1660', 'aes2501', 'aes2550', 'aes2660', 'aes3500', 'aes4000', 'vfs101', 'vfs301', 'vfs5011', 'upektc_img', 'etes603', 'vfs0050', 'elan' ] -default_drivers = [ 'upektc_img', 'vfs5011', 'aes3500', 'aes4000', 'aes1610', 'aes1660', 'aes2660', 'aes2501', 'aes2550', 'vfs101', 'vfs301', 'vfs0050', 'etes603', 'vcom5s' ] +default_drivers = [ 'upektc_img', 'vfs5011', 'aes3500', 'aes4000', 'aes1610', 'aes1660', 'aes2660', 'aes2501', 'aes2550', 'vfs101', 'vfs301', 'vfs0050', 'etes603', 'vcom5s', 'synaptics'] all_drivers = default_drivers + virtual_drivers