| /* 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 "common.h" |
| #include "egis_api.h" |
| #include "fpsensor/fpsensor.h" |
| #include "gpio.h" |
| #include "plat_reset.h" |
| #include "system.h" |
| #include "task.h" |
| #include "util.h" |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #define LOG_TAG "RBS-rapwer" |
| |
| /* recorded error flags */ |
| static uint16_t errors; |
| |
| /* Sensor description */ |
| static struct fp_sensor_info egis_sensor_info = { |
| /* Sensor identification */ |
| .vendor_id = FOURCC('E', 'G', 'I', 'S'), |
| .product_id = 9, |
| .model_id = 1, |
| .version = 1, |
| }; |
| |
| #define EGIS_DEFAULT_IMAGE_PARAMS \ |
| .bpp = FP_SENSOR_DEFAULT_BPP_EGIS, \ |
| .frame_size = FP_SENSOR_RES_X_EGIS * FP_SENSOR_RES_Y_EGIS, \ |
| .pixel_format = V4L2_PIX_FMT_GREY, .width = FP_SENSOR_RES_X_EGIS, \ |
| .height = FP_SENSOR_RES_Y_EGIS |
| |
| #define EGIS_TEST_IMAGE_PARAMS \ |
| .bpp = FP_SENSOR_TEST_BPP_EGIS, \ |
| .frame_size = FP_SENSOR_RES_X_EGIS * FP_SENSOR_RES_Y_EGIS * \ |
| sizeof(uint16_t), \ |
| .pixel_format = V4L2_PIX_FMT_GREY, .width = FP_SENSOR_RES_X_EGIS, \ |
| .height = FP_SENSOR_RES_Y_EGIS |
| |
| static const struct fp_image_frame_params egis_image_frame_params[] = { |
| [EGIS_CAPTURE_NORMAL_FORMAT] = |
| { |
| EGIS_DEFAULT_IMAGE_PARAMS, |
| .fp_capture_type = FP_CAPTURE_SIMPLE_IMAGE, |
| }, |
| [EGIS_CAPTURE_BLACK_PXL_TEST] = |
| { |
| EGIS_TEST_IMAGE_PARAMS, |
| .fp_capture_type = FP_CAPTURE_PATTERN0, |
| }, |
| [EGIS_CAPTURE_WHITE_PXL_TEST] = |
| { |
| EGIS_TEST_IMAGE_PARAMS, |
| .fp_capture_type = FP_CAPTURE_PATTERN1, |
| }, |
| [EGIS_CAPTURE_DEFECT_PXL_TEST] = |
| { |
| EGIS_TEST_IMAGE_PARAMS, |
| .fp_capture_type = FP_CAPTURE_DEFECT_PXL_TEST, |
| }, |
| [EGIS_CAPTURE_NOISE_TEST] = |
| { |
| EGIS_TEST_IMAGE_PARAMS, |
| .fp_capture_type = FP_CAPTURE_NOISE_TEST, |
| }, |
| [EGIS_CAPTURE_ABNORMAL_TEST] = |
| { |
| EGIS_TEST_IMAGE_PARAMS, |
| .fp_capture_type = FP_CAPTURE_ABNORMAL_TEST, |
| }, |
| [EGIS_CAPTURE_RV_INT_TEST] = |
| { |
| EGIS_TEST_IMAGE_PARAMS, |
| .fp_capture_type = FP_CAPTURE_QUALITY_TEST, |
| }, |
| }; |
| |
| static int convert_egis_get_image_error_code(egis_api_return_t code) |
| { |
| switch (code) { |
| case EGIS_API_IMAGE_QUALITY_GOOD: |
| return EC_SUCCESS; |
| case EGIS_API_IMAGE_QUALITY_BAD: |
| case EGIS_API_IMAGE_QUALITY_WATER: |
| return FP_SENSOR_LOW_IMAGE_QUALITY; |
| case EGIS_API_IMAGE_EMPTY: |
| return FP_SENSOR_TOO_FAST; |
| case EGIS_API_IMAGE_QUALITY_PARTIAL: |
| return FP_SENSOR_LOW_SENSOR_COVERAGE; |
| default: |
| assert(code < 0); |
| return code; |
| } |
| } |
| |
| static egis_capture_mode_t |
| convert_fp_capture_type_to_egis_capture_type(enum fp_capture_type capture_type) |
| { |
| switch (capture_type) { |
| case FP_CAPTURE_VENDOR_FORMAT: |
| case FP_CAPTURE_SIMPLE_IMAGE: |
| return EGIS_CAPTURE_NORMAL_FORMAT; |
| case FP_CAPTURE_PATTERN0: |
| return EGIS_CAPTURE_BLACK_PXL_TEST; |
| case FP_CAPTURE_PATTERN1: |
| return EGIS_CAPTURE_WHITE_PXL_TEST; |
| case FP_CAPTURE_QUALITY_TEST: |
| return EGIS_CAPTURE_RV_INT_TEST; |
| case FP_CAPTURE_DEFECT_PXL_TEST: |
| return EGIS_CAPTURE_DEFECT_PXL_TEST; |
| case FP_CAPTURE_ABNORMAL_TEST: |
| return EGIS_CAPTURE_ABNORMAL_TEST; |
| case FP_CAPTURE_NOISE_TEST: |
| return EGIS_CAPTURE_NOISE_TEST; |
| /* Egis does not support the reset test. */ |
| case FP_CAPTURE_RESET_TEST: |
| default: |
| return EGIS_CAPTURE_TYPE_INVALID; |
| } |
| } |
| |
| void fp_sensor_low_power(void) |
| { |
| egis_sensor_power_down(); |
| } |
| |
| int fp_sensor_init(void) |
| { |
| egis_fp_reset_sensor(); |
| /* |
| * Sensor has two INT pads (INT and INTB), and the polarities of INT and |
| * INTB are opposite, Not sure about the final wiring configuration, |
| * so we use a comparison approach. |
| */ |
| int int_pin_value = gpio_get_level(GPIO_FPS_INT); |
| egis_api_return_t ret = egis_sensor_init(); |
| errors = 0; |
| if (ret == EGIS_API_ERROR_IO_SPI) { |
| errors |= FP_ERROR_SPI_COMM; |
| } else if (ret == EGIS_API_ERROR_DEVICE_NOT_FOUND) { |
| errors |= FP_ERROR_BAD_HWID; |
| } else if (ret != EGIS_API_OK) { |
| errors |= FP_ERROR_INIT_FAIL; |
| } |
| |
| if (int_pin_value == gpio_get_level(GPIO_FPS_INT)) { |
| CPRINTS("Sensor IRQ not ready"); |
| errors |= FP_ERROR_NO_IRQ; |
| } |
| |
| return EC_SUCCESS; |
| } |
| |
| int fp_sensor_deinit(void) |
| { |
| return egis_sensor_deinit(); |
| } |
| |
| int fp_sensor_get_info(struct ec_response_fp_info_v2 *resp, size_t resp_size) |
| { |
| if (sizeof(struct ec_response_fp_info_v2) + |
| sizeof(egis_image_frame_params) > |
| resp_size) { |
| return EC_RES_OVERFLOW; |
| } |
| |
| uint16_t sensor_id; |
| if (egis_get_hwid(&sensor_id) != EGIS_API_OK) |
| return EC_RES_ERROR; |
| |
| memcpy(&resp->sensor_info, &egis_sensor_info, |
| sizeof(struct fp_sensor_info)); |
| |
| memcpy(&resp->image_frame_params, &egis_image_frame_params, |
| sizeof(egis_image_frame_params)); |
| |
| resp->sensor_info.model_id = sensor_id; |
| resp->sensor_info.errors = errors; |
| resp->sensor_info.num_capture_types = |
| ARRAY_SIZE(egis_image_frame_params); |
| |
| return EC_SUCCESS; |
| } |
| |
| __overridable int fp_finger_match(void *templ, uint32_t templ_count, |
| uint8_t *image, int32_t *match_index, |
| uint32_t *update_bitmap) |
| { |
| egis_api_return_t ret = egis_finger_match(templ, templ_count, image, |
| match_index, update_bitmap); |
| |
| switch (ret) { |
| case EGIS_API_MATCH_MATCHED: |
| return EC_MKBP_FP_ERR_MATCH_YES; |
| case EGIS_API_MATCH_MATCHED_UPDATED: |
| return EC_MKBP_FP_ERR_MATCH_YES_UPDATED; |
| case EGIS_API_MATCH_MATCHED_UPDATED_FAILED: |
| return EC_MKBP_FP_ERR_MATCH_YES_UPDATE_FAILED; |
| case EGIS_API_MATCH_NOT_MATCHED: |
| return EC_MKBP_FP_ERR_MATCH_NO; |
| case EGIS_API_MATCH_LOW_QUALITY: |
| return EC_MKBP_FP_ERR_MATCH_NO_LOW_QUALITY; |
| case EGIS_API_MATCH_LOW_COVERAGE: |
| return EC_MKBP_FP_ERR_MATCH_NO_LOW_COVERAGE; |
| default: |
| assert(ret < 0); |
| return ret; |
| } |
| } |
| |
| __overridable int fp_enrollment_begin(void) |
| { |
| return egis_enrollment_begin(); |
| } |
| |
| __overridable int fp_enrollment_finish(void *templ) |
| { |
| return egis_enrollment_finish(templ); |
| } |
| |
| __overridable int fp_finger_enroll(uint8_t *image, int *completion) |
| { |
| egis_api_return_t ret = egis_finger_enroll(image, completion); |
| switch (ret) { |
| case EGIS_API_ENROLL_FINISH: |
| case EGIS_API_ENROLL_IMAGE_OK: |
| return EC_MKBP_FP_ERR_ENROLL_OK; |
| case EGIS_API_ENROLL_REDUNDANT_INPUT: |
| return EC_MKBP_FP_ERR_ENROLL_IMMOBILE; |
| case EGIS_API_ENROLL_LOW_QUALITY: |
| return EC_MKBP_FP_ERR_ENROLL_LOW_QUALITY; |
| case EGIS_API_ENROLL_LOW_COVERAGE: |
| return EC_MKBP_FP_ERR_ENROLL_LOW_COVERAGE; |
| default: |
| assert(ret < 0); |
| return ret; |
| } |
| } |
| |
| int fp_maintenance(void) |
| { |
| return EC_SUCCESS; |
| } |
| |
| int fp_acquire_image(uint8_t *image_data, enum fp_capture_type capture_type) |
| { |
| egis_capture_mode_t rc = |
| convert_fp_capture_type_to_egis_capture_type(capture_type); |
| |
| if (rc == EGIS_CAPTURE_TYPE_INVALID) { |
| CPRINTS("Unsupported capture_type %d provided", capture_type); |
| return -EINVAL; |
| } |
| return convert_egis_get_image_error_code( |
| egis_get_image_with_mode(image_data, rc)); |
| } |
| |
| enum finger_state fp_finger_status(void) |
| { |
| egislog_i(""); |
| egis_api_return_t ret = egis_check_int_status(); |
| |
| switch (ret) { |
| case EGIS_API_FINGER_PRESENT: |
| return FINGER_PRESENT; |
| case EGIS_API_FINGER_LOST: |
| default: |
| return FINGER_NONE; |
| } |
| } |
| |
| void fp_configure_detect(void) |
| { |
| egislog_i(""); |
| egis_set_detect_mode(); |
| } |