blob: a79237c9acfff511254485eaa05fc018ab7532e0 [file]
/* Copyright 2024 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <drivers/fingerprint.h>
#include <fingerprint/fingerprint_alg.h>
#include <fpsensor/fpsensor.h>
#include <fpsensor/fpsensor_detect.h>
#include <fpsensor/fpsensor_state_driver.h>
#include <fpsensor_driver.h>
#include <task.h>
#if !DT_HAS_CHOSEN(cros_fp_fingerprint_sensor)
#error "cros-fp,fingerprint-sensor device must be chosen"
#else
#define fp_sensor_dev DEVICE_DT_GET(DT_CHOSEN(cros_fp_fingerprint_sensor))
#endif
#define CROS_FP_HAS_COMPAT(compat) \
DT_NODE_HAS_COMPAT(DT_CHOSEN(cros_fp_fingerprint_sensor), compat)
static const struct fingerprint_algorithm *fp_algorithm;
enum fp_sensor_type fpsensor_detect_get_type(void)
{
#if DT_NODE_EXISTS(DT_NODELABEL(fp_sensor_sel))
enum fp_sensor_type ret = FP_SENSOR_TYPE_UNKNOWN;
gpio_pin_set_dt(GPIO_DT_FROM_NODELABEL(div_highside), 1);
k_usleep(1);
switch (gpio_pin_get_dt(GPIO_DT_FROM_NODELABEL(fp_sensor_sel))) {
case 0:
ret = FP_SENSOR_TYPE_ELAN;
break;
case 1:
ret = FP_SENSOR_TYPE_FPC;
break;
}
/* We leave GPIO_DIVIDER_HIGHSIDE enabled, since the dragonclaw
* development board use it to enable the AND gate (U10) to CS.
* Production boards could disable this to save power since it's
* only needed for initial detection on those boards.
*/
return ret;
#elif CROS_FP_HAS_COMPAT(fpc_fpc1025) || CROS_FP_HAS_COMPAT(fpc_fpc1145)
return FP_SENSOR_TYPE_FPC;
#elif CROS_FP_HAS_COMPAT(elan_elan80sg) || CROS_FP_HAS_COMPAT(elan_elani80sa)
return FP_SENSOR_TYPE_ELAN;
#elif CROS_FP_HAS_COMPAT(egis_egis630) || CROS_FP_HAS_COMPAT(egis_egis660)
return FP_SENSOR_TYPE_EGIS;
#elif CROS_FP_HAS_COMPAT(ft_ft9865)
return FP_SENSOR_TYPE_FOCALTECH;
#else
#error "Unsupported sensor type"
#endif
}
static void fp_sensor_irq(const struct device *dev)
{
task_set_event(TASK_ID_FPSENSOR, TASK_EVENT_SENSOR_IRQ);
}
int fp_sensor_init(void)
{
int rc;
rc = fingerprint_init(fp_sensor_dev);
if (rc) {
return rc;
}
rc = fingerprint_algorithm_count_get();
if (rc < 1) {
return -ENOENT;
}
/* Get the first available algorithm for now */
fp_algorithm = fingerprint_algorithm_get(0);
if (fp_algorithm == NULL) {
return -ENOENT;
}
rc = fingerprint_algorithm_init(fp_algorithm);
if (rc) {
return rc;
}
rc = fingerprint_config(fp_sensor_dev, fp_sensor_irq);
if (rc) {
return rc;
}
return 0;
}
int fp_sensor_deinit(void)
{
int rc;
rc = fingerprint_algorithm_exit(fp_algorithm);
if (rc) {
return rc;
}
rc = fingerprint_deinit(fp_sensor_dev);
if (rc) {
return rc;
}
return 0;
}
int fp_sensor_get_info(struct ec_response_fp_info_v3 *resp, size_t resp_size)
{
if (resp == NULL) {
return -EINVAL;
}
const size_t expected_min_size =
sizeof(struct ec_response_fp_info_v3) +
NUM_IMAGE_CAPTURE_TYPES *
sizeof(struct fingerprint_image_frame_params);
if (resp_size < expected_min_size) {
return -EOVERFLOW;
}
// Zero-initialize in case fingerprint_get_info doesn't fill all fields.
struct fingerprint_sensor_info sensor_info = { 0 };
struct fingerprint_image_frame_params
image_frame_params_array[NUM_IMAGE_CAPTURE_TYPES] = { 0 };
uint8_t num_params = NUM_IMAGE_CAPTURE_TYPES;
int rc = fingerprint_get_info(fp_sensor_dev, &sensor_info,
image_frame_params_array, &num_params);
if (rc) {
return rc;
}
if (sensor_info.num_capture_types < 0 ||
sensor_info.num_capture_types > NUM_IMAGE_CAPTURE_TYPES) {
return -EINVAL;
}
BUILD_ASSERT(sizeof(resp->sensor_info) == sizeof(sensor_info),
"struct fingerprint_sensor_info size mismatch");
memcpy(&resp->sensor_info, &sensor_info,
sizeof(struct fingerprint_sensor_info));
size_t copy_size = sensor_info.num_capture_types *
sizeof(struct fingerprint_image_frame_params);
memcpy(&resp->image_frame_params, &image_frame_params_array, copy_size);
return 0;
}
void fp_configure_detect(void)
{
fingerprint_set_mode(fp_sensor_dev, FINGERPRINT_SENSOR_MODE_DETECT);
}
int fp_acquire_image(uint8_t *image_data, enum fp_capture_type capture_type)
{
return fingerprint_acquire_image(
fp_sensor_dev, (enum fingerprint_capture_type)capture_type,
image_data, FP_SENSOR_IMAGE_SIZE);
}
/* BUILD_ASSERTs to ensure enum values are the same. */
BUILD_ASSERT((int)FINGERPRINT_CAPTURE_TYPE_VENDOR_FORMAT ==
(int)FP_CAPTURE_VENDOR_FORMAT);
BUILD_ASSERT((int)FINGERPRINT_CAPTURE_TYPE_DEFECT_PXL_TEST ==
(int)FP_CAPTURE_DEFECT_PXL_TEST);
BUILD_ASSERT((int)FINGERPRINT_CAPTURE_TYPE_ABNORMAL_TEST ==
(int)FP_CAPTURE_ABNORMAL_TEST);
BUILD_ASSERT((int)FINGERPRINT_CAPTURE_TYPE_NOISE_TEST ==
(int)FP_CAPTURE_NOISE_TEST);
BUILD_ASSERT((int)FINGERPRINT_CAPTURE_TYPE_SIMPLE_IMAGE ==
(int)FP_CAPTURE_SIMPLE_IMAGE);
BUILD_ASSERT((int)FINGERPRINT_CAPTURE_TYPE_PATTERN0 ==
(int)FP_CAPTURE_PATTERN0);
BUILD_ASSERT((int)FINGERPRINT_CAPTURE_TYPE_PATTERN1 ==
(int)FP_CAPTURE_PATTERN1);
BUILD_ASSERT((int)FINGERPRINT_CAPTURE_TYPE_QUALITY_TEST ==
(int)FP_CAPTURE_QUALITY_TEST);
BUILD_ASSERT((int)FINGERPRINT_CAPTURE_TYPE_RESET_TEST ==
(int)FP_CAPTURE_RESET_TEST);
BUILD_ASSERT((int)FINGERPRINT_CAPTURE_TYPE_MAX == (int)FP_CAPTURE_TYPE_MAX);
enum finger_state fp_finger_status(void)
{
int rc;
rc = fingerprint_finger_status(fp_sensor_dev);
if (rc < 0) {
return FINGER_NONE;
}
return rc;
}
int fp_enrollment_begin(void)
{
return fingerprint_enroll_start(fp_algorithm);
}
int fp_finger_enroll(uint8_t *image, int *completion)
{
return fingerprint_enroll_step(fp_algorithm, image, completion);
}
int fp_enrollment_finish(void *templ)
{
return fingerprint_enroll_finish(fp_algorithm, templ);
}
int fp_finger_match(void *templ, uint32_t templ_count, uint8_t *image,
bool template_update, int32_t *match_index,
uint32_t *update_bitmap)
{
return fingerprint_match(fp_algorithm, templ, templ_count, image,
template_update, match_index, update_bitmap);
}
void fp_sensor_low_power(void)
{
fingerprint_set_mode(fp_sensor_dev, FINGERPRINT_SENSOR_MODE_LOW_POWER);
}
int fp_maintenance(void)
{
return fingerprint_maintenance(fp_sensor_dev, fp_buffer,
sizeof(fp_buffer));
}
int fp_idle(void)
{
return fingerprint_set_mode(fp_sensor_dev,
FINGERPRINT_SENSOR_MODE_IDLE);
}