Initial GXFP5130 userspace prototype
This commit is contained in:
99
examples/fdt-monitor.c
Normal file
99
examples/fdt-monitor.c
Normal file
@@ -0,0 +1,99 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include "gxfp/device.h"
|
||||
#include "gxfp/fdt.h"
|
||||
#include "gxfp/protocol.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char *event_name(enum gxfp_fdt_event event)
|
||||
{
|
||||
switch (event) {
|
||||
case GXFP_FDT_EVENT_READY:
|
||||
return "ready";
|
||||
case GXFP_FDT_EVENT_FINGER_DOWN:
|
||||
return "finger-down";
|
||||
case GXFP_FDT_EVENT_FINGER_UP:
|
||||
return "finger-up";
|
||||
case GXFP_FDT_EVENT_REVERSE:
|
||||
return "reverse";
|
||||
default:
|
||||
return "none";
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *path = argc > 1 ? argv[1] : "/dev/gxfp";
|
||||
struct gxfp_device device = { .fd = -1 };
|
||||
struct gxfp_fdt fdt;
|
||||
int ret;
|
||||
|
||||
ret = gxfp_device_open(&device, path);
|
||||
if (ret) {
|
||||
fprintf(stderr, "open failed: %s\n", strerror(-ret));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ret = gxfp_device_flush_rx(&device);
|
||||
if (ret)
|
||||
fprintf(stderr, "warning: flush failed: %s\n", strerror(-ret));
|
||||
|
||||
gxfp_fdt_init(&fdt);
|
||||
|
||||
ret = gxfp_fdt_arm_down(&device);
|
||||
if (ret) {
|
||||
fprintf(stderr, "FDT arm failed: %s\n", strerror(-ret));
|
||||
gxfp_device_close(&device);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("FDT monitor active. Touch and release the sensor.\n");
|
||||
|
||||
for (;;) {
|
||||
struct gxfp_rx_record record;
|
||||
struct gxfp_frame_view frame;
|
||||
enum gxfp_fdt_event event;
|
||||
uint16_t status = 0;
|
||||
|
||||
ret = gxfp_device_receive(&device, &record, -1);
|
||||
if (ret == -EINTR)
|
||||
break;
|
||||
if (ret) {
|
||||
fprintf(stderr, "receive failed: %s\n", strerror(-ret));
|
||||
break;
|
||||
}
|
||||
|
||||
ret = gxfp_frame_parse(record.payload, record.payload_len, &frame);
|
||||
if (ret) {
|
||||
fprintf(stderr, "bad frame: %s\n", strerror(-ret));
|
||||
gxfp_rx_record_release(&record);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (frame.command == GXFP_GOODIX_ACK) {
|
||||
gxfp_rx_record_release(&record);
|
||||
continue;
|
||||
}
|
||||
|
||||
event = gxfp_fdt_decode(&frame, &status);
|
||||
|
||||
printf("cmd=0x%02x status=0x%04x event=%s\n",
|
||||
frame.command, status, event_name(event));
|
||||
|
||||
ret = gxfp_fdt_handle_event(&fdt, &device, event);
|
||||
gxfp_rx_record_release(&record);
|
||||
|
||||
if (ret) {
|
||||
fprintf(stderr, "FDT transition failed: %s\n",
|
||||
strerror(-ret));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gxfp_device_close(&device);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
161
examples/sensor-probe.c
Normal file
161
examples/sensor-probe.c
Normal file
@@ -0,0 +1,161 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include "gxfp/session.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char *error_string(int error)
|
||||
{
|
||||
if (error >= 0)
|
||||
return "non-negative API error";
|
||||
|
||||
return strerror(-error);
|
||||
}
|
||||
|
||||
static int print_image_stats(unsigned int index,
|
||||
const struct gxfp_image12 *image)
|
||||
{
|
||||
size_t count;
|
||||
uint16_t minimum = UINT16_MAX;
|
||||
uint16_t maximum = 0;
|
||||
uint64_t sum = 0;
|
||||
size_t i;
|
||||
|
||||
if (!image || !image->pixels || !image->width || !image->height) {
|
||||
fprintf(stderr, "invalid image returned by capture\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (image->width > SIZE_MAX / image->height) {
|
||||
fprintf(stderr, "image dimensions overflow\n");
|
||||
return -EOVERFLOW;
|
||||
}
|
||||
count = image->width * image->height;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
uint16_t value = image->pixels[i];
|
||||
if (value < minimum)
|
||||
minimum = value;
|
||||
if (value > maximum)
|
||||
maximum = value;
|
||||
sum += value;
|
||||
}
|
||||
printf("capture_index: %u\n", index);
|
||||
printf("image: %zux%zu, samples=%zu\n",
|
||||
image->width, image->height, count);
|
||||
printf("CRC: stored=0x%08x calculated=0x%08x valid=yes\n",
|
||||
image->crc_stored, image->crc_calculated);
|
||||
printf("header: %02x %02x %02x %02x %02x\n",
|
||||
image->header[0], image->header[1], image->header[2],
|
||||
image->header[3], image->header[4]);
|
||||
printf("frame_trailer: 0x%02x\n", image->trailer);
|
||||
printf("pixel_min=%u pixel_max=%u pixel_mean=%.2f\n",
|
||||
minimum, maximum, (double)sum / count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *path = argc > 1 ? argv[1] : "/dev/gxfp";
|
||||
unsigned int capture_count = 1u;
|
||||
struct gxfp_session *session = NULL;
|
||||
const char *stage = "open";
|
||||
unsigned int completed = 0;
|
||||
unsigned int index;
|
||||
int active = 0;
|
||||
int ret;
|
||||
|
||||
if (argc > 3) {
|
||||
fprintf(stderr, "usage: %s [device] [capture-count]\n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (argc > 2) {
|
||||
char *end = NULL;
|
||||
unsigned long value;
|
||||
|
||||
errno = 0;
|
||||
value = strtoul(argv[2], &end, 10);
|
||||
if (errno == ERANGE || end == argv[2] || *end != '\0' ||
|
||||
value == 0 || value > 100) {
|
||||
fprintf(stderr, "capture count must be in range 1..100\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
capture_count = (unsigned int)value;
|
||||
}
|
||||
|
||||
ret = gxfp_session_open(&session, path);
|
||||
if (ret) {
|
||||
fprintf(stderr, "session open failed: %s (%d)\n",
|
||||
error_string(ret), ret);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
stage = "activate";
|
||||
ret = gxfp_session_activate(session, 10000);
|
||||
if (ret) {
|
||||
fprintf(stderr, "session activate failed: %s (%d)\n",
|
||||
error_string(ret), ret);
|
||||
goto out;
|
||||
}
|
||||
active = 1;
|
||||
printf("Session active; TLS established.\n");
|
||||
|
||||
for (index = 1; index <= capture_count; index++) {
|
||||
struct gxfp_image12 image = { 0 };
|
||||
|
||||
printf("\n=== Capture %u/%u ===\n", index, capture_count);
|
||||
stage = "arm-finger-down";
|
||||
ret = gxfp_session_arm_finger_down(session);
|
||||
if (ret)
|
||||
goto capture_error;
|
||||
printf("FDT down armed. Put finger on sensor...\n");
|
||||
stage = "wait-finger-down";
|
||||
ret = gxfp_session_wait_finger_down(session, 10000);
|
||||
if (ret)
|
||||
goto capture_error;
|
||||
stage = "capture";
|
||||
ret = gxfp_session_capture(session, &image, 20000);
|
||||
if (ret)
|
||||
goto capture_error;
|
||||
stage = "print-image-stats";
|
||||
ret = print_image_stats(index, &image);
|
||||
if (ret)
|
||||
goto capture_error;
|
||||
gxfp_image12_clear(&image);
|
||||
|
||||
stage = "arm-finger-up";
|
||||
ret = gxfp_session_arm_finger_up(session);
|
||||
if (ret)
|
||||
goto capture_error;
|
||||
printf("Remove your finger...\n");
|
||||
stage = "wait-finger-up";
|
||||
ret = gxfp_session_wait_finger_up(session, 10000);
|
||||
if (ret)
|
||||
goto capture_error;
|
||||
completed++;
|
||||
continue;
|
||||
|
||||
capture_error:
|
||||
gxfp_image12_clear(&image);
|
||||
fprintf(stderr,
|
||||
"capture %u failed during %s, state=%d: %s (%d)\n",
|
||||
index, stage,
|
||||
(int)gxfp_session_get_state(session),
|
||||
error_string(ret), ret);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
printf("\ncompleted captures: %u/%u\n", completed, capture_count);
|
||||
if (session) {
|
||||
if (active) {
|
||||
int deactivate_ret = gxfp_session_deactivate(session);
|
||||
if (!ret && deactivate_ret)
|
||||
ret = deactivate_ret;
|
||||
}
|
||||
gxfp_session_close(session);
|
||||
}
|
||||
return ret ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
212
examples/session-regression.c
Normal file
212
examples/session-regression.c
Normal file
@@ -0,0 +1,212 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include "gxfp/session.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
struct wait_context {
|
||||
struct gxfp_session *session;
|
||||
int result;
|
||||
};
|
||||
|
||||
static const char *error_string(int error)
|
||||
{
|
||||
return error < 0 ? strerror(-error) : "non-negative API error";
|
||||
}
|
||||
|
||||
static void sleep_ms(long milliseconds)
|
||||
{
|
||||
struct timespec delay = {
|
||||
.tv_sec = milliseconds / 1000,
|
||||
.tv_nsec = (milliseconds % 1000) * 1000000L,
|
||||
};
|
||||
|
||||
while (nanosleep(&delay, &delay) < 0 && errno == EINTR)
|
||||
;
|
||||
}
|
||||
|
||||
static void *wait_down_thread(void *opaque)
|
||||
{
|
||||
struct wait_context *context = opaque;
|
||||
|
||||
context->result = gxfp_session_wait_finger_down(context->session,
|
||||
30000);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int activate(struct gxfp_session *session)
|
||||
{
|
||||
int ret = gxfp_session_activate(session, 10000);
|
||||
|
||||
if (ret)
|
||||
fprintf(stderr, "activate failed: %s (%d), state=%d\n",
|
||||
error_string(ret), ret,
|
||||
(int)gxfp_session_get_state(session));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int recover(struct gxfp_session *session)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = gxfp_session_deactivate(session);
|
||||
if (ret) {
|
||||
fprintf(stderr, "deactivate/reset failed: %s (%d)\n",
|
||||
error_string(ret), ret);
|
||||
return ret;
|
||||
}
|
||||
if (gxfp_session_get_state(session) != GXFP_SESSION_OPEN) {
|
||||
fprintf(stderr, "deactivate did not reach OPEN\n");
|
||||
return -EPROTO;
|
||||
}
|
||||
return activate(session);
|
||||
}
|
||||
|
||||
static int test_cancel_wait_down(struct gxfp_session *session)
|
||||
{
|
||||
struct wait_context context = { .session = session, .result = 0 };
|
||||
pthread_t thread;
|
||||
int pthread_ret;
|
||||
int ret;
|
||||
|
||||
puts("\n[1/3] cross-thread cancel during wait-finger-down");
|
||||
ret = gxfp_session_arm_finger_down(session);
|
||||
if (ret)
|
||||
return ret;
|
||||
pthread_ret = pthread_create(&thread, NULL, wait_down_thread, &context);
|
||||
if (pthread_ret)
|
||||
return -pthread_ret;
|
||||
sleep_ms(500);
|
||||
gxfp_session_cancel(session);
|
||||
pthread_ret = pthread_join(thread, NULL);
|
||||
if (pthread_ret)
|
||||
return -pthread_ret;
|
||||
if (context.result != -ECANCELED) {
|
||||
fprintf(stderr, "expected -ECANCELED, got %d\n",
|
||||
context.result);
|
||||
return -EPROTO;
|
||||
}
|
||||
if (gxfp_session_get_state(session) != GXFP_SESSION_ERROR) {
|
||||
fprintf(stderr, "cancel did not leave session in ERROR\n");
|
||||
return -EPROTO;
|
||||
}
|
||||
puts("PASS: waiter returned -ECANCELED; recovering hardware");
|
||||
return recover(session);
|
||||
}
|
||||
|
||||
static int capture_while_present(struct gxfp_session *session,
|
||||
struct gxfp_image12 *image)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = gxfp_session_arm_finger_down(session);
|
||||
if (ret)
|
||||
return ret;
|
||||
puts("Put finger on sensor and keep it there...");
|
||||
ret = gxfp_session_wait_finger_down(session, 10000);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = gxfp_session_capture(session, image, 20000);
|
||||
if (!ret)
|
||||
printf("capture CRC: stored=0x%08x calculated=0x%08x\n",
|
||||
image->crc_stored, image->crc_calculated);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test_finger_up_timeout(struct gxfp_session *session)
|
||||
{
|
||||
struct gxfp_image12 image = { 0 };
|
||||
int ret;
|
||||
|
||||
puts("\n[2/3] wait-finger-up timeout and hardware recovery");
|
||||
ret = capture_while_present(session, &image);
|
||||
if (ret)
|
||||
goto out;
|
||||
ret = gxfp_session_arm_finger_up(session);
|
||||
if (ret)
|
||||
goto out;
|
||||
puts("KEEP finger pressed for 3 seconds (timeout is intentional)...");
|
||||
ret = gxfp_session_wait_finger_up(session, 3000);
|
||||
if (ret != -ETIMEDOUT) {
|
||||
fprintf(stderr, "expected -ETIMEDOUT, got %d\n", ret);
|
||||
ret = -EPROTO;
|
||||
goto out;
|
||||
}
|
||||
if (gxfp_session_get_state(session) != GXFP_SESSION_ERROR) {
|
||||
fprintf(stderr, "timeout did not leave session in ERROR\n");
|
||||
ret = -EPROTO;
|
||||
goto out;
|
||||
}
|
||||
puts("PASS: finger-up timed out; remove finger now");
|
||||
sleep_ms(1000);
|
||||
ret = recover(session);
|
||||
out:
|
||||
gxfp_image12_clear(&image);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test_normal_cycle(struct gxfp_session *session)
|
||||
{
|
||||
struct gxfp_image12 image = { 0 };
|
||||
int ret;
|
||||
|
||||
puts("\n[3/3] normal capture after both recoveries");
|
||||
ret = capture_while_present(session, &image);
|
||||
if (ret)
|
||||
goto out;
|
||||
ret = gxfp_session_arm_finger_up(session);
|
||||
if (ret)
|
||||
goto out;
|
||||
puts("Remove finger...");
|
||||
ret = gxfp_session_wait_finger_up(session, 10000);
|
||||
if (!ret && gxfp_session_get_state(session) != GXFP_SESSION_ACTIVE)
|
||||
ret = -EPROTO;
|
||||
if (!ret)
|
||||
puts("PASS: normal cycle completed in ACTIVE state");
|
||||
out:
|
||||
gxfp_image12_clear(&image);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *path = argc == 2 ? argv[1] : "/dev/gxfp";
|
||||
struct gxfp_session *session = NULL;
|
||||
int ret;
|
||||
|
||||
if (argc > 2) {
|
||||
fprintf(stderr, "usage: %s [device]\n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
ret = gxfp_session_open(&session, path);
|
||||
if (ret)
|
||||
goto out;
|
||||
ret = activate(session);
|
||||
if (ret)
|
||||
goto out;
|
||||
ret = test_cancel_wait_down(session);
|
||||
if (ret)
|
||||
goto out;
|
||||
ret = test_finger_up_timeout(session);
|
||||
if (ret)
|
||||
goto out;
|
||||
ret = test_normal_cycle(session);
|
||||
out:
|
||||
if (ret)
|
||||
fprintf(stderr, "regression failed: %s (%d), state=%d\n",
|
||||
error_string(ret), ret,
|
||||
session ? (int)gxfp_session_get_state(session) : -1);
|
||||
if (session) {
|
||||
int cleanup_ret = gxfp_session_deactivate(session);
|
||||
if (!ret && cleanup_ret)
|
||||
ret = cleanup_ret;
|
||||
gxfp_session_close(session);
|
||||
}
|
||||
if (!ret)
|
||||
puts("\nALL SESSION REGRESSIONS PASSED");
|
||||
return ret ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user