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, };