diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/build.mk | 1 | ||||
-rw-r--r-- | common/fpsensor.c | 306 |
2 files changed, 307 insertions, 0 deletions
diff --git a/common/build.mk b/common/build.mk index 70922f95fe..e48a7f2c35 100644 --- a/common/build.mk +++ b/common/build.mk @@ -122,6 +122,7 @@ common-$(CONFIG_WIRELESS)+=wireless.o common-$(HAS_TASK_CHIPSET)+=chipset.o common-$(HAS_TASK_CONSOLE)+=console.o console_output.o uart_buffering.o common-$(CONFIG_CMD_MEM)+=memory_commands.o +common-$(HAS_TASK_FPSENSOR)+=fpsensor.o common-$(HAS_TASK_HOSTCMD)+=host_command.o ec_features.o common-$(HAS_TASK_PDCMD)+=host_command_pd.o common-$(HAS_TASK_KEYSCAN)+=keyboard_scan.o diff --git a/common/fpsensor.c b/common/fpsensor.c new file mode 100644 index 0000000000..50057ca3e6 --- /dev/null +++ b/common/fpsensor.c @@ -0,0 +1,306 @@ +/* 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" +#ifdef HAVE_PRIVATE +#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) + +/* 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 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_PRIVATE + /* 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 (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(fp_buffer); + + 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_PRIVATE */ + while (1) { + uint32_t evt = task_wait_event(timeout_us); + + send_mkbp_event(evt); + } +#endif /* !HAVE_PRIVATE */ +} + +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_PRIVATE + 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; + + if (params->offset + params->size > sizeof(fp_buffer) || + params->size > args->response_max) + return EC_RES_INVALID_PARAM; + + memcpy(out, fp_buffer + params->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 --- */ + +/* raw image offset inside the acquired frame */ +#ifndef FP_SENSOR_IMAGE_OFFSET +#define FP_SENSOR_IMAGE_OFFSET 0 +#endif + +/* + * 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; + + ccprintf("Waiting for finger ...\n"); + sensor_mode = FP_MODE_CAPTURE; + 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 */ |