diff --git a/.gitignore b/.gitignore index 07d73995..9e2641e4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.o *.swp _build +.cache/ +tests/__pycache__/ diff --git a/libfprint/drivers/samsung730b.c b/libfprint/drivers/samsung730b.c new file mode 100644 index 00000000..39f7a216 --- /dev/null +++ b/libfprint/drivers/samsung730b.c @@ -0,0 +1,728 @@ +/* + * Samsung 730B driver for libfprint + * 730B is a press-typed sensor, which captures image in 112x96 + * + * Copyright (C) 2025 Jang Han-gil + * + * 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 "samsung730b" + +#include "drivers_api.h" + +#define SAMSUNG730B_VID 0x04e8 +#define SAMSUNG730B_PID 0x730b + +#define SAMSUNG730B_EP_IN (0x82 | FPI_USB_ENDPOINT_IN) +#define SAMSUNG730B_EP_OUT (0x01 | FPI_USB_ENDPOINT_OUT) +#define BULK_PACKET_SIZE 256 + +#define IMG_W 112 +#define IMG_H 96 +#define IMG_OFFSET 180 + +/* Detection parameters */ +#define DETECT_MAX_LOOPS 5 +#define DETECT_PER_LOOP 5 +#define DETECT_PACKETS 6 +#define DETECT_INTERVAL_MS 400 +#define DETECT_INIT_TIMEOUT_MS 500 +#define DETECT_BULK_TIMEOUT_MS 700 + +/* Capture parameters (Vendor Protocol) */ +#define CAPTURE_START_IDX 0x032a +#define CAPTURE_CTRL_REQ 0xCA +#define CAPTURE_CTRL_VAL 0x0003 + +G_DECLARE_FINAL_TYPE (FpiDeviceSamsung730b, + fpi_device_samsung730b, + FPI, DEVICE_SAMSUNG730B, + FpImageDevice); + +struct _FpiDeviceSamsung730b +{ + FpImageDevice parent; + GCancellable *cancellable; +}; + +G_DEFINE_TYPE (FpiDeviceSamsung730b, + fpi_device_samsung730b, + FP_TYPE_IMAGE_DEVICE); + +/* ---------- Initialization Sequence ---------- */ + +typedef struct +{ + const guint8 *data; + gsize len; +} SamsungCmd; + +#define CMD(...) (const guint8[]){ __VA_ARGS__ }, sizeof((const guint8[]){ __VA_ARGS__ }) + +/* initialization sequence */ +static const SamsungCmd init_cmds[] = { + { CMD(0x4f, 0x80) }, + { CMD(0xa9, 0x4f, 0x80) }, + { CMD(0xa8, 0xb9, 0x00) }, + { CMD(0xa9, 0x60, 0x1b, 0x00) }, + { CMD(0xa9, 0x50, 0x21, 0x00) }, + { CMD(0xa9, 0x61, 0x00, 0x00) }, + { CMD(0xa9, 0x62, 0x00, 0x1a) }, + { CMD(0xa9, 0x63, 0x00, 0x1a) }, + { CMD(0xa9, 0x64, 0x04, 0x0a) }, + { CMD(0xa9, 0x66, 0x0f, 0x80) }, + { CMD(0xa9, 0x67, 0x1b, 0x00) }, + { CMD(0xa9, 0x68, 0x00, 0x0f) }, + { CMD(0xa9, 0x69, 0x00, 0x14) }, + { CMD(0xa9, 0x6a, 0x00, 0x19) }, + { CMD(0xa9, 0x6c, 0x00, 0x19) }, + { CMD(0xa9, 0x40, 0x43, 0x00) }, + { CMD(0xa9, 0x41, 0x6f, 0x00) }, + { CMD(0xa9, 0x55, 0x20, 0x00) }, + { CMD(0xa9, 0x5f, 0x00, 0x00) }, + { CMD(0xa9, 0x52, 0x27, 0x00) }, + { CMD(0xa9, 0x09, 0x00, 0x00) }, + { CMD(0xa9, 0x5d, 0x4d, 0x00) }, + { CMD(0xa9, 0x51, 0xa8, 0x25) }, + { CMD(0xa9, 0x03, 0x00) }, + { CMD(0xa9, 0x38, 0x01, 0x00) }, + { CMD(0xa9, 0x3d, 0xff, 0x0f) }, + { CMD(0xa9, 0x10, 0x60, 0x00) }, + { CMD(0xa9, 0x3b, 0x14, 0x00) }, + { CMD(0xa9, 0x2f, 0xf6, 0xff) }, + { CMD(0xa9, 0x09, 0x00, 0x00) }, + { CMD(0xa9, 0x0c, 0x00) }, + { CMD(0xa8, 0x20, 0x00, 0x00) }, + { CMD(0xa9, 0x04, 0x00) }, + { CMD(0xa8, 0x08, 0x00) }, + { CMD(0xa9, 0x09, 0x00, 0x00) }, + { CMD(0xa8, 0x3e, 0x00, 0x00) }, + { CMD(0xa9, 0x03, 0x00, 0x00) }, + { CMD(0xa8, 0x20, 0x00, 0x00) }, + { CMD(0xa9, 0x10, 0x00, 0x01) }, + { CMD(0xa9, 0x2f, 0xef, 0x00) }, + { CMD(0xa9, 0x09, 0x00, 0x00) }, + { CMD(0xa9, 0x5d, 0x4d, 0x00) }, + { CMD(0xa9, 0x51, 0x3a, 0x25) }, + { CMD(0xa9, 0x0c, 0x00) }, + { CMD(0xa8, 0x20, 0x00, 0x00) }, + { CMD(0xa9, 0x04, 0x00, 0x00) }, + { CMD(0xa9, 0x09, 0x00, 0x00) }, +}; + +/* + * Sequence of wIndex values used to request image chunks from the sensor. + * The driver iterates through these indices to read the full image frame + * in 256-byte blocks. The values were derived from USB traffic analysis. + */ +static const guint16 capture_indices[] = { + 0x032a, 0x042a, 0x052a, 0x062a, 0x072a, 0x082a, 0x092a, 0x0a2a, + 0x0b2a, 0x0c2a, 0x0d2a, 0x0e2a, 0x0f2a, 0x102a, 0x112a, 0x122a, + 0x132a, 0x142a, 0x152a, 0x162a, 0x172a, 0x182a, 0x192a, 0x1a2a, + 0x1b2a, 0x1c2a, 0x1d2a, 0x1e2a, 0x1f2a, 0x202a, 0x212a, 0x222a, + 0x232a, 0x242a, 0x252a, 0x262a, 0x272a, 0x282a, 0x292a, 0x2a2a, + 0x2b2a, 0x2c2a, 0x2d2a, 0x2e2a, 0x2f2a, 0x302a, 0x312a, 0x322a, + 0x332a, 0x342a, 0x352a, 0x362a, 0x372a, 0x382a, 0x392a, 0x3a2a, + 0x3b2a, 0x3c2a, 0x3d2a, 0x3e2a, 0x3f2a, 0x402a, 0x412a, 0x422a, + 0x432a, 0x442a, 0x452a, 0x462a, 0x472a, 0x482a, 0x492a, 0x4a2a, + 0x4b2a, 0x4c2a, 0x4d2a, 0x4e2a, 0x4f2a, 0x502a, 0x512a, 0x522a, + 0x532a, 0x542a, 0x552a, 0x562a, 0x572a, +}; +#define CAPTURE_NUM_PACKETS (G_N_ELEMENTS (capture_indices)) + +/* ---------- USB Helpers ---------- */ + +static gboolean +usb_ctrl_out (GUsbDevice *usb_dev, guint8 req, guint16 val, guint16 idx, + const guint8 *data, gsize len, guint timeout_ms, GError **error) +{ + gsize actual = 0; + return g_usb_device_control_transfer (usb_dev, + G_USB_DEVICE_DIRECTION_HOST_TO_DEVICE, + G_USB_DEVICE_REQUEST_TYPE_VENDOR, + G_USB_DEVICE_RECIPIENT_DEVICE, + req, val, idx, (guchar *) data, len, + &actual, timeout_ms, NULL, error); +} + +static gboolean +usb_bulk_out (GUsbDevice *usb_dev, guint8 ep, const guint8 *data, gsize len, + guint timeout_ms, GError **error) +{ + gsize actual = 0; + return g_usb_device_bulk_transfer (usb_dev, ep, (guchar *) data, len, + &actual, timeout_ms, NULL, error); +} + +static gboolean +usb_bulk_in (GUsbDevice *usb_dev, guint8 ep, guint8 *buf, gsize len, + guint timeout_ms, gsize *actual_out, GError **error) +{ + return g_usb_device_bulk_transfer (usb_dev, ep, buf, len, + actual_out, timeout_ms, NULL, error); +} + +/* ---------- Device Logic ---------- */ + +/* + * Resets and initializes the sensor. + * This sequence seems to be required before every capture or detection loop. + */ +static gboolean +s730b_init (GUsbDevice *usb_dev, GError **error) +{ + guint8 c3_data[16] = { + 0x80, 0x84, 0x1e, 0x00, 0x08, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 + }; + gsize i; + + if (!usb_ctrl_out (usb_dev, 0xC3, 0x0000, 0x0000, c3_data, sizeof (c3_data), 500, error)) + return FALSE; + + for (i = 0; i < G_N_ELEMENTS (init_cmds); i++) + { + if (!usb_bulk_out (usb_dev, SAMSUNG730B_EP_OUT, init_cmds[i].data, init_cmds[i].len, 500, error)) + return FALSE; + } + return TRUE; +} + +static guint8 * +s730b_detect_finger (GUsbDevice *usb_dev, gsize *out_len, GError **error) +{ + guint8 *buf = NULL; + gsize capacity = DETECT_PACKETS * BULK_PACKET_SIZE + 64; + gsize total = 0; + guint8 start_cmd[BULK_PACKET_SIZE] = { 0 }; + guint i; + + buf = g_malloc0 (capacity); + if (!buf) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE, "alloc fail"); + return NULL; + } + + /* packet 0 */ + if (!usb_ctrl_out (usb_dev, CAPTURE_CTRL_REQ, CAPTURE_CTRL_VAL, CAPTURE_START_IDX, NULL, 0, DETECT_INIT_TIMEOUT_MS, error)) + goto fail; + + start_cmd[0] = 0xa8; + start_cmd[1] = 0x06; + if (!usb_bulk_out (usb_dev, SAMSUNG730B_EP_OUT, start_cmd, sizeof (start_cmd), DETECT_INIT_TIMEOUT_MS, error)) + goto fail; + + { + guint8 tmp[BULK_PACKET_SIZE]; + gsize got = 0; + if (!usb_bulk_in (usb_dev, SAMSUNG730B_EP_IN, tmp, sizeof (tmp), DETECT_INIT_TIMEOUT_MS, &got, error)) + goto fail; + if (got > 0) + { + memcpy (buf + total, tmp, got); + total += got; + } + } + + /* packet 1..N: Read remaining chunks */ + for (i = 1; i < DETECT_PACKETS; i++) + { + guint8 tmp[BULK_PACKET_SIZE]; + guint8 ack[BULK_PACKET_SIZE] = { 0 }; + gsize got = 0; + guint16 widx = CAPTURE_START_IDX + i * BULK_PACKET_SIZE; + + if (!usb_ctrl_out (usb_dev, CAPTURE_CTRL_REQ, CAPTURE_CTRL_VAL, widx, NULL, 0, DETECT_INIT_TIMEOUT_MS, error)) + goto fail; + if (!usb_bulk_in (usb_dev, SAMSUNG730B_EP_IN, tmp, sizeof (tmp), DETECT_BULK_TIMEOUT_MS, &got, error)) + goto fail; + if (got > 0) + { + memcpy (buf + total, tmp, got); + total += got; + } + if (!usb_bulk_out (usb_dev, SAMSUNG730B_EP_OUT, ack, sizeof (ack), DETECT_INIT_TIMEOUT_MS, error)) + goto fail; + } + + *out_len = total; + return buf; + +fail: + g_free (buf); + *out_len = 0; + return NULL; +} + +static gboolean +s730b_has_fingerprint (const guint8 *data, gsize len) +{ + if (!data || len < 512) + return FALSE; + + gsize total = MIN (len, (gsize) 4096); + gsize zeros = 0, ff = 0; + + for (gsize i = 0; i < total; i++) + { + guint8 v = data[i]; + if (v == 0x00) zeros++; + else if (v == 0xFF) ff++; + } + + double ff_ratio = (double) ff / (double) total; + if (ff_ratio > 0.30 && zeros < total * 0.95) + return TRUE; + + return FALSE; +} + +/* Wait for finger ON */ +static gboolean +s730b_wait_finger (GUsbDevice *usb_dev, GCancellable *cancellable) +{ + GError *local_error = NULL; + + for (guint r = 0; r < DETECT_MAX_LOOPS; r++) + { + if (g_cancellable_is_cancelled (cancellable)) return FALSE; + g_message ("s730b: wait_finger loop %u/%u", r + 1, DETECT_MAX_LOOPS); + + for (guint i = 0; i < DETECT_PER_LOOP; i++) + { + if (g_cancellable_is_cancelled (cancellable)) return FALSE; + + local_error = NULL; + if (!s730b_init (usb_dev, &local_error)) + { + g_clear_error (&local_error); + g_usleep ((gulong) DETECT_INTERVAL_MS * 1000); + continue; + } + + gsize figner_len = 0; + guint8 *finger = s730b_detect_finger (usb_dev, &figner_len, &local_error); + if (!finger) + { + g_clear_error (&local_error); + g_usleep ((gulong) DETECT_INTERVAL_MS * 1000); + continue; + } + + gboolean fingerprint = s730b_has_fingerprint (finger, figner_len); + g_free (finger); + + if (fingerprint) + { + g_message ("s730b: fingerprint detected!"); + /* Re-init required before full capture */ + if (!s730b_init (usb_dev, &local_error)) + { + g_clear_error (&local_error); + g_usleep ((gulong) DETECT_INTERVAL_MS * 1000); + continue; + } + return TRUE; + } + g_usleep ((gulong) DETECT_INTERVAL_MS * 1000); + } + if (!s730b_init (usb_dev, &local_error)) g_clear_error (&local_error); + } + return FALSE; +} + +/* Wait for finger OFF */ +static gboolean +s730b_wait_finger_lost (GUsbDevice *usb_dev, GCancellable *cancellable) +{ + GError *local_error = NULL; + + g_message ("s730b: waiting for fingerprint removal (indefinite)..."); + + while (!g_cancellable_is_cancelled (cancellable)) + { + local_error = NULL; + if (!s730b_init (usb_dev, &local_error)) + { + g_clear_error (&local_error); + g_usleep (200 * 1000); + continue; + } + + gsize figner_len = 0; + guint8 *finger = s730b_detect_finger (usb_dev, &figner_len, &local_error); + if (!finger) + { + g_clear_error (&local_error); + g_usleep (200 * 1000); + continue; + } + + gboolean fingerprint = s730b_has_fingerprint (finger, figner_len); + g_free (finger); + + if (!fingerprint) + return TRUE; + + g_usleep (200 * 1000); + } + + return FALSE; +} + +/* Capture fingerprint image */ +static gboolean +s730b_capture (GUsbDevice *usb_dev, guint8 **out_buf, gsize *out_len, GError **error) +{ + guint8 *buf = NULL; + gsize capacity = (gsize) CAPTURE_NUM_PACKETS * BULK_PACKET_SIZE + 1024; + gsize total = 0; + + buf = g_malloc (capacity); + if (!buf) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE, "alloc fail"); + return FALSE; + } + + /* packet 0 */ + { + guint8 start_cmd[BULK_PACKET_SIZE] = { 0 }; + start_cmd[0] = 0xa8; + start_cmd[1] = 0x06; + + if (!usb_ctrl_out (usb_dev, CAPTURE_CTRL_REQ, CAPTURE_CTRL_VAL, capture_indices[0], NULL, 0, 500, error)) goto fail; + if (!usb_bulk_out (usb_dev, SAMSUNG730B_EP_OUT, start_cmd, sizeof (start_cmd), 500, error)) goto fail; + guint8 tmp[BULK_PACKET_SIZE]; + gsize got = 0; + if (!usb_bulk_in (usb_dev, SAMSUNG730B_EP_IN, tmp, sizeof (tmp), 500, &got, error)) goto fail; + } + + /* packet 1..N */ + for (gsize i = 1; i < CAPTURE_NUM_PACKETS; i++) + { + guint8 tmp[BULK_PACKET_SIZE]; + guint8 ack[BULK_PACKET_SIZE] = { 0 }; + gsize got = 0; + guint16 widx = capture_indices[i]; + + if (!usb_ctrl_out (usb_dev, CAPTURE_CTRL_REQ, CAPTURE_CTRL_VAL, widx, NULL, 0, 500, error)) goto fail; + if (!usb_bulk_in (usb_dev, SAMSUNG730B_EP_IN, tmp, sizeof (tmp), 1000, &got, error)) goto fail; + + if (got == 0) break; + if (total + got > capacity) + { + capacity *= 2; + guint8 *tmp_buf = g_realloc (buf, capacity); + if (!tmp_buf) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE, "realloc fail"); goto fail; } + buf = tmp_buf; + } + memcpy (buf + total, tmp, got); + total += got; + if (!usb_bulk_out (usb_dev, SAMSUNG730B_EP_OUT, ack, sizeof (ack), 500, error)) goto fail; + } + + *out_buf = buf; + *out_len = total; + return TRUE; + +fail: + g_free (buf); + *out_buf = NULL; + *out_len = 0; + return FALSE; +} + +/* Process and submit the image to libfprint */ +static void +s730b_submit_image (FpImageDevice *dev, guint8 *raw, gsize raw_len) +{ + gsize needed = IMG_OFFSET + (gsize) IMG_W * IMG_H; + if (raw_len < needed) + { + g_warning ("s730b: captured too short"); + g_free (raw); + fpi_image_device_report_finger_status (dev, FALSE); + fpi_image_device_image_captured (dev, NULL); + return; + } + + FpImage *img = fp_image_new (IMG_W, IMG_H); + gsize img_size = 0; + guchar *dst = (guchar *) fp_image_get_data (img, &img_size); + + memcpy (dst, raw + IMG_OFFSET, IMG_W * IMG_H); + g_free (raw); + + fpi_image_device_report_finger_status (dev, TRUE); + fpi_image_device_image_captured (dev, img); +} + +/* ---------- Asynchronous Task Management ---------- */ + +typedef enum { + TASK_MODE_ENROLL, + TASK_MODE_WAIT_OFF +} TaskMode; + +typedef struct { + GUsbDevice *usb_dev; + guint8 *raw_data; + gsize raw_len; + TaskMode mode; +} S730bTaskData; + +static void +s730b_task_data_free (gpointer user_data) +{ + S730bTaskData *data = user_data; + if (data->raw_data) g_free (data->raw_data); + g_free (data); +} + +/* Forward declarations to Asynchronous Task for Compiler */ +static void s730b_enroll_done (GObject *source_object, GAsyncResult *res, gpointer user_data); +static void s730b_enroll_worker (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable); + +static void +s730b_run_async_task (FpImageDevice *dev, TaskMode mode) +{ + FpiDeviceSamsung730b *self = FPI_DEVICE_SAMSUNG730B (dev); + GUsbDevice *usb = fpi_device_get_usb_device (FP_DEVICE (dev)); + GTask *task; + S730bTaskData *data; + + task = g_task_new (dev, self->cancellable, s730b_enroll_done, NULL); + data = g_new0 (S730bTaskData, 1); + data->usb_dev = usb; + data->mode = mode; + + g_task_set_task_data (task, data, s730b_task_data_free); + g_task_run_in_thread (task, s730b_enroll_worker); + g_object_unref (task); +} + +/* + * Worker thread for blocking USB operations (s730b_wait_finger(), s730b_capture()). + * Prevent the main GLib loop from stopping by using the thread + */ +static void +s730b_enroll_worker (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) +{ + S730bTaskData *data = task_data; + GError *error = NULL; + + if (g_task_return_error_if_cancelled (task)) return; + + if (data->mode == TASK_MODE_WAIT_OFF) + { + /* Unlimited waiting until you take off finger */ + if (s730b_wait_finger_lost (data->usb_dev, cancellable)) + { + g_task_return_boolean (task, TRUE); + } + else + { + /* Check if cancelled or failed */ + if (g_task_return_error_if_cancelled (task)) + return; + + g_task_return_new_error (task, FP_DEVICE_ERROR, FP_DEVICE_ERROR_PROTO, "Wait lost failed"); + } + return; + } + + /* TASK_MODE_ENROLL */ + if (!s730b_wait_finger (data->usb_dev, cancellable)) + { + if (g_task_return_error_if_cancelled (task)) + return; + + g_task_return_new_error (task, FP_DEVICE_ERROR, FP_DEVICE_ERROR_PROTO, "No fingerprint detected"); + return; + } + + if (g_task_return_error_if_cancelled (task)) return; + + /* Short delay for stabilization */ + g_usleep (50 * 1000); + + if (!s730b_capture (data->usb_dev, &data->raw_data, &data->raw_len, &error)) + { + g_task_return_error (task, error); + return; + } + + g_task_return_boolean (task, TRUE); +} + +static void +s730b_enroll_done (GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + FpImageDevice *dev = FP_IMAGE_DEVICE (source_object); + GTask *task = G_TASK (res); + S730bTaskData *data = g_task_get_task_data (task); + GError *error = NULL; + + if (g_task_propagate_boolean (task, &error)) + { + if (data->mode == TASK_MODE_WAIT_OFF) + { + fpi_image_device_report_finger_status (dev, FALSE); + + /* Restart enroll loop */ + s730b_run_async_task (dev, TASK_MODE_ENROLL); + } + else + { + /* Capture done, submit image */ + guint8 *buf = data->raw_data; + gsize len = data->raw_len; + data->raw_data = NULL; + s730b_submit_image (dev, buf, len); + + /* Chain next task: Wait for finger removal */ + s730b_run_async_task (dev, TASK_MODE_WAIT_OFF); + } + } + else + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + { + g_warning ("s730b: task failed: %s", error->message); + if (data->mode == TASK_MODE_ENROLL) + { + fpi_image_device_report_finger_status (dev, FALSE); + fpi_image_device_image_captured (dev, NULL); + + /* Retry detection on error */ + s730b_run_async_task (dev, TASK_MODE_ENROLL); + } + } + g_error_free (error); + } +} + +/* ---------- libfprint callbacks ---------- */ + +static void +samsung730b_dev_init (FpImageDevice *dev) +{ + GUsbDevice *usb = fpi_device_get_usb_device (FP_DEVICE (dev)); + GError *error = NULL; + + if (!g_usb_device_claim_interface (usb, 0, 0, &error)) + { + fpi_image_device_open_complete (dev, error); + return; + } + if (!s730b_init (usb, &error)) + { + fpi_image_device_open_complete (dev, error); + return; + } + fpi_image_device_open_complete (dev, NULL); +} + +static void +samsung730b_dev_deinit (FpImageDevice *dev) +{ + GUsbDevice *usb = fpi_device_get_usb_device (FP_DEVICE (dev)); + GError *error = NULL; + + g_usb_device_release_interface (usb, 0, 0, &error); + fpi_image_device_close_complete (dev, error); +} + +static void +samsung730b_dev_activate (FpImageDevice *dev) +{ + FpiDeviceSamsung730b *self = FPI_DEVICE_SAMSUNG730B (dev); + FpiImageDeviceState state; + + if (self->cancellable) g_object_unref (self->cancellable); + self->cancellable = g_cancellable_new (); + + g_object_get (dev, "fpi-image-device-state", &state, NULL); + fpi_image_device_activate_complete (dev, NULL); + + if (state == FPI_IMAGE_DEVICE_STATE_AWAIT_FINGER_OFF) + s730b_run_async_task (dev, TASK_MODE_WAIT_OFF); + else + s730b_run_async_task (dev, TASK_MODE_ENROLL); +} + +static void +samsung730b_dev_deactivate (FpImageDevice *dev) +{ + FpiDeviceSamsung730b *self = FPI_DEVICE_SAMSUNG730B (dev); + + if (self->cancellable) + g_cancellable_cancel (self->cancellable); + + fpi_image_device_deactivate_complete (dev, NULL); +} + +/* ---------- ID & Boilerplate ---------- */ + +static const FpIdEntry samsung730b_ids[] = { + { .vid = SAMSUNG730B_VID, .pid = SAMSUNG730B_PID, .driver_data = 0 }, + { .vid = 0, .pid = 0, .driver_data = 0 }, +}; + +static void +fpi_device_samsung730b_init (FpiDeviceSamsung730b *self) +{ + self->cancellable = NULL; +} + +static void +fpi_device_samsung730b_finalize (GObject *object) +{ + FpiDeviceSamsung730b *self = FPI_DEVICE_SAMSUNG730B (object); + if (self->cancellable) + { + g_cancellable_cancel (self->cancellable); + g_object_unref (self->cancellable); + } + G_OBJECT_CLASS (fpi_device_samsung730b_parent_class)->finalize (object); +} + +static void +fpi_device_samsung730b_class_init (FpiDeviceSamsung730bClass *klass) +{ + FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass); + FpImageDeviceClass *img_class = FP_IMAGE_DEVICE_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + dev_class->id = "samsung730b"; + dev_class->full_name = "Samsung 730B (experimental)"; + dev_class->id_table = samsung730b_ids; + dev_class->type = FP_DEVICE_TYPE_USB; + dev_class->scan_type = FP_SCAN_TYPE_PRESS; + + img_class->img_open = samsung730b_dev_init; + img_class->img_close = samsung730b_dev_deinit; + img_class->activate = samsung730b_dev_activate; + img_class->deactivate = samsung730b_dev_deactivate; + + img_class->img_width = IMG_W; + img_class->img_height = IMG_H; + img_class->bz3_threshold = 20; + + gobject_class->finalize = fpi_device_samsung730b_finalize; +} \ No newline at end of file diff --git a/libfprint/meson.build b/libfprint/meson.build index 34494813..10ed9b9e 100644 --- a/libfprint/meson.build +++ b/libfprint/meson.build @@ -153,6 +153,8 @@ driver_sources = { [ 'drivers/realtek/realtek.c' ], 'focaltech_moc' : [ 'drivers/focaltech_moc/focaltech_moc.c' ], + 'samsung730b' : + [ 'drivers/samsung730b.c' ], } helper_sources = { diff --git a/meson.build b/meson.build index baafa19c..3dc548da 100644 --- a/meson.build +++ b/meson.build @@ -144,6 +144,7 @@ default_drivers = [ 'fpcmoc', 'realtek', 'focaltech_moc', + 'samsung730b', ] spi_drivers = [