upeksonly: make 147e:1002 enrollment work

This commit is contained in:
Nikolay Metchev 2026-05-01 08:50:24 +00:00
parent 17a13fec10
commit fafa3718da
2 changed files with 794 additions and 49 deletions

View file

@ -31,7 +31,15 @@
#define CTRL_TIMEOUT 1000
#define NUM_BULK_TRANSFERS 24
#define MAX_ROWS 2048
#define MAX_ROWS_1002 24000
#define MIN_ROWS 64
#define DETAIL_THRESHOLD_SQ_1002 800.0
#define MOTION_THRESHOLD_1002 18.0
#define DETAIL_TRIGGER_ROWS_1002 3
#define MIN_DETECTION_ROW_1002 1000
#define INITIAL_FINGER_IRQ_IGNORE_MS_1002 250
#define MAX_EARLY_FINGER_IRQS_1002 3
#define AWAIT_FINGER_SETTLE_DELAY_MS_1002 100
#define BLANK_THRESHOLD 250
#define FINGER_PRESENT_THRESHOLD 32
@ -61,6 +69,18 @@ enum sonly_fs {
FINGER_REMOVED,
};
enum loopsm_states {
LOOPSM_RUN_AWFSM,
LOOPSM_DELAY_AWAIT_FINGER,
LOOPSM_STATUS_READ,
LOOPSM_AWAIT_FINGER,
LOOPSM_RUN_CAPSM,
LOOPSM_CAPTURE,
LOOPSM_RUN_DEINITSM,
LOOPSM_FINAL,
LOOPSM_NUM_STATES,
};
struct _FpiDeviceUpeksonly
{
FpImageDevice parent;
@ -76,6 +96,7 @@ struct _FpiDeviceUpeksonly
/* Do we really need multiple concurrent transfers? */
GCancellable *img_cancellable;
GCancellable *await_cancellable;
GPtrArray *img_transfers;
int num_flying;
@ -89,6 +110,14 @@ struct _FpiDeviceUpeksonly
int num_nonblank;
enum sonly_fs finger_state;
int last_seqnum;
guint32 marker_window;
int marker_skip;
int finger_start_row_1002;
unsigned detail_rows_1002;
gboolean have_avg_row_1002;
guchar avg_row_1002[IMG_CROP_WIDTH_1002];
gint64 await_intr_start_us;
unsigned early_finger_irq_count_1002;
enum sonly_kill_transfers_action killing_transfers;
GError *kill_error;
@ -150,6 +179,43 @@ upeksonly_get_pixel (struct fpi_line_asmbl_ctx *ctx,
return buf[offset];
}
static int
upeksonly_get_deviation_simple (struct fpi_line_asmbl_ctx *ctx,
GSList *line1,
GSList *line2)
{
unsigned char *buf1 = line1->data, *buf2 = line2->data;
int res = 0, mean = 0;
g_assert (ctx->line_width > 0);
for (int i = 0; i < ctx->line_width; i++)
mean += (int) buf1[i] + (int) buf2[i];
mean /= ctx->line_width;
for (int i = 0; i < ctx->line_width; i++)
{
int dev = (int) buf1[i] + (int) buf2[i] - mean;
res += dev * dev;
}
return res / ctx->line_width;
}
static unsigned char
upeksonly_get_pixel_simple (struct fpi_line_asmbl_ctx *ctx,
GSList *row,
unsigned x)
{
unsigned char *buf = row->data;
if (x >= ctx->line_width)
return 0;
return buf[x];
}
/***** IMAGE PROCESSING *****/
static void
@ -197,7 +263,9 @@ cancel_img_transfers (FpImageDevice *dev)
static gboolean
is_capturing (FpiDeviceUpeksonly *sdev)
{
return sdev->num_rows < MAX_ROWS && (sdev->finger_state != FINGER_REMOVED);
guint max_rows = sdev->dev_model == UPEKSONLY_1002 ? MAX_ROWS_1002 : MAX_ROWS;
return sdev->num_rows < max_rows && (sdev->finger_state != FINGER_REMOVED);
}
static void
@ -217,7 +285,64 @@ handoff_img (FpImageDevice *dev)
self->rows = g_slist_reverse (self->rows);
fp_dbg ("%u rows", self->num_rows);
img = fpi_assemble_lines (&self->assembling_ctx, self->rows, self->num_rows);
if (self->dev_model == UPEKSONLY_1002)
{
guint crop_start = 0;
guint cropped_rows;
guint out_height;
guint row_count = g_slist_length (self->rows);
g_autofree guchar **rows = g_new (guchar *, row_count);
guint row_idx = 0;
if (row_count != self->num_rows)
{
fp_warn ("1002 row list length %u differs from row counter %u",
row_count,
self->num_rows);
self->num_rows = row_count;
}
if (self->finger_start_row_1002 >= 0)
crop_start = self->finger_start_row_1002 + IMG_CROP_OFFSET_1002;
else if (row_count > IMG_CROP_ROWS_1002)
crop_start = row_count - IMG_CROP_ROWS_1002;
if (crop_start >= row_count)
crop_start = 0;
cropped_rows = MIN (IMG_CROP_ROWS_1002, row_count - crop_start);
out_height = cropped_rows / IMG_HEIGHT_SCALE_1002;
for (GSList *row = self->rows; row; row = g_slist_next (row))
rows[row_idx++] = row->data;
img = fp_image_new (IMG_CROP_WIDTH_1002, out_height);
img->flags = FPI_IMAGE_COLORS_INVERTED;
for (guint y = 0; y < out_height; y++)
{
for (guint x = 0; x < IMG_CROP_WIDTH_1002; x++)
{
guint sum = 0;
for (guint i = 0; i < IMG_HEIGHT_SCALE_1002; i++)
{
guint src_y = crop_start + y * IMG_HEIGHT_SCALE_1002 + i;
guint src_x = IMG_CROP_X_1002 + x;
sum += rows[src_y][src_x];
}
img->data[y * IMG_CROP_WIDTH_1002 + x] = sum / IMG_HEIGHT_SCALE_1002;
}
}
}
else
{
img = fpi_assemble_lines (&self->assembling_ctx, self->rows, self->num_rows);
}
g_slist_free_full (self->rows, g_free);
self->rows = NULL;
@ -230,21 +355,116 @@ handoff_img (FpImageDevice *dev)
cancel_img_transfers (dev);
}
static gboolean
update_1002_finger_detection (FpiDeviceUpeksonly *self)
{
GSList *row;
double sum = 0.0;
double sq_sum = 0.0;
double motion = 0.0;
guchar avg_row[IMG_CROP_WIDTH_1002];
if (self->num_rows % IMG_HEIGHT_SCALE_1002 != 0)
return FALSE;
for (guint x = 0; x < IMG_CROP_WIDTH_1002; x++)
{
guint col_sum = 0;
row = self->rows;
for (guint i = 0; i < IMG_HEIGHT_SCALE_1002 && row; i++, row = g_slist_next (row))
col_sum += ((guchar *) row->data)[IMG_CROP_X_1002 + x];
avg_row[x] = col_sum / IMG_HEIGHT_SCALE_1002;
sum += avg_row[x];
sq_sum += avg_row[x] * avg_row[x];
if (self->have_avg_row_1002)
motion += ABS ((int) avg_row[x] - (int) self->avg_row_1002[x]);
}
if (self->have_avg_row_1002)
motion /= IMG_CROP_WIDTH_1002;
if (self->num_rows >= MIN_DETECTION_ROW_1002 &&
(sq_sum / IMG_CROP_WIDTH_1002) -
(sum / IMG_CROP_WIDTH_1002) * (sum / IMG_CROP_WIDTH_1002) > DETAIL_THRESHOLD_SQ_1002 &&
self->have_avg_row_1002 &&
motion > MOTION_THRESHOLD_1002)
{
self->detail_rows_1002++;
if (self->finger_start_row_1002 < 0 &&
self->detail_rows_1002 >= DETAIL_TRIGGER_ROWS_1002)
{
self->finger_start_row_1002 =
MAX (0, (int) self->num_rows -
(int) self->detail_rows_1002 * IMG_HEIGHT_SCALE_1002);
fp_dbg ("detected 1002 finger data at row %d", self->finger_start_row_1002);
}
}
else if (self->finger_start_row_1002 < 0)
{
self->detail_rows_1002 = 0;
}
memcpy (self->avg_row_1002, avg_row, sizeof (avg_row));
self->have_avg_row_1002 = TRUE;
return self->finger_start_row_1002 >= 0 &&
self->num_rows >= (guint) self->finger_start_row_1002 +
IMG_CROP_OFFSET_1002 +
IMG_CROP_ROWS_1002;
}
static void
row_complete (FpImageDevice *dev)
{
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY (dev);
int line_width = self->assembling_ctx.line_width;
self->rowbuf_offset = -1;
if (self->dev_model == UPEKSONLY_1002)
{
if (!is_capturing (self) || self->killing_transfers)
{
g_clear_pointer (&self->rowbuf, g_free);
return;
}
self->rows = g_slist_prepend (self->rows, self->rowbuf);
self->num_rows++;
self->rowbuf = NULL;
if (update_1002_finger_detection (self))
{
fp_dbg ("row limit met");
self->finger_state = FINGER_REMOVED;
handoff_img (dev);
}
else if (self->num_rows >= MAX_ROWS_1002)
{
fp_dbg ("1002 capture timed out without finger data");
self->finger_state = FINGER_REMOVED;
fpi_image_device_retry_scan (dev, FP_DEVICE_RETRY_TOO_SHORT);
fpi_image_device_report_finger_status (dev, FALSE);
self->killing_transfers = ITERATE_SSM;
self->kill_ssm = self->loopsm;
cancel_img_transfers (dev);
}
return;
}
if (self->num_rows > 0)
{
unsigned char *lastrow = self->rows->data;
int std_sq_dev, mean_sq_diff;
std_sq_dev = fpi_std_sq_dev (self->rowbuf, self->img_width);
std_sq_dev = fpi_std_sq_dev (self->rowbuf, line_width);
mean_sq_diff = fpi_mean_sq_diff_norm (lastrow, self->rowbuf,
self->img_width);
line_width);
switch (self->finger_state)
{
@ -498,6 +718,56 @@ handle_packet (FpImageDevice *dev, unsigned char *data)
start_new_row (self, data + diff, 62 - diff);
}
static void
handle_packet_1002 (FpImageDevice *dev, unsigned char *data)
{
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY (dev);
data += 2; /* skip sequence number */
for (int i = 0; i < 62; i++)
{
guint8 byte = data[i];
if (self->marker_skip > 0)
{
self->marker_skip--;
continue;
}
if (self->rowbuf)
{
if (self->rowbuf_offset < self->img_width)
self->rowbuf[self->rowbuf_offset++] = byte;
}
self->marker_window = ((self->marker_window << 8) | byte) & 0xffffff;
if (self->marker_window == 0xffa5a5)
{
if (self->rowbuf)
{
if (self->rowbuf_offset >= 3)
self->rowbuf_offset -= 3;
if (self->rowbuf_offset > IMG_WIDTH_1002 / 2)
{
row_complete (dev);
if (!is_capturing (self))
return;
}
else
g_clear_pointer (&self->rowbuf, g_free);
}
self->rowbuf = g_malloc0 (self->img_width);
self->rowbuf_offset = 0;
self->marker_skip = 5;
self->marker_window = 0;
}
}
}
static void
img_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
gpointer user_data, GError *error)
@ -508,11 +778,12 @@ img_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
self->num_flying--;
fp_dbg ("bulk cb: error=%s actual_length=%zu num_flying=%d capturing=%d",
error ? error->message : "none",
transfer->actual_length,
self->num_flying,
self->capturing);
if (self->dev_model != UPEKSONLY_1002)
fp_dbg ("bulk cb: error=%s actual_length=%zu num_flying=%d capturing=%d",
error ? error->message : "none",
transfer->actual_length,
self->num_flying,
self->capturing);
if (self->killing_transfers)
{
@ -556,15 +827,20 @@ img_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
{
if (!is_capturing (self))
return;
handle_packet (dev, transfer->buffer + i);
if (self->dev_model == UPEKSONLY_1002)
handle_packet_1002 (dev, transfer->buffer + i);
else
handle_packet (dev, transfer->buffer + i);
}
if (is_capturing (self))
{
fp_dbg ("re-submitting bulk transfer actual_length=%zu rows=%u finger_state=%d",
transfer->actual_length,
self->num_rows,
self->finger_state);
if (self->dev_model != UPEKSONLY_1002)
fp_dbg ("re-submitting bulk transfer actual_length=%zu rows=%u finger_state=%d",
transfer->actual_length,
self->num_rows,
self->finger_state);
fpi_usb_transfer_submit (fpi_usb_transfer_ref (transfer),
0,
self->img_cancellable,
@ -586,6 +862,17 @@ struct write_regs_data
size_t regs_written;
};
struct write_regs_data_data
{
FpDevice *dev;
FpiSsm *ssm;
const struct sonly_regwrite_data *regs;
size_t num_regs;
size_t regs_written;
};
static void sm_await_intr (FpiSsm *ssm, FpImageDevice *dev);
static void
write_regs_finished (struct write_regs_data *wrdata, GError *error)
{
@ -663,6 +950,161 @@ sm_write_regs (FpiSsm *ssm,
write_regs_iterate (wrdata);
}
static void write_regs_data_iterate (struct write_regs_data_data *wrdata);
static void write_regs_data_finished (struct write_regs_data_data *wrdata, GError *error);
static void
write_regs_data_readback_cb (FpiUsbTransfer *transfer, FpDevice *device,
gpointer user_data, GError *error)
{
struct write_regs_data_data *wrdata = user_data;
if (error)
{
write_regs_data_finished (wrdata, error);
return;
}
fp_dbg ("read %02x result = %02x %02x %02x %02x %02x %02x %02x %02x",
transfer->endpoint & 0xff,
transfer->buffer[0], transfer->buffer[1], transfer->buffer[2], transfer->buffer[3],
transfer->buffer[4], transfer->buffer[5], transfer->buffer[6], transfer->buffer[7]);
wrdata->regs_written++;
write_regs_data_iterate (wrdata);
}
static void
write_regs_data_finished (struct write_regs_data_data *wrdata, GError *error)
{
if (!error)
fpi_ssm_next_state (wrdata->ssm);
else
fpi_ssm_mark_failed (wrdata->ssm, error);
g_free (wrdata);
}
static void
write_regs_data_cb (FpiUsbTransfer *transfer, FpDevice *device,
gpointer user_data, GError *error)
{
struct write_regs_data_data *wrdata = user_data;
const struct sonly_regwrite_data *regwrite;
if (error)
{
write_regs_data_finished (wrdata, error);
return;
}
regwrite = &wrdata->regs[wrdata->regs_written];
if (regwrite->reg == 0x47 &&
regwrite->len == 1 &&
(regwrite->value[0] == 0x00 || regwrite->value[0] == 0x04))
{
FpiUsbTransfer *read_transfer = fpi_usb_transfer_new (wrdata->dev);
fp_dbg ("read 58 len=8");
fpi_usb_transfer_fill_control (read_transfer,
G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST,
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
G_USB_DEVICE_RECIPIENT_DEVICE,
0x0c,
0,
0x58,
8);
read_transfer->short_is_error = TRUE;
read_transfer->ssm = wrdata->ssm;
read_transfer->endpoint = 0x58;
fpi_usb_transfer_submit (read_transfer,
CTRL_TIMEOUT,
NULL,
write_regs_data_readback_cb,
wrdata);
return;
}
wrdata->regs_written++;
write_regs_data_iterate (wrdata);
}
static void
write_regs_data_iterate (struct write_regs_data_data *wrdata)
{
FpiUsbTransfer *transfer;
const struct sonly_regwrite_data *regwrite;
if (wrdata->regs_written >= wrdata->num_regs)
{
write_regs_data_finished (wrdata, NULL);
return;
}
regwrite = &wrdata->regs[wrdata->regs_written];
fp_dbg ("set %02x len=%u", regwrite->reg, regwrite->len);
if (regwrite->len == 0)
{
guint8 read_len = regwrite->value[0] ? regwrite->value[0] : 8;
fp_dbg ("read %02x len=%u", regwrite->reg, read_len);
transfer = fpi_usb_transfer_new (wrdata->dev);
fpi_usb_transfer_fill_control (transfer,
G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST,
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
G_USB_DEVICE_RECIPIENT_DEVICE,
0x0c,
0,
regwrite->reg,
read_len);
transfer->short_is_error = TRUE;
transfer->ssm = wrdata->ssm;
transfer->endpoint = regwrite->reg;
fpi_usb_transfer_submit (transfer,
CTRL_TIMEOUT,
NULL,
write_regs_data_readback_cb,
wrdata);
return;
}
transfer = fpi_usb_transfer_new (wrdata->dev);
fpi_usb_transfer_fill_control (transfer,
G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE,
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
G_USB_DEVICE_RECIPIENT_DEVICE,
0x0c,
0,
regwrite->reg,
regwrite->len);
transfer->short_is_error = TRUE;
transfer->ssm = wrdata->ssm;
memcpy (transfer->buffer, regwrite->value, regwrite->len);
fpi_usb_transfer_submit (transfer, CTRL_TIMEOUT, NULL, write_regs_data_cb, wrdata);
}
static void
sm_write_regs_data (FpiSsm *ssm,
FpDevice *dev,
const struct sonly_regwrite_data *regs,
size_t num_regs)
{
struct write_regs_data_data *wrdata = g_malloc (sizeof (*wrdata));
wrdata->ssm = ssm;
wrdata->regs = regs;
wrdata->num_regs = num_regs;
wrdata->regs_written = 0;
wrdata->dev = dev;
write_regs_data_iterate (wrdata);
}
static void
sm_write_reg (FpiSsm *ssm,
FpImageDevice *dev,
@ -732,15 +1174,65 @@ sm_read_reg (FpiSsm *ssm,
NULL);
}
static void
sm_read_status_cb (FpiUsbTransfer *transfer, FpDevice *device,
gpointer user_data, GError *error)
{
if (error)
{
fpi_ssm_mark_failed (transfer->ssm, error);
return;
}
fp_dbg ("read status result = %02x %02x %02x %02x %02x %02x %02x %02x",
transfer->buffer[0], transfer->buffer[1], transfer->buffer[2], transfer->buffer[3],
transfer->buffer[4], transfer->buffer[5], transfer->buffer[6], transfer->buffer[7]);
fpi_ssm_next_state (transfer->ssm);
}
static void
sm_read_status (FpiSsm *ssm,
FpImageDevice *dev)
{
FpiUsbTransfer *transfer = fpi_usb_transfer_new (FP_DEVICE (dev));
fp_dbg ("read status");
fpi_usb_transfer_fill_control (transfer,
G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST,
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
G_USB_DEVICE_RECIPIENT_DEVICE,
0x04,
0,
0,
8);
transfer->ssm = ssm;
transfer->short_is_error = TRUE;
fpi_usb_transfer_submit (transfer,
CTRL_TIMEOUT,
NULL,
sm_read_status_cb,
NULL);
}
static void
sm_await_intr_cb (FpiUsbTransfer *transfer, FpDevice *device,
gpointer user_data, GError *error)
{
FpImageDevice *dev = FP_IMAGE_DEVICE (device);
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY (dev);
gboolean finger_detected = TRUE;
g_clear_object (&self->await_cancellable);
if (error)
{
if (self->deactivating &&
g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
fpi_ssm_mark_completed (transfer->ssm);
return;
}
fpi_ssm_mark_failed (transfer->ssm, error);
return;
}
@ -749,6 +1241,42 @@ sm_await_intr_cb (FpiUsbTransfer *transfer, FpDevice *device,
transfer->buffer[0], transfer->buffer[1],
transfer->buffer[2], transfer->buffer[3]);
if (self->dev_model == UPEKSONLY_1002)
{
gint64 elapsed_ms = (g_get_monotonic_time () - self->await_intr_start_us) / 1000;
/* Windows keeps polling 0x83 with 12 12 12 12 while idle and only
* switches to 01 00 00 00 when a finger is actually detected. */
finger_detected = transfer->buffer[0] == 0x01 &&
transfer->buffer[1] == 0x00 &&
transfer->buffer[2] == 0x00 &&
transfer->buffer[3] == 0x00;
if (finger_detected && elapsed_ms < INITIAL_FINGER_IRQ_IGNORE_MS_1002)
{
self->early_finger_irq_count_1002++;
if (self->early_finger_irq_count_1002 < MAX_EARLY_FINGER_IRQS_1002)
{
fp_dbg ("1002 early finger interrupt after %" G_GINT64_FORMAT " ms, restart wait setup",
elapsed_ms);
fpi_ssm_jump_to_state_delayed (transfer->ssm, LOOPSM_RUN_AWFSM, 200);
return;
}
fp_dbg ("1002 accepting repeated early finger interrupt after %u attempts",
self->early_finger_irq_count_1002);
}
if (!finger_detected)
{
fp_dbg ("1002 idle interrupt, continue waiting");
sm_await_intr (transfer->ssm, dev);
return;
}
}
self->early_finger_irq_count_1002 = 0;
self->finger_state = FINGER_DETECTED;
fpi_image_device_report_finger_status (dev, TRUE);
fpi_ssm_next_state (transfer->ssm);
@ -759,9 +1287,14 @@ sm_await_intr (FpiSsm *ssm,
FpImageDevice *dev)
{
FpiUsbTransfer *transfer = fpi_usb_transfer_new (FP_DEVICE (dev));
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY (dev);
G_DEBUG_HERE ();
self->await_intr_start_us = g_get_monotonic_time ();
g_clear_object (&self->await_cancellable);
self->await_cancellable = g_cancellable_new ();
fpi_usb_transfer_fill_interrupt (transfer, 0x83, 4);
transfer->short_is_error = TRUE;
transfer->ssm = ssm;
@ -769,7 +1302,7 @@ sm_await_intr (FpiSsm *ssm,
/* NOTE: This was changed to be cancellable with the version 2 port! */
fpi_usb_transfer_submit (transfer,
0,
fpi_device_get_cancellable (FP_DEVICE (dev)),
self->await_cancellable,
sm_await_intr_cb,
NULL);
}
@ -860,7 +1393,10 @@ awfsm_1000_run_state (FpiSsm *ssm, FpDevice *_dev)
switch (fpi_ssm_get_cur_state (ssm))
{
case AWFSM_1000_WRITEV_1:
sm_write_regs (ssm, _dev, awfsm_1000_writev_1, G_N_ELEMENTS (awfsm_1000_writev_1));
if (FPI_DEVICE_UPEKSONLY (_dev)->dev_model == UPEKSONLY_1002)
fpi_ssm_next_state (ssm);
else
sm_write_regs (ssm, _dev, awfsm_1000_writev_1, G_N_ELEMENTS (awfsm_1000_writev_1));
break;
case AWFSM_1000_WRITEV_2:
@ -892,8 +1428,9 @@ enum capsm_1000_states {
enum capsm_1002_states {
CAPSM_1002_INIT,
CAPSM_1002_WRITEV,
CAPSM_1002_STREAM_SETUP,
CAPSM_1002_FIRE_BULK,
CAPSM_1002_WRITEV,
CAPSM_1002_NUM_STATES,
};
@ -933,6 +1470,14 @@ capsm_fire_bulk (FpiSsm *ssm,
fpi_ssm_next_state (ssm);
}
static void
clear_capture_rows (FpiDeviceUpeksonly *self)
{
g_clear_pointer (&self->rowbuf, g_free);
g_slist_free_full (self->rows, g_free);
self->rows = NULL;
}
static void
capsm_2016_run_state (FpiSsm *ssm, FpDevice *_dev)
{
@ -942,6 +1487,7 @@ capsm_2016_run_state (FpiSsm *ssm, FpDevice *_dev)
switch (fpi_ssm_get_cur_state (ssm))
{
case CAPSM_2016_INIT:
clear_capture_rows (self);
self->rowbuf_offset = -1;
self->num_rows = 0;
self->wraparounds = -1;
@ -949,6 +1495,8 @@ capsm_2016_run_state (FpiSsm *ssm, FpDevice *_dev)
self->num_nonblank = 0;
self->finger_state = FINGER_DETECTED;
self->last_seqnum = 16383;
self->marker_window = 0;
self->marker_skip = 0;
self->killing_transfers = 0;
fpi_ssm_next_state (ssm);
break;
@ -979,6 +1527,7 @@ capsm_1000_run_state (FpiSsm *ssm, FpDevice *_dev)
switch (fpi_ssm_get_cur_state (ssm))
{
case CAPSM_1000_INIT:
clear_capture_rows (self);
self->rowbuf_offset = -1;
self->num_rows = 0;
self->wraparounds = -1;
@ -986,6 +1535,8 @@ capsm_1000_run_state (FpiSsm *ssm, FpDevice *_dev)
self->num_nonblank = 0;
self->finger_state = FINGER_DETECTED;
self->last_seqnum = 16383;
self->marker_window = 0;
self->marker_skip = 0;
self->killing_transfers = 0;
fpi_ssm_next_state (ssm);
break;
@ -1008,6 +1559,7 @@ capsm_1001_run_state (FpiSsm *ssm, FpDevice *_dev)
switch (fpi_ssm_get_cur_state (ssm))
{
case CAPSM_1001_INIT:
clear_capture_rows (self);
self->rowbuf_offset = -1;
self->num_rows = 0;
self->wraparounds = -1;
@ -1015,6 +1567,8 @@ capsm_1001_run_state (FpiSsm *ssm, FpDevice *_dev)
self->num_nonblank = 0;
self->finger_state = AWAIT_FINGER;
self->last_seqnum = 16383;
self->marker_window = 0;
self->marker_skip = 0;
self->killing_transfers = 0;
fpi_ssm_next_state (ssm);
break;
@ -1053,6 +1607,7 @@ capsm_1002_run_state (FpiSsm *ssm, FpDevice *_dev)
switch (fpi_ssm_get_cur_state (ssm))
{
case CAPSM_1002_INIT:
clear_capture_rows (self);
self->rowbuf_offset = -1;
self->num_rows = 0;
self->wraparounds = -1;
@ -1060,17 +1615,27 @@ capsm_1002_run_state (FpiSsm *ssm, FpDevice *_dev)
self->num_nonblank = 0;
self->finger_state = FINGER_DETECTED;
self->last_seqnum = 16383;
self->marker_window = 0;
self->marker_skip = 0;
self->finger_start_row_1002 = -1;
self->detail_rows_1002 = 0;
self->have_avg_row_1002 = FALSE;
self->early_finger_irq_count_1002 = 0;
self->killing_transfers = 0;
fpi_ssm_next_state (ssm);
break;
case CAPSM_1002_FIRE_BULK:
capsm_fire_bulk (ssm, _dev);
case CAPSM_1002_STREAM_SETUP:
sm_write_regs_data (ssm, _dev, capsm_1002_writev_data, G_N_ELEMENTS (capsm_1002_writev_data));
break;
case CAPSM_1002_WRITEV:
sm_write_regs (ssm, _dev, capsm_1002_writev, G_N_ELEMENTS (capsm_1002_writev));
break;
case CAPSM_1002_FIRE_BULK:
capsm_fire_bulk (ssm, _dev);
break;
}
}
@ -1196,7 +1761,7 @@ initsm_1000_run_state (FpiSsm *ssm, FpDevice *_dev)
{
case INITSM_1000_WRITEV_1:
if (FPI_DEVICE_UPEKSONLY (_dev)->dev_model == UPEKSONLY_1002)
sm_write_regs (ssm, _dev, initsm_1002_writev_1, G_N_ELEMENTS (initsm_1002_writev_1));
sm_write_regs_data (ssm, _dev, initsm_1002_writev_1, G_N_ELEMENTS (initsm_1002_writev_1));
else
sm_write_regs (ssm, _dev, initsm_1000_writev_1, G_N_ELEMENTS (initsm_1000_writev_1));
break;
@ -1232,16 +1797,6 @@ initsm_1001_run_state (FpiSsm *ssm, FpDevice *_dev)
/***** CAPTURE LOOP *****/
enum loopsm_states {
LOOPSM_RUN_AWFSM,
LOOPSM_AWAIT_FINGER,
LOOPSM_RUN_CAPSM,
LOOPSM_CAPTURE,
LOOPSM_RUN_DEINITSM,
LOOPSM_FINAL,
LOOPSM_NUM_STATES,
};
static void
loopsm_run_state (FpiSsm *ssm, FpDevice *_dev)
{
@ -1292,7 +1847,39 @@ loopsm_run_state (FpiSsm *ssm, FpDevice *_dev)
}
break;
case LOOPSM_DELAY_AWAIT_FINGER:
if (self->deactivating)
{
fpi_ssm_mark_completed (ssm);
break;
}
if (self->dev_model == UPEKSONLY_1002)
fpi_ssm_next_state_delayed (ssm, AWAIT_FINGER_SETTLE_DELAY_MS_1002);
else
fpi_ssm_next_state (ssm);
break;
case LOOPSM_STATUS_READ:
if (self->deactivating)
{
fpi_ssm_mark_completed (ssm);
break;
}
if (self->dev_model == UPEKSONLY_1002)
sm_read_status (ssm, dev);
else
fpi_ssm_next_state (ssm);
break;
case LOOPSM_AWAIT_FINGER:
if (self->deactivating)
{
fpi_ssm_mark_completed (ssm);
break;
}
switch (self->dev_model)
{
case UPEKSONLY_1001:
@ -1373,7 +1960,10 @@ loopsm_run_state (FpiSsm *ssm, FpDevice *_dev)
break;
case LOOPSM_FINAL:
fpi_ssm_jump_to_state (ssm, LOOPSM_RUN_AWFSM);
if (self->deactivating)
fpi_ssm_mark_completed (ssm);
else
fpi_ssm_jump_to_state (ssm, LOOPSM_RUN_AWFSM);
break;
}
@ -1388,6 +1978,7 @@ deactivate_done (FpImageDevice *dev, GError *error)
G_DEBUG_HERE ();
free_img_transfers (self);
g_clear_object (&self->await_cancellable);
g_free (self->rowbuf);
self->rowbuf = NULL;
@ -1404,7 +1995,16 @@ dev_deactivate (FpImageDevice *dev)
if (!self->capturing)
{
deactivate_done (dev, NULL);
if (self->loopsm)
{
self->deactivating = TRUE;
if (self->await_cancellable)
g_cancellable_cancel (self->await_cancellable);
}
else
{
deactivate_done (dev, NULL);
}
return;
}
@ -1421,6 +2021,8 @@ loopsm_complete (FpiSsm *ssm, FpDevice *_dev, GError *error)
FpiDeviceUpeksonly *self = FPI_DEVICE_UPEKSONLY (_dev);
self->loopsm = NULL;
if (self->deactivating)
{
deactivate_done (dev, error);
@ -1591,20 +2193,29 @@ dev_init (FpImageDevice *dev)
self->assembling_ctx.resolution = 8;
self->assembling_ctx.median_filter_size = 25;
self->assembling_ctx.max_search_offset = 30;
self->assembling_ctx.get_deviation = upeksonly_get_deviation2;
self->assembling_ctx.get_pixel = upeksonly_get_pixel;
self = FPI_DEVICE_UPEKSONLY (dev);
self->dev_model = (int) fpi_device_get_driver_data (FP_DEVICE (dev));
switch (self->dev_model)
{
case UPEKSONLY_1000:
case UPEKSONLY_1002:
self->assembling_ctx.get_deviation = upeksonly_get_deviation2;
self->assembling_ctx.get_pixel = upeksonly_get_pixel;
self->img_width = IMG_WIDTH_1000;
self->assembling_ctx.line_width = IMG_WIDTH_1000;
break;
case UPEKSONLY_1002:
self->assembling_ctx.get_deviation = upeksonly_get_deviation_simple;
self->assembling_ctx.get_pixel = upeksonly_get_pixel_simple;
self->img_width = IMG_WIDTH_1002;
self->assembling_ctx.line_width = IMG_WIDTH_1002;
fpi_image_device_set_bz3_threshold (dev, 7);
break;
case UPEKSONLY_1001:
self->assembling_ctx.get_deviation = upeksonly_get_deviation2;
self->assembling_ctx.get_pixel = upeksonly_get_pixel;
self->img_width = IMG_WIDTH_1001;
self->assembling_ctx.line_width = IMG_WIDTH_1001;
@ -1613,6 +2224,8 @@ dev_init (FpImageDevice *dev)
break;
case UPEKSONLY_2016:
self->assembling_ctx.get_deviation = upeksonly_get_deviation2;
self->assembling_ctx.get_pixel = upeksonly_get_pixel;
self->img_width = IMG_WIDTH_2016;
self->assembling_ctx.line_width = IMG_WIDTH_2016;
break;

View file

@ -25,6 +25,12 @@
#define IMG_WIDTH_2016 288
#define IMG_WIDTH_1000 288
#define IMG_WIDTH_1002 162
#define IMG_CROP_X_1002 18
#define IMG_CROP_WIDTH_1002 108
#define IMG_CROP_ROWS_1002 992
#define IMG_CROP_OFFSET_1002 0
#define IMG_HEIGHT_SCALE_1002 4
#define IMG_WIDTH_1001 216
struct sonly_regwrite
@ -33,6 +39,15 @@ struct sonly_regwrite
guint8 value;
};
struct sonly_regwrite_data
{
guint8 reg;
guint8 len;
guint8 value[8];
};
#define UPEKSONLY_READ(reg, len) { reg, 0, { len } }
/***** AWAIT FINGER *****/
static const struct sonly_regwrite awfsm_2016_writev_1[] = {
@ -55,6 +70,15 @@ static const struct sonly_regwrite awfsm_1000_writev_1[] = {
{ 0x10, 0x00 }, { 0x11, 0xbf },
};
static const struct sonly_regwrite awfsm_1002_writev_1[] = {
/* Windows sets up the 1002 wait path with this 3e/1a prelude before
* enabling the finger detector. */
{ 0x49, 0x08 },
{ 0x3e, 0x45 }, { 0x3e, 0x92 }, { 0x3e, 0xd9 }, { 0x3e, 0x3a },
{ 0x3e, 0x99 }, { 0x3e, 0x8c }, { 0x3e, 0xc6 }, { 0x3e, 0xb9 },
{ 0x1a, 0x01 },
};
static const struct sonly_regwrite awfsm_2016_writev_2[] = {
{ 0x01, 0xc6 }, { 0x0c, 0x13 }, { 0x0d, 0x0d }, { 0x0e, 0x0e },
{ 0x0f, 0x0d }, { 0x0b, 0x00 },
@ -66,7 +90,9 @@ static const struct sonly_regwrite awfsm_1000_writev_2[] = {
};
static const struct sonly_regwrite awfsm_1002_writev_2[] = {
/* Windows enables 147e:1002 finger detection with bit 0x01 clear here. */
/* Windows arms 147e:1002 wait mode with the 1a/0b/49 prelude followed by
* the actual detector-enable writes on 0x30/0x15. */
{ 0x1a, 0x03 }, { 0x0b, 0x00 }, { 0x49, 0x0c },
{ 0x30, 0xe1 }, { 0x15, 0x05 }, { 0x15, 0x85 },
};
@ -98,13 +124,49 @@ static const struct sonly_regwrite capsm_1000_writev[] = {
};
static const struct sonly_regwrite capsm_1002_writev[] = {
/* 147e:1002 appears close to the 1000 family for init/interrupts, but it
* does not start streaming image data with the plain 1000/2016 capture
* writes. This sequence matches the Windows driver immediately before bulk
* image packets start arriving on endpoint 0x81.
*/
{ 0x15, 0x25 }, { 0x30, 0xe0 }, { 0x09, 0x23 }, { 0x13, 0x75 },
{ 0x0b, 0x80 },
/* BSAPI submits bulk URBs before these capture-start writes. */
{ 0x09, 0x22 }, { 0x13, 0x75 }, { 0x0b, 0x80 },
};
static const struct sonly_regwrite_data capsm_1002_writev_data[] = {
/* Finger-detect mode changes security state; replay the final BSAPI
* key/tail immediately before the low-security streaming setup. */
{ 0x49, 1, { 0x08 } },
{ 0x3e, 1, { 0x15 } }, { 0x3e, 1, { 0x5c } },
{ 0x3e, 1, { 0x27 } }, { 0x3e, 1, { 0xf5 } },
{ 0x3e, 1, { 0xfb } }, { 0x3e, 1, { 0x82 } },
{ 0x3e, 1, { 0x25 } }, { 0x3e, 1, { 0x61 } },
{ 0x09, 1, { 0x2a } }, { 0x1a, 1, { 0x01 } },
UPEKSONLY_READ (0x0b, 8), { 0x0b, 1, { 0x00 } },
UPEKSONLY_READ (0x13, 8), { 0x13, 1, { 0x45 } },
{ 0x04, 1, { 0x00 } }, { 0x05, 1, { 0x00 } },
/* Replay the BSAPI low-security streaming setup after finger-detect mode. */
{ 0x7e, 1, { 0x0f } },
{ 0x6e, 1, { 0x20 } }, { 0x6e, 1, { 0x00 } },
{ 0x6f, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ 0x77, 7, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ 0x74, 8, { 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff } },
{ 0x7c, 1, { 0x07 } }, { 0x6e, 1, { 0x10 } },
{ 0x7e, 1, { 0x05 } }, { 0x6e, 1, { 0x1f } },
{ 0x7e, 1, { 0x0f } }, { 0x6e, 1, { 0x10 } },
{ 0x7e, 1, { 0x05 } }, { 0x1a, 1, { 0x03 } },
UPEKSONLY_READ (0x17, 8), UPEKSONLY_READ (0x0a, 8),
UPEKSONLY_READ (0x03, 8), UPEKSONLY_READ (0x00, 8),
UPEKSONLY_READ (0x01, 8), UPEKSONLY_READ (0x30, 8),
UPEKSONLY_READ (0x15, 8), UPEKSONLY_READ (0x12, 8),
UPEKSONLY_READ (0x25, 8), UPEKSONLY_READ (0x31, 8),
UPEKSONLY_READ (0x08, 8), UPEKSONLY_READ (0x07, 8),
UPEKSONLY_READ (0x24, 8), UPEKSONLY_READ (0x26, 8),
UPEKSONLY_READ (0x1d, 8),
{ 0x00, 2, { 0x53, 0xee } },
{ 0x03, 1, { 0x27 } },
{ 0x07, 8, { 0x00, 0x00, 0x2a, 0x20, 0x00, 0x0f, 0x09, 0x0a } },
{ 0x0f, 3, { 0x09, 0x00, 0x8f } },
{ 0x1d, 1, { 0x02 } },
{ 0x25, 4, { 0x10, 0x00, 0x8f, 0x23 } },
{ 0x31, 1, { 0x44 } },
{ 0x07, 1, { 0x10 } },
};
static const struct sonly_regwrite capsm_1001_writev_1[] = {
@ -206,14 +268,84 @@ static const struct sonly_regwrite initsm_1000_writev_1[] = {
{ 0x0b, 0x00 }, { 0x08, 0x00 }, /* Initialize capture control registers */
};
static const struct sonly_regwrite initsm_1002_writev_1[] = {
/* Captured from the Windows 147e:1002 driver before finger detection. */
{ 0x49, 0x08 },
{ 0x3e, 0x3d }, { 0x3e, 0x18 }, { 0x3e, 0x38 }, { 0x3e, 0x63 },
{ 0x3e, 0x9d }, { 0x3e, 0x5c }, { 0x3e, 0xb1 }, { 0x3e, 0x1f },
{ 0x1a, 0x01 }, { 0x1a, 0x03 }, { 0x0b, 0x00 }, { 0x49, 0x0c },
#define UPEKSONLY_1002_CAL_PASS \
{ 0x47, 1, { 0x02 } }, { 0x47, 1, { 0x02 } }, \
{ 0x47, 1, { 0x02 } }, { 0x47, 1, { 0x02 } }, \
{ 0x47, 1, { 0x02 } }, { 0x47, 1, { 0x02 } }, \
{ 0x47, 1, { 0x02 } }, { 0x47, 1, { 0x0a } }, \
{ 0x47, 1, { 0x00 } }, { 0x47, 1, { 0x04 } }, \
{ 0x47, 1, { 0x04 } }, { 0x47, 1, { 0x04 } }, \
{ 0x47, 1, { 0x04 } }, { 0x47, 1, { 0x04 } }, \
{ 0x47, 1, { 0x04 } }, { 0x47, 1, { 0x04 } }, \
{ 0x47, 1, { 0x04 } }, \
{ 0x47, 1, { 0x02 } }, { 0x47, 1, { 0x02 } }, \
{ 0x47, 1, { 0x02 } }, { 0x47, 1, { 0x02 } }, \
{ 0x47, 1, { 0x02 } }, { 0x47, 1, { 0x02 } }, \
{ 0x47, 1, { 0x02 } }, { 0x47, 1, { 0x0a } }, \
{ 0x47, 1, { 0x00 } }, { 0x47, 1, { 0x04 } }, \
{ 0x47, 1, { 0x04 } }, { 0x47, 1, { 0x04 } }, \
{ 0x47, 1, { 0x04 } }, { 0x47, 1, { 0x04 } }, \
{ 0x47, 1, { 0x04 } }, { 0x47, 1, { 0x04 } }, \
{ 0x47, 1, { 0x04 } }, \
{ 0x47, 1, { 0x02 } }, { 0x47, 1, { 0x02 } }, \
{ 0x47, 1, { 0x02 } }, { 0x47, 1, { 0x02 } }, \
{ 0x47, 1, { 0x02 } }, { 0x47, 1, { 0x02 } }, \
{ 0x47, 1, { 0x02 } }, { 0x47, 1, { 0x0a } }, \
{ 0x47, 1, { 0x00 } }, { 0x47, 1, { 0x04 } }, \
{ 0x47, 1, { 0x04 } }, { 0x47, 1, { 0x04 } }, \
{ 0x47, 1, { 0x04 } }, { 0x47, 1, { 0x04 } }, \
{ 0x47, 1, { 0x04 } }, { 0x47, 1, { 0x04 } }, \
{ 0x47, 1, { 0x04 } }, \
{ 0x47, 1, { 0x02 } }, { 0x47, 1, { 0x02 } }, \
{ 0x47, 1, { 0x02 } }, { 0x47, 1, { 0x02 } }, \
{ 0x47, 1, { 0x02 } }, { 0x47, 1, { 0x02 } }, \
{ 0x47, 1, { 0x02 } }, { 0x47, 1, { 0x0a } }, \
{ 0x47, 1, { 0x00 } }, { 0x47, 1, { 0x04 } }, \
{ 0x47, 1, { 0x04 } }, { 0x47, 1, { 0x04 } }, \
{ 0x47, 1, { 0x04 } }, { 0x47, 1, { 0x04 } }, \
{ 0x47, 1, { 0x04 } }, { 0x47, 1, { 0x04 } }, \
{ 0x47, 1, { 0x04 } }
static const struct sonly_regwrite_data initsm_1002_writev_1[] = {
/* Captured from Linux BSAPI 4.0 in low sensor-security mode. */
UPEKSONLY_READ (0x4b, 8),
{ 0x4b, 1, { 0x01 } }, { 0x4f, 1, { 0x06 } },
{ 0x4f, 1, { 0x05 } }, { 0x4f, 1, { 0x04 } },
{ 0x4b, 1, { 0x00 } },
UPEKSONLY_READ (0x49, 8),
{ 0x3e, 1, { 0x45 } }, { 0x3e, 1, { 0x92 } },
{ 0x3e, 1, { 0xd9 } }, { 0x3e, 1, { 0x3a } },
{ 0x3e, 1, { 0x99 } }, { 0x3e, 1, { 0x8c } },
{ 0x3e, 1, { 0xc6 } }, { 0x3e, 1, { 0xb9 } },
UPEKSONLY_READ (0x1a, 8), UPEKSONLY_READ (0x09, 8),
{ 0x09, 1, { 0x2a } }, { 0x1a, 1, { 0x01 } },
{ 0x09, 1, { 0x22 } }, { 0x1a, 1, { 0x03 } },
{ 0x49, 1, { 0x09 } },
UPEKSONLY_1002_CAL_PASS,
{ 0x49, 1, { 0x08 } },
{ 0x3e, 1, { 0x2e } }, { 0x3e, 1, { 0xfe } },
{ 0x3e, 1, { 0x14 } }, { 0x3e, 1, { 0xd5 } },
{ 0x3e, 1, { 0x33 } }, { 0x3e, 1, { 0x34 } },
{ 0x3e, 1, { 0xc5 } }, { 0x3e, 1, { 0xbf } },
{ 0x09, 1, { 0x2a } }, { 0x1a, 1, { 0x01 } },
{ 0x09, 1, { 0x22 } }, { 0x1a, 1, { 0x03 } },
{ 0x49, 1, { 0x09 } },
UPEKSONLY_1002_CAL_PASS,
{ 0x49, 1, { 0x08 } },
{ 0x3e, 1, { 0x15 } }, { 0x3e, 1, { 0x5c } },
{ 0x3e, 1, { 0x27 } }, { 0x3e, 1, { 0xf5 } },
{ 0x3e, 1, { 0xfb } }, { 0x3e, 1, { 0x82 } },
{ 0x3e, 1, { 0x25 } }, { 0x3e, 1, { 0x61 } },
{ 0x09, 1, { 0x2a } }, { 0x1a, 1, { 0x01 } },
UPEKSONLY_READ (0x0b, 8), { 0x0b, 1, { 0x00 } },
UPEKSONLY_READ (0x13, 8), { 0x13, 1, { 0x45 } },
/* Do not preload the capture stream here. The capture-specific low-security
* programming is replayed in CAPSM once a real finger interrupt arrives. */
{ 0x04, 1, { 0x00 } }, { 0x05, 1, { 0x00 } },
};
#undef UPEKSONLY_1002_CAL_PASS
static const struct sonly_regwrite initsm_1001_writev_1[] = {
{ 0x4a, 0x9d },
{ 0x4f, 0x06 },