Files
OpenCellular/common/fpsensor.c
Vincent Palatin 96a7e9fe81 fpsensor: update interface
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>
2018-01-23 05:25:08 -08:00

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 */