mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-28 02:35:28 +00:00
Update the FP MCU interface to include a few convenient diagnostics functions for factory testing. It's mostly backward compatible, but overall this interface never shipped in anything, so not a big deal regardless. Signed-off-by: Vincent Palatin <vpalatin@chromium.org> BRANCH=none BUG=b:71986991 TEST=ectool --name=cros_fp fpinfo && ectool --name=cros_fp fpcheckpixels CQ-DEPEND=CL:*546799 Change-Id: Ic641f891ace02d79af9339cf6cb59a2960e506a7 Reviewed-on: https://chromium-review.googlesource.com/873924 Commit-Ready: Vincent Palatin <vpalatin@chromium.org> Tested-by: Vincent Palatin <vpalatin@chromium.org> Reviewed-by: Shawn N <shawnn@chromium.org>
340 lines
9.2 KiB
C
340 lines
9.2 KiB
C
/* Copyright 2017 The Chromium OS Authors. All rights reserved.
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "atomic.h"
|
|
#include "common.h"
|
|
#include "console.h"
|
|
#include "ec_commands.h"
|
|
#include "fpsensor.h"
|
|
#include "gpio.h"
|
|
#include "host_command.h"
|
|
#include "mkbp_event.h"
|
|
#include "spi.h"
|
|
#include "system.h"
|
|
#include "task.h"
|
|
#include "timer.h"
|
|
#include "util.h"
|
|
#include "watchdog.h"
|
|
#if defined(HAVE_PRIVATE) && !defined(TEST_BUILD)
|
|
#define HAVE_FP_PRIVATE_DRIVER
|
|
#define PRIV_HEADER(header) STRINGIFY(header)
|
|
#include PRIV_HEADER(FP_SENSOR_PRIVATE)
|
|
#else
|
|
#define FP_SENSOR_IMAGE_SIZE 0
|
|
#define FP_SENSOR_RES_X 0
|
|
#define FP_SENSOR_RES_Y 0
|
|
#endif
|
|
|
|
/* Last acquired frame */
|
|
static uint8_t fp_buffer[FP_SENSOR_IMAGE_SIZE];
|
|
|
|
#define CPRINTF(format, args...) cprintf(CC_FP, format, ## args)
|
|
#define CPRINTS(format, args...) cprints(CC_FP, format, ## args)
|
|
|
|
/* raw image offset inside the acquired frame */
|
|
#ifndef FP_SENSOR_IMAGE_OFFSET
|
|
#define FP_SENSOR_IMAGE_OFFSET 0
|
|
#endif
|
|
|
|
/* Events for the FPSENSOR task */
|
|
#define TASK_EVENT_SENSOR_IRQ TASK_EVENT_CUSTOM(1)
|
|
#define TASK_EVENT_UPDATE_CONFIG TASK_EVENT_CUSTOM(2)
|
|
|
|
#define FP_MODE_ANY_DETECT_FINGER (FP_MODE_FINGER_DOWN | FP_MODE_FINGER_UP | \
|
|
FP_MODE_CAPTURE)
|
|
#define FP_MODE_ANY_WAIT_IRQ (FP_MODE_FINGER_DOWN | FP_MODE_CAPTURE)
|
|
|
|
/* Delay between 2 s of the sensor to detect finger removal */
|
|
#define FINGER_POLLING_DELAY (100*MSEC)
|
|
|
|
static uint32_t fp_events;
|
|
static uint32_t sensor_mode;
|
|
|
|
/* Interrupt line from the fingerprint sensor */
|
|
void fps_event(enum gpio_signal signal)
|
|
{
|
|
task_set_event(TASK_ID_FPSENSOR, TASK_EVENT_SENSOR_IRQ, 0);
|
|
}
|
|
|
|
static inline int is_test_capture(uint32_t mode)
|
|
{
|
|
int capture_type = FP_CAPTURE_TYPE(mode);
|
|
|
|
return (mode & FP_MODE_CAPTURE)
|
|
&& (capture_type == FP_CAPTURE_PATTERN0
|
|
|| capture_type == FP_CAPTURE_PATTERN1);
|
|
}
|
|
|
|
static void send_mkbp_event(uint32_t event)
|
|
{
|
|
atomic_or(&fp_events, event);
|
|
mkbp_send_event(EC_MKBP_EVENT_FINGERPRINT);
|
|
}
|
|
|
|
void fp_task(void)
|
|
{
|
|
int timeout_us = -1;
|
|
|
|
/* configure the SPI controller (also ensure that CS_N is high) */
|
|
gpio_config_module(MODULE_SPI_MASTER, 1);
|
|
spi_enable(CONFIG_SPI_FP_PORT, 1);
|
|
|
|
#ifdef HAVE_FP_PRIVATE_DRIVER
|
|
/* Reset and initialize the sensor IC */
|
|
fp_sensor_init();
|
|
|
|
while (1) {
|
|
uint32_t evt;
|
|
enum finger_state st = FINGER_NONE;
|
|
|
|
/* Wait for a sensor IRQ or a new mode configuration */
|
|
evt = task_wait_event(timeout_us);
|
|
|
|
if (evt & TASK_EVENT_UPDATE_CONFIG) {
|
|
gpio_disable_interrupt(GPIO_FPS_INT);
|
|
if (is_test_capture(sensor_mode)) {
|
|
fp_sensor_acquire_image_with_mode(fp_buffer,
|
|
FP_CAPTURE_TYPE(sensor_mode));
|
|
sensor_mode &= ~FP_MODE_CAPTURE;
|
|
send_mkbp_event(EC_MKBP_FP_IMAGE_READY);
|
|
continue;
|
|
} else if (sensor_mode & FP_MODE_ANY_DETECT_FINGER) {
|
|
/* wait for a finger on the sensor */
|
|
fp_sensor_configure_detect();
|
|
}
|
|
if (sensor_mode & FP_MODE_DEEPSLEEP)
|
|
/* Shutdown the sensor */
|
|
fp_sensor_low_power();
|
|
if (sensor_mode & FP_MODE_FINGER_UP)
|
|
/* Poll the sensor to detect finger removal */
|
|
timeout_us = FINGER_POLLING_DELAY;
|
|
else
|
|
timeout_us = -1;
|
|
if (sensor_mode & FP_MODE_ANY_WAIT_IRQ)
|
|
gpio_enable_interrupt(GPIO_FPS_INT);
|
|
} else if (evt & (TASK_EVENT_SENSOR_IRQ | TASK_EVENT_TIMER)) {
|
|
gpio_disable_interrupt(GPIO_FPS_INT);
|
|
if (sensor_mode & FP_MODE_ANY_DETECT_FINGER) {
|
|
st = fp_sensor_finger_status();
|
|
if (st == FINGER_PRESENT &&
|
|
sensor_mode & FP_MODE_FINGER_DOWN) {
|
|
CPRINTS("Finger!");
|
|
sensor_mode &= ~FP_MODE_FINGER_DOWN;
|
|
send_mkbp_event(EC_MKBP_FP_FINGER_DOWN);
|
|
}
|
|
if (st == FINGER_NONE &&
|
|
sensor_mode & FP_MODE_FINGER_UP) {
|
|
sensor_mode &= ~FP_MODE_FINGER_UP;
|
|
timeout_us = -1;
|
|
send_mkbp_event(EC_MKBP_FP_FINGER_UP);
|
|
}
|
|
}
|
|
|
|
if (st == FINGER_PRESENT &&
|
|
sensor_mode & FP_MODE_CAPTURE) {
|
|
int res = fp_sensor_acquire_image_with_mode(
|
|
fp_buffer,
|
|
FP_CAPTURE_TYPE(sensor_mode));
|
|
|
|
if (!res) {
|
|
sensor_mode &= ~FP_MODE_CAPTURE;
|
|
send_mkbp_event(EC_MKBP_FP_IMAGE_READY);
|
|
}
|
|
}
|
|
if (sensor_mode & FP_MODE_ANY_WAIT_IRQ) {
|
|
fp_sensor_configure_detect();
|
|
gpio_enable_interrupt(GPIO_FPS_INT);
|
|
}
|
|
}
|
|
}
|
|
#else /* !HAVE_FP_PRIVATE_DRIVER */
|
|
while (1) {
|
|
uint32_t evt = task_wait_event(timeout_us);
|
|
|
|
send_mkbp_event(evt);
|
|
}
|
|
#endif /* !HAVE_FP_PRIVATE_DRIVER */
|
|
}
|
|
|
|
static int fp_get_next_event(uint8_t *out)
|
|
{
|
|
uint32_t event_out = atomic_read_clear(&fp_events);
|
|
|
|
memcpy(out, &event_out, sizeof(event_out));
|
|
|
|
return sizeof(event_out);
|
|
}
|
|
DECLARE_EVENT_SOURCE(EC_MKBP_EVENT_FINGERPRINT, fp_get_next_event);
|
|
|
|
static int fp_command_passthru(struct host_cmd_handler_args *args)
|
|
{
|
|
const struct ec_params_fp_passthru *params = args->params;
|
|
void *out = args->response;
|
|
int rc;
|
|
int ret = EC_RES_SUCCESS;
|
|
|
|
if (system_is_locked())
|
|
return EC_RES_ACCESS_DENIED;
|
|
|
|
if (params->len > args->params_size +
|
|
offsetof(struct ec_params_fp_passthru, data) ||
|
|
params->len > args->response_max)
|
|
return EC_RES_INVALID_PARAM;
|
|
|
|
rc = spi_transaction_async(&spi_devices[0], params->data,
|
|
params->len, out, SPI_READBACK_ALL);
|
|
if (params->flags & EC_FP_FLAG_NOT_COMPLETE)
|
|
rc |= spi_transaction_wait(&spi_devices[0]);
|
|
else
|
|
rc |= spi_transaction_flush(&spi_devices[0]);
|
|
|
|
if (rc == EC_ERROR_TIMEOUT)
|
|
ret = EC_RES_TIMEOUT;
|
|
else if (rc)
|
|
ret = EC_RES_ERROR;
|
|
|
|
args->response_size = params->len;
|
|
return ret;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_FP_PASSTHRU, fp_command_passthru, EC_VER_MASK(0));
|
|
|
|
static int fp_command_sensor_config(struct host_cmd_handler_args *args)
|
|
{
|
|
/* const struct ec_params_fp_sensor_config *p = args->params; */
|
|
|
|
return EC_RES_UNAVAILABLE;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_FP_SENSOR_CONFIG, fp_command_sensor_config,
|
|
EC_VER_MASK(0));
|
|
|
|
static int fp_command_mode(struct host_cmd_handler_args *args)
|
|
{
|
|
const struct ec_params_fp_mode *p = args->params;
|
|
struct ec_response_fp_mode *r = args->response;
|
|
|
|
if (!(p->mode & FP_MODE_DONT_CHANGE)) {
|
|
sensor_mode = p->mode;
|
|
task_set_event(TASK_ID_FPSENSOR, TASK_EVENT_UPDATE_CONFIG, 0);
|
|
}
|
|
|
|
r->mode = sensor_mode;
|
|
args->response_size = sizeof(*r);
|
|
return EC_RES_SUCCESS;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_FP_MODE, fp_command_mode, EC_VER_MASK(0));
|
|
|
|
static int fp_command_info(struct host_cmd_handler_args *args)
|
|
{
|
|
struct ec_response_fp_info *r = args->response;
|
|
|
|
#ifdef HAVE_FP_PRIVATE_DRIVER
|
|
if (fp_sensor_get_info(r) < 0)
|
|
#endif
|
|
return EC_RES_UNAVAILABLE;
|
|
|
|
args->response_size = sizeof(*r);
|
|
return EC_RES_SUCCESS;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_FP_INFO, fp_command_info, EC_VER_MASK(0));
|
|
|
|
static int fp_command_frame(struct host_cmd_handler_args *args)
|
|
{
|
|
const struct ec_params_fp_frame *params = args->params;
|
|
void *out = args->response;
|
|
uint32_t offset = params->offset;
|
|
|
|
if (FP_CAPTURE_TYPE(sensor_mode) != FP_CAPTURE_VENDOR_FORMAT)
|
|
offset += FP_SENSOR_IMAGE_OFFSET;
|
|
|
|
if (offset + params->size > sizeof(fp_buffer) ||
|
|
params->size > args->response_max)
|
|
return EC_RES_INVALID_PARAM;
|
|
|
|
memcpy(out, fp_buffer + offset, params->size);
|
|
|
|
args->response_size = params->size;
|
|
return EC_RES_SUCCESS;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_FP_FRAME, fp_command_frame, EC_VER_MASK(0));
|
|
|
|
#ifdef CONFIG_CMD_FPSENSOR_DEBUG
|
|
/* --- Debug console commands --- */
|
|
|
|
/*
|
|
* Send the current Fingerprint buffer to the host
|
|
* it is formatted as an 8-bpp PGM ASCII file.
|
|
*
|
|
* In addition, it prepends a short Z-Modem download signature,
|
|
* which triggers automatically your preferred viewer if you configure it
|
|
* properly in "File transfer protocols" in the Minicom options menu.
|
|
* (as triggered by Ctrl-A O)
|
|
* +--------------------------------------------------------------------------+
|
|
* | Name Program Name U/D FullScr IO-Red. Multi |
|
|
* | A zmodem /usr/bin/sz -vv -b Y U N Y Y |
|
|
* [...]
|
|
* | L pgm /usr/bin/display_pgm N D N Y N |
|
|
* | M Zmodem download string activates... L |
|
|
*
|
|
* My /usr/bin/display_pgm looks like this:
|
|
* #!/bin/sh
|
|
* TMPF=$(mktemp)
|
|
* ascii-xfr -rdv ${TMPF}
|
|
* display ${TMPF}
|
|
*/
|
|
static void upload_pgm_image(uint8_t *frame)
|
|
{
|
|
int x, y;
|
|
uint8_t *ptr = frame;
|
|
|
|
/* fake Z-modem ZRQINIT signature */
|
|
ccprintf("#IGNORE for ZModem\r**\030B00");
|
|
msleep(100); /* let the download program start */
|
|
/* Print 8-bpp PGM ASCII header */
|
|
ccprintf("P2\n%d %d\n255\n", FP_SENSOR_RES_X, FP_SENSOR_RES_Y);
|
|
|
|
for (y = 0; y < FP_SENSOR_RES_Y; y++) {
|
|
watchdog_reload();
|
|
for (x = 0; x < FP_SENSOR_RES_X; x++, ptr++)
|
|
ccprintf("%d ", *ptr);
|
|
ccputs("\n");
|
|
cflush();
|
|
}
|
|
|
|
ccprintf("\x04"); /* End Of Transmission */
|
|
}
|
|
|
|
int command_fptest(int argc, char **argv)
|
|
{
|
|
int tries = 200;
|
|
int capture_type = FP_CAPTURE_SIMPLE_IMAGE;
|
|
|
|
if (argc >= 2) {
|
|
char *e;
|
|
|
|
capture_type = strtoi(argv[1], &e, 0);
|
|
if (*e || capture_type < 0 || capture_type > 3)
|
|
return EC_ERROR_PARAM1;
|
|
}
|
|
|
|
ccprintf("Waiting for finger ...\n");
|
|
sensor_mode = FP_MODE_CAPTURE |
|
|
(capture_type << FP_MODE_CAPTURE_TYPE_SHIFT);
|
|
task_set_event(TASK_ID_FPSENSOR, TASK_EVENT_UPDATE_CONFIG, 0);
|
|
|
|
while (tries--) {
|
|
if (!(sensor_mode & FP_MODE_CAPTURE)) {
|
|
ccprintf("done\n");
|
|
upload_pgm_image(fp_buffer + FP_SENSOR_IMAGE_OFFSET);
|
|
return 0;
|
|
}
|
|
usleep(100 * MSEC);
|
|
}
|
|
|
|
return EC_ERROR_TIMEOUT;
|
|
}
|
|
DECLARE_CONSOLE_COMMAND(fptest, command_fptest, "", "");
|
|
|
|
#endif /* CONFIG_CMD_FPSENSOR_DEBUG */
|