diff --git a/libfprint/drivers/upeksonly.c b/libfprint/drivers/upeksonly.c index fe3c110d..c84c25c6 100644 --- a/libfprint/drivers/upeksonly.c +++ b/libfprint/drivers/upeksonly.c @@ -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; diff --git a/libfprint/drivers/upeksonly.h b/libfprint/drivers/upeksonly.h index 596249ff..bd7c814b 100644 --- a/libfprint/drivers/upeksonly.h +++ b/libfprint/drivers/upeksonly.h @@ -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 },