diff options
-rw-r--r-- | common/fpsensor.c | 327 | ||||
-rw-r--r-- | include/ec_commands.h | 94 | ||||
-rw-r--r-- | include/fpsensor.h | 57 | ||||
-rw-r--r-- | util/ectool.c | 134 |
4 files changed, 553 insertions, 59 deletions
diff --git a/common/fpsensor.c b/common/fpsensor.c index c465dd6963..7d1993a61f 100644 --- a/common/fpsensor.c +++ b/common/fpsensor.c @@ -4,6 +4,7 @@ */ #include "atomic.h" +#include "clock.h" #include "common.h" #include "console.h" #include "ec_commands.h" @@ -26,15 +27,29 @@ #define FP_SENSOR_IMAGE_SIZE 0 #define FP_SENSOR_RES_X 0 #define FP_SENSOR_RES_Y 0 +#define FP_ALGORITHM_TEMPLATE_SIZE 0 +#define FP_MAX_FINGER_COUNT 0 #endif /* if no special memory regions are defined, fallback on regular SRAM */ #ifndef FP_FRAME_SECTION #define FP_FRAME_SECTION #endif +#ifndef FP_TEMPLATE_SECTION +#define FP_TEMPLATE_SECTION +#endif /* Last acquired frame */ static uint8_t fp_buffer[FP_SENSOR_IMAGE_SIZE] FP_FRAME_SECTION; +/* Fingers templates for the current user */ +static uint8_t fp_template[FP_MAX_FINGER_COUNT][FP_ALGORITHM_TEMPLATE_SIZE] + FP_TEMPLATE_SECTION; +/* Number of used templates */ +static uint32_t templ_valid; +/* Bitmap of the templates with local modifications */ +static uint32_t templ_dirty; +/* Current user ID */ +static uint32_t user_id[FP_CONTEXT_USERID_WORDS]; #define CPRINTF(format, args...) cprintf(CC_FP, format, ## args) #define CPRINTS(format, args...) cprints(CC_FP, format, ## args) @@ -48,9 +63,11 @@ static uint8_t fp_buffer[FP_SENSOR_IMAGE_SIZE] FP_FRAME_SECTION; #define TASK_EVENT_SENSOR_IRQ TASK_EVENT_CUSTOM(1) #define TASK_EVENT_UPDATE_CONFIG TASK_EVENT_CUSTOM(2) +#define FP_MODE_ANY_CAPTURE (FP_MODE_CAPTURE | FP_MODE_ENROLL_IMAGE | \ + FP_MODE_MATCH) #define FP_MODE_ANY_DETECT_FINGER (FP_MODE_FINGER_DOWN | FP_MODE_FINGER_UP | \ - FP_MODE_CAPTURE) -#define FP_MODE_ANY_WAIT_IRQ (FP_MODE_FINGER_DOWN | FP_MODE_CAPTURE) + FP_MODE_ANY_CAPTURE) +#define FP_MODE_ANY_WAIT_IRQ (FP_MODE_FINGER_DOWN | FP_MODE_ANY_CAPTURE) /* Delay between 2 s of the sensor to detect finger removal */ #define FINGER_POLLING_DELAY (100*MSEC) @@ -64,6 +81,21 @@ void fps_event(enum gpio_signal signal) task_set_event(TASK_ID_FPSENSOR, TASK_EVENT_SENSOR_IRQ, 0); } +static void send_mkbp_event(uint32_t event) +{ + atomic_or(&fp_events, event); + mkbp_send_event(EC_MKBP_EVENT_FINGERPRINT); +} + +static inline int is_raw_capture(uint32_t mode) +{ + int capture_type = FP_CAPTURE_TYPE(mode); + + return (capture_type == FP_CAPTURE_VENDOR_FORMAT + || capture_type == FP_CAPTURE_QUALITY_TEST); +} + +#ifdef HAVE_FP_PRIVATE_DRIVER static inline int is_test_capture(uint32_t mode) { int capture_type = FP_CAPTURE_TYPE(mode); @@ -73,19 +105,78 @@ static inline int is_test_capture(uint32_t mode) || capture_type == FP_CAPTURE_PATTERN1); } -static inline int is_raw_capture(uint32_t mode) +/* + * contains the bit FP_MODE_ENROLL_SESSION if a finger enrollment is on-going. + * It is used to detect the ENROLL_SESSION transition when sensor_mode is + * updated by the host. + */ +static uint32_t enroll_session; + +static uint32_t fp_process_enroll(void) { - int capture_type = FP_CAPTURE_TYPE(mode); + int percent = 0; + int res; + + /* begin/continue enrollment */ + CPRINTS("[%d]Enrolling ...", templ_valid); + res = fp_finger_enroll(fp_buffer, &percent); + CPRINTS("[%d]Enroll =>%d (%d%%)", templ_valid, res, percent); + if (res < 0) + return EC_MKBP_FP_ENROLL + | EC_MKBP_FP_ERRCODE(EC_MKBP_FP_ERR_ENROLL_INTERNAL); + templ_dirty |= (1 << templ_valid); + if (percent == 100) { + res = fp_enrollment_finish(fp_template[templ_valid]); + if (res) + res = EC_MKBP_FP_ERR_ENROLL_INTERNAL; + else + templ_valid++; + sensor_mode &= ~FP_MODE_ENROLL_SESSION; + enroll_session &= ~FP_MODE_ENROLL_SESSION; + } + return EC_MKBP_FP_ENROLL | EC_MKBP_FP_ERRCODE(res) + | (percent << EC_MKBP_FP_ENROLL_PROGRESS_OFFSET); +} - return (capture_type == FP_CAPTURE_VENDOR_FORMAT - || capture_type == FP_CAPTURE_QUALITY_TEST); +static uint32_t fp_process_match(void) +{ + int res; + uint32_t updated = 0; + + /* match finger against current templates */ + CPRINTS("Matching/%d ...", templ_valid); + res = fp_finger_match(fp_template[0], templ_valid, fp_buffer, &updated); + CPRINTS("Match =>%d", res); + if (res < 0) + res = EC_MKBP_FP_ERR_MATCH_NO_INTERNAL; + if (res == EC_MKBP_FP_ERR_MATCH_YES_UPDATED) + templ_dirty |= updated; + return EC_MKBP_FP_MATCH | EC_MKBP_FP_ERRCODE(res); } -static void send_mkbp_event(uint32_t event) +static void fp_process_finger(void) { - atomic_or(&fp_events, event); - mkbp_send_event(EC_MKBP_EVENT_FINGERPRINT); + int res = fp_sensor_acquire_image_with_mode(fp_buffer, + FP_CAPTURE_TYPE(sensor_mode)); + if (!res) { + uint32_t evt = EC_MKBP_FP_IMAGE_READY; + + /* we need CPU power to do the computations */ + clock_enable_module(MODULE_FAST_CPU, 1); + + if (sensor_mode & FP_MODE_ENROLL_IMAGE) + evt = fp_process_enroll(); + else if (sensor_mode & FP_MODE_MATCH) + evt = fp_process_match(); + + sensor_mode &= ~FP_MODE_ANY_CAPTURE; + send_mkbp_event(evt); + + /* go back to lower power mode */ + clock_enable_module(MODULE_FAST_CPU, 0); + } } +#endif /* HAVE_FP_PRIVATE_DRIVER */ void fp_task(void) { @@ -107,10 +198,23 @@ void fp_task(void) evt = task_wait_event(timeout_us); if (evt & TASK_EVENT_UPDATE_CONFIG) { + uint32_t mode = sensor_mode; + gpio_disable_interrupt(GPIO_FPS_INT); - if (is_test_capture(sensor_mode)) { + if ((mode ^ enroll_session) & FP_MODE_ENROLL_SESSION) { + if (mode & FP_MODE_ENROLL_SESSION) { + if (fp_enrollment_begin()) + sensor_mode &= + ~FP_MODE_ENROLL_SESSION; + } else { + fp_enrollment_finish(NULL); + } + enroll_session = + sensor_mode & FP_MODE_ENROLL_SESSION; + } + if (is_test_capture(mode)) { fp_sensor_acquire_image_with_mode(fp_buffer, - FP_CAPTURE_TYPE(sensor_mode)); + FP_CAPTURE_TYPE(mode)); sensor_mode &= ~FP_MODE_CAPTURE; send_mkbp_event(EC_MKBP_FP_IMAGE_READY); continue; @@ -126,7 +230,7 @@ void fp_task(void) timeout_us = FINGER_POLLING_DELAY; else timeout_us = -1; - if (sensor_mode & FP_MODE_ANY_WAIT_IRQ) + if (mode & FP_MODE_ANY_WAIT_IRQ) gpio_enable_interrupt(GPIO_FPS_INT); } else if (evt & (TASK_EVENT_SENSOR_IRQ | TASK_EVENT_TIMER)) { gpio_disable_interrupt(GPIO_FPS_INT); @@ -147,16 +251,9 @@ void fp_task(void) } if (st == FINGER_PRESENT && - sensor_mode & FP_MODE_CAPTURE) { - int res = fp_sensor_acquire_image_with_mode( - fp_buffer, - FP_CAPTURE_TYPE(sensor_mode)); - - if (!res) { - sensor_mode &= ~FP_MODE_CAPTURE; - send_mkbp_event(EC_MKBP_FP_IMAGE_READY); - } - } + sensor_mode & FP_MODE_ANY_CAPTURE) + fp_process_finger(); + if (sensor_mode & FP_MODE_ANY_WAIT_IRQ) { fp_sensor_configure_detect(); gpio_enable_interrupt(GPIO_FPS_INT); @@ -172,6 +269,15 @@ void fp_task(void) #endif /* !HAVE_FP_PRIVATE_DRIVER */ } +static void fp_clear_context(void) +{ + templ_valid = 0; + templ_dirty = 0; + memset(fp_buffer, 0, sizeof(fp_buffer)); + memset(fp_template, 0, sizeof(fp_template)); + /* TODO maybe shutdown and re-init the private libraries ? */ +} + static int fp_get_next_event(uint8_t *out) { uint32_t event_out = atomic_read_clear(&fp_events); @@ -248,31 +354,95 @@ static int fp_command_info(struct host_cmd_handler_args *args) #endif return EC_RES_UNAVAILABLE; - args->response_size = sizeof(*r); + r->template_size = FP_ALGORITHM_TEMPLATE_SIZE; + r->template_max = FP_MAX_FINGER_COUNT; + r->template_valid = templ_valid; + r->template_dirty = templ_dirty; + + /* V1 is identical to V0 with more information appended */ + args->response_size = args->version ? sizeof(*r) : + sizeof(struct ec_response_fp_info_v0); return EC_RES_SUCCESS; } -DECLARE_HOST_COMMAND(EC_CMD_FP_INFO, fp_command_info, EC_VER_MASK(0)); +DECLARE_HOST_COMMAND(EC_CMD_FP_INFO, fp_command_info, + EC_VER_MASK(0) | EC_VER_MASK(1)); static int fp_command_frame(struct host_cmd_handler_args *args) { const struct ec_params_fp_frame *params = args->params; void *out = args->response; - uint32_t offset = params->offset; - - if (!is_raw_capture(sensor_mode)) - offset += FP_SENSOR_IMAGE_OFFSET; + uint32_t idx = FP_FRAME_TEMPLATE_INDEX(params->offset); + uint32_t offset = params->offset & FP_FRAME_OFFSET_MASK; + uint32_t max_size; + uint8_t *content; + + if (idx == FP_FRAME_INDEX_RAW_IMAGE) { + if (!is_raw_capture(sensor_mode)) + offset += FP_SENSOR_IMAGE_OFFSET; + max_size = sizeof(fp_buffer); + content = fp_buffer; + } else if (idx > FP_MAX_FINGER_COUNT) { + return EC_RES_INVALID_PARAM; + } else if (idx > templ_valid) { + return EC_RES_UNAVAILABLE; + } else { /* the host requested a template */ + max_size = sizeof(fp_template[0]); + /* Templates are numbered from 1 in this host request. */ + content = fp_template[idx - 1]; + templ_dirty &= ~(1 << (idx - 1)); + } - if (offset + params->size > sizeof(fp_buffer) || + if (offset + params->size > max_size || params->size > args->response_max) return EC_RES_INVALID_PARAM; - memcpy(out, fp_buffer + offset, params->size); + memcpy(out, content + offset, params->size); args->response_size = params->size; return EC_RES_SUCCESS; } DECLARE_HOST_COMMAND(EC_CMD_FP_FRAME, fp_command_frame, EC_VER_MASK(0)); +static int fp_command_template(struct host_cmd_handler_args *args) +{ + const struct ec_params_fp_template *params = args->params; + uint32_t size = params->size & ~FP_TEMPLATE_COMMIT; + uint32_t idx = templ_valid; + + /* Can we store one more template ? */ + if (idx >= FP_MAX_FINGER_COUNT) + return EC_RES_OVERFLOW; + + if ((args->params_size != + size + offsetof(struct ec_params_fp_template, data)) || + (params->offset + size > sizeof(fp_template[0]))) + return EC_RES_INVALID_PARAM; + + memcpy(&fp_template[idx][params->offset], params->data, size); + + if (params->size & FP_TEMPLATE_COMMIT) + templ_valid++; + + return EC_RES_SUCCESS; +} +DECLARE_HOST_COMMAND(EC_CMD_FP_TEMPLATE, fp_command_template, EC_VER_MASK(0)); + +static int fp_command_context(struct host_cmd_handler_args *args) +{ + const struct ec_params_fp_context *params = args->params; + struct ec_response_fp_context *resp = args->response; + + fp_clear_context(); + + memcpy(user_id, params->userid, sizeof(user_id)); + /* TODO(b/73337313): real crypto protocol */ + memcpy(resp->nonce, params->nonce, sizeof(resp->nonce)); + + args->response_size = sizeof(*resp); + return EC_RES_SUCCESS; +} +DECLARE_HOST_COMMAND(EC_CMD_FP_CONTEXT, fp_command_context, EC_VER_MASK(0)); + #ifdef CONFIG_CMD_FPSENSOR_DEBUG /* --- Debug console commands --- */ @@ -319,35 +489,102 @@ static void upload_pgm_image(uint8_t *frame) ccprintf("\x04"); /* End Of Transmission */ } -int command_fptest(int argc, char **argv) +static int fp_console_action(uint32_t mode) { int tries = 200; + ccprintf("Waiting for finger ...\n"); + sensor_mode = mode; + task_set_event(TASK_ID_FPSENSOR, TASK_EVENT_UPDATE_CONFIG, 0); + + while (tries--) { + if (!(sensor_mode & FP_MODE_ANY_CAPTURE)) { + ccprintf("done (events:%x)\n", fp_events); + return 0; + } + usleep(100 * MSEC); + } + return EC_ERROR_TIMEOUT; +} + +int command_fpcapture(int argc, char **argv) +{ int capture_type = FP_CAPTURE_SIMPLE_IMAGE; + uint32_t mode; + int rc; if (argc >= 2) { char *e; + int capture_type = strtoi(argv[1], &e, 0); - capture_type = strtoi(argv[1], &e, 0); - if (*e || capture_type < 0 || capture_type > 3) + if (*e || capture_type < 0) return EC_ERROR_PARAM1; } + mode = FP_MODE_CAPTURE | ((capture_type & FP_MODE_CAPTURE_TYPE_MASK) + << FP_MODE_CAPTURE_TYPE_SHIFT); - ccprintf("Waiting for finger ...\n"); - sensor_mode = FP_MODE_CAPTURE | - (capture_type << FP_MODE_CAPTURE_TYPE_SHIFT); + rc = fp_console_action(mode); + if (rc == EC_SUCCESS) + upload_pgm_image(fp_buffer + FP_SENSOR_IMAGE_OFFSET); + + return rc; +} +DECLARE_CONSOLE_COMMAND(fpcapture, command_fpcapture, "", ""); + +int command_fpenroll(int argc, char **argv) +{ + int rc; + int percent = 0; + uint32_t event; + static const char * const enroll_str[] = {"OK", "Low Quality", + "Immobile", "Low Coverage"}; + + do { + int tries = 1000; + + rc = fp_console_action(FP_MODE_ENROLL_SESSION | + FP_MODE_ENROLL_IMAGE); + if (rc != EC_SUCCESS) + break; + event = atomic_read_clear(&fp_events); + percent = EC_MKBP_FP_ENROLL_PROGRESS(event); + ccprintf("Enroll capture: %s (%d%%)\n", + enroll_str[EC_MKBP_FP_ERRCODE(event) & 3], percent); + /* wait for finger release between captures */ + sensor_mode = FP_MODE_ENROLL_SESSION | FP_MODE_FINGER_UP; + task_set_event(TASK_ID_FPSENSOR, TASK_EVENT_UPDATE_CONFIG, 0); + while (tries-- && sensor_mode & FP_MODE_FINGER_UP) + usleep(20 * MSEC); + } while (percent < 100); + sensor_mode = 0; /* reset FP_MODE_ENROLL_SESSION */ task_set_event(TASK_ID_FPSENSOR, TASK_EVENT_UPDATE_CONFIG, 0); - while (tries--) { - if (!(sensor_mode & FP_MODE_CAPTURE)) { - ccprintf("done\n"); - upload_pgm_image(fp_buffer + FP_SENSOR_IMAGE_OFFSET); - return 0; - } - usleep(100 * MSEC); + return rc; +} +DECLARE_CONSOLE_COMMAND(fpenroll, command_fpenroll, "", ""); + + +int command_fpmatch(int argc, char **argv) +{ + int rc = fp_console_action(FP_MODE_MATCH); + uint32_t event = atomic_read_clear(&fp_events); + + if (rc == EC_SUCCESS && event & EC_MKBP_FP_MATCH) { + uint32_t errcode = EC_MKBP_FP_ERRCODE(event); + + ccprintf("Match: %s (%d)\n", + errcode & EC_MKBP_FP_ERR_MATCH_YES ? "YES" : "NO", + errcode); } - return EC_ERROR_TIMEOUT; + return rc; +} +DECLARE_CONSOLE_COMMAND(fpmatch, command_fpmatch, "", ""); + +int command_fpclear(int argc, char **argv) +{ + fp_clear_context(); + return EC_SUCCESS; } -DECLARE_CONSOLE_COMMAND(fptest, command_fptest, "", ""); +DECLARE_CONSOLE_COMMAND(fpclear, command_fpclear, "", ""); #endif /* CONFIG_CMD_FPSENSOR_DEBUG */ diff --git a/include/ec_commands.h b/include/ec_commands.h index e1b5a77688..223691be24 100644 --- a/include/ec_commands.h +++ b/include/ec_commands.h @@ -3218,9 +3218,32 @@ struct __ec_align2 ec_response_keyboard_factory_test { /* Fingerprint events in 'fp_events' for EC_MKBP_EVENT_FINGERPRINT */ #define EC_MKBP_FP_RAW_EVENT(fp_events) ((fp_events) & 0x00FFFFFF) +#define EC_MKBP_FP_ERRCODE(fp_events) ((fp_events) & 0x0000000F) +#define EC_MKBP_FP_ENROLL_PROGRESS_OFFSET 4 +#define EC_MKBP_FP_ENROLL_PROGRESS(fpe) (((fpe) & 0x00000FF0) \ + >> EC_MKBP_FP_ENROLL_PROGRESS_OFFSET) +#define EC_MKBP_FP_ENROLL (1 << 27) +#define EC_MKBP_FP_MATCH (1 << 28) #define EC_MKBP_FP_FINGER_DOWN (1 << 29) #define EC_MKBP_FP_FINGER_UP (1 << 30) #define EC_MKBP_FP_IMAGE_READY (1 << 31) +/* code given by EC_MKBP_FP_ERRCODE() when EC_MKBP_FP_ENROLL is set */ +#define EC_MKBP_FP_ERR_ENROLL_OK 0 +#define EC_MKBP_FP_ERR_ENROLL_LOW_QUALITY 1 +#define EC_MKBP_FP_ERR_ENROLL_IMMOBILE 2 +#define EC_MKBP_FP_ERR_ENROLL_LOW_COVERAGE 3 +#define EC_MKBP_FP_ERR_ENROLL_INTERNAL 5 +/* Can be used to detect if image was usable for enrollment or not. */ +#define EC_MKBP_FP_ERR_ENROLL_PROBLEM_MASK 1 +/* code given by EC_MKBP_FP_ERRCODE() when EC_MKBP_FP_MATCH is set */ +#define EC_MKBP_FP_ERR_MATCH_NO 0 +#define EC_MKBP_FP_ERR_MATCH_NO_INTERNAL 6 +#define EC_MKBP_FP_ERR_MATCH_NO_LOW_QUALITY 2 +#define EC_MKBP_FP_ERR_MATCH_NO_LOW_COVERAGE 4 +#define EC_MKBP_FP_ERR_MATCH_YES 1 +#define EC_MKBP_FP_ERR_MATCH_YES_UPDATED 3 +#define EC_MKBP_FP_ERR_MATCH_YES_UPDATE_FAILED 5 + /*****************************************************************************/ /* Temperature sensor commands */ @@ -4694,8 +4717,14 @@ struct __ec_align2 ec_params_fp_sensor_config { /* Extracts the capture type from the sensor 'mode' word */ #define FP_CAPTURE_TYPE(mode) (((mode) >> FP_MODE_CAPTURE_TYPE_SHIFT) \ & FP_MODE_CAPTURE_TYPE_MASK) +/* Finger enrollment session on-going */ +#define FP_MODE_ENROLL_SESSION (1<<4) +/* Enroll the current finger image */ +#define FP_MODE_ENROLL_IMAGE (1<<5) +/* Try to match the current finger image */ +#define FP_MODE_MATCH (1<<6) /* special value: don't change anything just read back current mode */ -#define FP_MODE_DONT_CHANGE (1<<31) +#define FP_MODE_DONT_CHANGE (1<<31) struct __ec_align4 ec_params_fp_mode { uint32_t mode; /* as defined by FP_MODE_ constants */ @@ -4719,7 +4748,22 @@ struct __ec_align4 ec_response_fp_mode { /* Sensor initialization failed */ #define FP_ERROR_INIT_FAIL (1 << 15) -struct __ec_align2 ec_response_fp_info { +struct __ec_align4 ec_response_fp_info_v0 { + /* Sensor identification */ + uint32_t vendor_id; + uint32_t product_id; + uint32_t model_id; + uint32_t version; + /* Image frame characteristics */ + uint32_t frame_size; + uint32_t pixel_format; /* using V4L2_PIX_FMT_ */ + uint16_t width; + uint16_t height; + uint16_t bpp; + uint16_t errors; /* see FP_ERROR_ flags above */ +}; + +struct __ec_align4 ec_response_fp_info { /* Sensor identification */ uint32_t vendor_id; uint32_t product_id; @@ -4732,14 +4776,58 @@ struct __ec_align2 ec_response_fp_info { uint16_t height; uint16_t bpp; uint16_t errors; /* see FP_ERROR_ flags above */ + /* Template/finger current information */ + uint32_t template_size; /* max template size in bytes */ + uint16_t template_max; /* maximum number of fingers/templates */ + uint16_t template_valid; /* number of valid fingers/templates */ + uint32_t template_dirty; /* bitmap of templates with MCU side changes */ }; -/* Get the last captured finger frame */ +/* Get the last captured finger frame or a template content */ #define EC_CMD_FP_FRAME 0x0404 +/* constants defining the 'offset' field which also contains the frame index */ +#define FP_FRAME_INDEX_SHIFT 28 +#define FP_FRAME_INDEX_RAW_IMAGE 0 +#define FP_FRAME_TEMPLATE_INDEX(offset) ((offset) >> FP_FRAME_INDEX_SHIFT) +#define FP_FRAME_OFFSET_MASK 0x0FFFFFFF + struct __ec_align4 ec_params_fp_frame { + /* + * The offset contains the template index or FP_FRAME_INDEX_RAW_IMAGE + * in the high nibble, and the real offset within the frame in + * FP_FRAME_OFFSET_MASK. + */ + uint32_t offset; + uint32_t size; +}; + +/* Load a template into the MCU */ +#define EC_CMD_FP_TEMPLATE 0x0405 + +/* Flag in the 'size' field indicating that the full template has been sent */ +#define FP_TEMPLATE_COMMIT 0x80000000 + +struct __ec_align4 ec_params_fp_template { uint32_t offset; uint32_t size; + uint8_t data[]; +}; + +/* Clear the current fingerprint user context and set a new one */ +#define EC_CMD_FP_CONTEXT 0x0406 + +#define FP_CONTEXT_USERID_WORDS (32 / sizeof(uint32_t)) +#define FP_CONTEXT_NONCE_WORDS (32 / sizeof(uint32_t)) + +struct __ec_align4 ec_params_fp_context { + uint32_t userid[FP_CONTEXT_USERID_WORDS]; + /* TODO(b/73337313) mostly a placeholder, details to be implemented */ + uint32_t nonce[FP_CONTEXT_NONCE_WORDS]; +}; + +struct __ec_align4 ec_response_fp_context { + uint32_t nonce[FP_CONTEXT_NONCE_WORDS]; }; /*****************************************************************************/ diff --git a/include/fpsensor.h b/include/fpsensor.h index 3a77803223..5fc7f3b584 100644 --- a/include/fpsensor.h +++ b/include/fpsensor.h @@ -93,4 +93,61 @@ int fp_sensor_acquire_image(uint8_t *image_data); */ int fp_sensor_acquire_image_with_mode(uint8_t *image_data, int mode); +/* + * Compares given finger image against enrolled templates. + * + * The matching algorithm can update the template with additional biometric data + * from the image, if it chooses to do so. + * + * @param templ a pointer to the array of template buffers. + * @param templ_count the number of buffers in the array of templates. + * @param image the buffer containing the finger image + * @param update_bitmap contains one bit per template, the bit is set if the + * match has updated the given template. + * @return negative value on error, else one of the following code : + * - EC_MKBP_FP_ERR_MATCH_NO on non-match + * - EC_MKBP_FP_ERR_MATCH_YES for match when template was not updated with + * new data + * - EC_MKBP_FP_ERR_MATCH_YES_UPDATED for match when template was updated + * - EC_MKBP_FP_ERR_MATCH_YES_UPDATE_FAILED match, but update failed (not saved) + * - EC_MKBP_FP_ERR_MATCH_LOW_QUALITY when matching could not be performed due + * to low image quality + * - EC_MKBP_FP_ERR_MATCH_LOW_COVERAGE when matching could not be performed + * due to finger covering too little area of the sensor + */ +int fp_finger_match(void *templ, uint32_t templ_count, uint8_t *image, + uint32_t *update_bitmap); + +/* + * Start a finger enrollment session. + * + * @return 0 on success or a negative error code. + */ +int fp_enrollment_begin(void); + +/* + * Generate a template from the finger whose enrollment has just being + * completed. + * + * @param templ the buffer which will receive the template. + * templ can be set to NULL to abort the current enrollment process. + * + * @return 0 on success or a negative error code. + */ +int fp_enrollment_finish(void *templ); + +/* + * Adds fingerprint image to the current enrollment session. + * + * @return a negative value on error or one of the following codes: + * - EC_MKBP_FP_ERR_ENROLL_OK when image was successfully enrolled + * - EC_MKBP_FP_ERR_ENROLL_IMMOBILE when image added, but user should be + * advised to move finger + * - EC_MKBP_FP_ERR_ENROLL_LOW_QUALITY when image could not be used due to low + * image quality + * - EC_MKBP_FP_ERR_ENROLL_LOW_COVERAGE when image could not be used due to + * finger covering too little area of the sensor + */ +int fp_finger_enroll(uint8_t *image, int *completion); + #endif /* __CROS_EC_FPSENSOR_H */ diff --git a/util/ectool.c b/util/ectool.c index 28b2eeb8c4..83b263d74b 100644 --- a/util/ectool.c +++ b/util/ectool.c @@ -124,6 +124,8 @@ const char help_str[] = " Prints information about the Fingerprint sensor\n" " fpmode [capture|deepsleep|fingerdown|fingerup]\n" " Configure/Read the fingerprint sensor current mode\n" + " fptemplate [<infile>|<index 0..2>]\n" + " Add a template if <infile> is provided, else dump it\n" " forcelidopen <enable>\n" " Forces the lid switch to open position\n" " gpioget <GPIO name>\n" @@ -1092,22 +1094,51 @@ int cmd_rwsig_action(int argc, char *argv[]) return ec_command(EC_CMD_RWSIG_ACTION, 0, &req, sizeof(req), NULL, 0); } -static void *fp_download_frame(struct ec_response_fp_info *info, int all) +#define FP_FRAME_INDEX_SIMPLE_IMAGE -1 + +/* + * Download a frame buffer from the FPMCU. + * + * Might be either the finger image or a finger template depending on 'index'. + * + * @param info a pointer to store the struct ec_response_fp_info retrieved by + * this command. + * @param index the specific frame to retrieve, might be: + * -1 (aka FP_FRAME_INDEX_SIMPLE_IMAGE) for the a single grayscale image. + * 0 (aka FP_FRAME_INDEX_RAW_IMAGE) for the full vendor raw finger image. + * 1..n for a finger template. + * + * @returns a pointer to the buffer allocated to contain the frame or NULL + * if case of error. The caller must call free() once it no longer needs the + * buffer. + */ +static void *fp_download_frame(struct ec_response_fp_info *info, int index) { struct ec_params_fp_frame p; int rv = 0; size_t stride, size; void *buffer; uint8_t *ptr; + int cmdver = ec_cmd_version_supported(EC_CMD_FP_INFO, 1) ? 1 : 0; + int rsize = cmdver == 1 ? sizeof(*info) + : sizeof(struct ec_response_fp_info_v0); - rv = ec_command(EC_CMD_FP_INFO, 0, NULL, 0, info, sizeof(*info)); + /* templates not supported in command v0 */ + if (index > 0 && cmdver == 0) + return NULL; + + rv = ec_command(EC_CMD_FP_INFO, cmdver, NULL, 0, info, rsize); if (rv < 0) return NULL; - if (all) - size = info->frame_size; - else + if (index == FP_FRAME_INDEX_SIMPLE_IMAGE) { size = (size_t)info->width * info->bpp/8 * info->height; + index = FP_FRAME_INDEX_RAW_IMAGE; + } else if (index == FP_FRAME_INDEX_RAW_IMAGE) { + size = info->frame_size; + } else { + size = info->template_size; + } buffer = malloc(size); if (!buffer) { @@ -1116,7 +1147,7 @@ static void *fp_download_frame(struct ec_response_fp_info *info, int all) } ptr = buffer; - p.offset = 0; + p.offset = index << FP_FRAME_INDEX_SHIFT; while (size) { stride = MIN(ec_max_insize, size); p.size = stride; @@ -1153,7 +1184,7 @@ static int fp_pattern_frame(int capt_type, const char *title, int inv) return -1; /* ensure the capture has happened without using event support */ usleep(200000); - pattern = fp_download_frame(&info, 0); + pattern = fp_download_frame(&info, FP_FRAME_INDEX_SIMPLE_IMAGE); if (!pattern) return -1; @@ -1239,6 +1270,12 @@ int cmd_fp_mode(int argc, char *argv[]) mode |= FP_MODE_FINGER_DOWN; else if (!strncmp(argv[i], "fingerup", 8)) mode |= FP_MODE_FINGER_UP; + else if (!strncmp(argv[i], "enroll", 6)) + mode |= FP_MODE_ENROLL_IMAGE | FP_MODE_ENROLL_SESSION; + else if (!strncmp(argv[i], "match", 5)) + mode |= FP_MODE_MATCH; + else if (!strncmp(argv[i], "reset", 5)) + mode = 0; else if (!strncmp(argv[i], "capture", 7)) mode |= FP_MODE_CAPTURE; /* capture types */ @@ -1266,6 +1303,11 @@ int cmd_fp_mode(int argc, char *argv[]) printf("finger-down "); if (r.mode & FP_MODE_FINGER_UP) printf("finger-up "); + if (r.mode & FP_MODE_ENROLL_SESSION) + printf("enroll%s ", + r.mode & FP_MODE_ENROLL_IMAGE ? "+image" : ""); + if (r.mode & FP_MODE_MATCH) + printf("match "); if (r.mode & FP_MODE_CAPTURE) printf("capture "); printf("\n"); @@ -1276,8 +1318,11 @@ int cmd_fp_info(int argc, char *argv[]) { struct ec_response_fp_info r; int rv; + int cmdver = ec_cmd_version_supported(EC_CMD_FP_INFO, 1) ? 1 : 0; + int rsize = cmdver == 1 ? sizeof(r) + : sizeof(struct ec_response_fp_info_v0); - rv = ec_command(EC_CMD_FP_INFO, 0, NULL, 0, &r, sizeof(r)); + rv = ec_command(EC_CMD_FP_INFO, cmdver, NULL, 0, &r, rsize); if (rv < 0) return rv; @@ -1290,6 +1335,11 @@ int cmd_fp_info(int argc, char *argv[]) r.errors & FP_ERROR_BAD_HWID ? "BAD_HWID " : "", r.errors & FP_ERROR_INIT_FAIL ? "INIT_FAIL " : "", FP_ERROR_DEAD_PIXELS(r.errors)); + if (cmdver == 1) { + printf("Templates: size %d count %d/%d dirty bitmap %x\n", + r.template_size, r.template_valid, r.template_max, + r.template_dirty); + } return 0; } @@ -1297,8 +1347,9 @@ int cmd_fp_info(int argc, char *argv[]) int cmd_fp_frame(int argc, char *argv[]) { struct ec_response_fp_info r; - int raw = (argc == 2 && !strcasecmp(argv[1], "raw")); - void *buffer = fp_download_frame(&r, raw); + int idx = (argc == 2 && !strcasecmp(argv[1], "raw")) ? + FP_FRAME_INDEX_RAW_IMAGE : FP_FRAME_INDEX_SIMPLE_IMAGE; + void *buffer = fp_download_frame(&r, idx); uint8_t *ptr = buffer; int x, y; @@ -1307,7 +1358,7 @@ int cmd_fp_frame(int argc, char *argv[]) return -1; } - if (raw) { + if (idx == FP_FRAME_INDEX_RAW_IMAGE) { fwrite(buffer, r.frame_size, 1, stdout); goto frame_done; } @@ -1326,6 +1377,66 @@ frame_done: return 0; } +int cmd_fp_template(int argc, char *argv[]) +{ + struct ec_response_fp_info r; + struct ec_params_fp_template *p = ec_outbuf; + int max_chunk = ec_max_outsize + - offsetof(struct ec_params_fp_template, data); + int idx = -1; + char *e; + int size; + void *buffer = NULL; + uint32_t offset = 0; + int rv = 0; + + if (argc < 2) { + fprintf(stderr, "Usage: %s [<infile>|<index>]\n", argv[0]); + return -1; + } + + idx = strtol(argv[1], &e, 0); + if (!(e && *e)) { + buffer = fp_download_frame(&r, idx + 1); + if (!buffer) { + fprintf(stderr, "Failed to get FP template %d\n", idx); + return -1; + } + fwrite(buffer, r.template_size, 1, stdout); + free(buffer); + return 0; + } + /* not an index, is it a filename ? */ + buffer = read_file(argv[1], &size); + if (!buffer) { + fprintf(stderr, "Invalid parameter: %s\n", argv[1]); + return -1; + } + printf("sending template from: %s (%d bytes)\n", argv[1], size); + while (size) { + uint32_t tlen = MIN(max_chunk, size); + + p->offset = offset; + p->size = tlen; + size -= tlen; + if (!size) + p->size |= FP_TEMPLATE_COMMIT; + memcpy(p->data, buffer + offset, tlen); + rv = ec_command(EC_CMD_FP_TEMPLATE, 0, p, tlen + + offsetof(struct ec_params_fp_template, data), + NULL, 0); + if (rv < 0) + break; + offset += tlen; + } + if (rv < 0) + fprintf(stderr, "Failed with %d\n", rv); + else + rv = 0; + free(buffer); + return rv; +} + /** * determine if in GFU mode or not. * @@ -7726,6 +7837,7 @@ const struct command commands[] = { {"fpframe", cmd_fp_frame}, {"fpinfo", cmd_fp_info}, {"fpmode", cmd_fp_mode}, + {"fptemplate", cmd_fp_template}, {"gpioget", cmd_gpio_get}, {"gpioset", cmd_gpio_set}, {"hangdetect", cmd_hang_detect}, |