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:
Dinesh Kumar K 2021-12-28 11:02:04 +05:30 committed by Carles Cufí
parent df3fdcaaf3
commit f050e18798
10 changed files with 1270 additions and 0 deletions

View file

@ -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

View file

@ -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)

View file

@ -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"

View 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)

View 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

View 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)

View 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 doesnt 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 cant 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_*/

View 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;
}

View 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.

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