Commit 15d32a73 authored by Igor Filatov's avatar Igor Filatov

Update Elan driver

parent 21504c06
Pipeline #1767 passed with stage
in 2 minutes and 35 seconds
......@@ -2,6 +2,7 @@
* Elan driver for libfprint
*
* Copyright (C) 2017 Igor Filatov <ia.filatov@gmail.com>
* Copyright (C) 2018 Sébastien Béchet <sebastien.bechet@osinix.com >
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
......@@ -18,11 +19,36 @@
* 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"
#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)
......@@ -38,34 +64,49 @@ static struct fpi_frame_asmbl_ctx assembling_ctx = {
};
struct elan_dev {
gboolean deactivating;
const struct elan_cmd *cmds;
size_t cmds_len;
int cmd_idx;
/* device config */
unsigned short dev_type;
unsigned short fw_ver;
void (*process_frame) (unsigned short *raw_frame, GSList ** frames);
/* end device config */
/* commands */
const struct elan_cmd *cmd;
int cmd_timeout;
struct libusb_transfer *cur_transfer;
/* end commands */
/* state */
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;
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 */
};
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->cmds = NULL;
elandev->cmd_idx = 0;
elandev->cmd = NULL;
elandev->cmd_timeout = ELAN_CMD_TIMEOUT;
elandev->calib_status = 0;
g_free(elandev->last_read);
elandev->last_read = NULL;
......@@ -74,44 +115,126 @@ 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);
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 stirpes of ELAN_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++) {
int frame_idx =
y + (x - ELAN_FRAME_MARGIN) * raw_height;
int raw_idx = x + y * raw_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 */
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];
}
}
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 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++) {
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 that background; finger present during calibration?");
return -1;
}
elandev->frames = g_slist_prepend(elandev->frames, frame);
elandev->num_frames += 1;
return 0;
}
/* 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)
{
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));
G_DEBUG_HERE();
unsigned short min = 0xffff, max = 0;
for (int i = 0; i < frame_size; i++) {
if (raw_frame[i] < min)
......@@ -123,12 +246,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;
}
......@@ -145,15 +298,16 @@ 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;
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);
elandev->num_frames);
img = fpi_assemble_frames(&assembling_ctx, frames, elandev->num_frames);
img->flags |= FP_IMG_PARTIAL;
fpi_imgdev_image_captured(dev, img);
......@@ -161,16 +315,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)
......@@ -179,34 +325,32 @@ static void elan_cmd_cb(struct libusb_transfer *transfer)
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->cur_transfer = NULL;
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)
} 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 */
if (elandev->cmds[elandev->cmd_idx].response_len)
elan_cmd_read(ssm);
else
elan_cmd_done(ssm);
G_DEBUG_HERE();
elan_cmd_read(ssm);
}
break;
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");
// elan_dev_reset(elandev);
fpi_ssm_mark_aborted(ssm, -ETIMEDOUT);
break;
default:
......@@ -218,16 +362,22 @@ static void elan_cmd_cb(struct libusb_transfer *transfer)
static void elan_cmd_read(struct fpi_ssm *ssm)
{
G_DEBUG_HERE();
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->cmd->response_len == ELAN_CMD_SKIP_READ) {
fp_dbg("skipping read, not expecting anything");
elan_cmd_done(ssm);
return;
}
if (elandev->cmds[elandev->cmd_idx].cmd == read_cmds[0].cmd)
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) {
......@@ -240,21 +390,32 @@ 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->last_read, response_len, elan_cmd_cb,
ssm, elandev->cmd_timeout);
elandev->cmd->response_in, elandev->last_read,
response_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_next_cmd(struct fpi_ssm *ssm)
static void elan_run_cmd(struct fpi_ssm *ssm, const struct elan_cmd *cmd,
int cmd_timeout)
{
dbg_buf(cmd->cmd, 2);
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 != ELAN_ALL_DEV && !(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) {
......@@ -263,32 +424,13 @@ 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,
elandev->cmd_timeout);
libusb_fill_bulk_transfer(transfer, fpi_imgdev_get_usb_dev(dev),
ELAN_EP_CMD_OUT, 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,18 +438,21 @@ 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;
}
}
static void deactivate_complete(struct fpi_ssm *ssm)
{
G_DEBUG_HERE();
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
fpi_imgdev_deactivate_complete(dev);
......@@ -315,255 +460,346 @@ static void deactivate_complete(struct fpi_ssm *ssm)
static void elan_deactivate(struct fp_img_dev *dev)
{
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
elan_dev_reset(elandev);
struct fpi_ssm *ssm = fpi_ssm_new(fpi_imgdev_get_dev(dev), elan_deactivate_run_state,
DEACTIVATE_NUM_STATES);
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);
int r;
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);
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_SAVE_FRAME:
elan_save_frame(dev);
if (elandev->num_frames < ELAN_MAX_FRAMES) {
case CAPTURE_CHECK_ENOUGH_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);
} else {
fpi_ssm_next_state(ssm);
}
break;
}
}
static void elan_capture_async(void *data)
{
elan_capture((struct fp_img_dev *)data);
}
static void capture_complete(struct fpi_ssm *ssm)
{
G_DEBUG_HERE();
struct fp_img_dev *dev = fpi_ssm_get_user_data(ssm);
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
int error = fpi_ssm_get_error(ssm);
int state = fpi_ssm_get_cur_state(ssm);
int num_frames = elandev->num_frames;
G_DEBUG_HERE();
if (elandev->deactivating)
elan_deactivate(dev);
if (error == -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 (!error || (error == -ETIMEDOUT && state == CAPTURE_WAIT_FINGER))
if (num_frames >= ELAN_MIN_FRAMES)
elan_submit_image(dev);
fpi_imgdev_report_finger_status(dev, FALSE);
} else
fpi_imgdev_session_error(dev,
FP_VERIFY_RETRY_TOO_SHORT);
else {
fp_dbg("swipe too short: want >= %d frames, got %d",
ELAN_MIN_FRAMES, num_frames);
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, error);
/* 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);
/* 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);
/* ...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. 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);
}
static void elan_capture(struct fp_img_dev *dev)
{
struct elan_dev *elandev = fpi_imgdev_get_user_data(dev);
G_DEBUG_HERE();
struct elan_dev *elandev = fpi_imgdev_get_user_data(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 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();
unsigned short calib_mean =
elandev->last_read[0] * 0xff + elandev->last_read[1];
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 += elandev->background[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_GET_BACKGROUND,
CALIBRATE_SAVE_BACKGROUND,
CALIBRATE_GET_MEAN,
CALIBRATE_CHECK_NEEDED,
CALIBRATE_GET_STATUS,
CALIBRATE_CHECK_STATUS,
CALIBRATE_REPEAT_STATUS,
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);
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_GET_BACKGROUND:
elan_run_cmd