Add driver for SecuGen Hamster Pro 20

Native USB driver for the SecuGen Hamster Pro 20 (FDU05) fingerprint reader (1162:2200).
Talks directly to the SIDO020A sensor via I2C-over-USB control transfers.

The sensor captures 956x688 grayscale images via USB bulk transfers, which are processed (band compensation, unsharp mask, bilinear downsample, flat-field correction, directional sharpening) to produce 300x400 images at 500 DPI for NBIS minutiae extraction. Note: processing can probably be improved.

Tested with enrollment and verification on physical hardware via fprintd.
This commit is contained in:
Jared Vacanti 2026-02-07 20:23:38 -06:00
parent 87092d74ff
commit 8ccb0d5efb
4 changed files with 1986 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,151 @@
/*
* SecuGen Hamster Pro 20 (FDU05) native USB driver for libfprint
* Copyright (C) 2026
*
* Protocol reverse-engineered from USB packet captures.
* No proprietary code or SDK used.
*
* 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
*/
#pragma once
#include "drivers_api.h"
/* ---- Device constants ---- */
#define SECUGEN_VID 0x1162
/* Final output image (what libfprint sees) */
#define SECUGEN_IMG_WIDTH 300
#define SECUGEN_IMG_HEIGHT 400
#define SECUGEN_IMG_SIZE (SECUGEN_IMG_WIDTH * SECUGEN_IMG_HEIGHT) /* 120000 */
/* Raw sensor array (full SIDO020A readout) */
#define SECUGEN_RAW_WIDTH 956
#define SECUGEN_RAW_HEIGHT 688
#define SECUGEN_RAW_SIZE (SECUGEN_RAW_WIDTH * SECUGEN_RAW_HEIGHT) /* 657728 */
#define SECUGEN_BULK_BUF_SIZE 657920 /* Actual USB stream: 688*956 + 192 trailing */
#define SECUGEN_DPI 500
#define SECUGEN_PPMM 19.685 /* 500 DPI / 25.4 mm/in */
#define SECUGEN_EP_DATA 0x82 /* Bulk IN endpoint */
/* ---- Vendor control transfer bRequest codes ---- */
#define SECUGEN_REQ_START_STREAM 1
#define SECUGEN_REQ_STOP_STREAM 2
#define SECUGEN_REQ_START_CAPTURE 5
#define SECUGEN_REQ_READ_FW_DATA 8
#define SECUGEN_REQ_LED_CONTROL 17
#define SECUGEN_REQ_GET_STATUS 22
#define SECUGEN_REQ_I2C_REG 34
#define SECUGEN_REQ_GET_DEVICE_ID 37
#define SECUGEN_REQ_SET_EXPOSURE 64
/* ---- I2C / SIDO020A sensor ---- */
#define SECUGEN_I2C_ADDR 0x0037 /* wValue for I2C transfers */
/* ---- Exposure values ---- */
#define SECUGEN_EXPOSURE_INIT 1000 /* Initial calibration exposure */
#define SECUGEN_EXPOSURE_NORMAL 1116 /* Normal capture exposure */
/* ---- Calibration / FW data ---- */
#define SECUGEN_FW_DATA_START 0x2000
#define SECUGEN_FW_DATA_CHUNK 4096
#define SECUGEN_FW_DATA_CHUNKS 6
#define SECUGEN_FW_DATA_LAST_LEN 2462 /* Last chunk is partial */
#define SECUGEN_FW_DATA_SIZE 22942 /* Total FW data: 4*4096 + 2*3072 ... */
/* Flat-field reference image stored in FW data at struct offset 0x1df8 */
#define SECUGEN_REF_WIDTH 150
#define SECUGEN_REF_HEIGHT 100
#define SECUGEN_REF_SIZE (SECUGEN_REF_WIDTH * SECUGEN_REF_HEIGHT) /* 15000 */
#define SECUGEN_REF_OFFSET 0x1df8 /* Offset of ref image in FW data */
#define SECUGEN_BLEND_CAL_VAL 240 /* Target uniform brightness */
/* FW data offsets for image processing parameters */
#define SECUGEN_BLC_OFFSETS_FW 0x0e /* 16 × int16 BLC region offsets */
#define SECUGEN_CAL_VALUE_FW 0x5892 /* uint16 blend target (usually 240) */
#define SECUGEN_SHARPEN_THRESH_FW 0x599C /* uint8 sharpening threshold */
#define SECUGEN_SHARPEN_AMOUNT_FW 0x599D /* uint8 sharpening amount */
/* ---- Timeouts (ms) ---- */
#define SECUGEN_CTRL_TIMEOUT 2000
#define SECUGEN_BULK_TIMEOUT 10000 /* Longer timeout for 657KB bulk read */
#define SECUGEN_FW_READ_TIMEOUT 5000
#define SECUGEN_FINGER_POLL_MS 200 /* Finger detection polling interval */
#define SECUGEN_FINGER_THRESHOLD 25 /* Mean brightness above this = finger present */
/* ---- I2C register init table entry ---- */
struct secugen_reg_entry
{
guint8 reg;
guint8 val;
};
/* ---- GObject type ---- */
G_DECLARE_FINAL_TYPE (FpiDeviceSecugen,
fpi_device_secugen,
FPI,
DEVICE_SECUGEN,
FpImageDevice);
struct _FpiDeviceSecugen
{
FpImageDevice parent;
/* Initialization tracking */
int init_reg_idx; /* Current I2C register being written */
int fw_read_idx; /* Current calibration data chunk */
guint8 iface_num; /* Claimed USB interface number */
/* Finger detection */
guint finger_poll_source; /* GLib timeout source ID */
/* Calibration / flat-field correction */
guint8 *cal_raw; /* Background frame at raw sensor res (956x688) */
guint8 *fw_data; /* Accumulated FW data from device (22942 bytes) */
gsize fw_data_len; /* Bytes accumulated so far */
guint8 *ref_image; /* Resized 300x400 flat-field reference image */
gboolean has_ref_image; /* Whether ref_image was successfully extracted */
/* BLC band compensation */
gint16 blc_offsets[16]; /* Factory-calibrated region offsets from FW */
gboolean has_blc_offsets; /* Whether BLC offsets were extracted */
/* Image processing params from FW */
guint16 cal_value; /* Blend target brightness (from FW, default 240) */
guint8 sharpen_threshold; /* Min gradient for sharpening */
guint8 sharpen_amount; /* Max gradient contribution */
guint8 sharpen_limit; /* Overall scaling factor (/10) */
gboolean sharpen_enabled; /* Whether post-resize sharpening is active */
/* Capture state */
guint16 exposure; /* Current exposure value */
/* Device info */
guint8 device_id[30];
/* Last register readback */
guint8 last_reg_rd;
/* Last status */
guint8 last_status[4];
};

View file

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

View file

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