mirror of
https://gitlab.freedesktop.org/libfprint/libfprint.git
synced 2026-01-06 12:00:18 +01:00
samsung730b: translate comments to English and code update
This commit is contained in:
parent
fbaa97eaeb
commit
4668fbc591
1 changed files with 352 additions and 292 deletions
|
|
@ -1,3 +1,24 @@
|
|||
/*
|
||||
* 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"
|
||||
|
|
@ -13,34 +34,35 @@
|
|||
#define IMG_H 96
|
||||
#define IMG_OFFSET 180
|
||||
|
||||
/* detect 파라미터 (파이썬 v1.3 기준) */
|
||||
#define DETECT_MAX_ROUNDS 5
|
||||
#define DETECT_PROBES_PER_ROUND 5
|
||||
#define DETECT_PROBE_CHUNKS 6
|
||||
/* 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 파라미터 */
|
||||
#define CAPTURE_NUM_CHUNKS 85
|
||||
/* Capture parameters (Vendor Protocol) */
|
||||
#define CAPTURE_START_IDX 0x032a
|
||||
#define CAPTURE_CTRL_REQ 0xCA
|
||||
#define CAPTURE_CTRL_VAL 0x0003
|
||||
|
||||
typedef struct _FpiDeviceSamsung730b
|
||||
{
|
||||
FpImageDevice parent;
|
||||
} FpiDeviceSamsung730b;
|
||||
|
||||
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);
|
||||
|
||||
/* ---------- init 시퀀스 ---------- */
|
||||
/* ---------- Initialization Sequence ---------- */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
|
@ -50,10 +72,11 @@ typedef struct
|
|||
|
||||
#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(0xa9, 0xb9, 0x00) },
|
||||
{ CMD(0xa8, 0xb9, 0x00) },
|
||||
{ CMD(0xa9, 0x60, 0x1b, 0x00) },
|
||||
{ CMD(0xa9, 0x50, 0x21, 0x00) },
|
||||
{ CMD(0xa9, 0x61, 0x00, 0x00) },
|
||||
|
|
@ -100,8 +123,12 @@ static const SamsungCmd init_cmds[] = {
|
|||
{ CMD(0xa9, 0x09, 0x00, 0x00) },
|
||||
};
|
||||
|
||||
/* capture 인덱스: 파이썬 CAPTURE_START_INDEX/NUM_CHUNKS 기반 */
|
||||
static const guint16 capture_indices[CAPTURE_NUM_CHUNKS] = {
|
||||
/*
|
||||
* 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,
|
||||
|
|
@ -112,110 +139,73 @@ static const guint16 capture_indices[CAPTURE_NUM_CHUNKS] = {
|
|||
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,
|
||||
0x532a, 0x542a, 0x552a, 0x562a, 0x572a,
|
||||
};
|
||||
#define CAPTURE_NUM_PACKETS (G_N_ELEMENTS (capture_indices))
|
||||
|
||||
/* ---------- 공용 USB 헬퍼 ---------- */
|
||||
/* ---------- 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)
|
||||
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);
|
||||
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)
|
||||
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);
|
||||
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)
|
||||
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);
|
||||
return g_usb_device_bulk_transfer (usb_dev, ep, buf, len,
|
||||
actual_out, timeout_ms, NULL, error);
|
||||
}
|
||||
|
||||
/* ---------- 센서 init ---------- */
|
||||
/* ---------- Device Logic ---------- */
|
||||
|
||||
/*
|
||||
* Resets and initializes the sensor.
|
||||
* This sequence seems to be required before every capture or detection loop.
|
||||
*/
|
||||
static gboolean
|
||||
s730b_run_init (GUsbDevice *usb_dev, GError **error)
|
||||
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
|
||||
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))
|
||||
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))
|
||||
if (!usb_bulk_out (usb_dev, SAMSUNG730B_EP_OUT, init_cmds[i].data, init_cmds[i].len, 500, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* ---------- finger detect용 probe 캡처 ---------- */
|
||||
|
||||
static guint8 *
|
||||
s730b_detect_probe (GUsbDevice *usb_dev, gsize *out_len, GError **error)
|
||||
s730b_detect_finger (GUsbDevice *usb_dev, gsize *out_len, GError **error)
|
||||
{
|
||||
guint8 *buf = NULL;
|
||||
gsize capacity = DETECT_PROBE_CHUNKS * BULK_PACKET_SIZE + 64;
|
||||
gsize capacity = DETECT_PACKETS * BULK_PACKET_SIZE + 64;
|
||||
gsize total = 0;
|
||||
guint8 start_cmd[BULK_PACKET_SIZE] = { 0 };
|
||||
guint i;
|
||||
|
|
@ -223,46 +213,24 @@ s730b_detect_probe (GUsbDevice *usb_dev, gsize *out_len, GError **error)
|
|||
buf = g_malloc0 (capacity);
|
||||
if (!buf)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
|
||||
"s730b: detect probe alloc fail");
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE, "alloc fail");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_message ("s730b: detect_probe: start");
|
||||
/* chunk 0: control 0xCA + start_cmd */
|
||||
if (!usb_ctrl_out (usb_dev, CAPTURE_CTRL_REQ, CAPTURE_CTRL_VAL,
|
||||
CAPTURE_START_IDX, NULL, 0,
|
||||
DETECT_INIT_TIMEOUT_MS, error))
|
||||
{
|
||||
g_message ("s730b: detect_probe: ctrl_out failed");
|
||||
/* 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))
|
||||
{
|
||||
g_message ("s730b: detect_probe: bulk_out(start_cmd) failed");
|
||||
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))
|
||||
{
|
||||
g_message ("s730b: detect_probe: first bulk_in failed");
|
||||
if (!usb_bulk_in (usb_dev, SAMSUNG730B_EP_IN, tmp, sizeof (tmp), DETECT_INIT_TIMEOUT_MS, &got, error))
|
||||
goto fail;
|
||||
}
|
||||
g_message ("s730b: detect_probe: first bulk_in got=%"G_GSIZE_FORMAT, got);
|
||||
|
||||
if (got > 0)
|
||||
{
|
||||
memcpy (buf + total, tmp, got);
|
||||
|
|
@ -270,61 +238,38 @@ s730b_detect_probe (GUsbDevice *usb_dev, gsize *out_len, GError **error)
|
|||
}
|
||||
}
|
||||
|
||||
/* chunk 1..N: 일부만 읽기 */
|
||||
for (i = 1; i < DETECT_PROBE_CHUNKS; i++)
|
||||
/* 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))
|
||||
{
|
||||
g_message ("s730b: detect_probe: ctrl_out chunk %u failed", i);
|
||||
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))
|
||||
{
|
||||
g_message ("s730b: detect_probe: bulk_in chunk %u failed", i);
|
||||
if (!usb_bulk_in (usb_dev, SAMSUNG730B_EP_IN, tmp, sizeof (tmp), DETECT_BULK_TIMEOUT_MS, &got, error))
|
||||
goto fail;
|
||||
}
|
||||
g_message ("s730b: detect_probe: bulk_in chunk %u got=%"G_GSIZE_FORMAT, i, got);
|
||||
|
||||
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))
|
||||
{
|
||||
g_message ("s730b: detect_probe: ack out chunk %u failed", i);
|
||||
if (!usb_bulk_out (usb_dev, SAMSUNG730B_EP_OUT, ack, sizeof (ack), DETECT_INIT_TIMEOUT_MS, error))
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
g_message ("s730b: detect_probe: done total=%"G_GSIZE_FORMAT, total);
|
||||
|
||||
*out_len = total;
|
||||
return buf;
|
||||
|
||||
fail:
|
||||
g_message ("s730b: detect_probe: fail");
|
||||
g_free (buf);
|
||||
*out_len = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ---------- ff 비율 기반 finger 존재 여부 ---------- */
|
||||
|
||||
static gboolean
|
||||
s730b_has_finger (const guint8 *data, gsize len)
|
||||
s730b_has_fingerprint (const guint8 *data, gsize len)
|
||||
{
|
||||
if (!data || len < 512)
|
||||
return FALSE;
|
||||
|
|
@ -335,174 +280,160 @@ s730b_has_finger (const guint8 *data, gsize len)
|
|||
for (gsize i = 0; i < total; i++)
|
||||
{
|
||||
guint8 v = data[i];
|
||||
if (v == 0x00)
|
||||
zeros++;
|
||||
else if (v == 0xFF)
|
||||
ff++;
|
||||
if (v == 0x00) zeros++;
|
||||
else if (v == 0xFF) ff++;
|
||||
}
|
||||
|
||||
double ff_ratio = (double) ff / (double) total;
|
||||
|
||||
/* 파이썬 `_has_finger_in_detect`와 동일한 조건:
|
||||
* - ff_ratio > 0.3
|
||||
* - zeros가 전체의 95% 미만 */
|
||||
if (ff_ratio > 0.30 && zeros < total * 0.95)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* ---------- finger detect 루프 ---------- */
|
||||
|
||||
/* Wait for finger ON */
|
||||
static gboolean
|
||||
s730b_wait_finger (GUsbDevice *usb_dev)
|
||||
s730b_wait_finger (GUsbDevice *usb_dev, GCancellable *cancellable)
|
||||
{
|
||||
GError *local_error = NULL;
|
||||
|
||||
for (guint r = 0; r < DETECT_MAX_ROUNDS; r++)
|
||||
for (guint r = 0; r < DETECT_MAX_LOOPS; r++)
|
||||
{
|
||||
g_message ("s730b: detect round %u/%u", r + 1, DETECT_MAX_ROUNDS);
|
||||
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_PROBES_PER_ROUND; i++)
|
||||
for (guint i = 0; i < DETECT_PER_LOOP; i++)
|
||||
{
|
||||
gsize probe_len = 0;
|
||||
guint8 *probe;
|
||||
if (g_cancellable_is_cancelled (cancellable)) return FALSE;
|
||||
|
||||
local_error = NULL;
|
||||
probe = s730b_detect_probe (usb_dev, &probe_len, &local_error);
|
||||
|
||||
if (!probe)
|
||||
if (!s730b_init (usb_dev, &local_error))
|
||||
{
|
||||
/* USB 에러/타임아웃은 soft-fail로 처리 */
|
||||
if (local_error)
|
||||
{
|
||||
g_message ("s730b: detect probe error (round=%u, probe=%u): %s",
|
||||
r, i, local_error->message);
|
||||
g_clear_error (&local_error);
|
||||
}
|
||||
/* 이 probe는 실패, 다음 probe로 넘어감 */
|
||||
g_clear_error (&local_error);
|
||||
g_usleep ((gulong) DETECT_INTERVAL_MS * 1000);
|
||||
continue;
|
||||
}
|
||||
|
||||
gboolean finger = s730b_has_finger (probe, probe_len);
|
||||
g_free (probe);
|
||||
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;
|
||||
}
|
||||
|
||||
g_message ("s730b: probe %u: finger=%d", i, finger);
|
||||
if (finger)
|
||||
return TRUE;
|
||||
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);
|
||||
}
|
||||
|
||||
/* 라운드 실패 시 센서 재초기화도 soft하게 처리 */
|
||||
g_message ("s730b: re-init after failed detect round");
|
||||
local_error = NULL;
|
||||
if (!s730b_run_init (usb_dev, &local_error))
|
||||
{
|
||||
if (local_error)
|
||||
{
|
||||
g_message ("s730b: re-init failed after detect round: %s",
|
||||
local_error->message);
|
||||
g_clear_error (&local_error);
|
||||
}
|
||||
/* init 실패해도 여기선 그냥 detect 실패로 간주 */
|
||||
break;
|
||||
}
|
||||
if (!s730b_init (usb_dev, &local_error)) g_clear_error (&local_error);
|
||||
}
|
||||
|
||||
g_message ("s730b: finger detect timeout (no finger)");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* ---------- full frame 캡처 ---------- */
|
||||
|
||||
/* Wait for finger OFF */
|
||||
static gboolean
|
||||
s730b_capture_frame (GUsbDevice *usb_dev,
|
||||
guint8 **out_buf,
|
||||
gsize *out_len,
|
||||
GError **error)
|
||||
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_CHUNKS * BULK_PACKET_SIZE + 1024;
|
||||
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,
|
||||
"s730b: capture alloc fail");
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE, "alloc fail");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* chunk 0: control 0xCA + start_cmd */
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
|
||||
/* 파이썬처럼 chunk0 데이터는 버림 (상태 응답) */
|
||||
if (!usb_bulk_in (usb_dev, SAMSUNG730B_EP_IN, tmp, sizeof (tmp), 500, &got, error)) goto fail;
|
||||
}
|
||||
|
||||
/* chunk 1..N : 실제 이미지 데이터 + ACK */
|
||||
for (gsize i = 1; i < CAPTURE_NUM_CHUNKS; i++)
|
||||
/* 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 (!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,
|
||||
"s730b: capture realloc fail");
|
||||
goto fail;
|
||||
}
|
||||
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;
|
||||
if (!usb_bulk_out (usb_dev, SAMSUNG730B_EP_OUT, ack, sizeof (ack), 500, error)) goto fail;
|
||||
}
|
||||
|
||||
*out_buf = buf;
|
||||
|
|
@ -516,18 +447,15 @@ fail:
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
/* ---------- 이미지로 변환 + report ---------- */
|
||||
|
||||
/* Process and submit the image to libfprint */
|
||||
static void
|
||||
s730b_emit_image (FpImageDevice *dev, guint8 *raw, gsize raw_len)
|
||||
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_GSIZE_FORMAT")", raw_len);
|
||||
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;
|
||||
|
|
@ -537,26 +465,159 @@ s730b_emit_image (FpImageDevice *dev, guint8 *raw, gsize raw_len)
|
|||
gsize img_size = 0;
|
||||
guchar *dst = (guchar *) fp_image_get_data (img, &img_size);
|
||||
|
||||
if (img_size < (gsize) IMG_W * IMG_H)
|
||||
{
|
||||
g_warning ("s730b: image buffer too small (%"G_GSIZE_FORMAT")", img_size);
|
||||
g_free (raw);
|
||||
g_object_unref (img);
|
||||
|
||||
fpi_image_device_report_finger_status (dev, FALSE);
|
||||
fpi_image_device_image_captured (dev, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
g_object_unref (img);
|
||||
}
|
||||
|
||||
/* ---------- libfprint 콜백들 ---------- */
|
||||
/* ---------- 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)
|
||||
|
|
@ -569,13 +630,11 @@ samsung730b_dev_init (FpImageDevice *dev)
|
|||
fpi_image_device_open_complete (dev, error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!s730b_run_init (usb, &error))
|
||||
if (!s730b_init (usb, &error))
|
||||
{
|
||||
fpi_image_device_open_complete (dev, error);
|
||||
return;
|
||||
}
|
||||
|
||||
fpi_image_device_open_complete (dev, NULL);
|
||||
}
|
||||
|
||||
|
|
@ -592,57 +651,55 @@ samsung730b_dev_deinit (FpImageDevice *dev)
|
|||
static void
|
||||
samsung730b_dev_activate (FpImageDevice *dev)
|
||||
{
|
||||
GUsbDevice *usb = fpi_device_get_usb_device (FP_DEVICE (dev));
|
||||
GError *error = NULL;
|
||||
guint8 *raw = NULL;
|
||||
gsize raw_len = 0;
|
||||
FpiDeviceSamsung730b *self = FPI_DEVICE_SAMSUNG730B (dev);
|
||||
FpiImageDeviceState state;
|
||||
|
||||
/* 1) finger detect (이미지 기반) */
|
||||
if (!s730b_wait_finger (usb))
|
||||
{
|
||||
/* 손가락 없음: 에러로 치지 않고 조용히 끝냄 */
|
||||
fpi_image_device_report_finger_status (dev, FALSE);
|
||||
fpi_image_device_activate_complete (dev, NULL);
|
||||
return;
|
||||
}
|
||||
if (self->cancellable) g_object_unref (self->cancellable);
|
||||
self->cancellable = g_cancellable_new ();
|
||||
|
||||
/* 2) detect 동안 센서 상태가 바뀌었을 수 있으니 init 재실행 */
|
||||
if (!s730b_run_init (usb, &error))
|
||||
{
|
||||
fpi_image_device_activate_complete (dev, error);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 3) full capture */
|
||||
if (!s730b_capture_frame (usb, &raw, &raw_len, &error))
|
||||
{
|
||||
fpi_image_device_activate_complete (dev, error);
|
||||
return;
|
||||
}
|
||||
|
||||
/* activate 완료 알리고 이미지 전달 */
|
||||
g_object_get (dev, "fpi-image-device-state", &state, NULL);
|
||||
fpi_image_device_activate_complete (dev, NULL);
|
||||
s730b_emit_image (dev, raw, raw_len);
|
||||
|
||||
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 테이블 ---------- */
|
||||
/* ---------- ID & Boilerplate ---------- */
|
||||
|
||||
static const FpIdEntry samsung730b_ids[] = {
|
||||
{ .vid = SAMSUNG730B_VID, .pid = SAMSUNG730B_PID, .driver_data = 0 },
|
||||
{ .vid = 0, .pid = 0, .driver_data = 0 },
|
||||
{ .vid = 0, .pid = 0, .driver_data = 0 },
|
||||
};
|
||||
|
||||
/* ---------- GObject boilerplate ---------- */
|
||||
|
||||
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
|
||||
|
|
@ -650,6 +707,7 @@ 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)";
|
||||
|
|
@ -665,4 +723,6 @@ fpi_device_samsung730b_class_init (FpiDeviceSamsung730bClass *klass)
|
|||
img_class->img_width = IMG_W;
|
||||
img_class->img_height = IMG_H;
|
||||
img_class->bz3_threshold = 20;
|
||||
|
||||
gobject_class->finalize = fpi_device_samsung730b_finalize;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue