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