drivers: sensor: Add support for grow_r502a fingerprint sensor
Add driver support for grow_r502a fingerprint sensor Signed-off-by: Dinesh Kumar K <dinesh@linumiz.com>
This commit is contained in:
parent
df3fdcaaf3
commit
f050e18798
|
@ -371,6 +371,7 @@
|
|||
/drivers/sensor/ @MaureenHelm
|
||||
/drivers/sensor/ams_iAQcore/ @alexanderwachter
|
||||
/drivers/sensor/ens210/ @alexanderwachter
|
||||
/drivers/sensor/grow_r502a/ @DineshDK03
|
||||
/drivers/sensor/hts*/ @avisconti
|
||||
/drivers/sensor/ina23*/ @bbilas
|
||||
/drivers/sensor/lis*/ @avisconti
|
||||
|
|
|
@ -27,6 +27,7 @@ add_subdirectory_ifdef(CONFIG_FDC2X1X fdc2x1x)
|
|||
add_subdirectory_ifdef(CONFIG_FXAS21002 fxas21002)
|
||||
add_subdirectory_ifdef(CONFIG_FXOS8700 fxos8700)
|
||||
add_subdirectory_ifdef(CONFIG_GROVE_SENSORS grove)
|
||||
add_subdirectory_ifdef(CONFIG_GROW_R502A grow_r502a)
|
||||
add_subdirectory_ifdef(CONFIG_TI_HDC ti_hdc)
|
||||
add_subdirectory_ifdef(CONFIG_TI_HDC20XX ti_hdc20xx)
|
||||
add_subdirectory_ifdef(CONFIG_HMC5883L hmc5883l)
|
||||
|
|
|
@ -95,6 +95,8 @@ source "drivers/sensor/fxos8700/Kconfig"
|
|||
|
||||
source "drivers/sensor/grove/Kconfig"
|
||||
|
||||
source "drivers/sensor/grow_r502a/Kconfig"
|
||||
|
||||
source "drivers/sensor/ti_hdc/Kconfig"
|
||||
|
||||
source "drivers/sensor/ti_hdc20xx/Kconfig"
|
||||
|
|
5
drivers/sensor/grow_r502a/CMakeLists.txt
Normal file
5
drivers/sensor/grow_r502a/CMakeLists.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
zephyr_library_sources(grow_r502a.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_GROW_R502A_TRIGGER grow_r502a_trigger.c)
|
60
drivers/sensor/grow_r502a/Kconfig
Normal file
60
drivers/sensor/grow_r502a/Kconfig
Normal file
|
@ -0,0 +1,60 @@
|
|||
# GROW_R502A hzgrow Fingerprint sensor Configuration options
|
||||
|
||||
# Copyright (c) 2021 Linumiz
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig GROW_R502A
|
||||
bool "GROW_R502A Fingerprint Sensor"
|
||||
default y
|
||||
depends on DT_HAS_HZGROW_R502A_ENABLED
|
||||
depends on UART_INTERRUPT_DRIVEN && SERIAL
|
||||
help
|
||||
Enable driver for GROW_R502A Fingerprint Sensor.
|
||||
|
||||
if GROW_R502A
|
||||
|
||||
choice
|
||||
prompt "Trigger mode"
|
||||
default GROW_R502A_TRIGGER_NONE
|
||||
help
|
||||
Specify the type of triggering used by the driver.
|
||||
|
||||
config GROW_R502A_TRIGGER_NONE
|
||||
bool "No trigger"
|
||||
|
||||
config GROW_R502A_TRIGGER_GLOBAL_THREAD
|
||||
bool "Use global thread"
|
||||
depends on GPIO
|
||||
select GROW_R502A_TRIGGER
|
||||
|
||||
config GROW_R502A_TRIGGER_OWN_THREAD
|
||||
bool "Use own thread"
|
||||
depends on GPIO
|
||||
select GROW_R502A_TRIGGER
|
||||
|
||||
endchoice
|
||||
|
||||
|
||||
config GROW_R502A_TRIGGER
|
||||
bool
|
||||
|
||||
config GROW_R502A_THREAD_PRIORITY
|
||||
int "Thread priority"
|
||||
depends on GROW_R502A_TRIGGER_OWN_THREAD && GROW_R502A_TRIGGER
|
||||
default 10
|
||||
help
|
||||
Priority of thread used by the driver to handle interrupts.
|
||||
|
||||
config GROW_R502A_THREAD_STACK_SIZE
|
||||
int "Thread stack size"
|
||||
depends on GROW_R502A_TRIGGER_OWN_THREAD && GROW_R502A_TRIGGER
|
||||
default 1024
|
||||
help
|
||||
Stack size of thread used by the driver to handle interrupts.
|
||||
|
||||
config GROW_R502A_GPIO_POWER
|
||||
bool "GROW_R502A sensor VCC and VT GPIO"
|
||||
help
|
||||
Enable control of vin-gpios and act-gpios.
|
||||
|
||||
endif # GROW_R502A
|
786
drivers/sensor/grow_r502a/grow_r502a.c
Normal file
786
drivers/sensor/grow_r502a/grow_r502a.c
Normal file
|
@ -0,0 +1,786 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Linumiz
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT hzgrow_r502a
|
||||
|
||||
#include <zephyr/init.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include <zephyr/drivers/uart.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
|
||||
#include <zephyr/drivers/sensor/grow_r502a.h>
|
||||
#include "grow_r502a.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(GROW_R502A, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
static void transceive_packet(const struct device *dev, union r502a_packet *tx_packet,
|
||||
union r502a_packet *rx_packet, char const data_len)
|
||||
{
|
||||
const struct grow_r502a_config *cfg = dev->config;
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
uint16_t check_sum, pkg_len;
|
||||
|
||||
pkg_len = data_len + R502A_CHECKSUM_LEN;
|
||||
check_sum = pkg_len + tx_packet->pid;
|
||||
|
||||
sys_put_be16(R502A_STARTCODE, tx_packet->start);
|
||||
sys_put_be32(cfg->comm_addr, tx_packet->addr);
|
||||
sys_put_be16(pkg_len, tx_packet->len);
|
||||
for (int i = 0; i < data_len; i++) {
|
||||
check_sum += tx_packet->data[i];
|
||||
}
|
||||
sys_put_be16(check_sum, &tx_packet->buf[data_len + R502A_HEADER_LEN]);
|
||||
|
||||
drv_data->tx_buf.len = pkg_len + R502A_HEADER_LEN;
|
||||
drv_data->tx_buf.data = tx_packet->buf;
|
||||
|
||||
drv_data->rx_buf.data = rx_packet->buf;
|
||||
|
||||
LOG_HEXDUMP_DBG(drv_data->tx_buf.data, drv_data->tx_buf.len, "TX");
|
||||
|
||||
uart_irq_rx_disable(cfg->dev);
|
||||
uart_irq_tx_enable(cfg->dev);
|
||||
|
||||
k_sem_take(&drv_data->uart_rx_sem, K_FOREVER);
|
||||
}
|
||||
|
||||
static void uart_cb_tx_handler(const struct device *dev)
|
||||
{
|
||||
const struct grow_r502a_config *config = dev->config;
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
int sent = 0;
|
||||
uint8_t retries = 3;
|
||||
|
||||
while (drv_data->tx_buf.len) {
|
||||
sent = uart_fifo_fill(config->dev, &drv_data->tx_buf.data[sent],
|
||||
drv_data->tx_buf.len);
|
||||
drv_data->tx_buf.len -= sent;
|
||||
}
|
||||
|
||||
while (retries--) {
|
||||
if (uart_irq_tx_complete(config->dev)) {
|
||||
uart_irq_tx_disable(config->dev);
|
||||
drv_data->rx_buf.len = 0;
|
||||
uart_irq_rx_enable(config->dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void uart_cb_handler(const struct device *dev, void *user_data)
|
||||
{
|
||||
const struct device *uart_dev = user_data;
|
||||
struct grow_r502a_data *drv_data = uart_dev->data;
|
||||
int len, pkt_sz = 0;
|
||||
int offset = drv_data->rx_buf.len;
|
||||
|
||||
if ((uart_irq_update(dev) > 0) && (uart_irq_is_pending(dev) > 0)) {
|
||||
if (uart_irq_tx_ready(dev)) {
|
||||
uart_cb_tx_handler(uart_dev);
|
||||
}
|
||||
|
||||
while (uart_irq_rx_ready(dev)) {
|
||||
len = uart_fifo_read(dev, &drv_data->rx_buf.data[offset],
|
||||
R502A_BUF_SIZE - offset);
|
||||
offset += len;
|
||||
drv_data->rx_buf.len = offset;
|
||||
|
||||
if (offset >= R502A_HEADER_LEN) {
|
||||
pkt_sz = R502A_HEADER_LEN +
|
||||
drv_data->rx_buf.data[R502A_HEADER_LEN-1];
|
||||
}
|
||||
if (offset < pkt_sz) {
|
||||
continue;
|
||||
}
|
||||
LOG_HEXDUMP_DBG(drv_data->rx_buf.data, offset, "RX");
|
||||
k_sem_give(&drv_data->uart_rx_sem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int fps_led_control(const struct device *dev, struct led_params *led_control)
|
||||
{
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
union r502a_packet rx_packet = {0};
|
||||
char const led_ctrl_len = 5;
|
||||
|
||||
union r502a_packet tx_packet = {
|
||||
.pid = R502A_COMMAND_PACKET,
|
||||
.data = { R502A_LED_CONFIG, led_control->ctrl_code,
|
||||
led_control->speed, led_control->color_idx, led_control->cycle}
|
||||
};
|
||||
|
||||
transceive_packet(dev, &tx_packet, &rx_packet, led_ctrl_len);
|
||||
|
||||
if (rx_packet.pid != R502A_ACK_PACKET) {
|
||||
LOG_ERR("Error receiving ack packet 0x%X", rx_packet.pid);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (rx_packet.buf[R502A_CC_IDX] == R502A_OK) {
|
||||
LOG_DBG("R502A LED ON");
|
||||
k_sleep(K_MSEC(R502A_DELAY));
|
||||
} else {
|
||||
LOG_ERR("R502A LED control error %d", rx_packet.buf[R502A_CC_IDX]);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fps_verify_password(const struct device *dev)
|
||||
{
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
union r502a_packet rx_packet = {0};
|
||||
char const verify_pwd_len = 5;
|
||||
|
||||
union r502a_packet tx_packet = {
|
||||
.pid = R502A_COMMAND_PACKET,
|
||||
.data[0] = R502A_VERIFYPASSWORD,
|
||||
};
|
||||
|
||||
sys_put_be32(R502A_DEFAULT_PASSWORD, &tx_packet.data[1]);
|
||||
|
||||
transceive_packet(dev, &tx_packet, &rx_packet, verify_pwd_len);
|
||||
|
||||
if (rx_packet.pid != R502A_ACK_PACKET) {
|
||||
LOG_ERR("Error receiving ack packet 0x%X", rx_packet.pid);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (rx_packet.buf[R502A_CC_IDX] == R502A_OK) {
|
||||
LOG_DBG("Correct password, R502A verified");
|
||||
} else {
|
||||
LOG_ERR("Package receive error 0x%X", rx_packet.buf[R502A_CC_IDX]);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fps_get_template_count(const struct device *dev)
|
||||
{
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
union r502a_packet rx_packet = {0};
|
||||
char const get_temp_cnt_len = 1;
|
||||
|
||||
union r502a_packet tx_packet = {
|
||||
.pid = R502A_COMMAND_PACKET,
|
||||
.data = {R502A_TEMPLATECOUNT},
|
||||
};
|
||||
|
||||
transceive_packet(dev, &tx_packet, &rx_packet, get_temp_cnt_len);
|
||||
|
||||
if (rx_packet.pid != R502A_ACK_PACKET) {
|
||||
LOG_ERR("Error receiving ack packet 0x%X", rx_packet.pid);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (rx_packet.buf[R502A_CC_IDX] == R502A_OK) {
|
||||
LOG_DBG("Read success");
|
||||
drv_data->template_count = sys_get_be16(&rx_packet.data[1]);
|
||||
LOG_INF("Remaining templates count : %d", drv_data->template_count);
|
||||
} else {
|
||||
LOG_ERR("R502A template count get error");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fps_read_template_table(const struct device *dev)
|
||||
{
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
union r502a_packet rx_packet = {0};
|
||||
char const temp_table_len = 2;
|
||||
int ret = 0;
|
||||
|
||||
union r502a_packet tx_packet = {
|
||||
.pid = R502A_COMMAND_PACKET,
|
||||
.data = {R502A_READTEMPLATEINDEX, 0x00}
|
||||
};
|
||||
|
||||
k_mutex_lock(&drv_data->lock, K_FOREVER);
|
||||
|
||||
transceive_packet(dev, &tx_packet, &rx_packet, temp_table_len);
|
||||
|
||||
if (rx_packet.pid != R502A_ACK_PACKET) {
|
||||
LOG_ERR("Error receiving ack packet 0x%X", rx_packet.pid);
|
||||
ret = -EIO;
|
||||
goto unlock;
|
||||
|
||||
}
|
||||
|
||||
if (rx_packet.buf[R502A_CC_IDX] == R502A_OK) {
|
||||
LOG_DBG("Read success");
|
||||
} else {
|
||||
LOG_ERR("R502A template table get error");
|
||||
ret = -EIO;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
for (int group_idx = 0; group_idx < R502A_TEMP_TABLE_BUF_SIZE; group_idx++) {
|
||||
uint8_t group = rx_packet.data[group_idx + 1];
|
||||
|
||||
/* if group is all occupied */
|
||||
if (group == 0xff) {
|
||||
continue;
|
||||
}
|
||||
|
||||
drv_data->free_idx = (group_idx * 8) + find_lsb_set(~group) - 1;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
unlock:
|
||||
k_mutex_unlock(&drv_data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fps_get_image(const struct device *dev)
|
||||
{
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
union r502a_packet rx_packet = {0};
|
||||
char const get_img_len = 1;
|
||||
|
||||
struct led_params led_ctrl = {
|
||||
.ctrl_code = LED_CTRL_BREATHING,
|
||||
.color_idx = LED_COLOR_BLUE,
|
||||
.speed = LED_SPEED_HALF,
|
||||
.cycle = 0x01,
|
||||
};
|
||||
|
||||
union r502a_packet tx_packet = {
|
||||
.pid = R502A_COMMAND_PACKET,
|
||||
.data = {R502A_GENIMAGE},
|
||||
};
|
||||
|
||||
transceive_packet(dev, &tx_packet, &rx_packet, get_img_len);
|
||||
|
||||
if (rx_packet.pid != R502A_ACK_PACKET) {
|
||||
LOG_ERR("Error receiving ack packet 0x%X", rx_packet.pid);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (rx_packet.buf[R502A_CC_IDX] == R502A_OK) {
|
||||
fps_led_control(dev, &led_ctrl);
|
||||
LOG_DBG("Image taken");
|
||||
} else {
|
||||
led_ctrl.ctrl_code = LED_CTRL_ON_ALWAYS;
|
||||
led_ctrl.color_idx = LED_COLOR_RED;
|
||||
fps_led_control(dev, &led_ctrl);
|
||||
LOG_ERR("Error getting image 0x%X", rx_packet.buf[R502A_CC_IDX]);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fps_image_to_char(const struct device *dev, uint8_t char_buf_idx)
|
||||
{
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
union r502a_packet rx_packet = {0};
|
||||
char const img_to_char_len = 2;
|
||||
|
||||
union r502a_packet tx_packet = {
|
||||
.pid = R502A_COMMAND_PACKET,
|
||||
.data = {R502A_IMAGE2TZ, char_buf_idx}
|
||||
};
|
||||
|
||||
transceive_packet(dev, &tx_packet, &rx_packet, img_to_char_len);
|
||||
|
||||
if (rx_packet.pid != R502A_ACK_PACKET) {
|
||||
LOG_ERR("Error receiving ack packet 0x%X", rx_packet.pid);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (rx_packet.buf[R502A_CC_IDX] == R502A_OK) {
|
||||
LOG_DBG("Image converted");
|
||||
} else {
|
||||
LOG_ERR("Error converting image 0x%X", rx_packet.buf[R502A_CC_IDX]);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fps_create_model(const struct device *dev)
|
||||
{
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
union r502a_packet rx_packet = {0};
|
||||
char const create_model_len = 1;
|
||||
|
||||
union r502a_packet tx_packet = {
|
||||
.pid = R502A_COMMAND_PACKET,
|
||||
.data = {R502A_REGMODEL}
|
||||
};
|
||||
|
||||
transceive_packet(dev, &tx_packet, &rx_packet, create_model_len);
|
||||
|
||||
if (rx_packet.pid != R502A_ACK_PACKET) {
|
||||
LOG_ERR("Error receiving ack packet 0x%X", rx_packet.pid);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (rx_packet.buf[R502A_CC_IDX] == R502A_OK) {
|
||||
LOG_DBG("Model Created");
|
||||
} else {
|
||||
LOG_ERR("Error creating model 0x%X", rx_packet.buf[R502A_CC_IDX]);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fps_store_model(const struct device *dev, uint16_t id)
|
||||
{
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
union r502a_packet rx_packet = {0};
|
||||
char const store_model_len = 4;
|
||||
|
||||
struct led_params led_ctrl = {
|
||||
.ctrl_code = LED_CTRL_BREATHING,
|
||||
.color_idx = LED_COLOR_BLUE,
|
||||
.speed = LED_SPEED_HALF,
|
||||
.cycle = 0x01,
|
||||
};
|
||||
|
||||
union r502a_packet tx_packet = {
|
||||
.pid = R502A_COMMAND_PACKET,
|
||||
.data = {R502A_STORE, R502A_CHAR_BUF_1}
|
||||
};
|
||||
sys_put_be16(id, &tx_packet.data[2]);
|
||||
|
||||
transceive_packet(dev, &tx_packet, &rx_packet, store_model_len);
|
||||
|
||||
if (rx_packet.pid != R502A_ACK_PACKET) {
|
||||
LOG_ERR("Error receiving ack packet 0x%X", rx_packet.pid);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (rx_packet.buf[R502A_CC_IDX] == R502A_OK) {
|
||||
led_ctrl.color_idx = LED_COLOR_BLUE;
|
||||
led_ctrl.ctrl_code = LED_CTRL_FLASHING;
|
||||
led_ctrl.cycle = 0x03;
|
||||
fps_led_control(dev, &led_ctrl);
|
||||
LOG_INF("Fingerprint stored! at ID #%d", id);
|
||||
} else {
|
||||
LOG_ERR("Error storing model 0x%X", rx_packet.buf[R502A_CC_IDX]);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fps_delete_model(const struct device *dev, uint16_t id, uint16_t count)
|
||||
{
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
union r502a_packet rx_packet = {0};
|
||||
char const delete_model_len = 5;
|
||||
|
||||
union r502a_packet tx_packet = {
|
||||
.pid = R502A_COMMAND_PACKET,
|
||||
.data = {R502A_DELETE}
|
||||
};
|
||||
sys_put_be16(id, &tx_packet.data[1]);
|
||||
sys_put_be16(count + R502A_DELETE_COUNT_OFFSET, &tx_packet.data[3]);
|
||||
|
||||
transceive_packet(dev, &tx_packet, &rx_packet, delete_model_len);
|
||||
|
||||
if (rx_packet.pid != R502A_ACK_PACKET) {
|
||||
LOG_ERR("Error receiving ack packet 0x%X", rx_packet.pid);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (rx_packet.buf[R502A_CC_IDX] == R502A_OK) {
|
||||
LOG_INF("Fingerprint Deleted from ID #%d to #%d", id, (id + count));
|
||||
} else {
|
||||
LOG_ERR("Error deleting image 0x%X", rx_packet.buf[R502A_CC_IDX]);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fps_empty_db(const struct device *dev)
|
||||
{
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
union r502a_packet rx_packet = {0};
|
||||
char const empty_db_len = 1;
|
||||
int ret = 0;
|
||||
|
||||
union r502a_packet tx_packet = {
|
||||
.pid = R502A_COMMAND_PACKET,
|
||||
.data = {R502A_EMPTYLIBRARY}
|
||||
};
|
||||
|
||||
k_mutex_lock(&drv_data->lock, K_FOREVER);
|
||||
|
||||
transceive_packet(dev, &tx_packet, &rx_packet, empty_db_len);
|
||||
|
||||
if (rx_packet.pid != R502A_ACK_PACKET) {
|
||||
LOG_ERR("Error receiving ack packet 0x%X", rx_packet.pid);
|
||||
ret = -EIO;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (rx_packet.buf[R502A_CC_IDX] == R502A_OK) {
|
||||
LOG_INF("Emptied Fingerprint Library");
|
||||
} else {
|
||||
LOG_ERR("Error emptying fingerprint library 0x%X",
|
||||
rx_packet.buf[R502A_CC_IDX]);
|
||||
ret = -EIO;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
unlock:
|
||||
k_mutex_unlock(&drv_data->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fps_search(const struct device *dev, uint8_t char_buf_idx)
|
||||
{
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
union r502a_packet rx_packet = {0};
|
||||
char const search_len = 6;
|
||||
|
||||
struct led_params led_ctrl = {
|
||||
.ctrl_code = LED_CTRL_BREATHING,
|
||||
.color_idx = LED_COLOR_BLUE,
|
||||
.speed = LED_SPEED_HALF,
|
||||
.cycle = 0x01,
|
||||
};
|
||||
|
||||
union r502a_packet tx_packet = {
|
||||
.pid = R502A_COMMAND_PACKET,
|
||||
.data = {R502A_SEARCH, char_buf_idx}
|
||||
};
|
||||
sys_put_be16(R02A_LIBRARY_START_IDX, &tx_packet.data[1]);
|
||||
sys_put_be16(R502A_DEFAULT_CAPACITY, &tx_packet.data[3]);
|
||||
|
||||
transceive_packet(dev, &tx_packet, &rx_packet, search_len);
|
||||
|
||||
if (rx_packet.pid != R502A_ACK_PACKET) {
|
||||
LOG_ERR("Error receiving ack packet 0x%X", rx_packet.pid);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (rx_packet.buf[R502A_CC_IDX] == R502A_OK) {
|
||||
led_ctrl.ctrl_code = LED_CTRL_FLASHING;
|
||||
led_ctrl.color_idx = LED_COLOR_PURPLE;
|
||||
led_ctrl.cycle = 0x01;
|
||||
fps_led_control(dev, &led_ctrl);
|
||||
drv_data->finger_id = sys_get_be16(&rx_packet.data[1]);
|
||||
drv_data->matching_score = sys_get_be16(&rx_packet.data[3]);
|
||||
LOG_INF("Found a matching print! at ID #%d", drv_data->finger_id);
|
||||
} else if (rx_packet.buf[R502A_CC_IDX] == R502A_NOT_FOUND) {
|
||||
led_ctrl.ctrl_code = LED_CTRL_BREATHING;
|
||||
led_ctrl.color_idx = LED_COLOR_RED;
|
||||
led_ctrl.cycle = 0x02;
|
||||
fps_led_control(dev, &led_ctrl);
|
||||
LOG_ERR("Did not find a match");
|
||||
} else {
|
||||
led_ctrl.ctrl_code = LED_CTRL_ON_ALWAYS;
|
||||
led_ctrl.color_idx = LED_COLOR_RED;
|
||||
fps_led_control(dev, &led_ctrl);
|
||||
LOG_ERR("Error searching for image 0x%X", rx_packet.buf[R502A_CC_IDX]);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fps_enroll(const struct device *dev, const struct sensor_value *val)
|
||||
{
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
int ret = -1;
|
||||
|
||||
if (val->val1 < 0 || val->val1 > R502A_DEFAULT_CAPACITY) {
|
||||
LOG_ERR("Invalid ID number");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
k_mutex_lock(&drv_data->lock, K_FOREVER);
|
||||
|
||||
ret = fps_get_image(dev);
|
||||
if (ret != 0) {
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = fps_image_to_char(dev, R502A_CHAR_BUF_1);
|
||||
if (ret != 0) {
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = fps_get_image(dev);
|
||||
if (ret != 0) {
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = fps_image_to_char(dev, R502A_CHAR_BUF_2);
|
||||
if (ret != 0) {
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = fps_create_model(dev);
|
||||
if (ret != 0) {
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = fps_store_model(dev, val->val1);
|
||||
|
||||
unlock:
|
||||
k_mutex_unlock(&drv_data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fps_delete(const struct device *dev, const struct sensor_value *val)
|
||||
{
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
int ret = -1;
|
||||
|
||||
k_mutex_lock(&drv_data->lock, K_FOREVER);
|
||||
|
||||
ret = fps_delete_model(dev, val->val1, val->val2);
|
||||
if (ret != 0) {
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = fps_get_template_count(dev);
|
||||
|
||||
unlock:
|
||||
k_mutex_unlock(&drv_data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fps_match(const struct device *dev, struct sensor_value *val)
|
||||
{
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
int ret = -1;
|
||||
|
||||
k_mutex_lock(&drv_data->lock, K_FOREVER);
|
||||
|
||||
ret = fps_get_image(dev);
|
||||
if (ret != 0) {
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = fps_image_to_char(dev, R502A_CHAR_BUF_1);
|
||||
if (ret != 0) {
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = fps_search(dev, R502A_CHAR_BUF_1);
|
||||
if (ret == 0) {
|
||||
val->val1 = drv_data->finger_id;
|
||||
val->val2 = drv_data->matching_score;
|
||||
}
|
||||
|
||||
unlock:
|
||||
k_mutex_unlock(&drv_data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fps_init(const struct device *dev)
|
||||
{
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
int ret;
|
||||
|
||||
struct led_params led_ctrl = {
|
||||
.ctrl_code = LED_CTRL_FLASHING,
|
||||
.color_idx = LED_COLOR_PURPLE,
|
||||
.speed = LED_SPEED_HALF,
|
||||
.cycle = 0x02,
|
||||
};
|
||||
|
||||
k_mutex_lock(&drv_data->lock, K_FOREVER);
|
||||
|
||||
ret = fps_verify_password(dev);
|
||||
if (ret != 0) {
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = fps_led_control(dev, &led_ctrl);
|
||||
|
||||
unlock:
|
||||
k_mutex_unlock(&drv_data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int grow_r502a_sample_fetch(const struct device *dev, enum sensor_channel chan)
|
||||
{
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
int ret;
|
||||
|
||||
k_mutex_lock(&drv_data->lock, K_FOREVER);
|
||||
ret = fps_get_template_count(dev);
|
||||
k_mutex_unlock(&drv_data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int grow_r502a_channel_get(const struct device *dev, enum sensor_channel chan,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
|
||||
if ((enum sensor_channel_grow_r502a)chan == SENSOR_CHAN_FINGERPRINT) {
|
||||
val->val1 = drv_data->template_count;
|
||||
} else {
|
||||
LOG_ERR("Invalid channel");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int grow_r502a_attr_set(const struct device *dev, enum sensor_channel chan,
|
||||
enum sensor_attribute attr, const struct sensor_value *val)
|
||||
{
|
||||
if ((enum sensor_channel_grow_r502a)chan != SENSOR_CHAN_FINGERPRINT) {
|
||||
LOG_ERR("Channel not supported");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
switch ((enum sensor_attribute_grow_r502a)attr) {
|
||||
case SENSOR_ATTR_R502A_RECORD_ADD:
|
||||
return fps_enroll(dev, val);
|
||||
case SENSOR_ATTR_R502A_RECORD_DEL:
|
||||
return fps_delete(dev, val);
|
||||
case SENSOR_ATTR_R502A_RECORD_EMPTY:
|
||||
return fps_empty_db(dev);
|
||||
default:
|
||||
LOG_ERR("Sensor attribute not supported");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int grow_r502a_attr_get(const struct device *dev, enum sensor_channel chan,
|
||||
enum sensor_attribute attr, struct sensor_value *val)
|
||||
{
|
||||
int ret;
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
|
||||
if ((enum sensor_channel_grow_r502a)chan != SENSOR_CHAN_FINGERPRINT) {
|
||||
LOG_ERR("Channel not supported");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
switch ((enum sensor_attribute_grow_r502a)attr) {
|
||||
case SENSOR_ATTR_R502A_RECORD_FIND:
|
||||
ret = fps_match(dev, val);
|
||||
break;
|
||||
case SENSOR_ATTR_R502A_RECORD_FREE_IDX:
|
||||
ret = fps_read_template_table(dev);
|
||||
val->val1 = drv_data->free_idx;
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Sensor attribute not supported");
|
||||
ret = -ENOTSUP;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void grow_r502a_uart_flush(const struct device *dev)
|
||||
{
|
||||
uint8_t c;
|
||||
|
||||
while (uart_fifo_read(dev, &c, 1) > 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
static int grow_r502a_init(const struct device *dev)
|
||||
{
|
||||
const struct grow_r502a_config *cfg = dev->config;
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
int ret;
|
||||
|
||||
if (!device_is_ready(cfg->dev)) {
|
||||
LOG_ERR("%s: grow_r502a device not ready", dev->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_GROW_R502A_GPIO_POWER)) {
|
||||
if (!device_is_ready(cfg->vin_gpios.port)) {
|
||||
LOG_ERR("GPIO port %s not ready", cfg->vin_gpios.port->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = gpio_pin_configure_dt(&cfg->vin_gpios, GPIO_OUTPUT_ACTIVE);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
k_sleep(K_MSEC(R502A_DELAY));
|
||||
|
||||
if (!device_is_ready(cfg->act_gpios.port)) {
|
||||
LOG_ERR("GPIO port %s not ready", cfg->act_gpios.port->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = gpio_pin_configure_dt(&cfg->act_gpios, GPIO_OUTPUT_ACTIVE);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
k_sleep(K_MSEC(R502A_DELAY));
|
||||
}
|
||||
|
||||
grow_r502a_uart_flush(cfg->dev);
|
||||
|
||||
k_mutex_init(&drv_data->lock);
|
||||
k_sem_init(&drv_data->uart_rx_sem, 0, 1);
|
||||
|
||||
uart_irq_callback_user_data_set(cfg->dev, uart_cb_handler, (void *)dev);
|
||||
|
||||
#ifdef CONFIG_GROW_R502A_TRIGGER
|
||||
ret = grow_r502a_init_interrupt(dev);
|
||||
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to initialize interrupt!");
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
return fps_init(dev);
|
||||
}
|
||||
|
||||
static const struct sensor_driver_api grow_r502a_api = {
|
||||
.sample_fetch = grow_r502a_sample_fetch,
|
||||
.channel_get = grow_r502a_channel_get,
|
||||
.attr_set = grow_r502a_attr_set,
|
||||
.attr_get = grow_r502a_attr_get,
|
||||
#ifdef CONFIG_GROW_R502A_TRIGGER
|
||||
.trigger_set = grow_r502a_trigger_set,
|
||||
#endif
|
||||
};
|
||||
|
||||
#define GROW_R502A_INIT(index) \
|
||||
static struct grow_r502a_data grow_r502a_data_##index; \
|
||||
\
|
||||
static struct grow_r502a_config grow_r502a_config_##index = { \
|
||||
.dev = DEVICE_DT_GET(DT_INST_BUS(index)), \
|
||||
.comm_addr = DT_INST_REG_ADDR(index), \
|
||||
IF_ENABLED(CONFIG_GROW_R502A_GPIO_POWER, \
|
||||
(.vin_gpios = GPIO_DT_SPEC_INST_GET_OR(index, vin_gpios, {}), \
|
||||
.act_gpios = GPIO_DT_SPEC_INST_GET_OR(index, act_gpios, {}),)) \
|
||||
IF_ENABLED(CONFIG_GROW_R502A_TRIGGER, \
|
||||
(.int_gpios = GPIO_DT_SPEC_INST_GET_OR(index, int_gpios, {}),)) \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(index, &grow_r502a_init, NULL, &grow_r502a_data_##index, \
|
||||
&grow_r502a_config_##index, POST_KERNEL, \
|
||||
CONFIG_SENSOR_INIT_PRIORITY, &grow_r502a_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(GROW_R502A_INIT)
|
213
drivers/sensor/grow_r502a/grow_r502a.h
Normal file
213
drivers/sensor/grow_r502a/grow_r502a.h
Normal file
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Linumiz
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_GROW_R502A_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_GROW_R502A_H_
|
||||
|
||||
/*
|
||||
* @brief confirmation code present in acknowledgment packet
|
||||
*
|
||||
*################################################################################
|
||||
*|Confirmation code | Definition |
|
||||
*################################################################################
|
||||
*|0x00 |commad execution complete |
|
||||
*--------------------------------------------------------------------------------
|
||||
*|0x01 |error when receiving data package |
|
||||
*--------------------------------------------------------------------------------
|
||||
*|0x02 |no finger on the sensor |
|
||||
*--------------------------------------------------------------------------------
|
||||
*|0x03 |fail to enroll the finger |
|
||||
*--------------------------------------------------------------------------------
|
||||
*|0x06 |fail to generate character file due to over-disorderly |
|
||||
*| |fingerprint image |
|
||||
*--------------------------------------------------------------------------------
|
||||
*|0x07 |fail to generate character file due to lackness of |
|
||||
*| |character point or over-smallness of fingerprint image.|
|
||||
*--------------------------------------------------------------------------------
|
||||
*|0x08 |finger doesn’t match |
|
||||
*--------------------------------------------------------------------------------
|
||||
*|0x09 |fail to find the matching finger |
|
||||
*--------------------------------------------------------------------------------
|
||||
*|0x0A |fail to combine the character files |
|
||||
*--------------------------------------------------------------------------------
|
||||
*|0x0B |addressing PageID is beyond the finger library |
|
||||
*--------------------------------------------------------------------------------
|
||||
*|0x0C |error reading template from library or invalid |
|
||||
*| |template |
|
||||
*--------------------------------------------------------------------------------
|
||||
*|0x0D |error when uploading template |
|
||||
*--------------------------------------------------------------------------------
|
||||
*|0x0E |Module can’t receive the following data packages |
|
||||
*--------------------------------------------------------------------------------
|
||||
*|0x0F |error when uploading image |
|
||||
*--------------------------------------------------------------------------------
|
||||
*|0x10 |fail to delete the template |
|
||||
*--------------------------------------------------------------------------------
|
||||
*|0x11 |fail to clear finger library |
|
||||
*--------------------------------------------------------------------------------
|
||||
*|0x13 |wrong password! |
|
||||
*--------------------------------------------------------------------------------
|
||||
*|0x15 |fail to generate image for the lackness of valid |
|
||||
*| |primary image |
|
||||
*--------------------------------------------------------------------------------
|
||||
*|0x18 |error when writing flash |
|
||||
*--------------------------------------------------------------------------------
|
||||
*|0x1A |invalid register number |
|
||||
*--------------------------------------------------------------------------------
|
||||
*|0x1B |incorrect configuration of register |
|
||||
*--------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define R502A_OK 0x00 /*commad execution complete*/
|
||||
#define R502A_NOT_FOUND 0x09 /*fail to find the matching finger*/
|
||||
|
||||
/*Package Identifier's definition*/
|
||||
#define R502A_COMMAND_PACKET 0x1 /*Command packet*/
|
||||
#define R502A_DATA_PACKET 0x2 /*Data packet, must follow command packet or acknowledge packet*/
|
||||
#define R502A_ACK_PACKET 0x7 /*Acknowledge packet*/
|
||||
#define R502A_END_DATA_PACKET 0x8 /*End of data packet*/
|
||||
|
||||
/*Instruction code's definition*/
|
||||
#define R502A_GENIMAGE 0x01 /*Collect finger image*/
|
||||
#define R502A_IMAGE2TZ 0x02 /*To generate character file from image*/
|
||||
#define R502A_MATCH 0x03 /*Carry out precise matching of two templates*/
|
||||
#define R502A_SEARCH 0x04 /*Search the finger library*/
|
||||
#define R502A_REGMODEL 0x05 /*To combine character files and generate template*/
|
||||
#define R502A_STORE 0x06 /*To store template*/
|
||||
#define R502A_LOAD 0x07 /*To read/load template*/
|
||||
#define R502A_UPCHAR 0x08 /*To upload template*/
|
||||
#define R502A_DOWNCHAR 0x09 /*To download template*/
|
||||
#define R502A_IMGUPLOAD 0x0A /*To upload image*/
|
||||
#define R502A_DELETE 0x0C /*To delete template*/
|
||||
#define R502A_EMPTYLIBRARY 0x0D /*To empty the library*/
|
||||
#define R502A_SETSYSPARAM 0x0E /*To set system parameter*/
|
||||
#define R502A_READSYSPARAM 0x0F /*To read system parameter*/
|
||||
#define R502A_SETPASSWORD 0x12 /*To set password*/
|
||||
#define R502A_VERIFYPASSWORD 0x13 /*To verify password*/
|
||||
#define R502A_GETRANDOM 0x14 /*To generate a random code*/
|
||||
#define R502A_TEMPLATECOUNT 0x1D /*To read finger template numbers*/
|
||||
#define R502A_READTEMPLATEINDEX 0x1F /*Read fingerprint template index table*/
|
||||
#define R502A_LED_CONFIG 0x35 /*Aura LED Config*/
|
||||
#define R502A_CHECKSENSOR 0x36 /*Check sensor*/
|
||||
#define R502A_SOFTRESET 0x3D /*Soft reset*/
|
||||
#define R502A_HANDSHAKE 0x40 /*Handshake*/
|
||||
#define R502A_BADPACKET 0xFE /* Bad packet was sent*/
|
||||
|
||||
#define R502A_STARTCODE 0xEF01 /*Fixed value, High byte transferred first*/
|
||||
#define R502A_DEFAULT_PASSWORD 0x00000000
|
||||
#define R502A_DEFAULT_ADDRESS 0xFFFFFFFF
|
||||
#define R502A_DEFAULT_CAPACITY 200
|
||||
#define R502A_HANDSHAKE_BYTE 0x55
|
||||
#define R02A_LIBRARY_START_IDX 0
|
||||
|
||||
#define R502A_STARTCODE_IDX 0
|
||||
#define R502A_ADDRESS_IDX 2
|
||||
#define R502A_PID_IDX 6 /* Package identifier index*/
|
||||
#define R502A_PKG_LEN_IDX 7
|
||||
#define R502A_CC_IDX 9 /* Confirmation code index*/
|
||||
|
||||
#define R502A_STARTCODE_LEN 2
|
||||
#define R502A_ADDRESS_LEN 4
|
||||
#define R502A_PKG_LEN 2
|
||||
#define R502A_CHECKSUM_LEN 2 /* Checksum length in uart packages*/
|
||||
#define R502A_HEADER_LEN 9
|
||||
|
||||
#define R502A_CHAR_BUF_1 1
|
||||
#define R502A_CHAR_BUF_2 2
|
||||
#define R502A_CHAR_BUF_SIZE 384 /* Maximum size of characteristic value buffer*/
|
||||
#define R502A_TEMPLATE_SIZE 768 /* Maximum size of template, twice of CHAR_BUF*/
|
||||
#define R502A_MAX_BUF_SIZE 779 /*sum of checksum, header and template sizes*/
|
||||
#define R502A_BUF_SIZE 64
|
||||
#define R502A_TEMPLATES_PER_PAGE 256
|
||||
#define R502A_TEMP_TABLE_BUF_SIZE 32
|
||||
#define R502A_DELETE_COUNT_OFFSET 1
|
||||
|
||||
#define R502A_DELAY 200
|
||||
#define R502A_RETRY_DELAY 5
|
||||
|
||||
#define LED_CTRL_BREATHING 0x01
|
||||
#define LED_CTRL_FLASHING 0x02
|
||||
#define LED_CTRL_ON_ALWAYS 0x03
|
||||
#define LED_CTRL_OFF_ALWAYS 0x04
|
||||
#define LED_CTRL_ON_GRADUALLY 0x05
|
||||
#define LED_CTRL_OFF_GRADUALLY 0x06
|
||||
|
||||
#define LED_SPEED_HALF 0x50
|
||||
#define LED_SPEED_FULL 0xFF
|
||||
|
||||
#define LED_COLOR_RED 0x01
|
||||
#define LED_COLOR_BLUE 0x02
|
||||
#define LED_COLOR_PURPLE 0x03
|
||||
|
||||
struct led_params {
|
||||
uint8_t ctrl_code;
|
||||
uint8_t color_idx;
|
||||
uint8_t speed; /* Speed 0x00-0xff */
|
||||
uint8_t cycle; /* Number of cycles | 0-infinite, 1-255 */
|
||||
};
|
||||
|
||||
union r502a_packet {
|
||||
struct {
|
||||
uint8_t start[R502A_STARTCODE_LEN];
|
||||
uint8_t addr[R502A_ADDRESS_LEN];
|
||||
uint8_t pid;
|
||||
uint8_t len[R502A_PKG_LEN];
|
||||
uint8_t data[R502A_BUF_SIZE];
|
||||
};
|
||||
|
||||
uint8_t buf[R502A_BUF_SIZE];
|
||||
};
|
||||
|
||||
struct r502a_buf {
|
||||
uint8_t *data;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
struct grow_r502a_data {
|
||||
#ifdef CONFIG_GROW_R502A_TRIGGER
|
||||
const struct device *gpio_dev;
|
||||
struct gpio_callback gpio_cb;
|
||||
|
||||
sensor_trigger_handler_t th_handler;
|
||||
struct sensor_trigger th_trigger;
|
||||
#if defined(CONFIG_GROW_R502A_TRIGGER_OWN_THREAD)
|
||||
K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_GROW_R502A_THREAD_STACK_SIZE);
|
||||
struct k_sem gpio_sem;
|
||||
struct k_thread thread;
|
||||
#elif defined(CONFIG_GROW_R502A_TRIGGER_GLOBAL_THREAD)
|
||||
struct k_work work;
|
||||
#endif
|
||||
#endif /* CONFIG_GROW_R502A_TRIGGER */
|
||||
|
||||
struct r502a_buf tx_buf;
|
||||
struct r502a_buf rx_buf;
|
||||
|
||||
struct k_mutex lock;
|
||||
struct k_sem uart_rx_sem;
|
||||
|
||||
uint16_t finger_id;
|
||||
uint16_t matching_score;
|
||||
uint16_t template_count;
|
||||
int8_t free_idx;
|
||||
};
|
||||
|
||||
struct grow_r502a_config {
|
||||
const struct device *dev;
|
||||
struct gpio_dt_spec vin_gpios;
|
||||
struct gpio_dt_spec act_gpios;
|
||||
uint32_t comm_addr;
|
||||
#ifdef CONFIG_GROW_R502A_TRIGGER
|
||||
struct gpio_dt_spec int_gpios;
|
||||
#endif /* CONFIG_GROW_R502A_TRIGGER */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_GROW_R502A_TRIGGER
|
||||
int grow_r502a_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
|
||||
sensor_trigger_handler_t handler);
|
||||
|
||||
int grow_r502a_init_interrupt(const struct device *dev);
|
||||
#endif /* CONFIG_GROW_R502A_TRIGGER */
|
||||
|
||||
#endif /*_GROW_R502A_H_*/
|
131
drivers/sensor/grow_r502a/grow_r502a_trigger.c
Normal file
131
drivers/sensor/grow_r502a/grow_r502a_trigger.c
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Linumiz
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT hzgrow_r502a
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include <zephyr/drivers/uart.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include <zephyr/drivers/sensor/grow_r502a.h>
|
||||
#include "grow_r502a.h"
|
||||
|
||||
LOG_MODULE_DECLARE(GROW_R502A, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
static void setup_int(const struct device *dev, bool enable)
|
||||
{
|
||||
const struct grow_r502a_config *cfg = dev->config;
|
||||
|
||||
gpio_flags_t flags =
|
||||
enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE;
|
||||
|
||||
gpio_pin_interrupt_configure_dt(&cfg->int_gpios, flags);
|
||||
}
|
||||
|
||||
static void process_int(const struct device *dev)
|
||||
{
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
|
||||
if (drv_data->th_handler != NULL) {
|
||||
drv_data->th_handler(dev, &drv_data->th_trigger);
|
||||
}
|
||||
setup_int(dev, true);
|
||||
}
|
||||
|
||||
int grow_r502a_trigger_set(const struct device *dev,
|
||||
const struct sensor_trigger *trig,
|
||||
sensor_trigger_handler_t handler)
|
||||
{
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
|
||||
if ((enum sensor_trigger_type_grow_r502a)trig->type == SENSOR_TRIG_TOUCH) {
|
||||
drv_data->th_handler = handler;
|
||||
drv_data->th_trigger = *trig;
|
||||
setup_int(dev, true);
|
||||
} else {
|
||||
LOG_ERR("Unsupported sensor trigger");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void grow_r502a_gpio_callback(const struct device *dev,
|
||||
struct gpio_callback *cb, uint32_t pins)
|
||||
{
|
||||
struct grow_r502a_data *drv_data =
|
||||
CONTAINER_OF(cb, struct grow_r502a_data, gpio_cb);
|
||||
|
||||
setup_int(drv_data->gpio_dev, false);
|
||||
|
||||
#if defined(CONFIG_GROW_R502A_TRIGGER_OWN_THREAD)
|
||||
k_sem_give(&drv_data->gpio_sem);
|
||||
#elif defined(CONFIG_GROW_R502A_TRIGGER_GLOBAL_THREAD)
|
||||
k_work_submit(&drv_data->work);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(CONFIG_GROW_R502A_TRIGGER_OWN_THREAD)
|
||||
static void grow_r502a_thread(struct grow_r502a_data *drv_data)
|
||||
{
|
||||
while (true) {
|
||||
k_sem_take(&drv_data->gpio_sem, K_FOREVER);
|
||||
process_int(drv_data->gpio_dev);
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(CONFIG_GROW_R502A_TRIGGER_GLOBAL_THREAD)
|
||||
static void grow_r502a_work_cb(struct k_work *work)
|
||||
{
|
||||
struct grow_r502a_data *drv_data =
|
||||
CONTAINER_OF(work, struct grow_r502a_data, work);
|
||||
|
||||
process_int(drv_data->gpio_dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
int grow_r502a_init_interrupt(const struct device *dev)
|
||||
{
|
||||
struct grow_r502a_data *drv_data = dev->data;
|
||||
const struct grow_r502a_config *cfg = dev->config;
|
||||
int rc;
|
||||
|
||||
if (!device_is_ready(cfg->int_gpios.port)) {
|
||||
LOG_ERR("GPIO port %s not ready", cfg->int_gpios.port->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rc = gpio_pin_configure_dt(&cfg->int_gpios, GPIO_INPUT);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
drv_data->gpio_dev = dev;
|
||||
#if defined(CONFIG_GROW_R502A_TRIGGER_OWN_THREAD)
|
||||
k_sem_init(&drv_data->gpio_sem, 0, K_SEM_MAX_LIMIT);
|
||||
|
||||
k_thread_create(&drv_data->thread, drv_data->thread_stack,
|
||||
CONFIG_GROW_R502A_THREAD_STACK_SIZE,
|
||||
(k_thread_entry_t)grow_r502a_thread, drv_data, NULL,
|
||||
NULL, K_PRIO_COOP(CONFIG_GROW_R502A_THREAD_PRIORITY), 0,
|
||||
K_NO_WAIT);
|
||||
#elif defined(CONFIG_GROW_R502A_TRIGGER_GLOBAL_THREAD)
|
||||
drv_data->work.handler = grow_r502a_work_cb;
|
||||
#endif
|
||||
|
||||
gpio_init_callback(&drv_data->gpio_cb, grow_r502a_gpio_callback,
|
||||
BIT(cfg->int_gpios.pin));
|
||||
|
||||
rc = gpio_add_callback(cfg->int_gpios.port, &drv_data->gpio_cb);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("Could not set gpio callback.");
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
28
dts/bindings/sensor/hzgrow,r502a.yaml
Normal file
28
dts/bindings/sensor/hzgrow,r502a.yaml
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Copyright (c) 2021, Linumiz
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: HZ-Grow GROW_R502A Fingerprint sensor.
|
||||
|
||||
compatible: "hzgrow,r502a"
|
||||
|
||||
include: [sensor-device.yaml, uart-device.yaml]
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
int-gpios:
|
||||
type: phandle-array
|
||||
required: true
|
||||
description: |
|
||||
Interrupt pin. When the sensor is touched, the GPIO is set to high.
|
||||
It is used to trigger a fingerprint enroll or match operations.
|
||||
vin-gpios:
|
||||
type: phandle-array
|
||||
required: false
|
||||
description: |
|
||||
Voltage input pin to the fingerprint sensor.
|
||||
act-gpios:
|
||||
type: phandle-array
|
||||
required: false
|
||||
description: |
|
||||
Finger detection power pin to detect the presence of finger.
|
43
include/zephyr/drivers/sensor/grow_r502a.h
Normal file
43
include/zephyr/drivers/sensor/grow_r502a.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Linumiz
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_GROW_R502A_H_
|
||||
#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_GROW_R502A_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
|
||||
enum sensor_channel_grow_r502a {
|
||||
/** Fingerprint template count, ID number for enrolling and searching*/
|
||||
SENSOR_CHAN_FINGERPRINT = SENSOR_CHAN_PRIV_START,
|
||||
};
|
||||
|
||||
enum sensor_trigger_type_grow_r502a {
|
||||
/** Trigger fires when a touch is detected. */
|
||||
SENSOR_TRIG_TOUCH = SENSOR_TRIG_PRIV_START,
|
||||
};
|
||||
|
||||
enum sensor_attribute_grow_r502a {
|
||||
/** Add values to the sensor which are having record storage facility */
|
||||
SENSOR_ATTR_R502A_RECORD_ADD = SENSOR_ATTR_PRIV_START,
|
||||
/** To find requested data in record storage */
|
||||
SENSOR_ATTR_R502A_RECORD_FIND,
|
||||
/** To delete mentioned data from record storage */
|
||||
SENSOR_ATTR_R502A_RECORD_DEL,
|
||||
/** To get available position to store data on record storage */
|
||||
SENSOR_ATTR_R502A_RECORD_FREE_IDX,
|
||||
/** To empty the storage record*/
|
||||
SENSOR_ATTR_R502A_RECORD_EMPTY,
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_ZEPHYR_DRIVERS_SENSOR_GROW_R502A_H_ */
|
Loading…
Reference in a new issue