Merge branch 'feature/samsung730b' into 'master'

Draft: drivers: add initial Samsung 730B image driver

See merge request libfprint/libfprint!556
This commit is contained in:
lignah 2025-12-24 05:57:35 +00:00
commit 744cb9c756
4 changed files with 733 additions and 0 deletions

2
.gitignore vendored
View file

@ -1,3 +1,5 @@
*.o
*.swp
_build
.cache/
tests/__pycache__/

View file

@ -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 <lignah1@icloud.com>
*
* 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;
}

View file

@ -153,6 +153,8 @@ driver_sources = {
[ 'drivers/realtek/realtek.c' ],
'focaltech_moc' :
[ 'drivers/focaltech_moc/focaltech_moc.c' ],
'samsung730b' :
[ 'drivers/samsung730b.c' ],
}
helper_sources = {

View file

@ -144,6 +144,7 @@ default_drivers = [
'fpcmoc',
'realtek',
'focaltech_moc',
'samsung730b',
]
spi_drivers = [