From bccff4ffef07821355639bd2e2c2da1baca57749 Mon Sep 17 00:00:00 2001 From: Igor Filatov Date: Tue, 16 Jan 2018 18:04:31 +0200 Subject: [PATCH 1/9] elan: Change command structs, support more devices Make each command a separate struct to get finer control over which commands are called on which devices. Update ssm's accordingly. Add sensor_reset and fuse_load commands. Support 0x0903, 0x0c03, 0x0c16, 0x0c1a, 0x0c26 --- libfprint/drivers/elan.c | 391 +++++++++++++++++++++++++++------------ libfprint/drivers/elan.h | 190 +++++++++---------- 2 files changed, 368 insertions(+), 213 deletions(-) diff --git a/libfprint/drivers/elan.c b/libfprint/drivers/elan.c index dff75a6..3a2a0b0 100644 --- a/libfprint/drivers/elan.c +++ b/libfprint/drivers/elan.c @@ -2,6 +2,7 @@ * Elan driver for libfprint * * Copyright (C) 2017 Igor Filatov + * Copyright (C) 2018 Sébastien Béchet * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -38,20 +39,29 @@ static struct fpi_frame_asmbl_ctx assembling_ctx = { }; struct elan_dev { - gboolean deactivating; + /* device config */ + unsigned short dev_type; + /* number of pixels to discard on left and right (along raw image height) + * because they have different intensity from the rest of the frame */ + unsigned char frame_margin; + /* end device config */ - const struct elan_cmd *cmds; - size_t cmds_len; - int cmd_idx; + /* commands */ + const struct elan_cmd *cmd; int cmd_timeout; struct libusb_transfer *cur_transfer; + /* end commands */ + /* state */ + gboolean deactivating; + unsigned char calib_atts_left; unsigned char *last_read; unsigned char frame_width; unsigned char frame_height; unsigned char raw_frame_width; int num_frames; GSList *frames; + /* end state */ }; static void elan_dev_reset(struct elan_dev *elandev) @@ -62,8 +72,7 @@ static void elan_dev_reset(struct elan_dev *elandev) elandev->deactivating = FALSE; - elandev->cmds = NULL; - elandev->cmd_idx = 0; + elandev->cmd = NULL; elandev->cmd_timeout = ELAN_CMD_TIMEOUT; g_free(elandev->last_read); @@ -86,13 +95,13 @@ static void elan_save_frame(struct fp_img_dev *dev) /* Raw images are vertical and perpendicular to swipe direction of a * normalized image, which means we need to make them horizontal before - * assembling. We also discard stirpes of ELAN_FRAME_MARGIN along raw + * assembling. We also discard stripes of 'frame_margin' along raw * height. */ for (int y = 0; y < raw_height; y++) - for (int x = ELAN_FRAME_MARGIN; - x < raw_width - ELAN_FRAME_MARGIN; x++) { + for (int x = elandev->frame_margin; + x < raw_width - elandev->frame_margin; x++) { int frame_idx = - y + (x - ELAN_FRAME_MARGIN) * raw_height; + y + (x - elandev->frame_margin) * raw_height; int raw_idx = x + y * raw_width; frame[frame_idx] = ((unsigned short *)elandev->last_read)[raw_idx]; @@ -161,16 +170,8 @@ static void elan_submit_image(struct fp_img_dev *dev) static void elan_cmd_done(struct fpi_ssm *ssm) { - struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm); - struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); - G_DEBUG_HERE(); - - elandev->cmd_idx += 1; - if (elandev->cmd_idx < elandev->cmds_len) - elan_run_next_cmd(ssm); - else - fpi_ssm_next_state(ssm); + fpi_ssm_next_state(ssm); } static void elan_cmd_cb(struct libusb_transfer *transfer) @@ -186,19 +187,16 @@ static void elan_cmd_cb(struct libusb_transfer *transfer) switch (transfer->status) { case LIBUSB_TRANSFER_COMPLETED: if (transfer->length != transfer->actual_length) { - fp_dbg("unexpected transfer length"); + fp_dbg("transfer length error: expected %d, got %d", + transfer->length, transfer->actual_length); elan_dev_reset(elandev); fpi_ssm_mark_aborted(ssm, -EPROTO); } else if (transfer->endpoint & LIBUSB_ENDPOINT_IN) /* just finished receiving */ elan_cmd_done(ssm); - else { + else /* just finished sending */ - if (elandev->cmds[elandev->cmd_idx].response_len) - elan_cmd_read(ssm); - else - elan_cmd_done(ssm); - } + elan_cmd_read(ssm); break; case LIBUSB_TRANSFER_CANCELLED: fp_dbg("transfer cancelled"); @@ -206,7 +204,6 @@ static void elan_cmd_cb(struct libusb_transfer *transfer) break; case LIBUSB_TRANSFER_TIMED_OUT: fp_dbg("transfer timed out"); - // elan_dev_reset(elandev); fpi_ssm_mark_aborted(ssm, -ETIMEDOUT); break; default: @@ -220,11 +217,17 @@ static void elan_cmd_read(struct fpi_ssm *ssm) { struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm); struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); - int response_len = elandev->cmds[elandev->cmd_idx].response_len; + int response_len = elandev->cmd->response_len; G_DEBUG_HERE(); - if (elandev->cmds[elandev->cmd_idx].cmd == read_cmds[0].cmd) + if (!(elandev->cmd->response_len)) { + fp_dbg("skipping read, not expecting anything"); + elan_cmd_done(ssm); + return; + } + + if (elandev->cmd->cmd == get_image_cmd.cmd) /* raw data has 2-byte "pixels" and the frame is vertical */ response_len = elandev->raw_frame_width * elandev->frame_width * 2; @@ -240,7 +243,7 @@ static void elan_cmd_read(struct fpi_ssm *ssm) elandev->last_read = g_malloc(response_len); libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), - elandev->cmds[elandev->cmd_idx].response_in, + elandev->cmd->response_in, elandev->last_read, response_len, elan_cmd_cb, ssm, elandev->cmd_timeout); transfer->flags = LIBUSB_TRANSFER_FREE_TRANSFER; @@ -249,12 +252,21 @@ static void elan_cmd_read(struct fpi_ssm *ssm) fpi_ssm_mark_aborted(ssm, r); } -static void elan_run_next_cmd(struct fpi_ssm *ssm) +static void elan_run_cmd(struct fpi_ssm *ssm, const struct elan_cmd *cmd, + int cmd_timeout) { struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm); struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); - G_DEBUG_HERE(); + elandev->cmd = cmd; + if (cmd_timeout != -1) + elandev->cmd_timeout = cmd_timeout; + + if (!(cmd->devices & elandev->dev_type)) { + fp_dbg("skipping for this device"); + elan_cmd_done(ssm); + return; + } struct libusb_transfer *transfer = libusb_alloc_transfer(0); if (!transfer) { @@ -264,31 +276,12 @@ static void elan_run_next_cmd(struct fpi_ssm *ssm) elandev->cur_transfer = transfer; libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev), ELAN_EP_CMD_OUT, - (unsigned char *)elandev->cmds[elandev-> - cmd_idx].cmd, - ELAN_CMD_LEN, elan_cmd_cb, ssm, + (char *) cmd->cmd, ELAN_CMD_LEN, elan_cmd_cb, ssm, elandev->cmd_timeout); transfer->flags = LIBUSB_TRANSFER_FREE_TRANSFER; int r = libusb_submit_transfer(transfer); if (r < 0) fpi_ssm_mark_aborted(ssm, r); - -} - -static void elan_run_cmds(struct fpi_ssm *ssm, const struct elan_cmd *cmds, - size_t cmds_len, int cmd_timeout) -{ - struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm); - struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); - - G_DEBUG_HERE(); - - elandev->cmds = cmds; - elandev->cmds_len = cmds_len; - elandev->cmd_idx = 0; - if (cmd_timeout != -1) - elandev->cmd_timeout = cmd_timeout; - elan_run_next_cmd(ssm); } enum deactivate_states { @@ -296,12 +289,13 @@ enum deactivate_states { DEACTIVATE_NUM_STATES, }; -static void elan_deactivate_run_state(struct fpi_ssm *ssm) +static void deactivate_run_state(struct fpi_ssm *ssm) { + G_DEBUG_HERE(); + switch (fpi_ssm_get_cur_state(ssm)) { case DEACTIVATE: - elan_run_cmds(ssm, deactivate_cmds, deactivate_cmds_len, - ELAN_CMD_TIMEOUT); + elan_run_cmd(ssm, &stop_cmd, ELAN_CMD_TIMEOUT); break; } } @@ -310,6 +304,8 @@ static void deactivate_complete(struct fpi_ssm *ssm) { struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm); + G_DEBUG_HERE(); + fpi_imgdev_deactivate_complete(dev); } @@ -321,50 +317,49 @@ static void elan_deactivate(struct fp_img_dev *dev) elan_dev_reset(elandev); - struct fpi_ssm *ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), elan_deactivate_run_state, + struct fpi_ssm *ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), deactivate_run_state, DEACTIVATE_NUM_STATES); fpi_ssm_set_user_data(ssm, dev); fpi_ssm_start(ssm, deactivate_complete); } enum capture_states { - CAPTURE_START, + CAPTURE_LED_ON, CAPTURE_WAIT_FINGER, CAPTURE_READ_DATA, - CAPTURE_SAVE_FRAME, + CAPTURE_CHECK_ENOUGH_FRAMES, CAPTURE_NUM_STATES, }; -static void elan_capture_run_state(struct fpi_ssm *ssm) +static void capture_run_state(struct fpi_ssm *ssm) { struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm); struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); switch (fpi_ssm_get_cur_state(ssm)) { - case CAPTURE_START: - elan_run_cmds(ssm, capture_start_cmds, capture_start_cmds_len, - ELAN_CMD_TIMEOUT); + case CAPTURE_LED_ON: + elan_run_cmd(ssm, &led_on_cmd, ELAN_CMD_TIMEOUT); break; case CAPTURE_WAIT_FINGER: - elan_run_cmds(ssm, capture_wait_finger_cmds, - capture_wait_finger_cmds_len, -1); + elan_run_cmd(ssm, &pre_scan_cmd, -1); break; case CAPTURE_READ_DATA: /* 0x55 - finger present - * 0xff - device not calibrated */ + * 0xff - device not calibrated (probably) */ if (elandev->last_read && elandev->last_read[0] == 0x55) { fpi_imgdev_report_finger_status(dev, TRUE); - elan_run_cmds(ssm, read_cmds, read_cmds_len, - ELAN_CMD_TIMEOUT); + elan_run_cmd(ssm, &get_image_cmd, ELAN_CMD_TIMEOUT); } else fpi_ssm_mark_aborted(ssm, FP_VERIFY_RETRY); break; - case CAPTURE_SAVE_FRAME: + case CAPTURE_CHECK_ENOUGH_FRAMES: elan_save_frame(dev); if (elandev->num_frames < ELAN_MAX_FRAMES) { /* quickly stop if finger is removed */ elandev->cmd_timeout = ELAN_FINGER_TIMEOUT; fpi_ssm_jump_to_state(ssm, CAPTURE_WAIT_FINGER); + } else { + fpi_ssm_next_state(ssm); } break; } @@ -392,9 +387,12 @@ static void capture_complete(struct fpi_ssm *ssm) if (elandev->num_frames >= ELAN_MIN_FRAMES) { elan_submit_image(dev); fpi_imgdev_report_finger_status(dev, FALSE); - } else + } else { + fp_dbg("swipe too short: want >= %d frames, got %d", + ELAN_MIN_FRAMES, elandev->num_frames); fpi_imgdev_session_error(dev, FP_VERIFY_RETRY_TOO_SHORT); + } /* other error * It says "...session_error" but repotring 1 during verification @@ -421,37 +419,98 @@ static void elan_capture(struct fp_img_dev *dev) elan_dev_reset(elandev); struct fpi_ssm *ssm = - fpi_ssm_new(fpi_imgdev_get_dev(dev), elan_capture_run_state, CAPTURE_NUM_STATES); + fpi_ssm_new(fpi_imgdev_get_dev(dev), capture_run_state, CAPTURE_NUM_STATES); fpi_ssm_set_user_data(ssm, dev); fpi_ssm_start(ssm, capture_complete); } +static void fpi_ssm_next_state_async(void *data) +{ + fpi_ssm_next_state((struct fpi_ssm *)data); +} + +/* this function needs last_read to be the calibration mean and at least + * one frame */ +static int elan_need_calibration(struct elan_dev *elandev) +{ + G_DEBUG_HERE(); + + if (elandev->dev_type & ELAN_0903) { + fp_dbg("don't know how to calibrate this device"); + return 0; + } + + unsigned short calib_mean = + elandev->last_read[0] * 0xff + elandev->last_read[1]; + unsigned short *bg_data = ((GSList *) elandev->frames)->data; + unsigned int bg_mean = 0, delta; + unsigned int frame_size = elandev->frame_width * elandev->frame_height; + + for (int i = 0; i < frame_size; i++) + bg_mean += bg_data[i]; + bg_mean /= frame_size; + + delta = + bg_mean > calib_mean ? bg_mean - calib_mean : calib_mean - bg_mean; + + fp_dbg("calibration mean: %d, bg mean: %d, delta: %d", calib_mean, + bg_mean, delta); + + return delta > ELAN_CALIBRATION_MAX_DELTA ? 1 : 0; +} + enum calibrate_states { - CALIBRATE_START_1, - CALIBRATE_READ_DATA_1, - CALIBRATE_END_1, - CALIBRATE_START_2, - CALIBRATE_READ_DATA_2, - CALIBRATE_END_2, + CALIBRATE_START, + CALIBRATE_CHECK_RESULT, + CALIBRATE_REPEAT, + CALIBRATE_GET_BACKGROUND, + CALIBRATE_SAVE_BACKGROUND, + CALIBRATE_GET_MEAN, CALIBRATE_NUM_STATES, }; -static void elan_calibrate_run_state(struct fpi_ssm *ssm) +static void calibrate_run_state(struct fpi_ssm *ssm) { + struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm); + struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); + + G_DEBUG_HERE(); + switch (fpi_ssm_get_cur_state(ssm)) { - case CALIBRATE_START_1: - case CALIBRATE_START_2: - elan_run_cmds(ssm, calibrate_start_cmds, - calibrate_start_cmds_len, ELAN_CMD_TIMEOUT); + case CALIBRATE_START: + elandev->calib_atts_left -= 1; + if (elandev->calib_atts_left) + elan_run_cmd(ssm, &run_calibration_cmd, + ELAN_CMD_TIMEOUT); + else { + fp_dbg("too many calibration attempts"); + fpi_ssm_mark_aborted(ssm, -1); + } break; - case CALIBRATE_READ_DATA_1: - case CALIBRATE_READ_DATA_2: - elan_run_cmds(ssm, read_cmds, read_cmds_len, ELAN_CMD_TIMEOUT); + case CALIBRATE_CHECK_RESULT: + /* 0x01 - retry, 0x03 - ok + * but some devices send other responses so in order to avoid needless + * retries we don't check 0x3 but only retry on 0x1 (need to wait 50 ms) */ + fp_dbg("calibration status: 0x%02x", elandev->last_read[0]); + if (elandev->last_read[0] == 0x01) { + if (!fpi_timeout_add(50, fpi_ssm_next_state_async, ssm)) + fpi_ssm_mark_aborted(ssm, -ETIME); + } else + fpi_ssm_jump_to_state(ssm, CALIBRATE_GET_BACKGROUND); + break; + case CALIBRATE_REPEAT: + fpi_ssm_jump_to_state(ssm, CALIBRATE_START); + break; + case CALIBRATE_GET_BACKGROUND: + elan_run_cmd(ssm, &get_image_cmd, ELAN_CMD_TIMEOUT); + break; + case CALIBRATE_SAVE_BACKGROUND: + elan_save_frame(dev); + fpi_ssm_next_state(ssm); + break; + case CALIBRATE_GET_MEAN: + elan_run_cmd(ssm, &get_calib_mean_cmd, ELAN_CMD_TIMEOUT); break; - case CALIBRATE_END_1: - case CALIBRATE_END_2: - elan_run_cmds(ssm, calibrate_end_cmds, calibrate_end_cmds_len, - ELAN_CMD_TIMEOUT); } } @@ -465,11 +524,14 @@ static void calibrate_complete(struct fpi_ssm *ssm) if (elandev->deactivating) elan_deactivate(dev); else if (fpi_ssm_get_error(ssm)) - fpi_imgdev_session_error(dev, fpi_ssm_get_error(ssm)); - else { fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm)); + else if (elan_need_calibration(elandev)) + elan_calibrate(dev); + else { + fpi_imgdev_activate_complete(dev, 0); elan_capture(dev); } + fpi_ssm_free(ssm); } @@ -480,48 +542,67 @@ static void elan_calibrate(struct fp_img_dev *dev) G_DEBUG_HERE(); elan_dev_reset(elandev); - struct fpi_ssm *ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), elan_calibrate_run_state, + struct fpi_ssm *ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), calibrate_run_state, CALIBRATE_NUM_STATES); fpi_ssm_set_user_data(ssm, dev); fpi_ssm_start(ssm, calibrate_complete); } enum activate_states { + ACTIVATE_GET_FW_VER, + ACTIVATE_PRINT_FW_VER, ACTIVATE_GET_SENSOR_DIM, ACTIVATE_SET_SENSOR_DIM, - ACTIVATE_START, - ACTIVATE_READ_DATA, - ACTIVATE_END, + ACTIVATE_CMD_1, + ACTIVATE_GET_BACKGROUND, + ACTIVATE_SAVE_BACKGROUND, + ACTIVATE_GET_MEAN, ACTIVATE_NUM_STATES, }; -static void elan_activate_run_state(struct fpi_ssm *ssm) +static void activate_run_state(struct fpi_ssm *ssm) { struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm); struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); + G_DEBUG_HERE(); + switch (fpi_ssm_get_cur_state(ssm)) { + case ACTIVATE_GET_FW_VER: + elan_run_cmd(ssm, &get_fw_ver_cmd, ELAN_CMD_TIMEOUT); + break; + case ACTIVATE_PRINT_FW_VER: + fp_dbg("FW ver %d.%d", elandev->last_read[0], + elandev->last_read[1]); + fpi_ssm_next_state(ssm); + break; case ACTIVATE_GET_SENSOR_DIM: - elan_run_cmds(ssm, get_sensor_dim_cmds, get_sensor_dim_cmds_len, - ELAN_CMD_TIMEOUT); + elan_run_cmd(ssm, &get_sensor_dim_cmd, ELAN_CMD_TIMEOUT); break; case ACTIVATE_SET_SENSOR_DIM: elandev->frame_width = elandev->last_read[2]; elandev->raw_frame_width = elandev->last_read[0]; elandev->frame_height = - elandev->raw_frame_width - 2 * ELAN_FRAME_MARGIN; + elandev->raw_frame_width - 2 * elandev->frame_margin; + /* see elan_save_frame */ + fp_dbg("sensor dimensions, WxH: %dx%d", elandev->frame_width, + elandev->raw_frame_width); fpi_ssm_next_state(ssm); break; - case ACTIVATE_START: - elan_run_cmds(ssm, init_start_cmds, init_start_cmds_len, - ELAN_CMD_TIMEOUT); + case ACTIVATE_CMD_1: + /* TODO: find out what this does, if we need it */ + elan_run_cmd(ssm, &activate_cmd_1, ELAN_CMD_TIMEOUT); break; - case ACTIVATE_READ_DATA: - elan_run_cmds(ssm, read_cmds, read_cmds_len, ELAN_CMD_TIMEOUT); + case ACTIVATE_GET_BACKGROUND: + elan_run_cmd(ssm, &get_image_cmd, ELAN_CMD_TIMEOUT); + break; + case ACTIVATE_SAVE_BACKGROUND: + elan_save_frame(dev); + fpi_ssm_next_state(ssm); + break; + case ACTIVATE_GET_MEAN: + elan_run_cmd(ssm, &get_calib_mean_cmd, ELAN_CMD_TIMEOUT); break; - case ACTIVATE_END: - elan_run_cmds(ssm, init_end_cmds, init_end_cmds_len, - ELAN_CMD_TIMEOUT); } } @@ -535,13 +616,17 @@ static void activate_complete(struct fpi_ssm *ssm) if (elandev->deactivating) elan_deactivate(dev); else if (fpi_ssm_get_error(ssm)) - fpi_imgdev_session_error(dev, fpi_ssm_get_error(ssm)); - else + fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm)); + else if (elan_need_calibration(elandev)) elan_calibrate(dev); + else { + fpi_imgdev_activate_complete(dev, 0); + elan_capture(dev); + } fpi_ssm_free(ssm); } -static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state) +static void elan_activate(struct fp_img_dev *dev) { struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); @@ -549,11 +634,9 @@ static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state) elan_dev_reset(elandev); struct fpi_ssm *ssm = - fpi_ssm_new(fpi_imgdev_get_dev(dev), elan_activate_run_state, ACTIVATE_NUM_STATES); + fpi_ssm_new(fpi_imgdev_get_dev(dev), activate_run_state, ACTIVATE_NUM_STATES); fpi_ssm_set_user_data(ssm, dev); fpi_ssm_start(ssm, activate_complete); - - return 0; } static int dev_init(struct fp_img_dev *dev, unsigned long driver_data) @@ -571,10 +654,80 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data) elandev = g_malloc0(sizeof(struct elan_dev)); fpi_imgdev_set_user_data(dev, elandev); + + /* common params */ + elandev->dev_type = driver_data; + elandev->calib_atts_left = ELAN_CALIBRATION_ATTEMPTS; + elandev->frame_margin = 0; + + switch (driver_data) { + case ELAN_0907: + elandev->frame_margin = 12; + break; + } + fpi_imgdev_open_complete(dev, 0); return 0; } +enum reset_sensor_states { + RESET_SENSOR_DO_RESET, + RESET_SENSOR_WAIT, + RESET_SENSOR_FUSE_LOAD, + RESET_SENSOR_STATUS, + RESET_SENSOR_NUM_STATES, +}; + +static void reset_sensor_run_state(struct fpi_ssm *ssm) +{ + switch (fpi_ssm_get_cur_state(ssm)) { + case RESET_SENSOR_DO_RESET: + elan_run_cmd(ssm, &reset_sensor_cmd, ELAN_CMD_TIMEOUT); + break; + case RESET_SENSOR_WAIT: + /* must wait 5 ms after sensor reset command */ + if (!fpi_timeout_add(5, fpi_ssm_next_state_async, ssm)) + fpi_ssm_mark_aborted(ssm, -ETIME); + break; + case RESET_SENSOR_FUSE_LOAD: + elan_run_cmd(ssm, &fuse_load_cmd, ELAN_CMD_TIMEOUT); + break; + case RESET_SENSOR_STATUS: + elan_run_cmd(ssm, &read_sensor_status_cmd, ELAN_CMD_TIMEOUT); + break; + } +} + +static void reset_sensor_complete(struct fpi_ssm *ssm) +{ + struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm); + struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); + + G_DEBUG_HERE(); + + if (elandev->deactivating) + elan_deactivate(dev); + else if (fpi_ssm_get_error(ssm)) + fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm)); + else + elan_activate(dev); + + fpi_ssm_free(ssm); +} + +static void elan_reset_sensor(struct fp_img_dev *dev) +{ + struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); + + G_DEBUG_HERE(); + + elan_dev_reset(elandev); + struct fpi_ssm *ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), reset_sensor_run_state, + RESET_SENSOR_NUM_STATES); + fpi_ssm_set_user_data(ssm, dev); + fpi_ssm_start(ssm, reset_sensor_complete); +} + static void dev_deinit(struct fp_img_dev *dev) { struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); @@ -587,6 +740,12 @@ static void dev_deinit(struct fp_img_dev *dev) fpi_imgdev_close_complete(dev); } +static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state) +{ + elan_reset_sensor(dev); + return 0; +} + static void dev_deactivate(struct fp_img_dev *dev) { struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); @@ -602,8 +761,12 @@ static void dev_deactivate(struct fp_img_dev *dev) } static const struct usb_id id_table[] = { - {.vendor = 0x04f3,.product = 0x0907}, - {.vendor = 0x04f3,.product = 0x0c26}, + {.vendor = ELAN_VENDOR_ID,.product = 0x0903,.driver_data = ELAN_0903}, + {.vendor = ELAN_VENDOR_ID,.product = 0x0907,.driver_data = ELAN_0907}, + {.vendor = ELAN_VENDOR_ID,.product = 0x0c03,.driver_data = ELAN_0C03}, + {.vendor = ELAN_VENDOR_ID,.product = 0x0c16,.driver_data = ELAN_0C16}, + {.vendor = ELAN_VENDOR_ID,.product = 0x0c1a,.driver_data = ELAN_0C1A}, + {.vendor = ELAN_VENDOR_ID,.product = 0x0c26,.driver_data = ELAN_0C26}, {0, 0, 0,}, }; diff --git a/libfprint/drivers/elan.h b/libfprint/drivers/elan.h index c929287..74e9b3a 100644 --- a/libfprint/drivers/elan.h +++ b/libfprint/drivers/elan.h @@ -24,9 +24,17 @@ #include #include -/* number of pixels to discard on left and right (along raw image height) - * because they have different intensity from the rest of the frame */ -#define ELAN_FRAME_MARGIN 12 +#define ELAN_VENDOR_ID 0x04f3 + +/* supported devices */ +#define ELAN_0903 1 +#define ELAN_0907 (1 << 1) +#define ELAN_0C03 (1 << 2) +#define ELAN_0C16 (1 << 3) +#define ELAN_0C1A (1 << 4) +#define ELAN_0C26 (1 << 5) + +#define ELAN_ALL_DEVICES (ELAN_0903|ELAN_0907|ELAN_0C03|ELAN_0C16|ELAN_0C1A|ELAN_0C26) /* min and max frames in a capture */ #define ELAN_MIN_FRAMES 7 @@ -36,6 +44,13 @@ * while the finger is being lifted can be bad */ #define ELAN_SKIP_LAST_FRAMES 1 +/* max difference between background image mean and calibration mean +* (the response value of get_calib_mean_cmd)*/ +#define ELAN_CALIBRATION_MAX_DELTA 500 + +/* times to retry calibration */ +#define ELAN_CALIBRATION_ATTEMPTS 10 + #define ELAN_CMD_LEN 0x2 #define ELAN_EP_CMD_OUT (0x1 | LIBUSB_ENDPOINT_OUT) #define ELAN_EP_CMD_IN (0x3 | LIBUSB_ENDPOINT_IN) @@ -50,127 +65,104 @@ struct elan_cmd { unsigned char cmd[ELAN_CMD_LEN]; int response_len; int response_in; + unsigned short devices; }; -static const struct elan_cmd get_sensor_dim_cmds[] = { - { - .cmd = {0x00, 0x0c}, - .response_len = 0x4, - .response_in = ELAN_EP_CMD_IN, - }, +static const struct elan_cmd get_sensor_dim_cmd = { + .cmd = {0x00, 0x0c}, + .response_len = 0x4, + .response_in = ELAN_EP_CMD_IN, + .devices = ELAN_ALL_DEVICES, }; -static const size_t get_sensor_dim_cmds_len = -G_N_ELEMENTS(get_sensor_dim_cmds); - -static const struct elan_cmd init_start_cmds[] = { - { - .cmd = {0x40, 0x19}, - .response_len = 0x2, - .response_in = ELAN_EP_CMD_IN, - }, - { - .cmd = {0x40, 0x2a}, - .response_len = 0x2, - .response_in = ELAN_EP_CMD_IN, - }, +static const struct elan_cmd get_fw_ver_cmd = { + .cmd = {0x40, 0x19}, + .response_len = 0x2, + .response_in = ELAN_EP_CMD_IN, + .devices = ELAN_ALL_DEVICES, }; -static const size_t init_start_cmds_len = G_N_ELEMENTS(init_start_cmds); +/* unknown, returns 0x0 0x1 on 0907 */ +static const struct elan_cmd activate_cmd_1 = { + .cmd = {0x40, 0x2a}, + .response_len = 0x2, + .response_in = ELAN_EP_CMD_IN, + .devices = ELAN_0907, +}; -static const struct elan_cmd read_cmds[] = { - /* raw frame sizes are calculated from image dimesions reported by the +static const struct elan_cmd get_image_cmd = { + .cmd = {0x00, 0x09}, + /* raw frame sizes are calculated from image dimensions reported by the * device */ - { - .cmd = {0x00, 0x09}, - .response_len = -1, - .response_in = ELAN_EP_IMG_IN, - }, + .response_len = -1, + .response_in = ELAN_EP_IMG_IN, + .devices = ELAN_ALL_DEVICES, }; -const size_t read_cmds_len = G_N_ELEMENTS(read_cmds); - -/* issued after data reads during init and calibration */ -static const struct elan_cmd init_end_cmds[] = { - { - .cmd = {0x40, 0x24}, - .response_len = 0x2, - .response_in = ELAN_EP_CMD_IN, - }, +static const struct elan_cmd get_calib_mean_cmd = { + .cmd = {0x40, 0x24}, + .response_len = 0x2, + .response_in = ELAN_EP_CMD_IN, + .devices = ELAN_ALL_DEVICES & ~ELAN_0903, }; -static const size_t init_end_cmds_len = G_N_ELEMENTS(init_end_cmds); - -/* same command 2 times - * original driver may observe return value to determine how many times it - * should be repeated */ -static const struct elan_cmd calibrate_start_cmds[] = { - { - .cmd = {0x40, 0x23}, - .response_len = 0x1, - .response_in = ELAN_EP_CMD_IN, - }, - { - .cmd = {0x40, 0x23}, - .response_len = 0x1, - .response_in = ELAN_EP_CMD_IN, - }, +static const struct elan_cmd reset_sensor_cmd = { + .cmd = {0x40, 0x11}, + .response_len = 0x0, + .response_in = ELAN_EP_CMD_IN, + .devices = ELAN_ALL_DEVICES, }; -static const size_t calibrate_start_cmds_len = -G_N_ELEMENTS(calibrate_start_cmds); - -/* issued after data reads during init and calibration */ -static const struct elan_cmd calibrate_end_cmds[] = { - { - .cmd = {0x40, 0x24}, - .response_len = 0x2, - .response_in = ELAN_EP_CMD_IN, - }, +static const struct elan_cmd fuse_load_cmd = { + .cmd = {0x40, 0x14}, + .response_len = 0x0, + .response_in = ELAN_EP_CMD_IN, + .devices = ELAN_ALL_DEVICES, }; -static const size_t calibrate_end_cmds_len = -G_N_ELEMENTS(calibrate_end_cmds); - -static const struct elan_cmd capture_start_cmds[] = { - /* led on */ - { - .cmd = {0x40, 0x31}, - .response_len = 0x0, - .response_in = ELAN_EP_CMD_IN, - }, +static const struct elan_cmd read_sensor_status_cmd = { + .cmd = {0x40, 0x13}, + .response_len = 0x1, + .response_in = ELAN_EP_CMD_IN, + .devices = ELAN_ALL_DEVICES, }; -static size_t capture_start_cmds_len = G_N_ELEMENTS(capture_start_cmds); - -static const struct elan_cmd capture_wait_finger_cmds[] = { - /* wait for finger - * subsequent read will not complete until finger is placed on the reader */ - { - .cmd = {0x40, 0x3f}, - .response_len = 0x1, - .response_in = ELAN_EP_CMD_IN, - }, +static const struct elan_cmd run_calibration_cmd = { + .cmd = {0x40, 0x23}, + .response_len = 0x1, + .response_in = ELAN_EP_CMD_IN, + .devices = ELAN_ALL_DEVICES, }; -static size_t capture_wait_finger_cmds_len = -G_N_ELEMENTS(capture_wait_finger_cmds); - -static const struct elan_cmd deactivate_cmds[] = { - /* led off */ - { - .cmd = {0x00, 0x0b}, - .response_len = 0x0, - .response_in = ELAN_EP_CMD_IN, - }, +static const struct elan_cmd led_on_cmd = { + .cmd = {0x40, 0x31}, + .response_len = 0x0, + .response_in = ELAN_EP_CMD_IN, + .devices = ELAN_0907, }; -static const size_t deactivate_cmds_len = G_N_ELEMENTS(deactivate_cmds); +/* wait for finger + * subsequent read will not complete until finger is placed on the reader */ +static const struct elan_cmd pre_scan_cmd = { + .cmd = {0x40, 0x3f}, + .response_len = 0x1, + .response_in = ELAN_EP_CMD_IN, + .devices = ELAN_ALL_DEVICES, +}; -static void elan_cmd_cb(struct libusb_transfer *transfer); +/* led off, stop waiting for finger */ +static const struct elan_cmd stop_cmd = { + .cmd = {0x00, 0x0b}, + .response_len = 0x0, + .response_in = ELAN_EP_CMD_IN, + .devices = ELAN_ALL_DEVICES, +}; + +static void elan_cmd_done(struct fpi_ssm *ssm); static void elan_cmd_read(struct fpi_ssm *ssm); -static void elan_run_next_cmd(struct fpi_ssm *ssm); +static void elan_calibrate(struct fp_img_dev *dev); static void elan_capture(struct fp_img_dev *dev); +static void elan_deactivate(struct fp_img_dev *dev); #endif From fba3e682ea633eecdee2936bee1dd4b046d52ee9 Mon Sep 17 00:00:00 2001 From: Igor Filatov Date: Mon, 29 Jan 2018 21:48:12 +0200 Subject: [PATCH 2/9] elan: Remove supported devices from udev whitelist --- libfprint/fprint-list-udev-rules.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libfprint/fprint-list-udev-rules.c b/libfprint/fprint-list-udev-rules.c index f78ede1..de291f1 100644 --- a/libfprint/fprint-list-udev-rules.c +++ b/libfprint/fprint-list-udev-rules.c @@ -24,10 +24,6 @@ #include "fp_internal.h" static const struct usb_id whitelist_id_table[] = { - /* Unsupported (for now) Elantech finger print readers */ - { .vendor = 0x04f3, .product = 0x0c03 }, - { .vendor = 0x04f3, .product = 0x0c16 }, - { .vendor = 0x04f3, .product = 0x0c26 }, /* Unsupported (for now) Validity Sensors finger print readers */ { .vendor = 0x138a, .product = 0x0090 }, /* Found on e.g. Lenovo T460s */ { .vendor = 0x138a, .product = 0x0091 }, From 65bbdff3fc16383dc7be0255e53945eae20ffc66 Mon Sep 17 00:00:00 2001 From: Igor Filatov Date: Thu, 9 Aug 2018 14:45:33 +0200 Subject: [PATCH 3/9] elan: Stop doing sensor reset, change calibration and frame logic Sensor reset code has been removed because it is not needed during normal operation. Calibration and frame processing logic has been improved according to recommendations from Elantech. --- libfprint/drivers/elan.c | 376 ++++++++++++++++++++++----------------- libfprint/drivers/elan.h | 70 ++++---- 2 files changed, 244 insertions(+), 202 deletions(-) diff --git a/libfprint/drivers/elan.c b/libfprint/drivers/elan.c index 3a2a0b0..f853cb0 100644 --- a/libfprint/drivers/elan.c +++ b/libfprint/drivers/elan.c @@ -41,9 +41,8 @@ static struct fpi_frame_asmbl_ctx assembling_ctx = { struct elan_dev { /* device config */ unsigned short dev_type; - /* number of pixels to discard on left and right (along raw image height) - * because they have different intensity from the rest of the frame */ - unsigned char frame_margin; + unsigned short fw_ver; + void (*process_frame) (unsigned short *raw_frame, GSList ** frames); /* end device config */ /* commands */ @@ -54,8 +53,10 @@ struct elan_dev { /* state */ gboolean deactivating; - unsigned char calib_atts_left; unsigned char *last_read; + unsigned char calib_atts_left; + unsigned char calib_status; + unsigned short *background; unsigned char frame_width; unsigned char frame_height; unsigned char raw_frame_width; @@ -64,17 +65,23 @@ struct elan_dev { /* end state */ }; +int cmp_short(const void *a, const void *b) +{ + return (int)(*(short *)a - *(short *)b); +} + static void elan_dev_reset(struct elan_dev *elandev) { G_DEBUG_HERE(); BUG_ON(elandev->cur_transfer); - elandev->deactivating = FALSE; - elandev->cmd = NULL; elandev->cmd_timeout = ELAN_CMD_TIMEOUT; + elandev->deactivating = FALSE; + elandev->calib_status = 0; + g_free(elandev->last_read); elandev->last_read = NULL; @@ -83,36 +90,93 @@ static void elan_dev_reset(struct elan_dev *elandev) elandev->num_frames = 0; } -static void elan_save_frame(struct fp_img_dev *dev) +static void elan_save_frame(struct elan_dev *elandev, unsigned short *frame) { - struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); + G_DEBUG_HERE(); + unsigned char raw_height = elandev->frame_width; unsigned char raw_width = elandev->raw_frame_width; - unsigned short *frame = - g_malloc(elandev->frame_width * elandev->frame_height * 2); - - G_DEBUG_HERE(); /* Raw images are vertical and perpendicular to swipe direction of a * normalized image, which means we need to make them horizontal before * assembling. We also discard stripes of 'frame_margin' along raw * height. */ + unsigned char frame_margin = (raw_width - elandev->frame_height) / 2; for (int y = 0; y < raw_height; y++) - for (int x = elandev->frame_margin; - x < raw_width - elandev->frame_margin; x++) { - int frame_idx = - y + (x - elandev->frame_margin) * raw_height; + for (int x = frame_margin; x < raw_width - frame_margin; x++) { + int frame_idx = y + (x - frame_margin) * raw_height; int raw_idx = x + y * raw_width; frame[frame_idx] = ((unsigned short *)elandev->last_read)[raw_idx]; } +} + +static void elan_save_background(struct elan_dev *elandev) +{ + G_DEBUG_HERE(); + + g_free(elandev->background); + elandev->background = + g_malloc(elandev->frame_width * elandev->frame_height * + sizeof(short)); + elan_save_frame(elandev, elandev->background); +} + +/* save a frame as part of the fingerprint image + * background needs to have been captured for this routine to work + * Elantech recommends 2-step non-linear normalization in order to reduce + * 2^14 ADC resolution to 2^8 image: + * + * 1. background is subtracted (done here) + * + * 2. pixels are grouped in 3 groups by intensity and each group is mapped + * separately onto the normalized frame (done in elan_process_frame_*) + * ==== 16383 ____> ======== 255 + * / + * ----- lvl3 __/ + * 35% pixels + * + * ----- lvl2 --------> ======== 156 + * + * 30% pixels + * ----- lvl1 --------> ======== 99 + * + * 35% pixels + * ----- lvl0 __ + * \ + * ======== 0 \____> ======== 0 + * + * For some devices we don't do 2. but instead do a simple linear mapping + * because it seems to produce better results (or at least as good): + * ==== 16383 ___> ======== 255 + * / + * ------ max __/ + * + * + * ------ min __ + * \ + * ======== 0 \___> ======== 0 + */ +static void elan_save_img_frame(struct elan_dev *elandev) +{ + G_DEBUG_HERE(); + + unsigned int frame_size = elandev->frame_width * elandev->frame_height; + unsigned short *frame = g_malloc(frame_size * sizeof(short)); + elan_save_frame(elandev, frame); + + for (int i = 0; i < frame_size; i++) + if (elandev->background[i] > frame[i]) + frame[i] = 0; + else + frame[i] -= elandev->background[i]; elandev->frames = g_slist_prepend(elandev->frames, frame); elandev->num_frames += 1; } -/* Transform raw sensor data to normalized 8-bit grayscale image. */ -static void elan_process_frame(unsigned short *raw_frame, GSList ** frames) +static void elan_process_frame_linear(unsigned short *raw_frame, + GSList ** frames) { unsigned int frame_size = assembling_ctx.frame_width * assembling_ctx.frame_height; @@ -132,12 +196,42 @@ static void elan_process_frame(unsigned short *raw_frame, GSList ** frames) unsigned short px; for (int i = 0; i < frame_size; i++) { px = raw_frame[i]; - if (px <= min) - px = 0; - else if (px >= max) - px = 0xff; - else - px = (px - min) * 0xff / (max - min); + px = (px - min) * 0xff / (max - min); + frame->data[i] = (unsigned char)px; + } + + *frames = g_slist_prepend(*frames, frame); +} + +static void elan_process_frame_thirds(unsigned short *raw_frame, + GSList ** frames) +{ + G_DEBUG_HERE(); + + unsigned int frame_size = + assembling_ctx.frame_width * assembling_ctx.frame_height; + struct fpi_frame *frame = + g_malloc(frame_size + sizeof(struct fpi_frame)); + + unsigned short lvl0, lvl1, lvl2, lvl3; + unsigned short *sorted = g_malloc(frame_size * sizeof(short)); + memcpy(sorted, raw_frame, frame_size * sizeof(short)); + qsort(sorted, frame_size, sizeof(short), cmp_short); + lvl0 = sorted[0]; + lvl1 = sorted[frame_size * 3 / 10]; + lvl2 = sorted[frame_size * 65 / 100]; + lvl3 = sorted[frame_size - 1]; + g_free(sorted); + + unsigned short px; + for (int i = 0; i < frame_size; i++) { + px = raw_frame[i]; + if (lvl0 <= px && px < lvl1) + px = (px - lvl0) * 99 / (lvl1 - lvl0); + else if (lvl1 <= px && px < lvl2) + px = 99 + ((px - lvl1) * 56 / (lvl2 - lvl1)); + else // (lvl2 <= px && px <= lvl3) + px = 155 + ((px - lvl2) * 100 / (lvl3 - lvl2)); frame->data[i] = (unsigned char)px; } @@ -158,11 +252,13 @@ static void elan_submit_image(struct fp_img_dev *dev) assembling_ctx.frame_width = elandev->frame_width; assembling_ctx.frame_height = elandev->frame_height; assembling_ctx.image_width = elandev->frame_width * 3 / 2; - g_slist_foreach(elandev->frames, (GFunc) elan_process_frame, &frames); + g_slist_foreach(elandev->frames, (GFunc) elandev->process_frame, + &frames); fpi_do_movement_estimation(&assembling_ctx, frames, elandev->num_frames - ELAN_SKIP_LAST_FRAMES); - img = fpi_assemble_frames(&assembling_ctx, frames, - elandev->num_frames - ELAN_SKIP_LAST_FRAMES); + img = + fpi_assemble_frames(&assembling_ctx, frames, + elandev->num_frames - ELAN_SKIP_LAST_FRAMES); img->flags |= FP_IMG_PARTIAL; fpi_imgdev_image_captured(dev, img); @@ -221,7 +317,7 @@ static void elan_cmd_read(struct fpi_ssm *ssm) G_DEBUG_HERE(); - if (!(elandev->cmd->response_len)) { + if (elandev->cmd->response_len == ELAN_CMD_SKIP_READ) { fp_dbg("skipping read, not expecting anything"); elan_cmd_done(ssm); return; @@ -258,11 +354,14 @@ static void elan_run_cmd(struct fpi_ssm *ssm, const struct elan_cmd *cmd, struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm); struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); + dbg_buf(cmd->cmd, 2); + elandev->cmd = cmd; if (cmd_timeout != -1) elandev->cmd_timeout = cmd_timeout; - if (!(cmd->devices & elandev->dev_type)) { + if (elandev->dev_type && cmd->devices + && !(cmd->devices & elandev->dev_type)) { fp_dbg("skipping for this device"); elan_cmd_done(ssm); return; @@ -353,7 +452,7 @@ static void capture_run_state(struct fpi_ssm *ssm) fpi_ssm_mark_aborted(ssm, FP_VERIFY_RETRY); break; case CAPTURE_CHECK_ENOUGH_FRAMES: - elan_save_frame(dev); + elan_save_img_frame(elandev); if (elandev->num_frames < ELAN_MAX_FRAMES) { /* quickly stop if finger is removed */ elandev->cmd_timeout = ELAN_FINGER_TIMEOUT; @@ -429,25 +528,19 @@ static void fpi_ssm_next_state_async(void *data) fpi_ssm_next_state((struct fpi_ssm *)data); } -/* this function needs last_read to be the calibration mean and at least - * one frame */ +/* this function needs to have elandev->background and elandev->last_read to be + * the calibration mean */ static int elan_need_calibration(struct elan_dev *elandev) { G_DEBUG_HERE(); - if (elandev->dev_type & ELAN_0903) { - fp_dbg("don't know how to calibrate this device"); - return 0; - } - unsigned short calib_mean = elandev->last_read[0] * 0xff + elandev->last_read[1]; - unsigned short *bg_data = ((GSList *) elandev->frames)->data; unsigned int bg_mean = 0, delta; unsigned int frame_size = elandev->frame_width * elandev->frame_height; for (int i = 0; i < frame_size; i++) - bg_mean += bg_data[i]; + bg_mean += elandev->background[i]; bg_mean /= frame_size; delta = @@ -460,12 +553,13 @@ static int elan_need_calibration(struct elan_dev *elandev) } enum calibrate_states { - CALIBRATE_START, - CALIBRATE_CHECK_RESULT, - CALIBRATE_REPEAT, CALIBRATE_GET_BACKGROUND, CALIBRATE_SAVE_BACKGROUND, CALIBRATE_GET_MEAN, + CALIBRATE_CHECK_NEEDED, + CALIBRATE_GET_STATUS, + CALIBRATE_CHECK_STATUS, + CALIBRATE_REPEAT_STATUS, CALIBRATE_NUM_STATES, }; @@ -477,40 +571,60 @@ static void calibrate_run_state(struct fpi_ssm *ssm) G_DEBUG_HERE(); switch (fpi_ssm_get_cur_state(ssm)) { - case CALIBRATE_START: - elandev->calib_atts_left -= 1; - if (elandev->calib_atts_left) - elan_run_cmd(ssm, &run_calibration_cmd, - ELAN_CMD_TIMEOUT); - else { - fp_dbg("too many calibration attempts"); - fpi_ssm_mark_aborted(ssm, -1); - } - break; - case CALIBRATE_CHECK_RESULT: - /* 0x01 - retry, 0x03 - ok - * but some devices send other responses so in order to avoid needless - * retries we don't check 0x3 but only retry on 0x1 (need to wait 50 ms) */ - fp_dbg("calibration status: 0x%02x", elandev->last_read[0]); - if (elandev->last_read[0] == 0x01) { - if (!fpi_timeout_add(50, fpi_ssm_next_state_async, ssm)) - fpi_ssm_mark_aborted(ssm, -ETIME); - } else - fpi_ssm_jump_to_state(ssm, CALIBRATE_GET_BACKGROUND); - break; - case CALIBRATE_REPEAT: - fpi_ssm_jump_to_state(ssm, CALIBRATE_START); - break; case CALIBRATE_GET_BACKGROUND: elan_run_cmd(ssm, &get_image_cmd, ELAN_CMD_TIMEOUT); break; case CALIBRATE_SAVE_BACKGROUND: - elan_save_frame(dev); - fpi_ssm_next_state(ssm); + elan_save_background(elandev); + if (elandev->fw_ver < ELAN_MIN_CALIBRATION_FW) { + fp_dbg("FW does not support calibration"); + fpi_ssm_mark_completed(ssm); + } else + fpi_ssm_next_state(ssm); break; case CALIBRATE_GET_MEAN: elan_run_cmd(ssm, &get_calib_mean_cmd, ELAN_CMD_TIMEOUT); break; + case CALIBRATE_CHECK_NEEDED: + if (elan_need_calibration(elandev)) { + elandev->calib_status = 0; + fpi_ssm_next_state(ssm); + } else + fpi_ssm_mark_completed(ssm); + break; + case CALIBRATE_GET_STATUS: + elandev->calib_atts_left -= 1; + if (elandev->calib_atts_left) + elan_run_cmd(ssm, &get_calib_status_cmd, + ELAN_CMD_TIMEOUT); + else { + fp_dbg("calibration failed"); + fpi_ssm_mark_aborted(ssm, -1); + } + break; + case CALIBRATE_CHECK_STATUS: + /* 0x01 - retry, 0x03 - ok + * It appears that when reading the response soon after 0x4023 the device + * can return 0x03, and only after some time (up to 100 ms) the response + * changes to 0x01. It stays that way for some time and then changes back + * to 0x03. Because of this we don't just expect 0x03, we want to see 0x01 + * first. This is to make sure that a full calibration loop has completed */ + fp_dbg("calibration status: 0x%02x", elandev->last_read[0]); + if (elandev->calib_status == 0x01 + && elandev->last_read[0] == 0x03) { + elandev->calib_status = 0x03; + fpi_ssm_jump_to_state(ssm, CALIBRATE_GET_BACKGROUND); + } else { + if (elandev->calib_status == 0x00 + && elandev->last_read[0] == 0x01) + elandev->calib_status = 0x01; + if (!fpi_timeout_add(50, fpi_ssm_next_state_async, ssm)) + fpi_ssm_mark_aborted(ssm, -ETIME); + } + break; + case CALIBRATE_REPEAT_STATUS: + fpi_ssm_jump_to_state(ssm, CALIBRATE_GET_STATUS); + break; } } @@ -525,8 +639,6 @@ static void calibrate_complete(struct fpi_ssm *ssm) elan_deactivate(dev); else if (fpi_ssm_get_error(ssm)) fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm)); - else if (elan_need_calibration(elandev)) - elan_calibrate(dev); else { fpi_imgdev_activate_complete(dev, 0); elan_capture(dev); @@ -542,6 +654,8 @@ static void elan_calibrate(struct fp_img_dev *dev) G_DEBUG_HERE(); elan_dev_reset(elandev); + elandev->calib_atts_left = ELAN_CALIBRATION_ATTEMPTS; + struct fpi_ssm *ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), calibrate_run_state, CALIBRATE_NUM_STATES); fpi_ssm_set_user_data(ssm, dev); @@ -550,13 +664,10 @@ static void elan_calibrate(struct fp_img_dev *dev) enum activate_states { ACTIVATE_GET_FW_VER, - ACTIVATE_PRINT_FW_VER, + ACTIVATE_SET_FW_VER, ACTIVATE_GET_SENSOR_DIM, ACTIVATE_SET_SENSOR_DIM, ACTIVATE_CMD_1, - ACTIVATE_GET_BACKGROUND, - ACTIVATE_SAVE_BACKGROUND, - ACTIVATE_GET_MEAN, ACTIVATE_NUM_STATES, }; @@ -571,9 +682,10 @@ static void activate_run_state(struct fpi_ssm *ssm) case ACTIVATE_GET_FW_VER: elan_run_cmd(ssm, &get_fw_ver_cmd, ELAN_CMD_TIMEOUT); break; - case ACTIVATE_PRINT_FW_VER: - fp_dbg("FW ver %d.%d", elandev->last_read[0], - elandev->last_read[1]); + case ACTIVATE_SET_FW_VER: + elandev->fw_ver = + (elandev->last_read[0] << 8 | elandev->last_read[1]); + fp_dbg("FW ver 0x%04hx", elandev->fw_ver); fpi_ssm_next_state(ssm); break; case ACTIVATE_GET_SENSOR_DIM: @@ -582,9 +694,11 @@ static void activate_run_state(struct fpi_ssm *ssm) case ACTIVATE_SET_SENSOR_DIM: elandev->frame_width = elandev->last_read[2]; elandev->raw_frame_width = elandev->last_read[0]; - elandev->frame_height = - elandev->raw_frame_width - 2 * elandev->frame_margin; - /* see elan_save_frame */ + if (elandev->raw_frame_width < ELAN_MAX_FRAME_HEIGHT) + elandev->frame_height = elandev->raw_frame_width; + else + elandev->frame_height = ELAN_MAX_FRAME_HEIGHT; + /* see elan_save_frame for why it's width x raw_width */ fp_dbg("sensor dimensions, WxH: %dx%d", elandev->frame_width, elandev->raw_frame_width); fpi_ssm_next_state(ssm); @@ -593,16 +707,6 @@ static void activate_run_state(struct fpi_ssm *ssm) /* TODO: find out what this does, if we need it */ elan_run_cmd(ssm, &activate_cmd_1, ELAN_CMD_TIMEOUT); break; - case ACTIVATE_GET_BACKGROUND: - elan_run_cmd(ssm, &get_image_cmd, ELAN_CMD_TIMEOUT); - break; - case ACTIVATE_SAVE_BACKGROUND: - elan_save_frame(dev); - fpi_ssm_next_state(ssm); - break; - case ACTIVATE_GET_MEAN: - elan_run_cmd(ssm, &get_calib_mean_cmd, ELAN_CMD_TIMEOUT); - break; } } @@ -617,12 +721,9 @@ static void activate_complete(struct fpi_ssm *ssm) elan_deactivate(dev); else if (fpi_ssm_get_error(ssm)) fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm)); - else if (elan_need_calibration(elandev)) + else elan_calibrate(dev); - else { - fpi_imgdev_activate_complete(dev, 0); - elan_capture(dev); - } + fpi_ssm_free(ssm); } @@ -631,8 +732,8 @@ static void elan_activate(struct fp_img_dev *dev) struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); G_DEBUG_HERE(); - elan_dev_reset(elandev); + struct fpi_ssm *ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), activate_run_state, ACTIVATE_NUM_STATES); fpi_ssm_set_user_data(ssm, dev); @@ -657,12 +758,12 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data) /* common params */ elandev->dev_type = driver_data; - elandev->calib_atts_left = ELAN_CALIBRATION_ATTEMPTS; - elandev->frame_margin = 0; + elandev->background = NULL; + elandev->process_frame = elan_process_frame_thirds; switch (driver_data) { case ELAN_0907: - elandev->frame_margin = 12; + elandev->process_frame = elan_process_frame_linear; break; } @@ -670,64 +771,6 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data) return 0; } -enum reset_sensor_states { - RESET_SENSOR_DO_RESET, - RESET_SENSOR_WAIT, - RESET_SENSOR_FUSE_LOAD, - RESET_SENSOR_STATUS, - RESET_SENSOR_NUM_STATES, -}; - -static void reset_sensor_run_state(struct fpi_ssm *ssm) -{ - switch (fpi_ssm_get_cur_state(ssm)) { - case RESET_SENSOR_DO_RESET: - elan_run_cmd(ssm, &reset_sensor_cmd, ELAN_CMD_TIMEOUT); - break; - case RESET_SENSOR_WAIT: - /* must wait 5 ms after sensor reset command */ - if (!fpi_timeout_add(5, fpi_ssm_next_state_async, ssm)) - fpi_ssm_mark_aborted(ssm, -ETIME); - break; - case RESET_SENSOR_FUSE_LOAD: - elan_run_cmd(ssm, &fuse_load_cmd, ELAN_CMD_TIMEOUT); - break; - case RESET_SENSOR_STATUS: - elan_run_cmd(ssm, &read_sensor_status_cmd, ELAN_CMD_TIMEOUT); - break; - } -} - -static void reset_sensor_complete(struct fpi_ssm *ssm) -{ - struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm); - struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); - - G_DEBUG_HERE(); - - if (elandev->deactivating) - elan_deactivate(dev); - else if (fpi_ssm_get_error(ssm)) - fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm)); - else - elan_activate(dev); - - fpi_ssm_free(ssm); -} - -static void elan_reset_sensor(struct fp_img_dev *dev) -{ - struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); - - G_DEBUG_HERE(); - - elan_dev_reset(elandev); - struct fpi_ssm *ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), reset_sensor_run_state, - RESET_SENSOR_NUM_STATES); - fpi_ssm_set_user_data(ssm, dev); - fpi_ssm_start(ssm, reset_sensor_complete); -} - static void dev_deinit(struct fp_img_dev *dev) { struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); @@ -735,6 +778,7 @@ static void dev_deinit(struct fp_img_dev *dev) G_DEBUG_HERE(); elan_dev_reset(elandev); + g_free(elandev->background); g_free(elandev); libusb_release_interface(fpi_imgdev_get_usb_dev(dev), 0); fpi_imgdev_close_complete(dev); @@ -742,7 +786,8 @@ static void dev_deinit(struct fp_img_dev *dev) static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state) { - elan_reset_sensor(dev); + G_DEBUG_HERE(); + elan_activate(dev); return 0; } @@ -761,12 +806,17 @@ static void dev_deactivate(struct fp_img_dev *dev) } static const struct usb_id id_table[] = { - {.vendor = ELAN_VENDOR_ID,.product = 0x0903,.driver_data = ELAN_0903}, + {.vendor = ELAN_VENDOR_ID,.product = 0x0903,.driver_data = + ELAN_ALL_DEVICES}, {.vendor = ELAN_VENDOR_ID,.product = 0x0907,.driver_data = ELAN_0907}, - {.vendor = ELAN_VENDOR_ID,.product = 0x0c03,.driver_data = ELAN_0C03}, - {.vendor = ELAN_VENDOR_ID,.product = 0x0c16,.driver_data = ELAN_0C16}, - {.vendor = ELAN_VENDOR_ID,.product = 0x0c1a,.driver_data = ELAN_0C1A}, - {.vendor = ELAN_VENDOR_ID,.product = 0x0c26,.driver_data = ELAN_0C26}, + {.vendor = ELAN_VENDOR_ID,.product = 0x0c03,.driver_data = + ELAN_ALL_DEVICES}, + {.vendor = ELAN_VENDOR_ID,.product = 0x0c16,.driver_data = + ELAN_ALL_DEVICES}, + {.vendor = ELAN_VENDOR_ID,.product = 0x0c1a,.driver_data = + ELAN_ALL_DEVICES}, + {.vendor = ELAN_VENDOR_ID,.product = 0x0c26,.driver_data = + ELAN_ALL_DEVICES}, {0, 0, 0,}, }; diff --git a/libfprint/drivers/elan.h b/libfprint/drivers/elan.h index 74e9b3a..2b73f23 100644 --- a/libfprint/drivers/elan.h +++ b/libfprint/drivers/elan.h @@ -26,36 +26,42 @@ #define ELAN_VENDOR_ID 0x04f3 -/* supported devices */ -#define ELAN_0903 1 -#define ELAN_0907 (1 << 1) -#define ELAN_0C03 (1 << 2) -#define ELAN_0C16 (1 << 3) -#define ELAN_0C1A (1 << 4) -#define ELAN_0C26 (1 << 5) +/* a default device type */ +#define ELAN_ALL_DEVICES 0 -#define ELAN_ALL_DEVICES (ELAN_0903|ELAN_0907|ELAN_0C03|ELAN_0C16|ELAN_0C1A|ELAN_0C26) +/* devices with quirks */ +#define ELAN_0907 1 + +/* min FW version that supports calibration */ +#define ELAN_MIN_CALIBRATION_FW 0x0138 + +/* max difference between background image mean and calibration mean + * (the response value of get_calib_mean_cmd)*/ +#define ELAN_CALIBRATION_MAX_DELTA 500 + +/* times to retry reading calibration status during one session + * generally prevents calibration from looping indefinitely */ +#define ELAN_CALIBRATION_ATTEMPTS 10 /* min and max frames in a capture */ #define ELAN_MIN_FRAMES 7 #define ELAN_MAX_FRAMES 30 +/* crop frames to this height to improve stitching */ +#define ELAN_MAX_FRAME_HEIGHT 30 + /* number of frames to drop at the end of capture because frames captured * while the finger is being lifted can be bad */ #define ELAN_SKIP_LAST_FRAMES 1 -/* max difference between background image mean and calibration mean -* (the response value of get_calib_mean_cmd)*/ -#define ELAN_CALIBRATION_MAX_DELTA 500 - -/* times to retry calibration */ -#define ELAN_CALIBRATION_ATTEMPTS 10 - #define ELAN_CMD_LEN 0x2 #define ELAN_EP_CMD_OUT (0x1 | LIBUSB_ENDPOINT_OUT) #define ELAN_EP_CMD_IN (0x3 | LIBUSB_ENDPOINT_IN) #define ELAN_EP_IMG_IN (0x2 | LIBUSB_ENDPOINT_IN) +/* used as response length to tell the driver to skip reading response */ +#define ELAN_CMD_SKIP_READ 0 + /* usual command timeout and timeout for when we need to check if the finger is * still on the device */ #define ELAN_CMD_TIMEOUT 10000 @@ -99,27 +105,6 @@ static const struct elan_cmd get_image_cmd = { .devices = ELAN_ALL_DEVICES, }; -static const struct elan_cmd get_calib_mean_cmd = { - .cmd = {0x40, 0x24}, - .response_len = 0x2, - .response_in = ELAN_EP_CMD_IN, - .devices = ELAN_ALL_DEVICES & ~ELAN_0903, -}; - -static const struct elan_cmd reset_sensor_cmd = { - .cmd = {0x40, 0x11}, - .response_len = 0x0, - .response_in = ELAN_EP_CMD_IN, - .devices = ELAN_ALL_DEVICES, -}; - -static const struct elan_cmd fuse_load_cmd = { - .cmd = {0x40, 0x14}, - .response_len = 0x0, - .response_in = ELAN_EP_CMD_IN, - .devices = ELAN_ALL_DEVICES, -}; - static const struct elan_cmd read_sensor_status_cmd = { .cmd = {0x40, 0x13}, .response_len = 0x1, @@ -127,16 +112,23 @@ static const struct elan_cmd read_sensor_status_cmd = { .devices = ELAN_ALL_DEVICES, }; -static const struct elan_cmd run_calibration_cmd = { +static const struct elan_cmd get_calib_status_cmd = { .cmd = {0x40, 0x23}, .response_len = 0x1, .response_in = ELAN_EP_CMD_IN, .devices = ELAN_ALL_DEVICES, }; +static const struct elan_cmd get_calib_mean_cmd = { + .cmd = {0x40, 0x24}, + .response_len = 0x2, + .response_in = ELAN_EP_CMD_IN, + .devices = ELAN_ALL_DEVICES, +}; + static const struct elan_cmd led_on_cmd = { .cmd = {0x40, 0x31}, - .response_len = 0x0, + .response_len = ELAN_CMD_SKIP_READ, .response_in = ELAN_EP_CMD_IN, .devices = ELAN_0907, }; @@ -153,7 +145,7 @@ static const struct elan_cmd pre_scan_cmd = { /* led off, stop waiting for finger */ static const struct elan_cmd stop_cmd = { .cmd = {0x00, 0x0b}, - .response_len = 0x0, + .response_len = ELAN_CMD_SKIP_READ, .response_in = ELAN_EP_CMD_IN, .devices = ELAN_ALL_DEVICES, }; From b098399bbc0ae82750b59656a0b5bd03ca415a23 Mon Sep 17 00:00:00 2001 From: Igor Filatov Date: Sun, 4 Feb 2018 17:18:07 +0200 Subject: [PATCH 4/9] elan: Support 0x0c01~0x0c33 --- libfprint/drivers/elan.c | 17 +-------- libfprint/drivers/elan.h | 77 ++++++++++++++++++++++++++++++++++------ 2 files changed, 68 insertions(+), 26 deletions(-) diff --git a/libfprint/drivers/elan.c b/libfprint/drivers/elan.c index f853cb0..6c5052c 100644 --- a/libfprint/drivers/elan.c +++ b/libfprint/drivers/elan.c @@ -805,27 +805,12 @@ static void dev_deactivate(struct fp_img_dev *dev) elan_deactivate(dev); } -static const struct usb_id id_table[] = { - {.vendor = ELAN_VENDOR_ID,.product = 0x0903,.driver_data = - ELAN_ALL_DEVICES}, - {.vendor = ELAN_VENDOR_ID,.product = 0x0907,.driver_data = ELAN_0907}, - {.vendor = ELAN_VENDOR_ID,.product = 0x0c03,.driver_data = - ELAN_ALL_DEVICES}, - {.vendor = ELAN_VENDOR_ID,.product = 0x0c16,.driver_data = - ELAN_ALL_DEVICES}, - {.vendor = ELAN_VENDOR_ID,.product = 0x0c1a,.driver_data = - ELAN_ALL_DEVICES}, - {.vendor = ELAN_VENDOR_ID,.product = 0x0c26,.driver_data = - ELAN_ALL_DEVICES}, - {0, 0, 0,}, -}; - struct fp_img_driver elan_driver = { .driver = { .id = ELAN_ID, .name = FP_COMPONENT, .full_name = "ElanTech Fingerprint Sensor", - .id_table = id_table, + .id_table = elan_id_table, .scan_type = FP_SCAN_TYPE_SWIPE, }, .flags = 0, diff --git a/libfprint/drivers/elan.h b/libfprint/drivers/elan.h index 2b73f23..132374f 100644 --- a/libfprint/drivers/elan.h +++ b/libfprint/drivers/elan.h @@ -24,10 +24,10 @@ #include #include -#define ELAN_VENDOR_ID 0x04f3 +#define ELAN_VEND_ID 0x04f3 /* a default device type */ -#define ELAN_ALL_DEVICES 0 +#define ELAN_ALL_DEV 0 /* devices with quirks */ #define ELAN_0907 1 @@ -78,14 +78,14 @@ static const struct elan_cmd get_sensor_dim_cmd = { .cmd = {0x00, 0x0c}, .response_len = 0x4, .response_in = ELAN_EP_CMD_IN, - .devices = ELAN_ALL_DEVICES, + .devices = ELAN_ALL_DEV, }; static const struct elan_cmd get_fw_ver_cmd = { .cmd = {0x40, 0x19}, .response_len = 0x2, .response_in = ELAN_EP_CMD_IN, - .devices = ELAN_ALL_DEVICES, + .devices = ELAN_ALL_DEV, }; /* unknown, returns 0x0 0x1 on 0907 */ @@ -102,28 +102,28 @@ static const struct elan_cmd get_image_cmd = { * device */ .response_len = -1, .response_in = ELAN_EP_IMG_IN, - .devices = ELAN_ALL_DEVICES, + .devices = ELAN_ALL_DEV, }; static const struct elan_cmd read_sensor_status_cmd = { .cmd = {0x40, 0x13}, .response_len = 0x1, .response_in = ELAN_EP_CMD_IN, - .devices = ELAN_ALL_DEVICES, + .devices = ELAN_ALL_DEV, }; static const struct elan_cmd get_calib_status_cmd = { .cmd = {0x40, 0x23}, .response_len = 0x1, .response_in = ELAN_EP_CMD_IN, - .devices = ELAN_ALL_DEVICES, + .devices = ELAN_ALL_DEV, }; static const struct elan_cmd get_calib_mean_cmd = { .cmd = {0x40, 0x24}, .response_len = 0x2, .response_in = ELAN_EP_CMD_IN, - .devices = ELAN_ALL_DEVICES, + .devices = ELAN_ALL_DEV, }; static const struct elan_cmd led_on_cmd = { @@ -139,7 +139,7 @@ static const struct elan_cmd pre_scan_cmd = { .cmd = {0x40, 0x3f}, .response_len = 0x1, .response_in = ELAN_EP_CMD_IN, - .devices = ELAN_ALL_DEVICES, + .devices = ELAN_ALL_DEV, }; /* led off, stop waiting for finger */ @@ -147,7 +147,64 @@ static const struct elan_cmd stop_cmd = { .cmd = {0x00, 0x0b}, .response_len = ELAN_CMD_SKIP_READ, .response_in = ELAN_EP_CMD_IN, - .devices = ELAN_ALL_DEVICES, + .devices = ELAN_ALL_DEV, +}; + +static const struct usb_id elan_id_table[] = { + {.vendor = ELAN_VEND_ID,.product = 0x0903,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0907,.driver_data = ELAN_0907}, + {.vendor = ELAN_VEND_ID,.product = 0x0c01,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c02,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c03,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c04,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c05,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c06,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c07,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c08,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c09,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c0a,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c0b,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c0c,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c0d,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c0e,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c0f,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c10,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c11,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c12,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c13,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c14,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c15,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c16,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c17,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c18,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c19,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c1a,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c1b,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c1c,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c1d,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c1e,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c1f,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c20,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c21,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c22,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c23,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c24,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c25,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c26,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c27,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c28,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c29,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c2a,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c2b,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c2c,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c2d,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c2e,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c2f,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c30,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c31,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c32,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c33,.driver_data = ELAN_ALL_DEV}, + {0, 0, 0,}, }; static void elan_cmd_done(struct fpi_ssm *ssm); From c3b1c982bc7bdbd01da86276e3454dff68273eb0 Mon Sep 17 00:00:00 2001 From: Igor Filatov Date: Sun, 4 Feb 2018 17:57:27 +0200 Subject: [PATCH 5/9] elan: Set max frame height to 50, add note about performance Frame height is a loosely experience-backed guesstimation. 50 seems to be a good middle ground between swipe speed and quality. --- libfprint/drivers/elan.c | 17 +++++++++++++++++ libfprint/drivers/elan.h | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/libfprint/drivers/elan.c b/libfprint/drivers/elan.c index 6c5052c..7cd01dc 100644 --- a/libfprint/drivers/elan.c +++ b/libfprint/drivers/elan.c @@ -19,6 +19,23 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +/* + * The algorithm which libfprint uses to match fingerprints doesn't like small + * images like the ones these drivers produce. There's just not enough minutiae + * (recognizable print-specific points) on them for a reliable match. This means + * that unless another matching algo is found/implemented, these readers will + * not work as good with libfprint as they do with vendor drivers. + * + * To get bigger images the driver expects you to swipe the finger over the + * reader. This works quite well for readers with a rectangular 144x64 sensor. + * Worse than real swipe readers but good enough for day-to-day use. It needs + * a steady and relatively slow swipe. There are also square 96x96 sensors and + * I don't know whether they are in fact usable or not because I don't have one. + * I imagine they'd be less reliable because the resulting image is even + * smaller. If they can't be made usable with libfprint, I might end up dropping + * them because it's better than saying they work when they don't. + */ + #define FP_COMPONENT "elan" #include "drivers_api.h" diff --git a/libfprint/drivers/elan.h b/libfprint/drivers/elan.h index 132374f..cfe5e30 100644 --- a/libfprint/drivers/elan.h +++ b/libfprint/drivers/elan.h @@ -48,7 +48,7 @@ #define ELAN_MAX_FRAMES 30 /* crop frames to this height to improve stitching */ -#define ELAN_MAX_FRAME_HEIGHT 30 +#define ELAN_MAX_FRAME_HEIGHT 50 /* number of frames to drop at the end of capture because frames captured * while the finger is being lifted can be bad */ From b28b006d617f2ff2c5c265245d0def14190bd980 Mon Sep 17 00:00:00 2001 From: Igor Filatov Date: Mon, 5 Feb 2018 11:56:58 +0200 Subject: [PATCH 6/9] elan: Use IMGDEV_STATE_* events instead of pre-set capture path --- libfprint/drivers/elan.c | 158 ++++++++++++++++++++++++++------------- libfprint/drivers/elan.h | 4 +- 2 files changed, 109 insertions(+), 53 deletions(-) diff --git a/libfprint/drivers/elan.c b/libfprint/drivers/elan.c index 7cd01dc..20e33ad 100644 --- a/libfprint/drivers/elan.c +++ b/libfprint/drivers/elan.c @@ -69,7 +69,8 @@ struct elan_dev { /* end commands */ /* state */ - gboolean deactivating; + enum fp_imgdev_state dev_state; + enum fp_imgdev_state dev_state_next; unsigned char *last_read; unsigned char calib_atts_left; unsigned char calib_status; @@ -96,7 +97,6 @@ static void elan_dev_reset(struct elan_dev *elandev) elandev->cmd = NULL; elandev->cmd_timeout = ELAN_CMD_TIMEOUT; - elandev->deactivating = FALSE; elandev->calib_status = 0; g_free(elandev->last_read); @@ -314,6 +314,7 @@ static void elan_cmd_cb(struct libusb_transfer *transfer) case LIBUSB_TRANSFER_CANCELLED: fp_dbg("transfer cancelled"); fpi_ssm_mark_aborted(ssm, -ECANCELED); + elan_deactivate(dev); break; case LIBUSB_TRANSFER_TIMED_OUT: fp_dbg("transfer timed out"); @@ -377,8 +378,7 @@ static void elan_run_cmd(struct fpi_ssm *ssm, const struct elan_cmd *cmd, if (cmd_timeout != -1) elandev->cmd_timeout = cmd_timeout; - if (elandev->dev_type && cmd->devices - && !(cmd->devices & elandev->dev_type)) { + if (cmd->devices != ELAN_ALL_DEV && !(cmd->devices & elandev->dev_type)) { fp_dbg("skipping for this device"); elan_cmd_done(ssm); return; @@ -463,10 +463,11 @@ static void capture_run_state(struct fpi_ssm *ssm) /* 0x55 - finger present * 0xff - device not calibrated (probably) */ if (elandev->last_read && elandev->last_read[0] == 0x55) { - fpi_imgdev_report_finger_status(dev, TRUE); + if (elandev->dev_state == IMGDEV_STATE_AWAIT_FINGER_ON) + fpi_imgdev_report_finger_status(dev, TRUE); elan_run_cmd(ssm, &get_image_cmd, ELAN_CMD_TIMEOUT); } else - fpi_ssm_mark_aborted(ssm, FP_VERIFY_RETRY); + fpi_ssm_mark_aborted(ssm, -EBADMSG); break; case CAPTURE_CHECK_ENOUGH_FRAMES: elan_save_img_frame(elandev); @@ -481,11 +482,6 @@ static void capture_run_state(struct fpi_ssm *ssm) } } -static void elan_capture_async(void *data) -{ - elan_capture((struct fp_img_dev *)data); -} - static void capture_complete(struct fpi_ssm *ssm) { struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm); @@ -493,36 +489,38 @@ static void capture_complete(struct fpi_ssm *ssm) G_DEBUG_HERE(); - if (elandev->deactivating) - elan_deactivate(dev); + if (fpi_ssm_get_error(ssm) == -ECANCELED) { + fpi_ssm_free(ssm); + return; + } /* either max frames captured or timed out waiting for the next frame */ - else if (!fpi_ssm_get_error(ssm) - || (fpi_ssm_get_error(ssm) == -ETIMEDOUT - && fpi_ssm_get_cur_state(ssm) == CAPTURE_WAIT_FINGER)) - if (elandev->num_frames >= ELAN_MIN_FRAMES) { + if (!fpi_ssm_get_error(ssm) + || (fpi_ssm_get_error(ssm) == -ETIMEDOUT + && fpi_ssm_get_cur_state(ssm) == CAPTURE_WAIT_FINGER)) + if (elandev->num_frames >= ELAN_MIN_FRAMES) elan_submit_image(dev); - fpi_imgdev_report_finger_status(dev, FALSE); - } else { + else { fp_dbg("swipe too short: want >= %d frames, got %d", ELAN_MIN_FRAMES, elandev->num_frames); - fpi_imgdev_session_error(dev, - FP_VERIFY_RETRY_TOO_SHORT); + fpi_imgdev_abort_scan(dev, FP_VERIFY_RETRY_TOO_SHORT); } /* other error - * It says "...session_error" but repotring 1 during verification - * makes it successful! */ + * It says "...abort_scan" but reporting 1 during verification makes it + * successful! */ else - fpi_imgdev_session_error(dev, FP_VERIFY_NO_MATCH); + fpi_imgdev_abort_scan(dev, fpi_ssm_get_error(ssm)); - /* When enrolling the lib won't restart the capture after a stage has - * completed, so we need to keep feeding it images till it's had enough. - * But after that it can't finalize enrollemnt until this callback exits. - * That's why we schedule elan_capture instead of running it directly. */ - if (fpi_dev_get_dev_state(fpi_imgdev_get_dev(dev)) == DEV_STATE_ENROLLING - && !fpi_timeout_add(10, elan_capture_async, dev)) - fpi_imgdev_session_error(dev, -ETIME); + /* this procedure must be called regardless of outcome because it advances + * dev_state to AWAIT_FINGER_ON under the hood... */ + fpi_imgdev_report_finger_status(dev, FALSE); + + /* ...but only on enroll! If verify or identify fails because of short swipe, + * we need to do it manually. It feels like libfprint or the application + * should know better if they want to retry, but they don't. */ + if (elandev->dev_state != IMGDEV_STATE_INACTIVE) + dev_change_state(dev, IMGDEV_STATE_AWAIT_FINGER_ON); fpi_ssm_free(ssm); } @@ -648,18 +646,12 @@ static void calibrate_run_state(struct fpi_ssm *ssm) static void calibrate_complete(struct fpi_ssm *ssm) { struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm); - struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); G_DEBUG_HERE(); - if (elandev->deactivating) - elan_deactivate(dev); - else if (fpi_ssm_get_error(ssm)) + + if (fpi_ssm_get_error(ssm) != -ECANCELED) fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm)); - else { - fpi_imgdev_activate_complete(dev, 0); - elan_capture(dev); - } fpi_ssm_free(ssm); } @@ -730,16 +722,15 @@ static void activate_run_state(struct fpi_ssm *ssm) static void activate_complete(struct fpi_ssm *ssm) { struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm); - struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); G_DEBUG_HERE(); - if (elandev->deactivating) - elan_deactivate(dev); - else if (fpi_ssm_get_error(ssm)) - fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm)); - else - elan_calibrate(dev); + if (fpi_ssm_get_error(ssm) != -ECANCELED) { + if (fpi_ssm_get_error(ssm)) + fpi_imgdev_activate_complete(dev, fpi_ssm_get_error(ssm)); + else + elan_calibrate(dev); + } fpi_ssm_free(ssm); } @@ -808,18 +799,80 @@ static int dev_activate(struct fp_img_dev *dev, enum fp_imgdev_state state) return 0; } -static void dev_deactivate(struct fp_img_dev *dev) +static void elan_change_state(struct fp_img_dev *dev) +{ + struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); + enum fp_imgdev_state next_state = elandev->dev_state_next; + + if (elandev->dev_state == next_state) { + fp_dbg("already in %d", next_state); + return; + } else + fp_dbg("changing to %d", next_state); + + switch (next_state) { + case IMGDEV_STATE_INACTIVE: + if (elandev->cur_transfer) + /* deactivation will complete in transfer callback */ + libusb_cancel_transfer(elandev->cur_transfer); + else + elan_deactivate(dev); + break; + case IMGDEV_STATE_AWAIT_FINGER_ON: + /* activation completed or another enroll stage started */ + elan_capture(dev); + break; + case IMGDEV_STATE_CAPTURE: + case IMGDEV_STATE_AWAIT_FINGER_OFF: + break; + } + + elandev->dev_state = next_state; +} + +static void elan_change_state_async(void *data) +{ + elan_change_state((struct fp_img_dev *)data); +} + +static int dev_change_state(struct fp_img_dev *dev, enum fp_imgdev_state state) { struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); G_DEBUG_HERE(); - elandev->deactivating = TRUE; + switch (state) { + case IMGDEV_STATE_INACTIVE: + case IMGDEV_STATE_AWAIT_FINGER_ON: + /* schedule state change instead of calling it directly to allow all actions + * related to the previous state to complete */ + elandev->dev_state_next = state; + if (!fpi_timeout_add(10, elan_change_state_async, dev)) { + fpi_imgdev_session_error(dev, -ETIME); + return -ETIME; + } + break; + case IMGDEV_STATE_CAPTURE: + case IMGDEV_STATE_AWAIT_FINGER_OFF: + /* TODO MAYBE: split capture ssm into smaller ssms and use these states */ + elandev->dev_state = state; + elandev->dev_state_next = state; + break; + default: + fp_err("unrecognized state %d", state); + fpi_imgdev_session_error(dev, -EINVAL); + return -EINVAL; + } - if (elandev->cur_transfer) - libusb_cancel_transfer(elandev->cur_transfer); - else - elan_deactivate(dev); + /* as of time of writing libfprint never checks the return value */ + return 0; +} + +static void dev_deactivate(struct fp_img_dev *dev) +{ + G_DEBUG_HERE(); + + dev_change_state(dev, IMGDEV_STATE_INACTIVE); } struct fp_img_driver elan_driver = { @@ -838,4 +891,5 @@ struct fp_img_driver elan_driver = { .close = dev_deinit, .activate = dev_activate, .deactivate = dev_deactivate, + .change_state = dev_change_state, }; diff --git a/libfprint/drivers/elan.h b/libfprint/drivers/elan.h index cfe5e30..9002a0e 100644 --- a/libfprint/drivers/elan.h +++ b/libfprint/drivers/elan.h @@ -130,7 +130,7 @@ static const struct elan_cmd led_on_cmd = { .cmd = {0x40, 0x31}, .response_len = ELAN_CMD_SKIP_READ, .response_in = ELAN_EP_CMD_IN, - .devices = ELAN_0907, + .devices = ELAN_ALL_DEV, }; /* wait for finger @@ -214,4 +214,6 @@ static void elan_calibrate(struct fp_img_dev *dev); static void elan_capture(struct fp_img_dev *dev); static void elan_deactivate(struct fp_img_dev *dev); +static int dev_change_state(struct fp_img_dev *dev, enum fp_imgdev_state state); + #endif From 75fe328f64f33b09e02d519e079efeb965af5a10 Mon Sep 17 00:00:00 2001 From: Igor Filatov Date: Wed, 28 Feb 2018 11:36:33 +0200 Subject: [PATCH 7/9] elan: Don't rotate frames on 0x0c03 There has been a report that a 0x0c03 was installed straight (other readers so far have produced images that are 90 degrees rotated). There is no way for the dirver to know how a device is installed, so for now just make an exception. --- libfprint/drivers/elan.c | 60 ++++++++++++++++++++++++++-------------- libfprint/drivers/elan.h | 8 ++++-- 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/libfprint/drivers/elan.c b/libfprint/drivers/elan.c index 20e33ad..cc54fb7 100644 --- a/libfprint/drivers/elan.c +++ b/libfprint/drivers/elan.c @@ -77,7 +77,7 @@ struct elan_dev { unsigned short *background; unsigned char frame_width; unsigned char frame_height; - unsigned char raw_frame_width; + unsigned char raw_frame_height; int num_frames; GSList *frames; /* end state */ @@ -111,18 +111,33 @@ static void elan_save_frame(struct elan_dev *elandev, unsigned short *frame) { G_DEBUG_HERE(); - unsigned char raw_height = elandev->frame_width; - unsigned char raw_width = elandev->raw_frame_width; + /* so far 3 types of readers by sensor dimensions and orientation have been + * seen in the wild: + * 1. 144x64. Raw images are in portrait orientation while readers themselves + * are placed (e.g. built into a touchpad) in landscape orientation. These + * need to be rotated before assembling. + * 2. 96x96 rotated. Like the first type but square. Likewise, need to be + * rotated before assembling. + * 3. 96x96 normal. Square and need NOT be rotated. So far there's only been + * 1 report of a 0c03 of this type. Hopefully this type can be identified + * by device id (and manufacturers don't just install the readers as they + * please). + * we also discard stripes of 'frame_margin' from bottom and top because + * assembling works bad for tall frames */ - /* Raw images are vertical and perpendicular to swipe direction of a - * normalized image, which means we need to make them horizontal before - * assembling. We also discard stripes of 'frame_margin' along raw - * height. */ - unsigned char frame_margin = (raw_width - elandev->frame_height) / 2; - for (int y = 0; y < raw_height; y++) - for (int x = frame_margin; x < raw_width - frame_margin; x++) { - int frame_idx = y + (x - frame_margin) * raw_height; - int raw_idx = x + y * raw_width; + unsigned char frame_width = elandev->frame_width; + unsigned char frame_height = elandev->frame_height; + unsigned char raw_height = elandev->raw_frame_height; + unsigned char frame_margin = (raw_height - elandev->frame_height) / 2; + int frame_idx, raw_idx; + + for (int y = 0; y < frame_height; y++) + for (int x = 0; x < frame_width; x++) { + if (elandev->dev_type & ELAN_NOT_ROTATED) + raw_idx = x + (y + frame_margin) * frame_width; + else + raw_idx = frame_margin + y + x * raw_height; + frame_idx = x + y * frame_width; frame[frame_idx] = ((unsigned short *)elandev->last_read)[raw_idx]; } @@ -344,7 +359,7 @@ static void elan_cmd_read(struct fpi_ssm *ssm) if (elandev->cmd->cmd == get_image_cmd.cmd) /* raw data has 2-byte "pixels" and the frame is vertical */ response_len = - elandev->raw_frame_width * elandev->frame_width * 2; + elandev->raw_frame_height * elandev->frame_width * 2; struct libusb_transfer *transfer = libusb_alloc_transfer(0); if (!transfer) { @@ -701,15 +716,20 @@ static void activate_run_state(struct fpi_ssm *ssm) elan_run_cmd(ssm, &get_sensor_dim_cmd, ELAN_CMD_TIMEOUT); break; case ACTIVATE_SET_SENSOR_DIM: - elandev->frame_width = elandev->last_read[2]; - elandev->raw_frame_width = elandev->last_read[0]; - if (elandev->raw_frame_width < ELAN_MAX_FRAME_HEIGHT) - elandev->frame_height = elandev->raw_frame_width; - else + /* see elan_save_frame for details */ + if (elandev->dev_type & ELAN_NOT_ROTATED) { + elandev->frame_width = elandev->last_read[0]; + elandev->frame_height = elandev->raw_frame_height = + elandev->last_read[2]; + } else { + elandev->frame_width = elandev->last_read[2]; + elandev->frame_height = elandev->raw_frame_height = + elandev->last_read[0]; + } + if (elandev->frame_height > ELAN_MAX_FRAME_HEIGHT) elandev->frame_height = ELAN_MAX_FRAME_HEIGHT; - /* see elan_save_frame for why it's width x raw_width */ fp_dbg("sensor dimensions, WxH: %dx%d", elandev->frame_width, - elandev->raw_frame_width); + elandev->raw_frame_height); fpi_ssm_next_state(ssm); break; case ACTIVATE_CMD_1: diff --git a/libfprint/drivers/elan.h b/libfprint/drivers/elan.h index 9002a0e..7c4e81a 100644 --- a/libfprint/drivers/elan.h +++ b/libfprint/drivers/elan.h @@ -30,7 +30,11 @@ #define ELAN_ALL_DEV 0 /* devices with quirks */ -#define ELAN_0907 1 +#define ELAN_0907 (1 << 0) +#define ELAN_0C03 (1 << 1) + +/* devices which don't require frame rotation before assembling */ +#define ELAN_NOT_ROTATED ELAN_0C03 /* min FW version that supports calibration */ #define ELAN_MIN_CALIBRATION_FW 0x0138 @@ -155,7 +159,7 @@ static const struct usb_id elan_id_table[] = { {.vendor = ELAN_VEND_ID,.product = 0x0907,.driver_data = ELAN_0907}, {.vendor = ELAN_VEND_ID,.product = 0x0c01,.driver_data = ELAN_ALL_DEV}, {.vendor = ELAN_VEND_ID,.product = 0x0c02,.driver_data = ELAN_ALL_DEV}, - {.vendor = ELAN_VEND_ID,.product = 0x0c03,.driver_data = ELAN_ALL_DEV}, + {.vendor = ELAN_VEND_ID,.product = 0x0c03,.driver_data = ELAN_0C03}, {.vendor = ELAN_VEND_ID,.product = 0x0c04,.driver_data = ELAN_ALL_DEV}, {.vendor = ELAN_VEND_ID,.product = 0x0c05,.driver_data = ELAN_ALL_DEV}, {.vendor = ELAN_VEND_ID,.product = 0x0c06,.driver_data = ELAN_ALL_DEV}, From 4ff97e7cbd737e207c5677f38627e516659d8b4e Mon Sep 17 00:00:00 2001 From: Igor Filatov Date: Thu, 1 Mar 2018 21:51:38 +0200 Subject: [PATCH 8/9] elan: Increase bz3 threshold to 24 Based on experience. Values more than 24 seem to work just after enrollment, but it becomes very hard to verify in a day or so. --- libfprint/drivers/elan.c | 52 ++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/libfprint/drivers/elan.c b/libfprint/drivers/elan.c index cc54fb7..97f4c34 100644 --- a/libfprint/drivers/elan.c +++ b/libfprint/drivers/elan.c @@ -41,6 +41,14 @@ #include "drivers_api.h" #include "elan.h" +#define dbg_buf(buf, len) \ + if (len == 1) \ + fp_dbg("%02hx", buf[0]); \ + else if (len == 2) \ + fp_dbg("%04hx", buf[0] << 8 | buf[1]); \ + else if (len > 2) \ + fp_dbg("%04hx... (%d bytes)", buf[0] << 8 | buf[1], len) + unsigned char elan_get_pixel(struct fpi_frame_asmbl_ctx *ctx, struct fpi_frame *frame, unsigned int x, unsigned int y) @@ -189,22 +197,32 @@ static void elan_save_background(struct elan_dev *elandev) * \ * ======== 0 \___> ======== 0 */ -static void elan_save_img_frame(struct elan_dev *elandev) +static int elan_save_img_frame(struct elan_dev *elandev) { G_DEBUG_HERE(); unsigned int frame_size = elandev->frame_width * elandev->frame_height; unsigned short *frame = g_malloc(frame_size * sizeof(short)); elan_save_frame(elandev, frame); + unsigned int sum = 0; - for (int i = 0; i < frame_size; i++) + for (int i = 0; i < frame_size; i++) { if (elandev->background[i] > frame[i]) frame[i] = 0; else frame[i] -= elandev->background[i]; + sum += frame[i]; + } + + if (sum == 0) { + fp_dbg + ("frame darker than background; finger present during calibration?"); + return -1; + } elandev->frames = g_slist_prepend(elandev->frames, frame); elandev->num_frames += 1; + return 0; } static void elan_process_frame_linear(unsigned short *raw_frame, @@ -280,6 +298,7 @@ static void elan_submit_image(struct fp_img_dev *dev) for (int i = 0; i < ELAN_SKIP_LAST_FRAMES; i++) elandev->frames = g_slist_next(elandev->frames); + elandev->num_frames -= ELAN_SKIP_LAST_FRAMES; assembling_ctx.frame_width = elandev->frame_width; assembling_ctx.frame_height = elandev->frame_height; @@ -287,10 +306,8 @@ static void elan_submit_image(struct fp_img_dev *dev) g_slist_foreach(elandev->frames, (GFunc) elandev->process_frame, &frames); fpi_do_movement_estimation(&assembling_ctx, frames, - elandev->num_frames - ELAN_SKIP_LAST_FRAMES); - img = - fpi_assemble_frames(&assembling_ctx, frames, - elandev->num_frames - ELAN_SKIP_LAST_FRAMES); + elandev->num_frames); + img = fpi_assemble_frames(&assembling_ctx, frames, elandev->num_frames); img->flags |= FP_IMG_PARTIAL; fpi_imgdev_image_captured(dev, img); @@ -319,12 +336,15 @@ static void elan_cmd_cb(struct libusb_transfer *transfer) transfer->length, transfer->actual_length); elan_dev_reset(elandev); fpi_ssm_mark_aborted(ssm, -EPROTO); - } else if (transfer->endpoint & LIBUSB_ENDPOINT_IN) + } else if (transfer->endpoint & LIBUSB_ENDPOINT_IN) { /* just finished receiving */ + dbg_buf(elandev->last_read, transfer->actual_length); elan_cmd_done(ssm); - else + } else { /* just finished sending */ + G_DEBUG_HERE(); elan_cmd_read(ssm); + } break; case LIBUSB_TRANSFER_CANCELLED: fp_dbg("transfer cancelled"); @@ -466,6 +486,7 @@ static void capture_run_state(struct fpi_ssm *ssm) { struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm); struct elan_dev *elandev = fpi_imgdev_get_user_data(dev); + int r; switch (fpi_ssm_get_cur_state(ssm)) { case CAPTURE_LED_ON: @@ -485,8 +506,10 @@ static void capture_run_state(struct fpi_ssm *ssm) fpi_ssm_mark_aborted(ssm, -EBADMSG); break; case CAPTURE_CHECK_ENOUGH_FRAMES: - elan_save_img_frame(elandev); - if (elandev->num_frames < ELAN_MAX_FRAMES) { + r = elan_save_img_frame(elandev); + if (r < 0) + fpi_ssm_mark_aborted(ssm, r); + else if (elandev->num_frames < ELAN_MAX_FRAMES) { /* quickly stop if finger is removed */ elandev->cmd_timeout = ELAN_FINGER_TIMEOUT; fpi_ssm_jump_to_state(ssm, CAPTURE_WAIT_FINGER); @@ -533,8 +556,11 @@ static void capture_complete(struct fpi_ssm *ssm) /* ...but only on enroll! If verify or identify fails because of short swipe, * we need to do it manually. It feels like libfprint or the application - * should know better if they want to retry, but they don't. */ - if (elandev->dev_state != IMGDEV_STATE_INACTIVE) + * should know better if they want to retry, but they don't. Unless we've + * been asked to deactivate, try to re-enter the capture loop. Since state + * change is async, there's still a chance to be deactivated by another + * pending event. */ + if (elandev->dev_state_next != IMGDEV_STATE_INACTIVE) dev_change_state(dev, IMGDEV_STATE_AWAIT_FINGER_ON); fpi_ssm_free(ssm); @@ -905,7 +931,7 @@ struct fp_img_driver elan_driver = { }, .flags = 0, - .bz3_threshold = 22, + .bz3_threshold = 24, .open = dev_init, .close = dev_deinit, From 9793d60c5af2f2d8fb6b4af835be433bb9d94d2f Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Fri, 10 Aug 2018 13:45:06 +0200 Subject: [PATCH 9/9] elan: Remove unused string.h include --- libfprint/drivers/elan.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libfprint/drivers/elan.h b/libfprint/drivers/elan.h index 7c4e81a..8a321b4 100644 --- a/libfprint/drivers/elan.h +++ b/libfprint/drivers/elan.h @@ -21,7 +21,6 @@ #ifndef __ELAN_H #define __ELAN_H -#include #include #define ELAN_VEND_ID 0x04f3