mirror of
https://github.com/morgan9e/libfprint-fpc1020
synced 2026-04-14 16:34:24 +09:00
1199 lines
33 KiB
C
1199 lines
33 KiB
C
/*
|
|
* Samsung USB FPC1020 driver for libfprint
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#define FP_COMPONENT "fpc1020_samsung"
|
|
|
|
#include "drivers_api.h"
|
|
|
|
#define FPC1020_EP_OUT (0x01 | FPI_USB_ENDPOINT_OUT)
|
|
#define FPC1020_EP_IN (0x02 | FPI_USB_ENDPOINT_IN)
|
|
|
|
#define FPC1020_IMG_W 160
|
|
#define FPC1020_IMG_H 160
|
|
#define FPC1020_HALF_SIZE 12800
|
|
#define FPC1020_XFER_SIZE 12802 // 2-byte header + 12800 pixels
|
|
|
|
#define FPC1020_TIMEOUT 2000
|
|
#define FPC1020_IMG_TIMEOUT 6000 // longer timout for images
|
|
|
|
#define FPC1020_NUM_INIT_CMDS 16
|
|
#define FPC1020_NUM_INIT_PASSES 2
|
|
#define FPC1020_POLL_MAX 100
|
|
|
|
static const guint8 SPI_CONFIG[16] = {
|
|
0xc0, 0xc6, 0x2d, 0x00, 0x08, 0x00, 0xff, 0x01,
|
|
0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
// backend device init command
|
|
static const guint8 CMD_RESET[] = { 0xf8 };
|
|
static const guint8 CMD_IRQ_READ[] = { 0x1c, 0x00 };
|
|
static const guint8 CMD_HWID[] = { 0xfc, 0x00, 0x00 };
|
|
static const guint8 CMD_CONFIG[] = { 0x9c, 0x55, 0x40, 0x00, 0x2d, 0x24 };
|
|
static const guint8 CMD_REG_6C[] = { 0x6c, 0x29 };
|
|
static const guint8 CMD_REG_90[] = { 0x90, 0x32 };
|
|
static const guint8 CMD_CAL[] = { 0x68, 0x31, 0x31, 0x31, 0x31,
|
|
0x3d, 0x3d, 0x3d, 0x3d };
|
|
static const guint8 CMD_CRYPTO[] = { 0x98, 0x03, 0xeb, 0x3f, 0xb8,
|
|
0x0a, 0x07, 0x11, 0x22, 0x6a, 0x5d };
|
|
static const guint8 CMD_REG_8C[] = { 0x8c, 0x12 };
|
|
static const guint8 CMD_PXLCTRL[] = { 0x5c, 0x0b };
|
|
static const guint8 CMD_FDET_THRES[] = { 0xd8, 0x20 };
|
|
static const guint8 CMD_FDET_CNTR[] = { 0xdc, 0x00, 0x0a };
|
|
static const guint8 CMD_ADC_GAIN[] = { 0xa8, 0x0f, 0x0a };
|
|
static const guint8 CMD_PXL_OFFSET[] = { 0xa0, 0x0a, 0x02 };
|
|
static const guint8 CMD_FINALIZE[] = { 0x28 };
|
|
|
|
struct spi_cmd
|
|
{
|
|
const guint8 *data;
|
|
gsize len;
|
|
};
|
|
|
|
static const struct spi_cmd INIT_CMDS[FPC1020_NUM_INIT_CMDS] = {
|
|
{ CMD_RESET, sizeof (CMD_RESET) },
|
|
{ CMD_IRQ_READ, sizeof (CMD_IRQ_READ) },
|
|
{ CMD_IRQ_READ, sizeof (CMD_IRQ_READ) },
|
|
{ CMD_HWID, sizeof (CMD_HWID) },
|
|
{ CMD_CONFIG, sizeof (CMD_CONFIG) },
|
|
{ CMD_REG_6C, sizeof (CMD_REG_6C) },
|
|
{ CMD_REG_90, sizeof (CMD_REG_90) },
|
|
{ CMD_CAL, sizeof (CMD_CAL) },
|
|
{ CMD_CRYPTO, sizeof (CMD_CRYPTO) },
|
|
{ CMD_REG_8C, sizeof (CMD_REG_8C) },
|
|
{ CMD_PXLCTRL, sizeof (CMD_PXLCTRL) },
|
|
{ CMD_FDET_THRES, sizeof (CMD_FDET_THRES) },
|
|
{ CMD_FDET_CNTR, sizeof (CMD_FDET_CNTR) },
|
|
{ CMD_ADC_GAIN, sizeof (CMD_ADC_GAIN) },
|
|
{ CMD_PXL_OFFSET, sizeof (CMD_PXL_OFFSET) },
|
|
{ CMD_FINALIZE, sizeof (CMD_FINALIZE) },
|
|
};
|
|
|
|
// full resolution
|
|
static const guint8 CMD_FR_OFFSET[] = { 0xa0, 0x0e, 0x03 };
|
|
static const guint8 CMD_FR_SAMPLE[] = { 0x64, 0x1e };
|
|
static const guint8 CMD_FR_PXLCTRL[] = { 0x5c, 0x0b };
|
|
static const guint8 CMD_FR_WINDOW[] = { 0x54, 0x00, 0xa0, 0x00, 0xa0 };
|
|
|
|
static const guint8 CMD_ARM[] = { 0x20 };
|
|
static const guint8 CMD_CAPTURE[] = { 0xc0 };
|
|
static const guint8 CMD_FINGER_DET[] = { 0xd4, 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
struct _FpDeviceFpc1020Samsung
|
|
{
|
|
FpImageDevice parent;
|
|
|
|
const guint8 *current_cmd;
|
|
gsize current_cmd_len;
|
|
guint8 spi_response[16];
|
|
guint8 *top_raw;
|
|
guint8 *bot_raw;
|
|
gint xfer_complete_count;
|
|
FpiSsm *capture_ssm;
|
|
gint init_pass;
|
|
gint cmd_idx;
|
|
gint poll_count;
|
|
GSource *finger_poll_source;
|
|
gboolean finger_detected;
|
|
gboolean needs_reinit;
|
|
gboolean deactivating;
|
|
gboolean ssm_active;
|
|
};
|
|
|
|
G_DECLARE_FINAL_TYPE (FpDeviceFpc1020Samsung, fpi_device_fpc1020_samsung,
|
|
FPI, DEVICE_FPC1020_SAMSUNG, FpImageDevice);
|
|
G_DEFINE_TYPE (FpDeviceFpc1020Samsung, fpi_device_fpc1020_samsung,
|
|
FP_TYPE_IMAGE_DEVICE);
|
|
|
|
static void start_finger_poll (FpDeviceFpc1020Samsung *self);
|
|
static void start_finger_off_poll (FpDeviceFpc1020Samsung *self);
|
|
static void start_capture (FpDeviceFpc1020Samsung *self);
|
|
|
|
enum spi_cmd_states {
|
|
SPI_CMD_BRIDGE_CONFIG,
|
|
SPI_CMD_BRIDGE_SET_SIZE,
|
|
SPI_CMD_BULK_WRITE,
|
|
SPI_CMD_BULK_READ,
|
|
SPI_CMD_NUM_STATES,
|
|
};
|
|
|
|
static void
|
|
spi_read_cb (FpiUsbTransfer *transfer, FpDevice *dev,
|
|
gpointer user_data, GError *error)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
|
|
if (error)
|
|
{
|
|
fpi_ssm_mark_failed (transfer->ssm, error);
|
|
return;
|
|
}
|
|
|
|
memset (self->spi_response, 0, sizeof (self->spi_response));
|
|
memcpy (self->spi_response, transfer->buffer,
|
|
MIN ((gsize) transfer->actual_length, sizeof (self->spi_response)));
|
|
|
|
fpi_ssm_next_state (transfer->ssm);
|
|
}
|
|
|
|
static void
|
|
spi_cmd_run_state (FpiSsm *ssm, FpDevice *dev)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
FpiUsbTransfer *transfer;
|
|
|
|
switch (fpi_ssm_get_cur_state (ssm))
|
|
{
|
|
case SPI_CMD_BRIDGE_CONFIG:
|
|
transfer = fpi_usb_transfer_new (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,
|
|
0xC3, 0x0000, 0x0000,
|
|
sizeof (SPI_CONFIG));
|
|
memcpy (transfer->buffer, SPI_CONFIG, sizeof (SPI_CONFIG));
|
|
transfer->ssm = ssm;
|
|
fpi_usb_transfer_submit (transfer, FPC1020_TIMEOUT, NULL,
|
|
fpi_ssm_usb_transfer_cb, NULL);
|
|
break;
|
|
|
|
case SPI_CMD_BRIDGE_SET_SIZE:
|
|
transfer = fpi_usb_transfer_new (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,
|
|
0xCA, 0x0003,
|
|
(guint16) self->current_cmd_len, 0);
|
|
transfer->ssm = ssm;
|
|
fpi_usb_transfer_submit (transfer, FPC1020_TIMEOUT, NULL,
|
|
fpi_ssm_usb_transfer_cb, NULL);
|
|
break;
|
|
|
|
case SPI_CMD_BULK_WRITE:
|
|
transfer = fpi_usb_transfer_new (dev);
|
|
fpi_usb_transfer_fill_bulk_full (transfer, FPC1020_EP_OUT,
|
|
g_memdup2 (self->current_cmd,
|
|
self->current_cmd_len),
|
|
self->current_cmd_len, g_free);
|
|
transfer->ssm = ssm;
|
|
transfer->short_is_error = TRUE;
|
|
fpi_usb_transfer_submit (transfer, FPC1020_TIMEOUT, NULL,
|
|
fpi_ssm_usb_transfer_cb, NULL);
|
|
break;
|
|
|
|
case SPI_CMD_BULK_READ:
|
|
transfer = fpi_usb_transfer_new (dev);
|
|
fpi_usb_transfer_fill_bulk (transfer, FPC1020_EP_IN,
|
|
self->current_cmd_len);
|
|
transfer->ssm = ssm;
|
|
fpi_usb_transfer_submit (transfer, FPC1020_TIMEOUT, NULL,
|
|
spi_read_cb, NULL);
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
start_spi_cmd_subsm (FpiSsm *parent,
|
|
FpDeviceFpc1020Samsung *self,
|
|
const guint8 *cmd,
|
|
gsize len)
|
|
{
|
|
FpiSsm *subsm;
|
|
|
|
self->current_cmd = cmd;
|
|
self->current_cmd_len = len;
|
|
|
|
subsm = fpi_ssm_new (FP_DEVICE (self), spi_cmd_run_state,
|
|
SPI_CMD_NUM_STATES);
|
|
fpi_ssm_start_subsm (parent, subsm);
|
|
}
|
|
|
|
enum capture_states {
|
|
CAP_CONFIRM_ARM,
|
|
CAP_BWAIT_1,
|
|
CAP_BWAIT_2,
|
|
CAP_BWAIT_3,
|
|
CAP_BWAIT_4,
|
|
CAP_CONFIRM_IRQ,
|
|
CAP_CONFIRM_DETAIL,
|
|
|
|
CAP_FR_OFFSET,
|
|
CAP_FR_SAMPLE,
|
|
CAP_FR_PXLCTRL,
|
|
CAP_FR_WINDOW,
|
|
|
|
CAP_TRIGGER,
|
|
CAP_POLL_IRQ,
|
|
CAP_POLL_CHECK,
|
|
CAP_POLL_BSTAT,
|
|
|
|
CAP_TOP_CONFIG,
|
|
CAP_TOP_SIZE,
|
|
CAP_TOP_XFER,
|
|
|
|
CAP_BOT_CONFIG,
|
|
CAP_BOT_SIZE,
|
|
CAP_BOT_XFER,
|
|
|
|
CAP_TEARDOWN_1,
|
|
CAP_TEARDOWN_2,
|
|
CAP_TEARDOWN_3,
|
|
CAP_TEARDOWN_4,
|
|
CAP_TEARDOWN_ARM,
|
|
|
|
CAP_SUBMIT,
|
|
|
|
CAP_NUM_STATES,
|
|
};
|
|
|
|
|
|
static void
|
|
bstat_delay_cb (FpiUsbTransfer *transfer, FpDevice *dev,
|
|
gpointer user_data, GError *error)
|
|
{
|
|
if (error)
|
|
{
|
|
fpi_ssm_mark_failed (transfer->ssm, error);
|
|
return;
|
|
}
|
|
fpi_ssm_next_state_delayed (transfer->ssm, 10);
|
|
}
|
|
|
|
static void
|
|
do_bridge_status (FpiSsm *ssm, FpDevice *dev,
|
|
FpiUsbTransferCallback cb)
|
|
{
|
|
FpiUsbTransfer *transfer = fpi_usb_transfer_new (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,
|
|
0xDA, 0x0007, 0x0000, 2);
|
|
transfer->ssm = ssm;
|
|
fpi_usb_transfer_submit (transfer, FPC1020_TIMEOUT, NULL, cb, NULL);
|
|
}
|
|
|
|
static void
|
|
poll_bstat_cb (FpiUsbTransfer *transfer, FpDevice *dev,
|
|
gpointer user_data, GError *error)
|
|
{
|
|
if (error)
|
|
{
|
|
fpi_ssm_mark_failed (transfer->ssm, error);
|
|
return;
|
|
}
|
|
fpi_ssm_jump_to_state_delayed (transfer->ssm, CAP_POLL_IRQ, 10);
|
|
}
|
|
|
|
enum open_states {
|
|
OPEN_BRIDGE_INIT_1,
|
|
OPEN_BRIDGE_INIT_2,
|
|
OPEN_INIT_CMD,
|
|
OPEN_INIT_NEXT,
|
|
OPEN_NUM_STATES,
|
|
};
|
|
|
|
static void
|
|
open_run_state (FpiSsm *ssm, FpDevice *dev)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
FpiUsbTransfer *transfer;
|
|
|
|
switch (fpi_ssm_get_cur_state (ssm))
|
|
{
|
|
case OPEN_BRIDGE_INIT_1:
|
|
transfer = fpi_usb_transfer_new (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,
|
|
0xDB, 0x0006, 0x0000, 0);
|
|
transfer->ssm = ssm;
|
|
fpi_usb_transfer_submit (transfer, FPC1020_TIMEOUT, NULL,
|
|
fpi_ssm_usb_transfer_cb, NULL);
|
|
break;
|
|
|
|
case OPEN_BRIDGE_INIT_2:
|
|
transfer = fpi_usb_transfer_new (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,
|
|
0xDB, 0x0006, 0x0001, 0);
|
|
transfer->ssm = ssm;
|
|
fpi_usb_transfer_submit (transfer, FPC1020_TIMEOUT, NULL,
|
|
fpi_ssm_usb_transfer_cb, NULL);
|
|
break;
|
|
|
|
case OPEN_INIT_CMD:
|
|
{
|
|
const struct spi_cmd *cmd = &INIT_CMDS[self->cmd_idx];
|
|
start_spi_cmd_subsm (ssm, self, cmd->data, cmd->len);
|
|
}
|
|
break;
|
|
|
|
case OPEN_INIT_NEXT:
|
|
self->cmd_idx++;
|
|
if (self->cmd_idx < FPC1020_NUM_INIT_CMDS)
|
|
{
|
|
fpi_ssm_jump_to_state (ssm, OPEN_INIT_CMD);
|
|
}
|
|
else
|
|
{
|
|
self->init_pass++;
|
|
if (self->init_pass < FPC1020_NUM_INIT_PASSES)
|
|
{
|
|
self->cmd_idx = 0;
|
|
fpi_ssm_jump_to_state (ssm, OPEN_INIT_CMD);
|
|
}
|
|
else
|
|
{
|
|
fp_dbg ("FPC1020 init complete (%d passes)",
|
|
FPC1020_NUM_INIT_PASSES);
|
|
fpi_ssm_mark_completed (ssm);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
open_complete (FpiSsm *ssm, FpDevice *dev, GError *error)
|
|
{
|
|
FpImageDevice *img_dev = FP_IMAGE_DEVICE (dev);
|
|
|
|
fpi_image_device_open_complete (img_dev, error);
|
|
}
|
|
|
|
static void
|
|
fpc1020_open (FpImageDevice *dev)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
FpiSsm *ssm;
|
|
GError *error = NULL;
|
|
|
|
if (!g_usb_device_claim_interface (fpi_device_get_usb_device (FP_DEVICE (dev)),
|
|
0, 0, &error))
|
|
{
|
|
fpi_image_device_open_complete (dev, error);
|
|
return;
|
|
}
|
|
|
|
self->top_raw = g_malloc0 (FPC1020_XFER_SIZE);
|
|
self->bot_raw = g_malloc0 (FPC1020_XFER_SIZE);
|
|
self->init_pass = 0;
|
|
self->cmd_idx = 0;
|
|
|
|
ssm = fpi_ssm_new (FP_DEVICE (dev), open_run_state, OPEN_NUM_STATES);
|
|
fpi_ssm_start (ssm, open_complete);
|
|
}
|
|
|
|
static void
|
|
fpc1020_close (FpImageDevice *dev)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
GError *error = NULL;
|
|
|
|
g_clear_pointer (&self->top_raw, g_free);
|
|
g_clear_pointer (&self->bot_raw, g_free);
|
|
|
|
g_usb_device_release_interface (fpi_device_get_usb_device (FP_DEVICE (dev)),
|
|
0, 0, &error);
|
|
|
|
fpi_image_device_close_complete (dev, error);
|
|
}
|
|
|
|
static void
|
|
fpc1020_activate (FpImageDevice *dev)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
|
|
self->deactivating = FALSE;
|
|
fpi_image_device_activate_complete (dev, NULL);
|
|
}
|
|
|
|
static void
|
|
fpc1020_deactivate (FpImageDevice *dev)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
|
|
self->deactivating = TRUE;
|
|
|
|
if (self->finger_poll_source)
|
|
{
|
|
g_source_destroy (self->finger_poll_source);
|
|
self->finger_poll_source = NULL;
|
|
}
|
|
|
|
if (!self->ssm_active)
|
|
fpi_image_device_deactivate_complete (dev, NULL);
|
|
}
|
|
|
|
|
|
enum finger_poll_states {
|
|
FPOLL_READ_IRQ,
|
|
FPOLL_CHECK_IRQ,
|
|
FPOLL_ARM,
|
|
FPOLL_BWAIT_1,
|
|
FPOLL_BWAIT_2,
|
|
FPOLL_BWAIT_3,
|
|
FPOLL_BWAIT_4,
|
|
FPOLL_REREAD_IRQ,
|
|
FPOLL_CHECK_REREAD,
|
|
FPOLL_READ_DETAIL,
|
|
FPOLL_DONE,
|
|
FPOLL_NUM_STATES,
|
|
};
|
|
|
|
static void finger_poll_timer_cb (FpDevice *dev, gpointer user_data);
|
|
|
|
static void
|
|
finger_poll_complete (FpiSsm *ssm, FpDevice *dev, GError *error)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
|
|
self->ssm_active = FALSE;
|
|
|
|
if (self->deactivating)
|
|
{
|
|
fpi_image_device_deactivate_complete (FP_IMAGE_DEVICE (dev), error);
|
|
return;
|
|
}
|
|
|
|
if (error)
|
|
{
|
|
fpi_image_device_session_error (FP_IMAGE_DEVICE (dev), error);
|
|
return;
|
|
}
|
|
|
|
if (self->finger_detected)
|
|
{
|
|
fpi_image_device_report_finger_status (FP_IMAGE_DEVICE (dev), TRUE);
|
|
}
|
|
else if (self->needs_reinit)
|
|
{
|
|
self->needs_reinit = FALSE;
|
|
start_finger_poll (self);
|
|
}
|
|
else
|
|
{
|
|
if (!self->deactivating)
|
|
self->finger_poll_source =
|
|
fpi_device_add_timeout (FP_DEVICE (self), 100,
|
|
finger_poll_timer_cb, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
finger_poll_run_state (FpiSsm *ssm, FpDevice *dev)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
|
|
if (self->deactivating)
|
|
{
|
|
fpi_ssm_mark_completed (ssm);
|
|
return;
|
|
}
|
|
|
|
switch (fpi_ssm_get_cur_state (ssm))
|
|
{
|
|
case FPOLL_READ_IRQ:
|
|
start_spi_cmd_subsm (ssm, self, CMD_IRQ_READ, sizeof (CMD_IRQ_READ));
|
|
break;
|
|
|
|
case FPOLL_CHECK_IRQ:
|
|
fp_dbg ("finger poll IRQ: 0x%02x", self->spi_response[1]);
|
|
if (self->spi_response[1] == 0x01)
|
|
{
|
|
fpi_ssm_next_state (ssm);
|
|
}
|
|
else if (self->spi_response[1] & 0x80)
|
|
{
|
|
fpi_ssm_jump_to_state (ssm, FPOLL_READ_DETAIL);
|
|
}
|
|
else
|
|
{
|
|
fpi_ssm_mark_completed (ssm);
|
|
}
|
|
break;
|
|
|
|
case FPOLL_ARM:
|
|
start_spi_cmd_subsm (ssm, self, CMD_ARM, sizeof (CMD_ARM));
|
|
break;
|
|
|
|
case FPOLL_BWAIT_1:
|
|
do_bridge_status (ssm, dev, bstat_delay_cb);
|
|
break;
|
|
|
|
case FPOLL_BWAIT_2:
|
|
do_bridge_status (ssm, dev, bstat_delay_cb);
|
|
break;
|
|
|
|
case FPOLL_BWAIT_3:
|
|
do_bridge_status (ssm, dev, bstat_delay_cb);
|
|
break;
|
|
|
|
case FPOLL_BWAIT_4:
|
|
do_bridge_status (ssm, dev, bstat_delay_cb);
|
|
break;
|
|
|
|
case FPOLL_REREAD_IRQ:
|
|
start_spi_cmd_subsm (ssm, self, CMD_IRQ_READ, sizeof (CMD_IRQ_READ));
|
|
break;
|
|
|
|
case FPOLL_CHECK_REREAD:
|
|
fp_dbg ("finger poll re-read IRQ: 0x%02x", self->spi_response[1]);
|
|
if (self->spi_response[1] & 0x80)
|
|
{
|
|
fpi_ssm_next_state (ssm);
|
|
}
|
|
else
|
|
{
|
|
fp_dbg ("ARM consumed state, will reinit");
|
|
self->needs_reinit = TRUE;
|
|
fpi_ssm_mark_completed (ssm);
|
|
}
|
|
break;
|
|
|
|
case FPOLL_READ_DETAIL:
|
|
start_spi_cmd_subsm (ssm, self, CMD_FINGER_DET,
|
|
sizeof (CMD_FINGER_DET));
|
|
break;
|
|
|
|
case FPOLL_DONE:
|
|
self->finger_detected = TRUE;
|
|
fpi_ssm_mark_completed (ssm);
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
finger_poll_timer_cb (FpDevice *dev, gpointer user_data)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
FpiSsm *ssm;
|
|
|
|
self->finger_poll_source = NULL;
|
|
|
|
if (self->deactivating)
|
|
{
|
|
fpi_image_device_deactivate_complete (FP_IMAGE_DEVICE (dev), NULL);
|
|
return;
|
|
}
|
|
|
|
self->finger_detected = FALSE;
|
|
ssm = fpi_ssm_new (dev, finger_poll_run_state, FPOLL_NUM_STATES);
|
|
self->ssm_active = TRUE;
|
|
fpi_ssm_start (ssm, finger_poll_complete);
|
|
}
|
|
|
|
enum reinit_states {
|
|
REINIT_CMD,
|
|
REINIT_NEXT,
|
|
REINIT_NUM_STATES,
|
|
};
|
|
|
|
static void
|
|
reinit_run_state (FpiSsm *ssm, FpDevice *dev)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
|
|
if (self->deactivating)
|
|
{
|
|
fpi_ssm_mark_completed (ssm);
|
|
return;
|
|
}
|
|
|
|
switch (fpi_ssm_get_cur_state (ssm))
|
|
{
|
|
case REINIT_CMD:
|
|
{
|
|
const struct spi_cmd *cmd = &INIT_CMDS[self->cmd_idx];
|
|
start_spi_cmd_subsm (ssm, self, cmd->data, cmd->len);
|
|
}
|
|
break;
|
|
|
|
case REINIT_NEXT:
|
|
self->cmd_idx++;
|
|
if (self->cmd_idx < FPC1020_NUM_INIT_CMDS)
|
|
{
|
|
fpi_ssm_jump_to_state (ssm, REINIT_CMD);
|
|
}
|
|
else
|
|
{
|
|
self->init_pass++;
|
|
if (self->init_pass < FPC1020_NUM_INIT_PASSES)
|
|
{
|
|
self->cmd_idx = 0;
|
|
fpi_ssm_jump_to_state (ssm, REINIT_CMD);
|
|
}
|
|
else
|
|
{
|
|
fp_dbg ("FPC1020 re-init complete");
|
|
fpi_ssm_mark_completed (ssm);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
reinit_complete (FpiSsm *ssm, FpDevice *dev, GError *error)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
|
|
self->ssm_active = FALSE;
|
|
|
|
if (self->deactivating)
|
|
{
|
|
fpi_image_device_deactivate_complete (FP_IMAGE_DEVICE (dev), error);
|
|
return;
|
|
}
|
|
|
|
if (error)
|
|
{
|
|
fpi_image_device_session_error (FP_IMAGE_DEVICE (dev), error);
|
|
return;
|
|
}
|
|
|
|
self->finger_poll_source =
|
|
fpi_device_add_timeout (FP_DEVICE (self), 100,
|
|
finger_poll_timer_cb, NULL, NULL);
|
|
}
|
|
|
|
static void
|
|
start_finger_poll (FpDeviceFpc1020Samsung *self)
|
|
{
|
|
FpiSsm *ssm;
|
|
|
|
if (self->deactivating)
|
|
return;
|
|
|
|
self->init_pass = 0;
|
|
self->cmd_idx = 0;
|
|
|
|
ssm = fpi_ssm_new (FP_DEVICE (self), reinit_run_state, REINIT_NUM_STATES);
|
|
self->ssm_active = TRUE;
|
|
fpi_ssm_start (ssm, reinit_complete);
|
|
}
|
|
|
|
static void
|
|
img_xfer_check_done (FpDeviceFpc1020Samsung *self)
|
|
{
|
|
if (g_atomic_int_add (&self->xfer_complete_count, 1) + 1 >= 2)
|
|
fpi_ssm_next_state (self->capture_ssm);
|
|
}
|
|
|
|
static void
|
|
img_in_cb (FpiUsbTransfer *transfer, FpDevice *dev,
|
|
gpointer user_data, GError *error)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
guint8 *dest = user_data;
|
|
|
|
if (error)
|
|
{
|
|
fpi_ssm_mark_failed (self->capture_ssm, error);
|
|
return;
|
|
}
|
|
|
|
memcpy (dest, transfer->buffer,
|
|
MIN ((gsize) transfer->actual_length, FPC1020_XFER_SIZE));
|
|
fp_dbg ("img IN: %ld bytes", (long) transfer->actual_length);
|
|
|
|
img_xfer_check_done (self);
|
|
}
|
|
|
|
static void
|
|
img_out_cb (FpiUsbTransfer *transfer, FpDevice *dev,
|
|
gpointer user_data, GError *error)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
|
|
if (error)
|
|
{
|
|
fpi_ssm_mark_failed (self->capture_ssm, error);
|
|
return;
|
|
}
|
|
|
|
fp_dbg ("img OUT: %ld bytes", (long) transfer->actual_length);
|
|
img_xfer_check_done (self);
|
|
}
|
|
|
|
static void
|
|
submit_concurrent_image_xfer (FpiSsm *ssm, FpDevice *dev, guint8 *dest)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
FpiUsbTransfer *in_xfer;
|
|
FpiUsbTransfer *out_xfer;
|
|
guint8 *out_buf;
|
|
|
|
g_atomic_int_set (&self->xfer_complete_count, 0);
|
|
self->capture_ssm = ssm;
|
|
|
|
/* Submit bulk IN (doesnt work if not ordered? bridge req.) */
|
|
in_xfer = fpi_usb_transfer_new (dev);
|
|
fpi_usb_transfer_fill_bulk (in_xfer, FPC1020_EP_IN, FPC1020_XFER_SIZE);
|
|
fpi_usb_transfer_submit (in_xfer, FPC1020_IMG_TIMEOUT, NULL,
|
|
img_in_cb, dest);
|
|
|
|
/* Submit bulk OUT */
|
|
out_buf = g_malloc0 (FPC1020_XFER_SIZE);
|
|
out_buf[0] = 0xC4;
|
|
|
|
out_xfer = fpi_usb_transfer_new (dev);
|
|
fpi_usb_transfer_fill_bulk_full (out_xfer, FPC1020_EP_OUT,
|
|
out_buf, FPC1020_XFER_SIZE, g_free);
|
|
out_xfer->short_is_error = TRUE;
|
|
fpi_usb_transfer_submit (out_xfer, FPC1020_IMG_TIMEOUT, NULL,
|
|
img_out_cb, NULL);
|
|
}
|
|
|
|
static void
|
|
capture_run_state (FpiSsm *ssm, FpDevice *dev)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
FpiUsbTransfer *transfer;
|
|
|
|
if (self->deactivating)
|
|
{
|
|
fpi_ssm_mark_completed (ssm);
|
|
return;
|
|
}
|
|
|
|
switch (fpi_ssm_get_cur_state (ssm))
|
|
{
|
|
case CAP_CONFIRM_ARM:
|
|
start_spi_cmd_subsm (ssm, self, CMD_ARM, sizeof (CMD_ARM));
|
|
break;
|
|
|
|
case CAP_BWAIT_1:
|
|
case CAP_BWAIT_2:
|
|
case CAP_BWAIT_3:
|
|
case CAP_BWAIT_4:
|
|
do_bridge_status (ssm, dev, bstat_delay_cb);
|
|
break;
|
|
|
|
case CAP_CONFIRM_IRQ:
|
|
start_spi_cmd_subsm (ssm, self, CMD_IRQ_READ, sizeof (CMD_IRQ_READ));
|
|
break;
|
|
|
|
case CAP_CONFIRM_DETAIL:
|
|
start_spi_cmd_subsm (ssm, self, CMD_FINGER_DET,
|
|
sizeof (CMD_FINGER_DET));
|
|
break;
|
|
|
|
case CAP_FR_OFFSET:
|
|
start_spi_cmd_subsm (ssm, self, CMD_FR_OFFSET,
|
|
sizeof (CMD_FR_OFFSET));
|
|
break;
|
|
|
|
case CAP_FR_SAMPLE:
|
|
start_spi_cmd_subsm (ssm, self, CMD_FR_SAMPLE,
|
|
sizeof (CMD_FR_SAMPLE));
|
|
break;
|
|
|
|
case CAP_FR_PXLCTRL:
|
|
start_spi_cmd_subsm (ssm, self, CMD_FR_PXLCTRL,
|
|
sizeof (CMD_FR_PXLCTRL));
|
|
break;
|
|
|
|
case CAP_FR_WINDOW:
|
|
start_spi_cmd_subsm (ssm, self, CMD_FR_WINDOW,
|
|
sizeof (CMD_FR_WINDOW));
|
|
break;
|
|
|
|
case CAP_TRIGGER:
|
|
self->poll_count = 0;
|
|
start_spi_cmd_subsm (ssm, self, CMD_CAPTURE, sizeof (CMD_CAPTURE));
|
|
break;
|
|
|
|
case CAP_POLL_IRQ:
|
|
start_spi_cmd_subsm (ssm, self, CMD_IRQ_READ, sizeof (CMD_IRQ_READ));
|
|
break;
|
|
|
|
case CAP_POLL_CHECK:
|
|
if (self->spi_response[1] == 0x20)
|
|
{
|
|
fp_dbg ("Capture ready after %d polls", self->poll_count + 1);
|
|
fpi_ssm_jump_to_state (ssm, CAP_TOP_CONFIG);
|
|
}
|
|
else
|
|
{
|
|
self->poll_count++;
|
|
if (self->poll_count >= FPC1020_POLL_MAX)
|
|
{
|
|
fpi_ssm_mark_failed (ssm,
|
|
fpi_device_error_new_msg (FP_DEVICE_ERROR_PROTO,
|
|
"Capture not ready after %d polls",
|
|
FPC1020_POLL_MAX));
|
|
return;
|
|
}
|
|
fpi_ssm_next_state (ssm);
|
|
}
|
|
break;
|
|
|
|
case CAP_POLL_BSTAT:
|
|
{
|
|
FpiUsbTransfer *xfer = fpi_usb_transfer_new (dev);
|
|
fpi_usb_transfer_fill_control (xfer,
|
|
G_USB_DEVICE_DIRECTION_DEVICE_TO_HOST,
|
|
G_USB_DEVICE_REQUEST_TYPE_VENDOR,
|
|
G_USB_DEVICE_RECIPIENT_DEVICE,
|
|
0xDA, 0x0007, 0x0000, 2);
|
|
xfer->ssm = ssm;
|
|
fpi_usb_transfer_submit (xfer, FPC1020_TIMEOUT, NULL,
|
|
poll_bstat_cb, NULL);
|
|
}
|
|
break;
|
|
|
|
case CAP_TOP_CONFIG:
|
|
transfer = fpi_usb_transfer_new (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,
|
|
0xC3, 0x0000, 0x0000,
|
|
sizeof (SPI_CONFIG));
|
|
memcpy (transfer->buffer, SPI_CONFIG, sizeof (SPI_CONFIG));
|
|
transfer->ssm = ssm;
|
|
fpi_usb_transfer_submit (transfer, FPC1020_TIMEOUT, NULL,
|
|
fpi_ssm_usb_transfer_cb, NULL);
|
|
break;
|
|
|
|
case CAP_TOP_SIZE:
|
|
transfer = fpi_usb_transfer_new (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,
|
|
0xCA, 0x0003,
|
|
(guint16) FPC1020_XFER_SIZE, 0);
|
|
transfer->ssm = ssm;
|
|
fpi_usb_transfer_submit (transfer, FPC1020_TIMEOUT, NULL,
|
|
fpi_ssm_usb_transfer_cb, NULL);
|
|
break;
|
|
|
|
case CAP_TOP_XFER:
|
|
submit_concurrent_image_xfer (ssm, dev, self->top_raw);
|
|
break;
|
|
|
|
case CAP_BOT_CONFIG:
|
|
transfer = fpi_usb_transfer_new (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,
|
|
0xC3, 0x0000, 0x0000,
|
|
sizeof (SPI_CONFIG));
|
|
memcpy (transfer->buffer, SPI_CONFIG, sizeof (SPI_CONFIG));
|
|
transfer->ssm = ssm;
|
|
fpi_usb_transfer_submit (transfer, FPC1020_TIMEOUT, NULL,
|
|
fpi_ssm_usb_transfer_cb, NULL);
|
|
break;
|
|
|
|
case CAP_BOT_SIZE:
|
|
transfer = fpi_usb_transfer_new (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,
|
|
0xCA, 0x0003,
|
|
(guint16) FPC1020_XFER_SIZE, 0);
|
|
transfer->ssm = ssm;
|
|
fpi_usb_transfer_submit (transfer, FPC1020_TIMEOUT, NULL,
|
|
fpi_ssm_usb_transfer_cb, NULL);
|
|
break;
|
|
|
|
case CAP_BOT_XFER:
|
|
submit_concurrent_image_xfer (ssm, dev, self->bot_raw);
|
|
break;
|
|
|
|
/* ── Teardown ── */
|
|
case CAP_TEARDOWN_1:
|
|
start_spi_cmd_subsm (ssm, self, CMD_ADC_GAIN, sizeof (CMD_ADC_GAIN));
|
|
break;
|
|
|
|
case CAP_TEARDOWN_2:
|
|
start_spi_cmd_subsm (ssm, self, CMD_PXL_OFFSET,
|
|
sizeof (CMD_PXL_OFFSET));
|
|
break;
|
|
|
|
case CAP_TEARDOWN_3:
|
|
start_spi_cmd_subsm (ssm, self, CMD_ADC_GAIN, sizeof (CMD_ADC_GAIN));
|
|
break;
|
|
|
|
case CAP_TEARDOWN_4:
|
|
start_spi_cmd_subsm (ssm, self, CMD_PXL_OFFSET,
|
|
sizeof (CMD_PXL_OFFSET));
|
|
break;
|
|
|
|
case CAP_TEARDOWN_ARM:
|
|
start_spi_cmd_subsm (ssm, self, CMD_ARM, sizeof (CMD_ARM));
|
|
break;
|
|
|
|
case CAP_SUBMIT:
|
|
{
|
|
FpImage *img = fp_image_new (FPC1020_IMG_W, FPC1020_IMG_H);
|
|
|
|
memcpy (img->data, self->top_raw + 2, FPC1020_HALF_SIZE);
|
|
memcpy (img->data + FPC1020_HALF_SIZE, self->bot_raw + 2,
|
|
FPC1020_HALF_SIZE);
|
|
|
|
/* Needs invert and reverse. */
|
|
img->flags = FPI_IMAGE_COLORS_INVERTED | FPI_IMAGE_PARTIAL;
|
|
img->ppmm = 20.0;
|
|
|
|
{
|
|
guint8 pmin = 255, pmax = 0;
|
|
gulong psum = 0;
|
|
int i;
|
|
for (i = 0; i < FPC1020_IMG_W * FPC1020_IMG_H; i++)
|
|
{
|
|
guint8 v = img->data[i];
|
|
if (v < pmin) pmin = v;
|
|
if (v > pmax) pmax = v;
|
|
psum += v;
|
|
}
|
|
fp_dbg ("Image stats: min=%u max=%u mean=%lu hdr_top=%02x%02x hdr_bot=%02x%02x",
|
|
pmin, pmax, psum / (FPC1020_IMG_W * FPC1020_IMG_H),
|
|
self->top_raw[0], self->top_raw[1],
|
|
self->bot_raw[0], self->bot_raw[1]);
|
|
}
|
|
|
|
fp_dbg ("Image captured (%dx%d)", FPC1020_IMG_W, FPC1020_IMG_H);
|
|
fpi_image_device_image_captured (FP_IMAGE_DEVICE (dev), img);
|
|
fpi_ssm_mark_completed (ssm);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
capture_complete (FpiSsm *ssm, FpDevice *dev, GError *error)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
|
|
self->ssm_active = FALSE;
|
|
self->capture_ssm = NULL;
|
|
|
|
if (self->deactivating)
|
|
{
|
|
fpi_image_device_deactivate_complete (FP_IMAGE_DEVICE (dev), error);
|
|
return;
|
|
}
|
|
|
|
if (error)
|
|
fpi_image_device_session_error (FP_IMAGE_DEVICE (dev), error);
|
|
}
|
|
|
|
static void
|
|
start_capture (FpDeviceFpc1020Samsung *self)
|
|
{
|
|
FpiSsm *ssm;
|
|
|
|
ssm = fpi_ssm_new (FP_DEVICE (self), capture_run_state, CAP_NUM_STATES);
|
|
self->ssm_active = TRUE;
|
|
self->capture_ssm = ssm;
|
|
fpi_ssm_start (ssm, capture_complete);
|
|
}
|
|
|
|
enum finger_off_states {
|
|
FOFF_READ_IRQ,
|
|
FOFF_CHECK,
|
|
FOFF_NUM_STATES,
|
|
};
|
|
|
|
static void finger_off_poll_complete (FpiSsm *ssm, FpDevice *dev,
|
|
GError *error);
|
|
|
|
static void
|
|
finger_off_run_state (FpiSsm *ssm, FpDevice *dev)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
|
|
if (self->deactivating)
|
|
{
|
|
fpi_ssm_mark_completed (ssm);
|
|
return;
|
|
}
|
|
|
|
switch (fpi_ssm_get_cur_state (ssm))
|
|
{
|
|
case FOFF_READ_IRQ:
|
|
start_spi_cmd_subsm (ssm, self, CMD_IRQ_READ, sizeof (CMD_IRQ_READ));
|
|
break;
|
|
|
|
case FOFF_CHECK:
|
|
fp_dbg ("finger off IRQ: 0x%02x", self->spi_response[1]);
|
|
if (self->spi_response[1] == 0x00)
|
|
{
|
|
fpi_ssm_mark_completed (ssm);
|
|
}
|
|
else
|
|
{
|
|
fpi_ssm_mark_completed (ssm);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
static void finger_off_timer_cb (FpDevice *dev, gpointer user_data);
|
|
|
|
static void
|
|
finger_off_poll_complete (FpiSsm *ssm, FpDevice *dev, GError *error)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
|
|
self->ssm_active = FALSE;
|
|
|
|
if (self->deactivating)
|
|
{
|
|
fpi_image_device_deactivate_complete (FP_IMAGE_DEVICE (dev), error);
|
|
return;
|
|
}
|
|
|
|
if (error)
|
|
{
|
|
fpi_image_device_session_error (FP_IMAGE_DEVICE (dev), error);
|
|
return;
|
|
}
|
|
|
|
if (self->spi_response[1] == 0x00)
|
|
{
|
|
fpi_image_device_report_finger_status (FP_IMAGE_DEVICE (dev), FALSE);
|
|
}
|
|
else
|
|
{
|
|
start_finger_off_poll (self);
|
|
}
|
|
}
|
|
|
|
static void
|
|
finger_off_timer_cb (FpDevice *dev, gpointer user_data)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
FpiSsm *ssm;
|
|
|
|
self->finger_poll_source = NULL;
|
|
|
|
if (self->deactivating)
|
|
{
|
|
fpi_image_device_deactivate_complete (FP_IMAGE_DEVICE (dev), NULL);
|
|
return;
|
|
}
|
|
|
|
ssm = fpi_ssm_new (dev, finger_off_run_state, FOFF_NUM_STATES);
|
|
self->ssm_active = TRUE;
|
|
fpi_ssm_start (ssm, finger_off_poll_complete);
|
|
}
|
|
|
|
static void
|
|
start_finger_off_poll (FpDeviceFpc1020Samsung *self)
|
|
{
|
|
if (self->deactivating)
|
|
return;
|
|
|
|
self->finger_poll_source =
|
|
fpi_device_add_timeout (FP_DEVICE (self), 100,
|
|
finger_off_timer_cb, NULL, NULL);
|
|
}
|
|
|
|
|
|
static void
|
|
fpc1020_change_state (FpImageDevice *dev, FpiImageDeviceState state)
|
|
{
|
|
FpDeviceFpc1020Samsung *self = FPI_DEVICE_FPC1020_SAMSUNG (dev);
|
|
|
|
switch (state)
|
|
{
|
|
case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_ON:
|
|
start_finger_poll (self);
|
|
break;
|
|
|
|
case FPI_IMAGE_DEVICE_STATE_CAPTURE:
|
|
start_capture (self);
|
|
break;
|
|
|
|
case FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF:
|
|
start_finger_off_poll (self);
|
|
break;
|
|
|
|
case FPI_IMAGE_DEVICE_STATE_INACTIVE:
|
|
case FPI_IMAGE_DEVICE_STATE_ACTIVATING:
|
|
case FPI_IMAGE_DEVICE_STATE_DEACTIVATING:
|
|
case FPI_IMAGE_DEVICE_STATE_IDLE:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static const FpIdEntry id_table[] = {
|
|
{ .vid = 0x04e8, .pid = 0x7301 },
|
|
{ .vid = 0, .pid = 0 },
|
|
};
|
|
|
|
static void
|
|
fpi_device_fpc1020_samsung_init (FpDeviceFpc1020Samsung *self)
|
|
{
|
|
}
|
|
|
|
static void
|
|
fpi_device_fpc1020_samsung_class_init (FpDeviceFpc1020SamsungClass *klass)
|
|
{
|
|
FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass);
|
|
FpImageDeviceClass *img_class = FP_IMAGE_DEVICE_CLASS (klass);
|
|
|
|
dev_class->id = FP_COMPONENT;
|
|
dev_class->full_name = "FPC1020 (Samsung USB-SPI Bridge)";
|
|
dev_class->type = FP_DEVICE_TYPE_USB;
|
|
dev_class->id_table = id_table;
|
|
dev_class->scan_type = FP_SCAN_TYPE_PRESS;
|
|
|
|
img_class->img_width = FPC1020_IMG_W;
|
|
img_class->img_height = FPC1020_IMG_H;
|
|
img_class->bz3_threshold = 12;
|
|
|
|
img_class->img_open = fpc1020_open;
|
|
img_class->img_close = fpc1020_close;
|
|
img_class->activate = fpc1020_activate;
|
|
img_class->deactivate = fpc1020_deactivate;
|
|
img_class->change_state = fpc1020_change_state;
|
|
}
|