diff --git a/CODEOWNERS b/CODEOWNERS index 3e651a9cd3a..729b88bc07f 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -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 diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index c5ec5cf5b41..9d416131000 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -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) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index c2276328e07..201d2f20e64 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -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" diff --git a/drivers/sensor/grow_r502a/CMakeLists.txt b/drivers/sensor/grow_r502a/CMakeLists.txt new file mode 100644 index 00000000000..78dd746a36f --- /dev/null +++ b/drivers/sensor/grow_r502a/CMakeLists.txt @@ -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) diff --git a/drivers/sensor/grow_r502a/Kconfig b/drivers/sensor/grow_r502a/Kconfig new file mode 100644 index 00000000000..53c31f3c804 --- /dev/null +++ b/drivers/sensor/grow_r502a/Kconfig @@ -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 diff --git a/drivers/sensor/grow_r502a/grow_r502a.c b/drivers/sensor/grow_r502a/grow_r502a.c new file mode 100644 index 00000000000..f677c324c4f --- /dev/null +++ b/drivers/sensor/grow_r502a/grow_r502a.c @@ -0,0 +1,786 @@ +/* + * Copyright (c) 2021 Linumiz + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT hzgrow_r502a + +#include +#include +#include +#include +#include +#include + +#include +#include "grow_r502a.h" + +#include +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) diff --git a/drivers/sensor/grow_r502a/grow_r502a.h b/drivers/sensor/grow_r502a/grow_r502a.h new file mode 100644 index 00000000000..2cfc7305cef --- /dev/null +++ b/drivers/sensor/grow_r502a/grow_r502a.h @@ -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_*/ diff --git a/drivers/sensor/grow_r502a/grow_r502a_trigger.c b/drivers/sensor/grow_r502a/grow_r502a_trigger.c new file mode 100644 index 00000000000..60a07adae96 --- /dev/null +++ b/drivers/sensor/grow_r502a/grow_r502a_trigger.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2021 Linumiz + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT hzgrow_r502a + +#include +#include +#include +#include +#include + +#include +#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; +} diff --git a/dts/bindings/sensor/hzgrow,r502a.yaml b/dts/bindings/sensor/hzgrow,r502a.yaml new file mode 100644 index 00000000000..8e5492bb849 --- /dev/null +++ b/dts/bindings/sensor/hzgrow,r502a.yaml @@ -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. diff --git a/include/zephyr/drivers/sensor/grow_r502a.h b/include/zephyr/drivers/sensor/grow_r502a.h new file mode 100644 index 00000000000..8b0db47188d --- /dev/null +++ b/include/zephyr/drivers/sensor/grow_r502a.h @@ -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 + +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_ */