just capture

This commit is contained in:
lignah 2025-12-04 03:39:52 +09:00
parent dede35fade
commit 063830777f

View file

@ -5,6 +5,15 @@
#define SAMSUNG730B_VID 0x04e8
#define SAMSUNG730B_PID 0x730b
#define SAMSUNG730B_BULK_EP_IN (0x82 | FPI_USB_ENDPOINT_IN)
#define SAMSUNG730B_BULK_EP_OUT (0x01 | FPI_USB_ENDPOINT_OUT)
#define SAMSUNG730B_IMG_WIDTH 112
#define SAMSUNG730B_IMG_HEIGHT 96
#define SAMSUNG730B_DATA_OFFSET 180
#define BULK_PACKET_SIZE 256
struct _FpiDeviceSamsung730b
{
FpImageDevice parent;
@ -19,22 +28,498 @@ G_DEFINE_TYPE (FpiDeviceSamsung730b,
fpi_device_samsung730b,
FP_TYPE_IMAGE_DEVICE);
struct Samsung730bCmd
{
const guint8 *data;
gsize len;
};
static const guint8 cmd0[] = { 0x4f, 0x80 };
static const guint8 cmd1[] = { 0xa9, 0x4f, 0x80 };
static const guint8 cmd2[] = { 0xa9, 0xb9, 0x00 };
static const guint8 cmd3[] = { 0xa9, 0x60, 0x1b, 0x00 };
static const guint8 cmd4[] = { 0xa9, 0x50, 0x21, 0x00 };
static const guint8 cmd5[] = { 0xa9, 0x61, 0x00, 0x00 };
static const guint8 cmd6[] = { 0xa9, 0x62, 0x00, 0x1a };
static const guint8 cmd7[] = { 0xa9, 0x63, 0x00, 0x1a };
static const guint8 cmd8[] = { 0xa9, 0x64, 0x04, 0x0a };
static const guint8 cmd9[] = { 0xa9, 0x66, 0x0f, 0x80 };
static const guint8 cmd10[] = { 0xa9, 0x67, 0x1b, 0x00 };
static const guint8 cmd11[] = { 0xa9, 0x68, 0x00, 0x0f };
static const guint8 cmd12[] = { 0xa9, 0x69, 0x00, 0x14 };
static const guint8 cmd13[] = { 0xa9, 0x6a, 0x00, 0x19 };
static const guint8 cmd14[] = { 0xa9, 0x6c, 0x00, 0x19 };
static const guint8 cmd15[] = { 0xa9, 0x40, 0x43, 0x00 };
static const guint8 cmd16[] = { 0xa9, 0x41, 0x6f, 0x00 };
static const guint8 cmd17[] = { 0xa9, 0x55, 0x20, 0x00 };
static const guint8 cmd18[] = { 0xa9, 0x5f, 0x00, 0x00 };
static const guint8 cmd19[] = { 0xa9, 0x52, 0x27, 0x00 };
static const guint8 cmd20[] = { 0xa9, 0x09, 0x00, 0x00 };
static const guint8 cmd21[] = { 0xa9, 0x5d, 0x4d, 0x00 };
static const guint8 cmd22[] = { 0xa9, 0x51, 0xa8, 0x25 };
static const guint8 cmd23[] = { 0xa9, 0x03, 0x00 };
static const guint8 cmd24[] = { 0xa9, 0x38, 0x01, 0x00 };
static const guint8 cmd25[] = { 0xa9, 0x3d, 0xff, 0x0f };
static const guint8 cmd26[] = { 0xa9, 0x10, 0x60, 0x00 };
static const guint8 cmd27[] = { 0xa9, 0x3b, 0x14, 0x00 };
static const guint8 cmd28[] = { 0xa9, 0x2f, 0xf6, 0xff };
static const guint8 cmd29[] = { 0xa9, 0x09, 0x00, 0x00 };
static const guint8 cmd30[] = { 0xa9, 0x0c, 0x00 };
static const guint8 cmd31[] = { 0xa8, 0x20, 0x00, 0x00 };
static const guint8 cmd32[] = { 0xa9, 0x04, 0x00 };
static const guint8 cmd33[] = { 0xa8, 0x08, 0x00 };
static const guint8 cmd34[] = { 0xa9, 0x09, 0x00, 0x00 };
static const guint8 cmd35[] = { 0xa8, 0x3e, 0x00, 0x00 };
static const guint8 cmd36[] = { 0xa9, 0x03, 0x00, 0x00 };
static const guint8 cmd37[] = { 0xa8, 0x20, 0x00, 0x00 };
static const guint8 cmd38[] = { 0xa9, 0x10, 0x00, 0x01 };
static const guint8 cmd39[] = { 0xa9, 0x2f, 0xef, 0x00 };
static const guint8 cmd40[] = { 0xa9, 0x09, 0x00, 0x00 };
static const guint8 cmd41[] = { 0xa9, 0x5d, 0x4d, 0x00 };
static const guint8 cmd42[] = { 0xa9, 0x51, 0x3a, 0x25 };
static const guint8 cmd43[] = { 0xa9, 0x0c, 0x00 };
static const guint8 cmd44[] = { 0xa8, 0x20, 0x00, 0x00 };
static const guint8 cmd45[] = { 0xa9, 0x04, 0x00, 0x00 };
static const guint8 cmd46[] = { 0xa9, 0x09, 0x00, 0x00 };
static const struct Samsung730bCmd samsung730b_init_cmds[] = {
{ cmd0, sizeof (cmd0) },
{ cmd1, sizeof (cmd1) },
{ cmd2, sizeof (cmd2) },
{ cmd3, sizeof (cmd3) },
{ cmd4, sizeof (cmd4) },
{ cmd5, sizeof (cmd5) },
{ cmd6, sizeof (cmd6) },
{ cmd7, sizeof (cmd7) },
{ cmd8, sizeof (cmd8) },
{ cmd9, sizeof (cmd9) },
{ cmd10, sizeof (cmd10) },
{ cmd11, sizeof (cmd11) },
{ cmd12, sizeof (cmd12) },
{ cmd13, sizeof (cmd13) },
{ cmd14, sizeof (cmd14) },
{ cmd15, sizeof (cmd15) },
{ cmd16, sizeof (cmd16) },
{ cmd17, sizeof (cmd17) },
{ cmd18, sizeof (cmd18) },
{ cmd19, sizeof (cmd19) },
{ cmd20, sizeof (cmd20) },
{ cmd21, sizeof (cmd21) },
{ cmd22, sizeof (cmd22) },
{ cmd23, sizeof (cmd23) },
{ cmd24, sizeof (cmd24) },
{ cmd25, sizeof (cmd25) },
{ cmd26, sizeof (cmd26) },
{ cmd27, sizeof (cmd27) },
{ cmd28, sizeof (cmd28) },
{ cmd29, sizeof (cmd29) },
{ cmd30, sizeof (cmd30) },
{ cmd31, sizeof (cmd31) },
{ cmd32, sizeof (cmd32) },
{ cmd33, sizeof (cmd33) },
{ cmd34, sizeof (cmd34) },
{ cmd35, sizeof (cmd35) },
{ cmd36, sizeof (cmd36) },
{ cmd37, sizeof (cmd37) },
{ cmd38, sizeof (cmd38) },
{ cmd39, sizeof (cmd39) },
{ cmd40, sizeof (cmd40) },
{ cmd41, sizeof (cmd41) },
{ cmd42, sizeof (cmd42) },
{ cmd43, sizeof (cmd43) },
{ cmd44, sizeof (cmd44) },
{ cmd45, sizeof (cmd45) },
{ cmd46, sizeof (cmd46) },
};
static const gsize samsung730b_init_cmds_len =
G_N_ELEMENTS (samsung730b_init_cmds);
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,
};
static const size_t CAPTURE_NUM_CHUNKS = sizeof(capture_indices) / sizeof(capture_indices[0]);
static gboolean
samsung730b_capture_frame (FpImageDevice *dev,
guint8 **out_buf,
gsize *out_len,
GError **error)
{
GUsbDevice *usb_dev;
guint8 *buf;
gsize total_len = 0;
gsize capacity = (gsize) CAPTURE_NUM_CHUNKS * BULK_PACKET_SIZE + 1024;
gsize actual_length;
gboolean ok;
gsize i;
guint16 windex0;
guint16 windex;
usb_dev = fpi_device_get_usb_device (FP_DEVICE (dev));
buf = g_malloc (capacity);
if (!buf)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
"samsung730b: failed to allocate capture buffer");
return FALSE;
}
/* ---------- 1) 첫 chunk: 상태 응답만 처리 (데이터 누적 X) ---------- */
windex0 = capture_indices[0];
ok = 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,
0xCA, /* request */
0x0003, /* value */
windex0, /* idx */
NULL, /* data */
0, /* length */
&actual_length,
500,
NULL,
error);
if (!ok)
goto out_fail;
/* 캡처 시작 명령 (a8 06 00 00...) */
{
guint8 start_cmd[256] = { 0 };
start_cmd[0] = 0xa8;
start_cmd[1] = 0x06;
start_cmd[2] = 0x00;
start_cmd[3] = 0x00;
ok = g_usb_device_bulk_transfer (usb_dev,
SAMSUNG730B_BULK_EP_OUT,
start_cmd,
sizeof (start_cmd),
&actual_length,
500,
NULL,
error);
if (!ok)
goto out_fail;
}
/* 첫 IN: 짧은 상태 응답만 읽고 버림 */
{
guint8 tmp0[256];
ok = g_usb_device_bulk_transfer (usb_dev,
SAMSUNG730B_BULK_EP_IN,
tmp0,
sizeof (tmp0),
&actual_length,
500,
NULL,
error);
if (!ok)
goto out_fail;
}
/* ---------- 2) 나머지 chunk: 실제 데이터 + ACK ---------- */
for (i = 1; i < CAPTURE_NUM_CHUNKS; i++)
{
guint8 tmp[256];
guint8 ack[256] = { 0 };
windex = capture_indices[i];
/* CONTROL 0xCA: 청크 설정 */
ok = 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,
0xCA,
0x0003,
windex,
NULL,
0,
&actual_length,
500,
NULL,
error);
if (!ok)
goto out_fail;
/* 데이터 IN (256 bytes 기대) */
ok = g_usb_device_bulk_transfer (usb_dev,
SAMSUNG730B_BULK_EP_IN,
tmp,
sizeof (tmp),
&actual_length,
1000,
NULL,
error);
if (!ok)
goto out_fail;
if (actual_length == 0)
break;
if (total_len + actual_length > capacity)
{
capacity *= 2;
buf = g_realloc (buf, capacity);
if (!buf)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
"samsung730b: realloc failed");
return FALSE;
}
}
memcpy (buf + total_len, tmp, actual_length);
total_len += actual_length;
/* ACK (256 zeros) */
ok = g_usb_device_bulk_transfer (usb_dev,
SAMSUNG730B_BULK_EP_OUT,
ack,
sizeof (ack),
&actual_length,
500,
NULL,
error);
if (!ok)
goto out_fail;
}
*out_buf = buf;
*out_len = total_len;
return TRUE;
out_fail:
g_free (buf);
*out_buf = NULL;
*out_len = 0;
return FALSE;
}
#include <stdio.h>
static void
samsung730b_do_capture_and_report (FpImageDevice *dev)
{
guint8 *raw = NULL;
gsize raw_len = 0;
GError *error = NULL;
FpImage *image = NULL;
gsize needed;
needed = SAMSUNG730B_DATA_OFFSET +
(gsize) SAMSUNG730B_IMG_WIDTH * SAMSUNG730B_IMG_HEIGHT;
if (!samsung730b_capture_frame (dev, &raw, &raw_len, &error))
goto out_error;
if (raw_len < needed)
{
g_clear_error (&error);
g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
"samsung730b: captured data too short (%"G_GSIZE_FORMAT")", raw_len);
goto out_error;
}
image = fp_image_new (SAMSUNG730B_IMG_WIDTH, SAMSUNG730B_IMG_HEIGHT);
{
gsize img_size;
guchar *data = (guchar *) fp_image_get_data (image, &img_size);
if (img_size < SAMSUNG730B_IMG_WIDTH * SAMSUNG730B_IMG_HEIGHT)
{
g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
"samsung730b: image buffer too small (%"G_GSIZE_FORMAT")", img_size);
goto out_error;
}
memcpy (data,
raw + SAMSUNG730B_DATA_OFFSET,
SAMSUNG730B_IMG_WIDTH * SAMSUNG730B_IMG_HEIGHT);
}
g_free (raw);
raw = NULL;
/* 디버그용 덤프 (원하면 유지) */
g_message ("samsung730b: dumping captured image to /tmp/s730b.pgm");
{
FILE *f = fopen ("/tmp/s730b.pgm", "wb");
if (f)
{
const guchar *data = fp_image_get_data (image, NULL);
fprintf (f, "P5\n%d %d\n255\n",
SAMSUNG730B_IMG_WIDTH, SAMSUNG730B_IMG_HEIGHT);
fwrite (data, 1,
SAMSUNG730B_IMG_WIDTH * SAMSUNG730B_IMG_HEIGHT,
f);
fclose (f);
}
else
{
g_message ("samsung730b: failed to open /tmp/s730b.pgm for writing");
}
}
g_object_unref (image);
out_error:
if (raw)
g_free (raw);
if (image)
g_object_unref (image);
if (error)
{
g_warning ("samsung730b: capture error: %s", error->message);
g_clear_error (&error);
}
}
static void
samsung730b_dev_init (FpImageDevice *dev)
{
GUsbDevice *usb_dev;
GError *error = NULL;
guint8 c3_data[16] = {
0x80, 0x84, 0x1e, 0x00,
0x08, 0x00, 0x00, 0x01,
0x01, 0x01, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00
};
guint8 iface = 0; /* 테스트 코드에서 claim한 인터페이스 번호 (보통 0) */
gboolean ok;
gsize actual_length;
gsize i;
usb_dev = fpi_device_get_usb_device (FP_DEVICE (dev));
/* 1. USB 인터페이스 claim */
if (!g_usb_device_claim_interface (usb_dev, iface, 0, &error))
{
fpi_image_device_open_complete (dev, error);
return;
}
/* 2. control 0xC3 전송 (libusb_control_transfer 대체) */
ok = 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,
0xC3, /* request */
0x0000, /* value */
0x0000, /* idx */
c3_data, /* data */
sizeof (c3_data), /* length */
&actual_length, /* actual_length */
500, /* timeout ms */
NULL, /* cancellable */
&error); /* error */
if (!ok || actual_length != sizeof (c3_data))
{
if (!error)
error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED,
"samsung730b: control 0xC3 transfer failed "
"(actual_length=%"G_GSIZE_FORMAT")",
actual_length);
fpi_image_device_open_complete (dev, error);
return;
}
/* 3. init 명령들 bulk OUT 전송 (libusb_bulk_transfer 대체) */
for (i = 0; i < samsung730b_init_cmds_len; i++)
{
const struct Samsung730bCmd *cmd = &samsung730b_init_cmds[i];
ok = g_usb_device_bulk_transfer (usb_dev,
SAMSUNG730B_BULK_EP_OUT,
(guint8 *) cmd->data,
cmd->len,
&actual_length,
500,
NULL, /* cancellable */
&error); /* error */
if (!ok || actual_length != cmd->len)
{
if (!error)
error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED,
"samsung730b: init bulk transfer failed "
"idx=%"G_GSIZE_FORMAT" actual_length=%"G_GSIZE_FORMAT"/%"G_GSIZE_FORMAT,
i, actual_length, cmd->len);
fpi_image_device_open_complete (dev, error);
return;
}
}
/* 4. 성공 */
fpi_image_device_open_complete (dev, NULL);
}
static void
samsung730b_dev_deinit (FpImageDevice *dev)
{
GUsbDevice *usb_dev;
GError *error = NULL;
guint8 iface = 0;
usb_dev = fpi_device_get_usb_device (FP_DEVICE (dev));
if (!g_usb_device_release_interface (usb_dev, iface, 0, &error))
{
fpi_image_device_close_complete (dev, error);
return;
}
fpi_image_device_close_complete (dev, NULL);
}
static void
samsung730b_dev_activate (FpImageDevice *dev)
{
/* 장치 준비 완료 (CAPTURE state 진입) */
fpi_image_device_activate_complete (dev, NULL);
/* 한 장 캡처해서 image_captured로 보고 */
samsung730b_do_capture_and_report (dev);
/* 단발 캡처 기기라서, 추가로 루프 안 돌고 바로 반환함 */
}
static void