213 lines
6.5 KiB
C
213 lines
6.5 KiB
C
// 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;
|
|
}
|