From b2a173b98f15791d178a1fff03bef0009038a9b9 Mon Sep 17 00:00:00 2001 From: Hessel van der Molen Date: Mon, 19 Feb 2024 11:30:35 +0100 Subject: [PATCH] drivers: nfc: NFC-driver structure + nRF-NFC and nt3h2x11 support NFC driver structure to support multiple NFC driver with the same API Signed-off-by: Hessel van der Molen --- drivers/CMakeLists.txt | 1 + drivers/Kconfig | 1 + drivers/nfc/CMakeLists.txt | 13 + drivers/nfc/Kconfig | 15 + drivers/nfc/Kconfig.nrfxnfc | 38 + drivers/nfc/Kconfig.nt3h2x11 | 36 + drivers/nfc/nrfxnfc.c | 286 +++++ drivers/nfc/nt3h2x11.c | 1311 ++++++++++++++++++++ dts/bindings/nfc/nfc,nt3h2x11.yaml | 17 + include/zephyr/drivers/nfc/nt3h2x11.h | 768 ++++++++++++ include/zephyr/nfc/nfc_tag.h | 208 ++++ samples/drivers/nfc/CMakeLists.txt | 12 + samples/drivers/nfc/README.rst | 50 + samples/drivers/nfc/boards/dk6_qn9090.conf | 9 + samples/drivers/nfc/boards/mg100.conf | 11 + samples/drivers/nfc/prj.conf | 9 + samples/drivers/nfc/src/main.c | 229 ++++ 17 files changed, 3014 insertions(+) create mode 100644 drivers/nfc/CMakeLists.txt create mode 100644 drivers/nfc/Kconfig create mode 100644 drivers/nfc/Kconfig.nrfxnfc create mode 100644 drivers/nfc/Kconfig.nt3h2x11 create mode 100644 drivers/nfc/nrfxnfc.c create mode 100644 drivers/nfc/nt3h2x11.c create mode 100644 dts/bindings/nfc/nfc,nt3h2x11.yaml create mode 100644 include/zephyr/drivers/nfc/nt3h2x11.h create mode 100644 include/zephyr/nfc/nfc_tag.h create mode 100644 samples/drivers/nfc/CMakeLists.txt create mode 100644 samples/drivers/nfc/README.rst create mode 100644 samples/drivers/nfc/boards/dk6_qn9090.conf create mode 100644 samples/drivers/nfc/boards/mg100.conf create mode 100644 samples/drivers/nfc/prj.conf create mode 100644 samples/drivers/nfc/src/main.c diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index f0236ef653a0e0..ec03e22ac2ed2f 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -62,6 +62,7 @@ add_subdirectory_ifdef(CONFIG_MM_DRV mm) add_subdirectory_ifdef(CONFIG_MODEM modem) add_subdirectory_ifdef(CONFIG_NET_DRIVERS net) add_subdirectory_ifdef(CONFIG_NET_L2_ETHERNET ethernet) +add_subdirectory_ifdef(CONFIG_NFC nfc) add_subdirectory_ifdef(CONFIG_PECI peci) add_subdirectory_ifdef(CONFIG_PINCTRL pinctrl) add_subdirectory_ifdef(CONFIG_PM_CPU_OPS pm_cpu_ops) diff --git a/drivers/Kconfig b/drivers/Kconfig index ac07add3c1f5ce..a91df784f5d2ea 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -58,6 +58,7 @@ source "drivers/misc/Kconfig" source "drivers/mm/Kconfig" source "drivers/modem/Kconfig" source "drivers/net/Kconfig" +source "drivers/nfc/Kconfig" source "drivers/pcie/Kconfig" source "drivers/peci/Kconfig" source "drivers/pinctrl/Kconfig" diff --git a/drivers/nfc/CMakeLists.txt b/drivers/nfc/CMakeLists.txt new file mode 100644 index 00000000000000..4613176d897def --- /dev/null +++ b/drivers/nfc/CMakeLists.txt @@ -0,0 +1,13 @@ +# +# Copyright (c) 2023 Sendrato +# +# SPDX-License-Identifier: Apache-2.0 +# + +if (CONFIG_NFC_NT3H2X11) + zephyr_sources(nt3h2x11.c) +endif() + +if (CONFIG_NFC_NRFX) + zephyr_sources(nrfxnfc.c) +endif() diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig new file mode 100644 index 00000000000000..eecbb015ca15b7 --- /dev/null +++ b/drivers/nfc/Kconfig @@ -0,0 +1,15 @@ +# +# Copyright (c) 2023 Sendrato +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig NFC + bool "NFC drivers" + +if NFC + +rsource "Kconfig.nt3h2x11" +rsource "Kconfig.nrfxnfc" + +endif # NFC diff --git a/drivers/nfc/Kconfig.nrfxnfc b/drivers/nfc/Kconfig.nrfxnfc new file mode 100644 index 00000000000000..d23fa14eb6a8ae --- /dev/null +++ b/drivers/nfc/Kconfig.nrfxnfc @@ -0,0 +1,38 @@ +# +# Copyright (c) 2023 Sendrato +# +# SPDX-License-Identifier: Apache-2.0 +# + +config NFC_NRFX + bool "nRFx-NFC driver" + default n + select NRFX_NFCT + help + Enable nRFx NFC driver. + +if NFC_NRFX + +config NFC_NRFX_DRV_NAME + string "Device Tree name of nRFx-NFC driver" + default "nRFxNFC" + help + Device Tree name of nRFx-NFC driver. + +config NFC_NRFX_INIT_PRIORITY + int "NFC_NRFX NFC driver init priority" + default 75 + +config NFC_NRFX_MAX_PAYLOAD_SIZE + int "Max payload in bytes" + default 988 + +# KConfig used by nrfxlib-sdk too +config NFC_T2T_NRFXLIB + bool "NFC Type 2 Tag library" + +# KConfig used by nrfxlib-sdk too +config NFC_T4T_NRFXLIB + bool "NFC Type 4 Tag library" + +endif # NFC_NRFX diff --git a/drivers/nfc/Kconfig.nt3h2x11 b/drivers/nfc/Kconfig.nt3h2x11 new file mode 100644 index 00000000000000..91cd92ebc721a1 --- /dev/null +++ b/drivers/nfc/Kconfig.nt3h2x11 @@ -0,0 +1,36 @@ +# +# Copyright (c) 2023 Sendrato +# +# SPDX-License-Identifier: Apache-2.0 +# + +config NFC_NT3H2X11 + bool "NT3H2X11 NFC driver" + default n + depends on I2C + help + Enable I2C-based driver for NT3H2X11 led driver. + +if NFC_NT3H2X11 + +config NFC_NT3H2X11_2K + bool "Use 2k memory map instead of 1k" + default n + help + Use 2k memory map instead of 1k + +config NFC_NT3H2X11_MAX_WRITE_DELAY + int "delay in ms" + default 10 + help + Max I2C write delay in ms for when writing EEPROM + +config NFC_NT3H2X11_INIT_PRIORITY + int "NT3H2X11 NFC driver init priority" + default 75 + +module = NT3H2X11 +module-str = nt3h2x11 +source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config" + +endif # NFC_NT3H2X11 diff --git a/drivers/nfc/nrfxnfc.c b/drivers/nfc/nrfxnfc.c new file mode 100644 index 00000000000000..929da3bf704c40 --- /dev/null +++ b/drivers/nfc/nrfxnfc.c @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2023 Sendrato + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include + +#include + +#if CONFIG_NFC_T2T_NRFXLIB +#include +#endif +#if CONFIG_NFC_T4T_NRFXLIB +#include +#endif + +/******************* DEVICE STRUCTURE *******************/ + +struct nrfxnfc_data { + const struct device *parent; + const struct device *dev; + struct k_work worker_irq; + /* NFC subsys data */ + nfc_tag_cb_t nfc_tag_cb; + enum nfc_tag_type tag_type; +}; + +static uint8_t _nrfxnfc_payload[CONFIG_NFC_NRFX_MAX_PAYLOAD_SIZE]; + +/******************* NFC SUBSYS DRIVER IMPLEMENTATION *******************/ + +#if CONFIG_NFC_T2T_NRFXLIB +static void nrfxnfc_t2t_cb(void *context, nfc_t2t_event_t event, const uint8_t *data, + size_t data_length) +{ + const struct device *dev = (const struct device *)context; + struct nrfxnfc_data *dev_data = dev->data; + enum nfc_tag_event nfc_event = NFC_TAG_EVENT_NONE; + + switch (event) { + case NFC_T2T_EVENT_NONE: + nfc_event = NFC_TAG_EVENT_NONE; + break; + + case NFC_T2T_EVENT_FIELD_ON: + nfc_event = NFC_TAG_EVENT_FIELD_ON; + break; + + case NFC_T2T_EVENT_FIELD_OFF: + nfc_event = NFC_TAG_EVENT_FIELD_OFF; + break; + + case NFC_T2T_EVENT_DATA_READ: + nfc_event = NFC_TAG_EVENT_READ_DONE; + break; + + case NFC_T2T_EVENT_STOPPED: + nfc_event = NFC_TAG_EVENT_STOPPED; + break; + } + + if (dev_data->nfc_tag_cb) { + dev_data->nfc_tag_cb(dev, nfc_event, NULL, 0); + } +} +#endif /* CONFIG_NFC_T2T_NRFXLIB */ + +#if CONFIG_NFC_T4T_NRFXLIB +static void nrfxnfc_t4t_cb(void *context, + nfc_t4t_event_t event, + const uint8_t *data, + size_t data_length, + uint32_t flags +{ + const struct device *dev = (const struct device *)context; + struct nrfxnfc_data *dev_data = dev->data; + enum nfc_tag_event nfc_event = NFC_TAG_EVENT_NONE; + + switch (event) { + case NFC_T4T_EVENT_NONE: + nfc_event = NFC_TAG_EVENT_NONE; + break; + + case NFC_T4T_EVENT_FIELD_ON: + nfc_event = NFC_TAG_EVENT_FIELD_ON; + break; + + case NFC_T4T_EVENT_FIELD_OFF: + nfc_event = NFC_TAG_EVENT_FIELD_OFF; + break; + + case NFC_T4T_EVENT_NDEF_READ: + nfc_event = NFC_TAG_EVENT_READ_DONE; + break; + + case NFC_T4T_EVENT_NDEF_UPDATED: + nfc_event = NFC_TAG_EVENT_WRITE_DONE; + break; + + case NFC_T4T_EVENT_DATA_TRANSMITTED: + nfc_event = NFC_TAG_EVENT_DATA_TRANSMITTED; + break; + + case NFC_T4T_EVENT_DATA_IND: + if (flags & NFC_T4T_DI_FLAG_MORE) { + nfc_event = NFC_TAG_EVENT_DATA_IND; + } else { + nfc_event = NFC_TAG_EVENT_DATA_IND_DONE; + } + break; + } + + if (dev_data->nfc_tag_cb) { + dev_data->nfc_tag_cb(dev, nfc_event, NULL, 0); + } +} +#endif /* CONFIG_NFC_T4T_NRFXLIB */ + + +static int nrfxnfc_tag_init(const struct device *dev, nfc_tag_cb_t cb) +{ + struct nrfxnfc_data *data = dev->data; + + /* Setup callback */ + data->nfc_tag_cb = cb; + + return 0; +} + + +static int nrfxnfc_tag_set_type(const struct device *dev, enum nfc_tag_type type) +{ + int rv = 0; + struct nrfxnfc_data *data = dev->data; + + if ((type != NFC_TAG_TYPE_T2T) && (type != NFC_TAG_TYPE_T4T)) { + return -ENOTSUP; + } + +#if CONFIG_NFC_T2T_NRFXLIB + if (type == NFC_TAG_TYPE_T2T) { + rv = nfc_t2t_setup(nrfxnfc_t2t_cb, (void *)dev); + if (rv != 0) { + return rv; + } + } +#endif + +#if CONFIG_NFC_T4T_NRFXLIB + if (type == NFC_TAG_TYPE_T4T) { + rv = nfc_t4t_setup(nrfxnfc_t4t_cb, (void *)dev); + if (rv != 0) { + return rv; + } + } +#endif + + /* Type is set */ + data->tag_type = type; + return 0; +} + + +static int nrfxnfc_tag_get_type(const struct device *dev, + enum nfc_tag_type *type) +{ + struct nrfxnfc_data *data = dev->data; + *type = data->tag_type; + return 0; +} + + +static int nrfxnfc_tag_start(const struct device *dev) +{ + struct nrfxnfc_data *data = dev->data; + +#if CONFIG_NFC_T2T_NRFXLIB + if (data->tag_type == NFC_TAG_TYPE_T2T) { + return nfc_t2t_emulation_start(); + } +#endif + +#if CONFIG_NFC_T4T_NRFXLIB + if (data->tag_type == NFC_TAG_TYPE_T4T) { + return nfc_t4t_emulation_start(); + } +#endif + + return -ENODEV; +} + + +static int nrfxnfc_tag_stop(const struct device *dev) +{ + struct nrfxnfc_data *data = dev->data; + +#if CONFIG_NFC_T2T_NRFXLIB + if (data->tag_type == NFC_TAG_TYPE_T2T) { + return nfc_t2t_emulation_stop(); + } +#endif + +#if CONFIG_NFC_T4T_NRFXLIB + if (data->tag_type == NFC_TAG_TYPE_T4T) { + return nfc_t4t_emulation_stop(); + } +#endif + + return -ENODEV; +} + + +static int nrfxnfc_tag_set_ndef(const struct device *dev, + uint8_t *buf, uint16_t len) +{ + struct nrfxnfc_data *data = dev->data; + + if (len > CONFIG_NFC_NRFX_MAX_PAYLOAD_SIZE) { + return -ENOMEM; + } + + /* copy buf to ensure longevity and validity of data */ + (void)memset(_nrfxnfc_payload, 0, CONFIG_NFC_NRFX_MAX_PAYLOAD_SIZE); + (void)memcpy(_nrfxnfc_payload, buf, len); + +#if CONFIG_NFC_T2T_NRFXLIB + if (data->tag_type == NFC_TAG_TYPE_T2T) { + return nfc_t2t_payload_set(_nrfxnfc_payload, len); + } +#endif + +#if CONFIG_NFC_T4T_NRFXLIB + if (data->tag_type == NFC_TAG_TYPE_T4T) { + return nfc_t4t_ndef_rwpayload_set(_nrfxnfc_payload, len); + } +#endif + + return -ENODEV; +} + + +static int nrfxnfc_tag_cmd(const struct device *dev, + enum nfc_tag_cmd cmd, + uint8_t *buf, uint16_t *buf_len) +{ + ARG_UNUSED(dev); + ARG_UNUSED(cmd); + ARG_UNUSED(buf); + ARG_UNUSED(buf_len); + return 0; +} + + +static struct nfc_tag_driver_api _nrfxnfc_driver_api = { + .init = nrfxnfc_tag_init, + .set_type = nrfxnfc_tag_set_type, + .get_type = nrfxnfc_tag_get_type, + .start = nrfxnfc_tag_start, + .stop = nrfxnfc_tag_stop, + .set_ndef = nrfxnfc_tag_set_ndef, + .cmd = nrfxnfc_tag_cmd, +}; + + +/******************* DEVICE INITIALISATION BY DTS *******************/ + +static struct nrfxnfc_data _nrfxnfc_driver_data = {0}; +static char _nrfxnfc_driver_name[] = CONFIG_NFC_NRFX_DRV_NAME; + + +static int _nrfxnfc_init(const struct device *dev) +{ + return 0; +} + +DEVICE_DEFINE(nrfxnfc, + _nrfxnfc_driver_name, + _nrfxnfc_init, + NULL, + &_nrfxnfc_driver_data, + NULL, POST_KERNEL, + CONFIG_NFC_NRFX_INIT_PRIORITY, + &_nrfxnfc_driver_api); diff --git a/drivers/nfc/nt3h2x11.c b/drivers/nfc/nt3h2x11.c new file mode 100644 index 00000000000000..9b8a731b4cf0ca --- /dev/null +++ b/drivers/nfc/nt3h2x11.c @@ -0,0 +1,1311 @@ +/* + * Copyright (c) 2023 Sendrato + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_nt3h2x11 + +#include + +#include +#include +#include + +#include + +#include + +#include + +LOG_MODULE_REGISTER(nt3h2x11, CONFIG_NT3H2X11_LOG_LEVEL); + +/******************* DEVICE STRUCTURE *******************/ + +struct nt3h2x11_cfg { + /* i2c parameters */ + struct i2c_dt_spec i2c; + /* irq DTS settings */ + const struct gpio_dt_spec irq_gpio; + uint8_t irq_pin; + /* internal (1) or external driver (0) */ + uint8_t internal; +}; + +struct nt3h2x11_data { + const struct device *parent; + const struct device *dev_i2c; + const struct device *dev_irq_external; + struct gpio_callback dev_irq_external_cb; + struct k_work worker_irq; + nt3h2x11_irq_callback_t app_irq_cb; + uint8_t initialized; + /* FD pin edge detection */ + uint8_t flag_fd_pin; + /* NFC subsys data */ + nfc_tag_cb_t nfc_tag_cb; + enum nfc_tag_type tag_type; +}; + +/** + * @brief Macro-helper to set bit in session/config-block + * + * Depends on NT3H2X11_REG_xx and NT3H2X11_MSK_xx definitions + */ +#define NT3H2X11_CSREG_SET_EN(_d, _csreg, _r, _m, _v) \ + _nt3h2x11_write_csreg_register_enable(_d, _csreg, NT3H2X11_REG_##_r, \ + NT3H2X11_MSK_##_r##_##_m, _v) + +/** + * @brief Macro-helper to read bit in session/config-block + * + * Depends on NT3H2X11_REG_xx and NT3H2X11_MSK_xx definitions + */ +#define NT3H2X11_CSREG_GET_EN(_d, _csreg, _r, _m, _v) \ + _nt3h2x11_read_csreg_register_enable(_d, _csreg, NT3H2X11_REG_##_r, \ + NT3H2X11_MSK_##_r##_##_m, _v) + +/** + * @brief Macro-helper to set multi-bit value in session/config-block + * + * Depends on NT3H2X11_REG_xx and NT3H2X11_MSK_xx definitions + */ +#define NT3H2X11_CSREG_SET_VAL(_d, _csreg, _r, _m, _v) \ + _nt3h2x11_write_csreg_register_value(_d, _csreg, NT3H2X11_REG_##_r, \ + NT3H2X11_MSK_##_r##_##_m, _v) + +/** + * @brief Macro-helper to read multi-bit value in session/config-block + * + * Depends on NT3H2X11_REG_xx and NT3H2X11_MSK_xx definitions + */ +#define NT3H2X11_CSREG_GET_VAL(_d, _csreg, _r, _m, _v) \ + _nt3h2x11_read_csreg_register_value(_d, _csreg, NT3H2X11_REG_##_r, \ + NT3H2X11_MSK_##_r##_##_m, _v) + +/******************* HELPER FUNCTIONS *******************/ + +/** + * @brief Read a single-byte value from register in the SESSION or CONFIG block + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[in] reg_addr : Register-address to read + * @param[out] *val : Pointer to value-buffer + * @return : 0 on success, negative upon error. + */ +int _nt3h2x11_read_csreg_register(const struct device *dev, enum nt3h2x11_csreg csreg, + uint8_t reg_addr, uint8_t *val) +{ + struct nt3h2x11_data *data = (struct nt3h2x11_data *)dev->data; + const struct nt3h2x11_cfg *cfg = (const struct nt3h2x11_cfg *)dev->config; + + if (data->dev_i2c == NULL) { + return -ENODEV; + } + + uint8_t wbuf[2]; + + wbuf[0] = csreg; + wbuf[1] = reg_addr; + + int rv = i2c_write_read(data->dev_i2c, cfg->i2c.addr, wbuf, 2u, val, 1u); + + if (rv != 0) { + LOG_ERR("I2C write_read error: %d", rv); + } + + return rv; +} + +/** + * @brief Write a single-byte value to a register in the SESSION or CONFIG block + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[in] reg_addr : Register-address to write to + * @param[in] mask : Mask of value within register + * @param[in] val : Value to write + * @return : 0 on success, negative upon error. + */ +static int _nt3h2x11_write_csreg_register(const struct device *dev, enum nt3h2x11_csreg csreg, + uint8_t reg_addr, uint8_t mask, uint8_t val) +{ + int rv = 0; + struct nt3h2x11_data *data = (struct nt3h2x11_data *)dev->data; + const struct nt3h2x11_cfg *cfg = (const struct nt3h2x11_cfg *)dev->config; + + if (data->dev_i2c == NULL) { + return -ENODEV; + } + + uint8_t wbuf[4]; + + wbuf[0] = csreg; + wbuf[1] = reg_addr; + wbuf[2] = mask; + wbuf[3] = val; + + rv = i2c_write(data->dev_i2c, wbuf, 4u, cfg->i2c.addr); + if (rv != 0) { + LOG_ERR("I2C write error: %d", rv); + return rv; + } + + return 0; +} + +/** + * @brief Write and validate a single-bit value to a register in the SESSION or + * CONFIG block + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[in] reg_addr : Register-address to write to + * @param[in] mask : Mask of value within register + * @param[in] val : Value to write + * @return : 0 on success, negative upon error. + */ +static int _nt3h2x11_write_csreg_register_enable(const struct device *dev, + enum nt3h2x11_csreg csreg, uint8_t reg, + uint8_t mask, uint8_t val) +{ + uint8_t mask_val = (val != 0) ? mask : 0u; + + return _nt3h2x11_write_csreg_register(dev, csreg, reg, mask, mask_val); +} + +/** + * @brief Read and validate a single-bit value to a register in the SESSION or + * CONFIG block + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[in] reg_addr : Register-address to write to + * @param[in] mask : Mask of value within register + * @param[out] *val : Pointer to value where result is stored + * @return : 0 on success, negative upon error. + */ +static int _nt3h2x11_read_csreg_register_enable(const struct device *dev, enum nt3h2x11_csreg csreg, + uint8_t reg, uint8_t mask, uint8_t *val) +{ + uint8_t regval = 0u; + int rv = _nt3h2x11_read_csreg_register(dev, csreg, reg, ®val); + + if (rv == 0) { + *val = ((regval & mask) != 0) ? 1u : 0u; + } + + return rv; +} + +/** + * @brief Write and validate a multi-bit value to a register in the SESSION or + * CONFIG block + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[in] reg_addr : Register-address to write to + * @param[in] mask : Mask of value within register + * @param[in] val : Value to write + * @return : 0 on success, negative upon error. + */ +static int _nt3h2x11_write_csreg_register_value(const struct device *dev, enum nt3h2x11_csreg csreg, + uint8_t reg, uint8_t mask, uint8_t val) +{ + return _nt3h2x11_write_csreg_register(dev, csreg, reg, mask, val); +} + +/** + * @brief Read and validate a multi-bit value to a register in the SESSION or + * CONFIG block + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[in] reg_addr : Register-address to write to + * @param[in] mask : Mask of value within register + * @param[out] *val : Pointer to value where result is stored + * @return : 0 on success, negative upon error. + */ +static int _nt3h2x11_read_csreg_register_value(const struct device *dev, enum nt3h2x11_csreg csreg, + uint8_t reg, uint8_t mask, uint8_t *val) +{ + uint8_t regval = 0u; + int rv = _nt3h2x11_read_csreg_register(dev, csreg, reg, ®val); + + if (rv == 0) { + *val = (regval & mask); + } + + return rv; +} + +/******************* DRIVER FUNCTIONS *******************/ + +int nt3h2x11_set_device(const struct device *dev, struct nt3h2x11_device *dblk) +{ + return nt3h2x11_write_blocks(dev, NT3H2X11_BLK_DEVICE, (uint8_t *)dblk, 1u); +} + +int nt3h2x11_get_device(const struct device *dev, struct nt3h2x11_device *dblk) +{ + return nt3h2x11_read_blocks(dev, NT3H2X11_BLK_DEVICE, (uint8_t *)dblk, 1u); +} + +int nt3h2x11_set_cfg_auth(const struct device *dev, struct nt3h2x11_cfg_auth *cfg_auth) +{ + int rv = 0; + uint8_t blk_la[NT3H2X11_BYTES_BLK]; /* lock&auth */ + uint8_t blk_ap[NT3H2X11_BYTES_BLK]; /* access&pwd */ + + /* read current authentication */ + rv = nt3h2x11_read_blocks(dev, NT3H2X11_BLK_LOCK_AUTH, blk_la, 1u); + if (rv != 0) { + return rv; + } + rv = nt3h2x11_read_blocks(dev, NT3H2X11_BLK_ACCESS_PWD, blk_ap, 1u); + if (rv != 0) { + return rv; + } + + /* update authentication */ + (void)memcpy(&blk_la[NT3H2X11_REG_DYNAMIC_LOCK], cfg_auth->dyn_lock, + NT3H2X11_BYTES_DYNAMIC_LOCK); + (void)memcpy(&blk_la[NT3H2X11_REG_AUTH0], cfg_auth->auth0, NT3H2X11_BYTES_AUTH0); + (void)memcpy(&blk_ap[NT3H2X11_REG_ACCESS], cfg_auth->access, NT3H2X11_BYTES_ACCESS); + (void)memcpy(&blk_ap[NT3H2X11_REG_PWD], cfg_auth->pwd, NT3H2X11_BYTES_PWD); + (void)memcpy(&blk_ap[NT3H2X11_REG_PACK], cfg_auth->pack, NT3H2X11_BYTES_PACK); + (void)memcpy(&blk_ap[NT3H2X11_REG_PT_I2C], cfg_auth->pt_i2c, NT3H2X11_BYTES_PT_I2C); + + /* write updated authentication */ + rv = nt3h2x11_write_blocks(dev, NT3H2X11_BLK_LOCK_AUTH, blk_la, 1u); + if (rv != 0) { + return rv; + } + rv = nt3h2x11_write_blocks(dev, NT3H2X11_BLK_ACCESS_PWD, blk_ap, 1u); + if (rv != 0) { + return rv; + } + + return 0; +} + +int nt3h2x11_get_cfg_auth(const struct device *dev, struct nt3h2x11_cfg_auth *cfg_auth) +{ + int rv = 0; + uint8_t blk_la[NT3H2X11_BYTES_BLK]; /* lock&auth */ + uint8_t blk_ap[NT3H2X11_BYTES_BLK]; /* access&pwd */ + + rv = nt3h2x11_read_blocks(dev, NT3H2X11_BLK_LOCK_AUTH, blk_la, 1u); + if (rv != 0) { + return rv; + } + rv = nt3h2x11_read_blocks(dev, NT3H2X11_BLK_ACCESS_PWD, blk_ap, 1u); + if (rv != 0) { + return rv; + } + + (void)memcpy(cfg_auth->dyn_lock, &blk_la[NT3H2X11_REG_DYNAMIC_LOCK], + NT3H2X11_BYTES_DYNAMIC_LOCK); + (void)memcpy(cfg_auth->auth0, &blk_la[NT3H2X11_REG_AUTH0], NT3H2X11_BYTES_AUTH0); + (void)memcpy(cfg_auth->access, &blk_ap[NT3H2X11_REG_ACCESS], NT3H2X11_BYTES_ACCESS); + (void)memcpy(cfg_auth->pwd, &blk_ap[NT3H2X11_REG_PWD], NT3H2X11_BYTES_PWD); + (void)memcpy(cfg_auth->pack, &blk_ap[NT3H2X11_REG_PACK], NT3H2X11_BYTES_PACK); + (void)memcpy(cfg_auth->pt_i2c, &blk_ap[NT3H2X11_REG_PT_I2C], NT3H2X11_BYTES_PT_I2C); + + return rv; +} + +/******************* SESSION AND CONFIG OPERATIONS *******************/ + +int nt3h2x11_set_softreset_en(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t enable) +{ + return NT3H2X11_CSREG_SET_EN(dev, csreg, NC_REG, RST_ON_OFF, enable); +} + +int nt3h2x11_get_softreset_en(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t *enable) +{ + return NT3H2X11_CSREG_GET_EN(dev, csreg, NC_REG, RST_ON_OFF, enable); +} + +int nt3h2x11_set_pthru_en(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t enable) +{ + return NT3H2X11_CSREG_SET_EN(dev, csreg, NC_REG, PTHRU_ON_OFF, enable); +} + +int nt3h2x11_get_pthru_en(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t *enable) +{ + return NT3H2X11_CSREG_GET_EN(dev, csreg, NC_REG, PTHRU_ON_OFF, enable); +} + +int nt3h2x11_set_fd_off(const struct device *dev, enum nt3h2x11_csreg csreg, + enum nt3h2x11_fd_off fd_off) +{ + return NT3H2X11_CSREG_SET_VAL(dev, csreg, NC_REG, FD_OFF, fd_off); +} + +int nt3h2x11_get_fd_off(const struct device *dev, enum nt3h2x11_csreg csreg, + enum nt3h2x11_fd_off *fd_off) +{ + return NT3H2X11_CSREG_GET_VAL(dev, csreg, NC_REG, FD_OFF, (uint8_t *)fd_off); +} + +int nt3h2x11_set_fd_on(const struct device *dev, enum nt3h2x11_csreg csreg, + enum nt3h2x11_fd_on fd_on) +{ + return NT3H2X11_CSREG_SET_VAL(dev, csreg, NC_REG, FD_ON, fd_on); +} + +int nt3h2x11_get_fd_on(const struct device *dev, enum nt3h2x11_csreg csreg, + enum nt3h2x11_fd_on *fd_on) +{ + return NT3H2X11_CSREG_GET_VAL(dev, csreg, NC_REG, FD_ON, (uint8_t *)fd_on); +} + +int nt3h2x11_set_srammirror_en(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t enable) +{ + return NT3H2X11_CSREG_SET_EN(dev, csreg, NC_REG, SRAM_MIRROR_ON_OFF, enable); +} + +int nt3h2x11_get_srammirror_en(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t *enable) +{ + return NT3H2X11_CSREG_GET_EN(dev, csreg, NC_REG, SRAM_MIRROR_ON_OFF, enable); +} + +int nt3h2x11_set_transfer_dir(const struct device *dev, enum nt3h2x11_csreg csreg, + enum nt3h2x11_transfer_dir dir) +{ + int rv = 0; + uint8_t regval = 0u; + + /* Read current setting */ + rv = _nt3h2x11_read_csreg_register(dev, csreg, NT3H2X11_REG_NC_REG, ®val); + if (rv != 0) { + return rv; + } + + /* only update if direction is different from current setting */ + if (dir != (regval & NT3H2X11_MSK_NC_REG_TRANSFER_DIR)) { + /* turn pthru on/off when active */ + if ((regval & NT3H2X11_MSK_NC_REG_TRANSFER_DIR) != 0u) { + + rv = NT3H2X11_CSREG_SET_EN(dev, csreg, NC_REG, PTHRU_ON_OFF, 1u); + if (rv != 0) { + return rv; + } + rv = NT3H2X11_CSREG_SET_VAL(dev, csreg, NC_REG, TRANSFER_DIR, dir); + if (rv != 0) { + return rv; + } + rv = NT3H2X11_CSREG_SET_EN(dev, csreg, NC_REG, PTHRU_ON_OFF, 0u); + if (rv != 0) { + return rv; + } + + } else { + rv = NT3H2X11_CSREG_SET_VAL(dev, csreg, NC_REG, TRANSFER_DIR, dir); + if (rv != 0) { + return rv; + } + } + } + return 0; +} + +int nt3h2x11_get_transfer_dir(const struct device *dev, enum nt3h2x11_csreg csreg, + enum nt3h2x11_transfer_dir *dir) +{ + return NT3H2X11_CSREG_GET_VAL(dev, csreg, NC_REG, TRANSFER_DIR, (uint8_t *)dir); +} + +int nt3h2x11_set_last_ndef_blk(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t block) +{ + if (block > NT3H2X11_LAST_NDEF_BLOCK_MAX) { + return -EINVAL; + } + return _nt3h2x11_write_csreg_register(dev, csreg, NT3H2X11_REG_LAST_NDEF_BLOCK, 0xFFu, + block); +} + +int nt3h2x11_get_last_ndef_blk(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t *block) +{ + return _nt3h2x11_read_csreg_register(dev, csreg, NT3H2X11_REG_LAST_NDEF_BLOCK, block); +} + +int nt3h2x11_set_srammirror_blk(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t block) +{ + return _nt3h2x11_write_csreg_register(dev, csreg, NT3H2X11_REG_SRAM_MIRROR_BLOCK, 0xFFu, + block); +} + +int nt3h2x11_get_srammirror_blk(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t *block) +{ + return _nt3h2x11_read_csreg_register(dev, csreg, NT3H2X11_REG_SRAM_MIRROR_BLOCK, block); +} + +int nt3h2x11_set_wdt(const struct device *dev, enum nt3h2x11_csreg csreg, uint16_t time) +{ + int rv = 0; + uint8_t val = 0u; + + /* Write LSB */ + val = (time >> 0u) & 0xFFu; + rv = _nt3h2x11_write_csreg_register(dev, csreg, NT3H2X11_REG_WDT_LS, 0xFFu, val); + if (rv != 0) { + return rv; + } + + /* Write MSB */ + val = (time >> 8u) & 0xFFu; + rv = _nt3h2x11_write_csreg_register(dev, csreg, NT3H2X11_REG_WDT_MS, 0xFFu, val); + if (rv != 0) { + return rv; + } + + return 0; +} + +int nt3h2x11_get_wdt(const struct device *dev, enum nt3h2x11_csreg csreg, uint16_t *wdt) +{ + int rv = 0; + uint8_t regval = 0u; + + rv = _nt3h2x11_read_csreg_register(dev, csreg, NT3H2X11_REG_WDT_LS, ®val); + if (rv != 0) { + return rv; + } + + *wdt |= (((uint16_t)regval) << 0u) & 0x00FFu; + + rv = _nt3h2x11_read_csreg_register(dev, csreg, NT3H2X11_REG_WDT_MS, ®val); + if (rv != 0) { + return rv; + } + + *wdt |= (((uint16_t)regval) << 8u) & 0xFF00u; + + return 0; +} + +int nt3h2x11_set_i2c_clkstr_en(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t enable) +{ + return NT3H2X11_CSREG_SET_EN(dev, csreg, I2C_CLOCK_STR, CLOCK_STR, enable); +} + +int nt3h2x11_get_i2c_clkstr_en(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t *enable) +{ + return NT3H2X11_CSREG_GET_EN(dev, csreg, I2C_CLOCK_STR, CLOCK_STR, (uint8_t *)enable); +} + +/******************* CONFIG ONLY OPERATIONS *******************/ + +int nt3h2x11_get_i2c_lock_config(const struct device *dev, uint8_t *locked) +{ + return NT3H2X11_CSREG_GET_EN(dev, NT3H2X11_CONFIG, CFG_REG_LOCK, LOCK_I2C, locked); +} + +int nt3h2x11_get_nfc_lock_config(const struct device *dev, uint8_t *locked) +{ + return NT3H2X11_CSREG_GET_EN(dev, NT3H2X11_CONFIG, CFG_REG_LOCK, LOCK_NFC, locked); +} + +/******************* SESSION ONLY OPERATIONS *******************/ + +int nt3h2x11_get_auth_en(const struct device *dev, uint8_t *enable) +{ + return NT3H2X11_CSREG_GET_EN(dev, NT3H2X11_SESSION, I2C_CLOCK_STR, NEG_AUTH, enable); +} + +int nt3h2x11_get_nsreg(const struct device *dev, uint8_t *nsreg) +{ + return _nt3h2x11_read_csreg_register(dev, NT3H2X11_SESSION, NT3H2X11_REG_NS_REG, nsreg); +} + +/******************* GENERIC READ/WRITE *******************/ + +int nt3h2x11_read_blocks(const struct device *dev, uint8_t block, uint8_t *buf, uint8_t count) +{ + int rv = 0u; + struct nt3h2x11_data *data = (struct nt3h2x11_data *)dev->data; + const struct nt3h2x11_cfg *cfg = (const struct nt3h2x11_cfg *)dev->config; + + if (data->dev_i2c == NULL) { + return -ENODEV; + } + + uint16_t buf_idx = 0u; + uint8_t block_idx = block; + + /* read blocks + store directly in buf[] */ + for (uint8_t i = 0u; i < count; i++) { + + rv = i2c_write_read(data->dev_i2c, cfg->i2c.addr, &block_idx, sizeof(block_idx), + &buf[buf_idx], NT3H2X11_BYTES_BLK); + if (rv != 0u) { + LOG_ERR("I2C write_read error: %d", rv); + return rv; + } + + /* shift number of bytes in buf ; shift 1 in block-idx */ + buf_idx += NT3H2X11_BYTES_BLK; + block_idx += 1; + } + + return 0; +} + +int nt3h2x11_write_blocks(const struct device *dev, uint8_t block, uint8_t *buf, uint8_t count) +{ + int rv = 0; + uint32_t timeout = 0u; + uint8_t nsreg = 0u; + struct nt3h2x11_data *data = (struct nt3h2x11_data *)dev->data; + const struct nt3h2x11_cfg *cfg = (const struct nt3h2x11_cfg *)dev->config; + + if (data->dev_i2c == NULL) { + return -ENODEV; + } + + uint16_t buf_idx = 0u; + uint8_t block_idx = block; + uint8_t wbuf[NT3H2X11_BYTES_BLK + 1]; + (void)memset(wbuf, 0u, sizeof(wbuf)); + + /* + * First byte in first block is i2c addr. + * Changing the address needs to be done outside this driver. + * Bitshift is required as LSB is used for RW-details + */ + if (block == 0) { + buf[0] = 0xFFu & (cfg->i2c.addr << 1); + } + + for (uint8_t i = 0u; i < count; i++) { + + /* First byte in write-buffer has to be block address */ + wbuf[0] = block_idx; + (void)memcpy(&wbuf[1], &buf[buf_idx], NT3H2X11_BYTES_BLK); + + /* Write block data */ + rv = i2c_write(data->dev_i2c, wbuf, NT3H2X11_BYTES_BLK + 1, cfg->i2c.addr); + if (rv != 0) { + LOG_ERR("I2C write_read error: %d", rv); + return rv; + } + + /* + * If address is within EEPROM memory region, wait for completion + * ==> Completion bit is written in SESSION block, NS_REG. + * Detection is done with NT3H2X11_MSK_NS_REG_EEPROM_WR_BUSY + */ + if ((block <= NT3H2X11_BLK_SRAM_START) || + (block > + (NT3H2X11_BLK_SRAM_START + (NT3H2X11_BYTES_SRAM / NT3H2X11_BYTES_BLK)))) { + + /* number of timeouts to wait */ + timeout = (CONFIG_NFC_NT3H2X11_MAX_WRITE_DELAY / 5u) + 1u; + do { + (void)k_sleep(K_MSEC(5)); + rv = _nt3h2x11_read_csreg_register(dev, NT3H2X11_SESSION, + NT3H2X11_REG_NS_REG, &nsreg); + if (rv != 0) { + LOG_ERR("I2C read error: %d", rv); + return rv; + } + timeout--; + } while ((timeout != 0u) && + ((nsreg & NT3H2X11_MSK_NS_REG_EEPROM_WR_BUSY) != 0u)); + + if (timeout == 0u) { + return -ETIMEDOUT; + } + } + + /* shift number of bytes in buf ; shift 1 in block-idx */ + buf_idx += NT3H2X11_BYTES_BLK; + block_idx += 1; + } + + return 0; +} + +int nt3h2x11_read_bytes(const struct device *dev, uint16_t addr, uint8_t *buf, uint16_t buf_len) +{ + int rv; + + /* Offset of addr within a block */ + uint8_t offset = addr % NT3H2X11_BYTES_BLK; + /* Remaining (unused)bytes in a block after buf_len */ + uint8_t remain = NT3H2X11_BYTES_BLK - ((buf_len + offset) % NT3H2X11_BYTES_BLK); + /* Start Block */ + uint8_t block = (addr - offset) / NT3H2X11_BYTES_BLK; + /* Number of Blocks */ + uint8_t blocks = (offset + buf_len + remain) / NT3H2X11_BYTES_BLK; + + /* Read blocks */ + uint8_t rbuf[NT3H2X11_BYTES_BLK]; + uint32_t idx_buf = 0u; /* index within `buf` to which we copy */ + uint32_t len_copy; /* number of bytes to copy per iteration */ + + for (uint8_t i = 0u; i <= blocks; i++) { + rv = nt3h2x11_read_blocks(dev, block, rbuf, 1u); + if (rv != 0) { + return rv; + } + + /* Copy data to rbuf */ + len_copy = MIN(NT3H2X11_BYTES_BLK - offset, buf_len - idx_buf); + (void)memcpy(&buf[idx_buf], &rbuf[offset], len_copy); + + block++; /* copy next block */ + offset = 0u; /* copy from start of next block */ + idx_buf += len_copy; + } + + return 0; +} + +int nt3h2x11_write_bytes(const struct device *dev, uint16_t addr, uint8_t *buf, uint16_t buf_len) +{ + int rv; + + /* Offset of addr within a block */ + uint8_t offset = addr % NT3H2X11_BYTES_BLK; + /* Remaining (unused)bytes in a block after buf_len */ + uint8_t remain = NT3H2X11_BYTES_BLK - ((buf_len + offset) % NT3H2X11_BYTES_BLK); + /* Start Block */ + uint8_t block = (addr - offset) / NT3H2X11_BYTES_BLK; + /* Number of Blocks */ + uint8_t blocks = (offset + buf_len + remain) / NT3H2X11_BYTES_BLK; + + /* Read blocks */ + uint8_t rbuf[NT3H2X11_BYTES_BLK]; + uint32_t idx_buf = 0u; /* index within `buf` from which we copy */ + uint32_t len_copy; /* number of bytes to copy per iteration */ + + for (uint8_t i = 0u; i <= blocks; i++) { + rv = nt3h2x11_read_blocks(dev, block, rbuf, 1u); + if (rv != 0) { + return rv; + } + + /* Copy data to rbuf */ + len_copy = MIN(NT3H2X11_BYTES_BLK - offset, buf_len - idx_buf); + (void)memcpy(&rbuf[offset], &buf[idx_buf], len_copy); + + /* Write blocks */ + rv = nt3h2x11_write_blocks(dev, block, rbuf, 1u); + if (rv != 0) { + return rv; + } + + block++; /* copy next block */ + offset = 0u; /* read from start of next block */ + idx_buf += len_copy; + } + + return 0; +} + +int nt3h2x11_set_i2c_addr(const struct device *dev, uint8_t addr_old, uint8_t addr_new) +{ + int rv = 0; + struct nt3h2x11_data *data = (struct nt3h2x11_data *)dev->data; + + uint8_t buf[NT3H2X11_BYTES_BLK + 1]; + uint8_t block_idx = 0u; + + rv = i2c_write_read(data->dev_i2c, addr_old, &block_idx, sizeof(block_idx), &buf[1], + NT3H2X11_BYTES_BLK); + if (rv != 0) { + LOG_DBG("Can't update I2C (%02x => %02x). Read Error: %d", addr_old, addr_new, rv); + return rv; + } + + /* First byte in write-buffer has to be block address */ + buf[0] = 0u; + + /* First byte of first block is i2c addr */ + /* bitshift is required as LSB is used for RW-details */ + buf[1] = 0xFFu & (addr_new << 1); + + /* Write block data */ + rv = i2c_write(data->dev_i2c, buf, NT3H2X11_BYTES_BLK + 1, addr_old); + if (rv != 0) { + LOG_DBG("Can't change I2C (%02x => %02x). Write Error: %d", addr_old, addr_new, rv); + return rv; + } + printk("\n!! - I2C addr has changed : %02x => %02x - !!\n", addr_old, addr_new); +#if CONFIG_REBOOT + printk("\n!! - REBOOTING.. - !!\n"); + k_sleep(K_MSEC(250)); + sys_reboot(SYS_REBOOT_COLD); +#else + printk("\n!! - DEVICE RESET REQUIRED - !!\n"); + while (true) { + } +#endif /* CONFIG_REBOOT */ + return 0; +} + +/******************* IRQ HANDLING *******************/ + +static enum nt3h2x11_event _nt3h2x11_reg2event(const struct device *dev, uint8_t nc_reg, + uint8_t ns_reg) +{ + struct nt3h2x11_data *data = (struct nt3h2x11_data *)dev->data; + enum nt3h2x11_event event = NT3H2X11_EVENT_NONE; + + /* process registers */ + enum nt3h2x11_fd_on fd_on = nc_reg & NT3H2X11_MSK_NC_REG_FD_ON; + enum nt3h2x11_fd_off fd_off = nc_reg & NT3H2X11_MSK_NC_REG_FD_OFF; + enum nt3h2x11_transfer_dir dir = nc_reg & NT3H2X11_MSK_NC_REG_TRANSFER_DIR; + uint8_t flag_pthru = nc_reg & NT3H2X11_MSK_NC_REG_PTHRU_ON_OFF; + + uint8_t flag_ndef_read = ns_reg & NT3H2X11_MSK_NS_REG_NDEF_DATA_READ; + uint8_t flag_i2c_locked = ns_reg & NT3H2X11_MSK_NS_REG_I2C_LOCKED; + uint8_t flag_nfc_locked = ns_reg & NT3H2X11_MSK_NS_REG_RF_LOCKED; + uint8_t flag_i2c_sram_ready = ns_reg & NT3H2X11_MSK_NS_REG_SRAM_I2C_READY; + uint8_t flag_nfc_sram_ready = ns_reg & NT3H2X11_MSK_NS_REG_SRAM_RF_READY; + uint8_t flag_fd_pin = ns_reg & NT3H2X11_MSK_NS_REG_RF_FIELD_PRESENT; + + /* Check FD-edge to discriminate field-on/off from other events */ + uint8_t fd_edge = (data->flag_fd_pin != flag_fd_pin) ? 1u : 0u; + + data->flag_fd_pin = flag_fd_pin; + + /* + * Section 8.4, p34 of NT3H2x11 user manualL: + * REMARK: When FD_ON is configured to trigger on NFC field presence (00b), + * FD will be pulled low again, when host is reading the NDEF_DATA_READ bit + * of NS_REG session register from I2C perspective. + * + * TODO: This might trigger additional "NFC_TAG_EVENT_FIELD_ON" callbacks. + * We should probably need to add a filter? + */ + + /* Field Detect = On */ + switch (fd_on) { + + /* Event upon which the signal output on the FD pin is pulled low + * 00b: if the field is switched on + */ + case NT3H2X11_FD_ON_RF_ON: + if ((fd_edge != 0u) && (flag_fd_pin != 0u)) { + event = NT3H2X11_EVENT_FD_ON; + } + break; + + /* Event upon which the signal output on the FD pin is pulled low + * 01b: by first valid start of communication (SoC) + */ + case NT3H2X11_FD_ON_RF_FIRST_VALID: + if ((fd_edge != 0u) && (flag_fd_pin != 0u)) { + event = NT3H2X11_EVENT_START_OF_COMM; + } + break; + + /* Event upon which the signal output on the FD pin is pulled low + * 10b: by selection of the tag + */ + case NT3H2X11_FD_ON_TAG_SELECTION: + if ((fd_edge != 0u) && (flag_fd_pin != 0u)) { + event = NT3H2X11_EVENT_SELECTED; + } + break; + + /* Event upon which the signal output on the FD pin is pulled low + * 11b: (pthru:NFC>I2C) if data is ready to be read from I2C + * 11b: (pthru:I2C>NFC) if data is read by the NFC interface + */ + case NT3H2X11_FD_ON_RF_DATA_READY: + /* only when pass-through is enabled */ + if (flag_pthru != 0u) { + /* if data is ready to be read from I2C (pthru:NFC>I2C) */ + if (dir == NT3H2X11_TRANSFER_DIR_RF_TO_I2C) { + if (flag_i2c_sram_ready != 0u) { + event = NT3H2X11_EVENT_DATA_READY_I2C; + } + /* if data is read by the NFC interface (pthru:I2C>NFC) */ + } else { + if (flag_nfc_locked == 0u) { + event = NT3H2X11_EVENT_LAST_DATA_READ_NFC; + } + } + } + break; + + default: + break; + } + + /* Field Detect = Off */ + if (event == NT3H2X11_EVENT_NONE) { + switch (fd_off) { + + /* Event upon which the signal output on the FD pin is released + * 00b: if the field is switched off + */ + case NT3H2X11_FD_OFF_RF_OFF: + if ((fd_edge != 0u) && (flag_fd_pin == 0u)) { + event = NT3H2X11_EVENT_FD_OFF; + } + break; + + /* Event upon which the signal output on the FD pin is released + * 01b: if the field is switched off + * or the tag is set to the HALT state + */ + case NT3H2X11_FD_OFF_RF_OFF_OR_HALT: + if ((fd_edge != 0u) && (flag_fd_pin == 0u)) { + event = NT3H2X11_EVENT_FD_OFF; + } else { + /* By elimination of event possibilities: we are halted */ + /* ==> FD_on is checked first, so no other events possible */ + event = NT3H2X11_EVENT_HALTED; + } + break; + + /* Event upon which the signal output on the FD pin is released + * 10b: if the field is switched off + * or the last page of the NDEF message has been read + */ + case NT3H2X11_FD_OFF_RF_OFF_OR_LAST_NDEF_READ: + if ((fd_edge != 0u) && (flag_fd_pin == 0u)) { + event = NT3H2X11_EVENT_FD_OFF; + } else if (flag_ndef_read != 0u) { + event = NT3H2X11_EVENT_LAST_NDEF_READ; + } else { + /* should not happen */ + } + break; + + /* Event upon which the signal output on the FD pin is released + * 11b: (if FD_ON = 11b) if the field is switched off + * or if last data is read by I2C (pthru:NFC>I2C) + * or last data is written by I2C (pthru:I2C>NFC) + * 11b: (if FD_ON = 00b or 01b or 10b) if the field is switched off + */ + case NT3H2X11_FD_OFF_RF_OFF_OR_LAST_DATA_RW: + if ((fd_edge != 0u) && (flag_fd_pin == 0u)) { + event = NT3H2X11_EVENT_FD_OFF; + + } else if (flag_pthru != 0u) { + /* Only when pass-through is enabled */ + + /* 11b: (if FD_ON = 11b) .. */ + if (fd_on == NT3H2X11_FD_ON_RF_ON) { + if ((dir == NT3H2X11_TRANSFER_DIR_RF_TO_I2C) && + (flag_i2c_locked == 0u)) { + /* .. if last data is read (pthru:NFC>I2C) */ + event = NT3H2X11_EVENT_LAST_DATA_READ_I2C; + + } else if ((dir != NT3H2X11_TRANSFER_DIR_RF_TO_I2C) && + (flag_nfc_sram_ready != 0u)) { + /* .. or last data is written (pthru:I2C>NFC) */ + event = NT3H2X11_EVENT_LAST_DATA_WRITTEN_I2C; + } else { + /* .. */ + } + } + + } else { + /* .. */ + } + break; + + default: + break; + } + } + + return event; +} + +int nt3h2x11_irq_set_callback(const struct device *dev, nt3h2x11_irq_callback_t cb) +{ + struct nt3h2x11_data *data = (struct nt3h2x11_data *)dev->data; + + data->app_irq_cb = cb; + return 0; +} + +static void _nt3h2x11_irq_cb_worker(struct k_work *worker) +{ + struct nt3h2x11_data *data = CONTAINER_OF(worker, struct nt3h2x11_data, worker_irq); + const struct device *dev = data->parent; + + uint8_t ns_reg, nc_reg; + (void)_nt3h2x11_read_csreg_register(dev, NT3H2X11_SESSION, NT3H2X11_REG_NC_REG, &nc_reg); + (void)_nt3h2x11_read_csreg_register(dev, NT3H2X11_SESSION, NT3H2X11_REG_NS_REG, &ns_reg); + enum nt3h2x11_event event = _nt3h2x11_reg2event(dev, nc_reg, ns_reg); + + if (data->app_irq_cb != NULL) { + data->app_irq_cb(dev, event, nc_reg, ns_reg); + } +} + +static void _nt3h2x11_irq_external_cb(const struct device *dev, struct gpio_callback *cb, + uint32_t pins) +{ + struct nt3h2x11_data *data = CONTAINER_OF(cb, struct nt3h2x11_data, dev_irq_external_cb); + + /* Push handling to worker */ + if (data->initialized == 1u) { + k_work_submit(&data->worker_irq); + } +} + +static void _nt3h2x11_irq_internal_cb(const void *param) +{ + const struct device *dev = (const struct device *)param; + struct nt3h2x11_data *data = (struct nt3h2x11_data *)dev->data; + +#ifdef CONFIG_SOC_SERIES_K32 + /* Toggle INT_INVERT bit to catch opposite FD transition and avoid immediate ISR reentry */ + ASYNC_SYSCON->NFCTAGPADSCTRL ^= ASYNC_SYSCON_NFCTAGPADSCTRL_INT_INVERT_MASK; +#endif + + /* Push handling to worker */ + if (data->initialized == 1u) { + k_work_submit(&data->worker_irq); + } +} + +/******************* NFC SUBSYS DRIVER IMPLEMENTATION *******************/ + +static void nt3h2x11_tag_irq_cb(const struct device *dev, enum nt3h2x11_event event, uint8_t nc_reg, + uint8_t ns_reg) +{ + ARG_UNUSED(nc_reg); + ARG_UNUSED(ns_reg); + + struct nt3h2x11_data *data = dev->data; + enum nfc_tag_event nfc_event = NFC_TAG_EVENT_NONE; + + switch (event) { + case NT3H2X11_EVENT_NONE: + nfc_event = NFC_TAG_EVENT_NONE; + break; + + case NT3H2X11_EVENT_FD_OFF: + nfc_event = NFC_TAG_EVENT_FIELD_OFF; + break; + + case NT3H2X11_EVENT_FD_ON: + case NT3H2X11_EVENT_START_OF_COMM: + case NT3H2X11_EVENT_SELECTED: + nfc_event = NFC_TAG_EVENT_FIELD_ON; + break; + + case NT3H2X11_EVENT_HALTED: + nfc_event = NFC_TAG_EVENT_STOPPED; + break; + + case NT3H2X11_EVENT_LAST_NDEF_READ: + case NT3H2X11_EVENT_LAST_DATA_READ_NFC: + nfc_event = NFC_TAG_EVENT_READ_DONE; + break; + + case NT3H2X11_EVENT_DATA_READY_I2C: + nfc_event = NFC_TAG_EVENT_DATA_IND; + break; + + case NT3H2X11_EVENT_LAST_DATA_READ_I2C: + nfc_event = NFC_TAG_EVENT_DATA_IND_DONE; + break; + + case NT3H2X11_EVENT_LAST_DATA_WRITTEN_I2C: + nfc_event = NFC_TAG_EVENT_DATA_TRANSMITTED; + break; + + default: + break; + } + + if (data->nfc_tag_cb != NULL) { + data->nfc_tag_cb(dev, nfc_event, NULL, 0u); + } +} + +static int nt3h2x11_tag_init(const struct device *dev, nfc_tag_cb_t cb) +{ + struct nt3h2x11_data *data = dev->data; + + /* overwrite external application callback with NFC-subsystem */ + if (cb != NULL) { + (void)nt3h2x11_irq_set_callback(dev, nt3h2x11_tag_irq_cb); + data->nfc_tag_cb = cb; + } + + /* Default register setup */ + (void)nt3h2x11_set_fd_off(dev, NT3H2X11_SESSION, NT3H2X11_FD_OFF_RF_OFF_OR_LAST_NDEF_READ); + (void)nt3h2x11_set_fd_on(dev, NT3H2X11_SESSION, NT3H2X11_FD_ON_TAG_SELECTION); + + return 0; +} + +static int nt3h2x11_tag_set_type(const struct device *dev, enum nfc_tag_type type) +{ + int rv = 0; + struct nt3h2x11_data *data = dev->data; + + /* nt3h2x11 only support T2T messages */ + if (type != NFC_TAG_TYPE_T2T) { + return -ENOTSUP; + } + + /* Load default settings = T2T */ + struct nt3h2x11_device default_device = NT3H2X11_DEFAULT_DEVICE; + + rv = nt3h2x11_set_device(dev, &default_device); + if (rv != 0) { + return rv; + } + + /* Type is set */ + data->tag_type = type; + return 0; +} + +static int nt3h2x11_tag_get_type(const struct device *dev, enum nfc_tag_type *type) +{ + struct nt3h2x11_data *data = dev->data; + *type = data->tag_type; + return 0; +} + +static int nt3h2x11_tag_start(const struct device *dev) +{ + ARG_UNUSED(dev); + /* nt3h2x11 is always active */ + return 0; +} + +static int nt3h2x11_tag_stop(const struct device *dev) +{ + ARG_UNUSED(dev); + /* nt3h2x11 is always active */ + return 0; +} + +static int nt3h2x11_tag_set_ndef(const struct device *dev, uint8_t *buf, uint16_t len) +{ + /* + * TODO: current implementation assumes a single continues block to write + * data to but it is actually split in SECTOR0 and SECTOR1 when using a + * 2k config. + */ + + int rv; + uint32_t addr = NT3H2X11_BLK_SECTOR0_START * NT3H2X11_BYTES_BLK; + + uint8_t buf_hdr[4]; + uint8_t terminatorTLV = 0xFEu; + + /* write header */ + if (len < 0xFF) { + buf_hdr[0] = 0x3u; + buf_hdr[1] = len; + rv = nt3h2x11_write_bytes(dev, addr, buf_hdr, 2u); + if (rv != 0) { + return rv; + } + addr += 2u; + + } else { + buf_hdr[0] = 0x3u; + buf_hdr[1] = 0xFFu; + buf_hdr[2] = (uint8_t)((len & 0x0000FF00u) >> 8); + buf_hdr[3] = (uint8_t)((len & 0x000000FFu) >> 0); + rv = nt3h2x11_write_bytes(dev, addr, buf_hdr, 4u); + if (rv != 0) { + return rv; + } + addr += 4u; + } + + /* write payload */ + rv = nt3h2x11_write_bytes(dev, addr, buf, len); + if (rv != 0) { + return rv; + } + addr += len; + + /* write terminator */ + rv = nt3h2x11_write_bytes(dev, addr, &terminatorTLV, 1u); + if (rv != 0) { + return rv; + } + addr += 1u; + + /* update LAST_NDEF_BLOCK */ + uint8_t blk_offset = addr % NT3H2X11_BYTES_BLK; + uint8_t blk_addr = (addr - blk_offset) / NT3H2X11_BYTES_BLK; + + rv = nt3h2x11_set_last_ndef_blk(dev, NT3H2X11_SESSION, blk_addr); + + return rv; +} + +static int nt3h2x11_tag_cmd(const struct device *dev, enum nfc_tag_cmd cmd, uint8_t *buf, + uint16_t *buf_len) +{ + ARG_UNUSED(dev); + ARG_UNUSED(cmd); + ARG_UNUSED(buf); + ARG_UNUSED(buf_len); + return 0; +} + +static struct nfc_tag_driver_api _nt3h2x11_driver_api = { + .init = nt3h2x11_tag_init, + .set_type = nt3h2x11_tag_set_type, + .get_type = nt3h2x11_tag_get_type, + .start = nt3h2x11_tag_start, + .stop = nt3h2x11_tag_stop, + .set_ndef = nt3h2x11_tag_set_ndef, + .cmd = nt3h2x11_tag_cmd, +}; + +/******************* DEVICE INITIALISATION BY DTS *******************/ + +/** + * @brief Initialize NTAG driver as an External IC + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @return : 0 on success, negative upon error. + */ +static int _nt3h2x11_init_external(const struct device *dev) +{ + int rv = 0; + struct nt3h2x11_data *data = (struct nt3h2x11_data *)dev->data; + const struct nt3h2x11_cfg *cfg = (const struct nt3h2x11_cfg *)dev->config; + + data->dev_irq_external = cfg->irq_gpio.port; + if (data->dev_irq_external != NULL) { + + rv = gpio_pin_configure_dt(&cfg->irq_gpio, GPIO_INPUT); + if (rv != 0) { + LOG_ERR("Init IRQ-pin failed, pin:%d, err:%d", cfg->irq_gpio.pin, rv); + return rv; + } + + gpio_init_callback(&data->dev_irq_external_cb, _nt3h2x11_irq_external_cb, + BIT(cfg->irq_gpio.pin)); + + rv = gpio_add_callback(data->dev_irq_external, &data->dev_irq_external_cb); + if (rv != 0) { + LOG_ERR("Init IRQ-cb callback, err:%d", rv); + return rv; + } + + rv = gpio_pin_interrupt_configure_dt(&cfg->irq_gpio, GPIO_INT_EDGE_BOTH); + if (rv != 0) { + LOG_ERR("Could not configure gpio %d, err: %d", cfg->irq_gpio.pin, rv); + return rv; + } + + LOG_DBG("IRQ: GPIO initialised (bus:%s)", cfg->i2c.bus->name); + } + + return 0; +} + +/** + * @brief Initialize NTAG driver as an Internal IC + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @return : 0 on success, negative upon error. + */ +static int _nt3h2x11_init_internal(const struct device *dev) +{ + const struct nt3h2x11_cfg *cfg = (const struct nt3h2x11_cfg *)dev->config; + + if (cfg->internal != 0) { + irq_enable(cfg->irq_gpio.pin); + } + +#ifdef CONFIG_SOC_SERIES_K32 + /* power on nt3h2x11 */ + ASYNC_SYSCON->NFCTAG_VDD = (ASYNC_SYSCON_NFCTAG_VDD_NFCTAG_VDD_OE_MASK | + ASYNC_SYSCON_NFCTAG_VDD_NFCTAG_VDD_OUT_MASK); + k_sleep(K_MSEC(300)); +#endif + + return 0; +} + +/** + * @brief Initialize NTAG driver IC. + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @return : 0 on success, negative upon error. + */ +static int _nt3h2x11_init(const struct device *dev) +{ + int rv = 0; + struct nt3h2x11_data *data = (struct nt3h2x11_data *)dev->data; + const struct nt3h2x11_cfg *cfg = (const struct nt3h2x11_cfg *)dev->config; + + LOG_DBG("nt3h2x11: init"); + + /* setup i2c */ + data->parent = (const struct device *)dev; + data->dev_i2c = cfg->i2c.bus; + + /* setup FD-edge detection : FD-pin is default low */ + data->flag_fd_pin = 0u; + + if (data->dev_i2c == NULL) { + LOG_ERR("Init I2C failed, could not bind, %s", cfg->i2c.bus->name); + return -ENXIO; + } + + /* setup IO / device as internal or externally connected */ + if (cfg->internal != 0) { + rv = _nt3h2x11_init_internal(dev); + if (rv != 0) { + return rv; + } + } else { + rv = _nt3h2x11_init_external(dev); + if (rv != 0) { + return rv; + } + } + + /* Init worker to process IRQ-callbacks */ + k_work_init(&data->worker_irq, _nt3h2x11_irq_cb_worker); + data->initialized = 1u; + + LOG_DBG("nt3h2x11: init OK"); + + return rv; +} + +#define NT3H2X11_INIT(inst) \ + static struct nt3h2x11_data nt3h2x11_data##inst = {0}; \ + static const struct nt3h2x11_cfg nt3h2x11_cfg##inst = { \ + .i2c = I2C_DT_SPEC_INST_GET(inst), \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, interrupts), \ + (.internal = 1, .irq_gpio = {0}, \ + .irq_pin = DT_INST_IRQ_BY_IDX(inst, 0, irq)), \ + (.internal = 0, \ + .irq_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, irq_gpios, {0}), \ + .irq_pin = 0))}; \ + \ + static int _nt3h2x11_init##inst(const struct device *dev) \ + { \ + IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, interrupts), \ + (IRQ_CONNECT(DT_INST_IRQ_BY_IDX(inst, 0, irq), \ + DT_INST_IRQ_BY_IDX(inst, 0, priority), \ + _nt3h2x11_irq_internal_cb, DEVICE_DT_INST_GET(inst), 0);)) \ + return _nt3h2x11_init(dev); \ + } \ + \ + DEVICE_DT_INST_DEFINE(inst, _nt3h2x11_init##inst, NULL, &nt3h2x11_data##inst, \ + &nt3h2x11_cfg##inst, POST_KERNEL, CONFIG_NFC_NT3H2X11_INIT_PRIORITY, \ + &_nt3h2x11_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(NT3H2X11_INIT) diff --git a/dts/bindings/nfc/nfc,nt3h2x11.yaml b/dts/bindings/nfc/nfc,nt3h2x11.yaml new file mode 100644 index 00000000000000..2de8fc633c17e0 --- /dev/null +++ b/dts/bindings/nfc/nfc,nt3h2x11.yaml @@ -0,0 +1,17 @@ +# +# Copyright (c) 2023 Sendrato +# +# SPDX-License-Identifier: Apache-2.0 +# + +description: NXP NT3H2X11 IC + +compatible: "nxp,nt3h2X11" + +include: i2c-device.yaml + +properties: + irq-gpios: + type: phandle-array + description: | + The INT signal configuration is active-low. diff --git a/include/zephyr/drivers/nfc/nt3h2x11.h b/include/zephyr/drivers/nfc/nt3h2x11.h new file mode 100644 index 00000000000000..36e51c1f0a85bf --- /dev/null +++ b/include/zephyr/drivers/nfc/nt3h2x11.h @@ -0,0 +1,768 @@ +/* + * Copyright (c) 2023 Sendrato + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_NFC_NT3H2X11_H_ +#define ZEPHYR_INCLUDE_DRIVERS_NFC_NT3H2X11_H_ + +#include +#include +#include + +/* Memory Layout */ +#ifdef CONFIG_NFC_NT3H2X11_2K +#define NT3H2X11_LAST_NDEF_BLOCK_MAX (0x7Fu) +#else +#define NT3H2X11_LAST_NDEF_BLOCK_MAX (0x37u) +#endif + +/* Blocks and I2C addresses */ +#define NT3H2X11_BLK_DEVICE (0x00u) +#define NT3H2X11_BLK_SECTOR0_START (0x01u) +#define NT3H2X11_BLK_LOCK_AUTH (0x38u) +#define NT3H2X11_BLK_ACCESS_PWD (0x39u) +#define NT3H2X11_BLK_CONFIG (0x3Au) +/* SECTOR1 only available if CONFIG_NFC_NT3H2X11_2K */ +#define NT3H2X11_BLK_SECTOR1_START (0x40u) +#define NT3H2X11_BLK_SRAM_START (0xF8u) +#define NT3H2X11_BLK_SESSION (0xFEu) + +/* Memory-sizes in bytes */ +#define NT3H2X11_BYTES_BLK ((uint16_t)(16u)) +#define NT3H2X11_BYTES_SECTOR0 ((uint16_t)((NT3H2X11_BYTES_BLK * 55u) + 8u)) +#define NT3H2X11_BYTES_SECTOR1 ((uint16_t)((NT3H2X11_BYTES_BLK * 64u) + 0u)) +#define NT3H2X11_BYTES_SRAM ((uint16_t)((NT3H2X11_BYTES_BLK * 4u) + 0u)) + +/* + * Registers for Device blocks + * ==> NT3H2X11_BLK_DEVICE + */ + +#define NT3H2X11_REG_ADDR (0x00u) +#define NT3H2X11_BYTES_ADDR (1u) + +#define NT3H2X11_REG_SERIAL (0x01u) +#define NT3H2X11_BYTES_SERIAL (6u) + +#define NT3H2X11_REG_INTERNAL (0x07u) +#define NT3H2X11_BYTES_INTERNAL (3u) + +#define NT3H2X11_REG_STATIC_LOCK (0x0Au) +#define NT3H2X11_BYTES_STATIC_LOCK (2u) + +#define NT3H2X11_REG_CC (0x0Cu) +#define NT3H2X11_BYTES_CC (4u) + +/* + * Registers for Password and Access blocks + * ==> NT3H2X11_BLK_LOCK_AUTH + * ==> NT3H2X11_BLK_ACCESS_PWD + */ + +#define NT3H2X11_REG_DYNAMIC_LOCK (0x08u) +#define NT3H2X11_BYTES_DYNAMIC_LOCK (3u) + +#define NT3H2X11_REG_AUTH0 (0x0Fu) +#define NT3H2X11_BYTES_AUTH0 (1u) +/* Disable authentication value */ +#define NT3H2X11_MSK_AUTH0_DISABLE (0xFFu) + +#define NT3H2X11_REG_ACCESS (0x00u) +#define NT3H2X11_BYTES_ACCESS (1u) +/* TODO: ACCESS-bits */ + +#define NT3H2X11_REG_PWD (0x04u) +#define NT3H2X11_BYTES_PWD (4u) + +#define NT3H2X11_REG_PACK (0x08u) +#define NT3H2X11_BYTES_PACK (2u) + +#define NT3H2X11_REG_PT_I2C (0x0Cu) +#define NT3H2X11_BYTES_PT_I2C (1u) +/* TODO: PT_I2C-bits */ + +/* + * Registers for CONFIG and SESSION blocks + * ==> NT3H2X11_BLK_CONFIG + * ==> NT3H2X11_BLK_SESSION + */ + +/* CONFIG and SESSION */ +#define NT3H2X11_REG_NC_REG (0x00u) +#define NT3H2X11_MSK_NC_REG_RST_ON_OFF ((uint8_t)0x80u) +#define NT3H2X11_MSK_NC_REG_PTHRU_ON_OFF ((uint8_t)0x40u) +#define NT3H2X11_MSK_NC_REG_FD_OFF ((uint8_t)0x30u) +#define NT3H2X11_MSK_NC_REG_FD_ON ((uint8_t)0x0Cu) +#define NT3H2X11_MSK_NC_REG_SRAM_MIRROR_ON_OFF ((uint8_t)0x02u) +#define NT3H2X11_MSK_NC_REG_TRANSFER_DIR ((uint8_t)0x01u) + +/* CONFIG and SESSION */ +#define NT3H2X11_REG_LAST_NDEF_BLOCK (0x01u) +#define NT3H2X11_REG_SRAM_MIRROR_BLOCK (0x02u) +#define NT3H2X11_REG_WDT_LS (0x03u) +#define NT3H2X11_REG_WDT_MS (0x04u) + +/* CONFIG and SESSION */ +#define NT3H2X11_REG_I2C_CLOCK_STR (0x05u) +#define NT3H2X11_MSK_I2C_CLOCK_STR_NEG_AUTH ((uint8_t)0x02u) /* session only */ +#define NT3H2X11_MSK_I2C_CLOCK_STR_CLOCK_STR ((uint8_t)0x01u) + +/* SESSION ONLY */ +#define NT3H2X11_REG_NS_REG (0x06u) +#define NT3H2X11_MSK_NS_REG_NDEF_DATA_READ ((uint8_t)0x80u) +#define NT3H2X11_MSK_NS_REG_I2C_LOCKED ((uint8_t)0x40u) +#define NT3H2X11_MSK_NS_REG_RF_LOCKED ((uint8_t)0x20u) +#define NT3H2X11_MSK_NS_REG_SRAM_I2C_READY ((uint8_t)0x10u) +#define NT3H2X11_MSK_NS_REG_SRAM_RF_READY ((uint8_t)0x08u) +#define NT3H2X11_MSK_NS_REG_EEPROM_WR_ERR ((uint8_t)0x04u) +#define NT3H2X11_MSK_NS_REG_EEPROM_WR_BUSY ((uint8_t)0x02u) +#define NT3H2X11_MSK_NS_REG_RF_FIELD_PRESENT ((uint8_t)0x01u) + +/* CONFIG ONLY */ +#define NT3H2X11_REG_CFG_REG_LOCK (0x06u) +#define NT3H2X11_MSK_CFG_REG_LOCK_LOCK_I2C ((uint8_t)0x02u) +#define NT3H2X11_MSK_CFG_REG_LOCK_LOCK_NFC ((uint8_t)0x01u) + +enum nt3h2x11_fd_off { + NT3H2X11_FD_OFF_RF_OFF = (0x0u << 4), + NT3H2X11_FD_OFF_RF_OFF_OR_HALT = (0x1u << 4), + NT3H2X11_FD_OFF_RF_OFF_OR_LAST_NDEF_READ = (0x2u << 4), + NT3H2X11_FD_OFF_RF_OFF_OR_LAST_DATA_RW = (0x3u << 4) +}; + +enum nt3h2x11_fd_on { + NT3H2X11_FD_ON_RF_ON = (0x0u << 2), + NT3H2X11_FD_ON_RF_FIRST_VALID = (0x1u << 2), + NT3H2X11_FD_ON_TAG_SELECTION = (0x2u << 2), + NT3H2X11_FD_ON_RF_DATA_READY = (0x3u << 2) +}; + +enum nt3h2x11_transfer_dir { + NT3H2X11_TRANSFER_DIR_I2C_TO_RF = (0x0u << 0), + NT3H2X11_TRANSFER_DIR_RF_TO_I2C = (0x1u << 0) +}; + +enum nt3h2x11_csreg { + NT3H2X11_CONFIG = NT3H2X11_BLK_CONFIG, + NT3H2X11_SESSION = NT3H2X11_BLK_SESSION +}; + +struct __packed nt3h2x11_cc { + uint8_t magic; + uint8_t version; + uint8_t size; + uint8_t access; +}; + +struct __packed nt3h2x11_device { + uint8_t addr[NT3H2X11_BYTES_ADDR]; + uint8_t serial[NT3H2X11_BYTES_SERIAL]; + uint8_t internal[NT3H2X11_BYTES_INTERNAL]; + uint8_t static_lock[NT3H2X11_BYTES_STATIC_LOCK]; + struct nt3h2x11_cc cc; +}; + +struct __packed nt3h2x11_cfg_auth { + uint8_t dyn_lock[NT3H2X11_BYTES_DYNAMIC_LOCK]; + uint8_t auth0[NT3H2X11_BYTES_AUTH0]; + uint8_t access[NT3H2X11_BYTES_ACCESS]; + uint8_t pwd[NT3H2X11_BYTES_PWD]; + uint8_t pack[NT3H2X11_BYTES_PACK]; + uint8_t pt_i2c[NT3H2X11_BYTES_PT_I2C]; +}; + +/* DEFAULT VALUES */ +#define NT3H2X11_DEFAULT_CC_MAGIC (0xE1u) +#define NT3H2X11_DEFAULT_CC_VERSION (0x10u) +#define NT3H2X11_DEFAULT_CC_SIZE (0xE9u) +#define NT3H2X11_DEFAULT_CC_ACCESS (0x00u) + +#define NT3H2X11_DEFAULT_DEVICE_ADDR \ + { \ + 0x04u \ + } +#define NT3H2X11_DEFAULT_DEVICE_SERIAL \ + { \ + 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u \ + } +#define NT3H2X11_DEFAULT_DEVICE_INTERNAL \ + { \ + 0x00u, 0x00u, 0x00u \ + } +#define NT3H2X11_DEFAULT_DEVICE_STATIC_LOCK \ + { \ + 0x00u, 0x00u \ + } + +#define NT3H2X11_DEFAULT_CFG_AUTH_DYN_LOCK \ + { \ + 0x00u, 0x00u, 0x00u \ + } +#define NT3H2X11_DEFAULT_CFG_AUTH_AUTH0 \ + { \ + 0xFFu \ + } +#define NT3H2X11_DEFAULT_CFG_AUTH_ACCESS \ + { \ + 0x00u \ + } +#define NT3H2X11_DEFAULT_CFG_AUTH_PWD \ + { \ + 0xFFu, 0xFFu, 0xFFu, 0xFFu \ + } +#define NT3H2X11_DEFAULT_CFG_AUTH_PACK \ + { \ + 0x00u, 0x00u \ + } +#define NT3H2X11_DEFAULT_CFG_AUTH_PT_I2C \ + { \ + 0x00u \ + } + +#define NT3H2X11_DEFAULT_CC \ + { \ + NT3H2X11_DEFAULT_CC_MAGIC, NT3H2X11_DEFAULT_CC_VERSION, NT3H2X11_DEFAULT_CC_SIZE, \ + NT3H2X11_DEFAULT_CC_ACCESS \ + } + +#define NT3H2X11_DEFAULT_DEVICE \ + { \ + NT3H2X11_DEFAULT_DEVICE_ADDR, NT3H2X11_DEFAULT_DEVICE_SERIAL, \ + NT3H2X11_DEFAULT_DEVICE_INTERNAL, NT3H2X11_DEFAULT_DEVICE_STATIC_LOCK, \ + NT3H2X11_DEFAULT_CC \ + } + +#define NT3H2X11_DEFAULT_CFG_AUTH \ + { \ + NT3H2X11_DEFAULT_CFG_AUTH_DYN_LOCK, NT3H2X11_DEFAULT_CFG_AUTH_AUTH0, \ + NT3H2X11_DEFAULT_CFG_AUTH_ACCESS, NT3H2X11_DEFAULT_CFG_AUTH_PWD, \ + NT3H2X11_DEFAULT_CFG_AUTH_PACK, NT3H2X11_DEFAULT_CFG_AUTH_PT_I2C \ + } + +/******************* EVENTS *******************/ + +enum nt3h2x11_event { + /* not used */ + NT3H2X11_EVENT_NONE = 0, + /* External NFC field detected */ + NT3H2X11_EVENT_FD_ON, + /* External NFC field has been removed */ + NT3H2X11_EVENT_FD_OFF, + /* Reader has started communication */ + NT3H2X11_EVENT_START_OF_COMM, + /* Reader has selected this tag for communication */ + NT3H2X11_EVENT_SELECTED, + /* NFC session has ended. Reader has sent HALT command */ + NT3H2X11_EVENT_HALTED, + /* Read has read last NDEF packet */ + NT3H2X11_EVENT_LAST_NDEF_READ, + /* Passthrough NFC>I2C: Data is ready to be read by I2C */ + NT3H2X11_EVENT_DATA_READY_I2C, + /* Passthrough NFC>I2C: I2C has read last data packet */ + NT3H2X11_EVENT_LAST_DATA_READ_I2C, + /* Passthrough I2C>NFC: I2C has written last data packet */ + NT3H2X11_EVENT_LAST_DATA_WRITTEN_I2C, + /* Passthrough I2C>NFC: Reader has read last data packet */ + NT3H2X11_EVENT_LAST_DATA_READ_NFC, +}; + +/** + * @brief Application callback to receive IRQ events + * + * This callback runs at its own worker, not in the ISR context + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] event : @ref(enum nt3h2x11_event) IRQ event + * @param[in] nc_reg : NC_REG contents (session) + * @param[in] ns_reg : NS_REG contents (session) + */ +typedef void (*nt3h2x11_irq_callback_t)(const struct device *dev, enum nt3h2x11_event event, + uint8_t nc_reg, uint8_t ns_reg); + +/******************* DEVICE SETTINGS *******************/ + +/** + * @brief Set device settings + * + * Corrects i2c-address to DTS-value + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] *device : Pointer to @ref(nt3h2x11_device) + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_set_device(const struct device *dev, struct nt3h2x11_device *device); + +/** + * @brief Read device settings + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] *device : Pointer to @ref(nt3h2x11_device) + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_get_device(const struct device *dev, struct nt3h2x11_device *device); + +/******************* AUTHENTICATION SETTINGS *******************/ + +/** + * @brief Set authentication settings + * + * Sets data in block NT3H2X11_BLK_LOCK_AUTH and NT3H2X11_BLK_ACCESS_PWD. + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] *device : Pointer to @ref(nt3h2x11_cfg_auth) + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_set_cfg_auth(const struct device *dev, struct nt3h2x11_cfg_auth *cfg_auth); + +/** + * @brief Read authentication settings + * + * Reads data from block NT3H2X11_BLK_LOCK_AUTH and NT3H2X11_BLK_ACCESS_PWD. + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] *device : Pointer to @ref(nt3h2x11_cfg_auth) + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_get_cfg_auth(const struct device *dev, struct nt3h2x11_cfg_auth *cfg_auth); + +/******************* SESSION AND CONFIG OPERATIONS *******************/ + +/** + * @brief Set NFCS_I2C_RST_ON_OFF + * + * Enables the NFC silence feature and enables soft reset through I2C repeated + * start - see Section 9.3 of datasheet for more information. + * + * Default : 0b + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[in] enable : enable, 0=disable, 1=enable + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_set_softreset_en(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t enable); + +/** + * @brief Read NFCS_I2C_RST_ON_OFF + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[out] *enable : Pointer to enable, 0=disable, 1=enable + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_get_softreset_en(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t *enable); + +/** + * @brief Set PTHRU_ON_OFF + * + * Activates the pass-through mode, that uses the NTAG's 64 Byte SRAM + * for communication between a NFC device and the I2C bus + * + * 1b: pass-through mode using SRAM enabled and SRAM mapped to end of Sector 0. + * 0b: pass-through mode disabled + * + * Default : 0b + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[in] enable : enable, 0=disable, 1=enable + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_set_pthru_en(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t enable); + +/** + * @brief Read PTHRU_ON_OFF + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[out] *enable : Pointer to enable, 0=disable, 1=enable + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_get_pthru_en(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t *enable); + +/** + * @brief Set FD_OFF + * + * Defines the event upon which the signal output on the FD pin is released + * 00b: if the field is switched off + * 01b: if the field is switched off or the tag is set to the HALT state + * 10b: if the field is switched off or the last page of the NDEF message has + * been read (defined in LAST_ NDEF_BLOCK) + * 11b: (if FD_ON = 11b) if the field is switched off or if last data is read + * by I2C (in pass-through mode NFC ---> I2C) or last data is written by + * I2C (in pass-through mode I2C---> NFC) + * 11b: (if FD_ON = 00b or 01b or 10b) if the field is switched of + * + * See Section 8.4 of datasheet for more details + * + * Default : 00b + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[in] fd_off : @ref(nt3h2x11_fd_off) function to set + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_set_fd_off(const struct device *dev, enum nt3h2x11_csreg csreg, + enum nt3h2x11_fd_off fd_off); + +/** + * @brief Read FD_OFF + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[out] *fd_off : Pointer to @ref(nt3h2x11_fd_off) + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_get_fd_off(const struct device *dev, enum nt3h2x11_csreg csreg, + enum nt3h2x11_fd_off *fd_off); + +/** + * @brief Set FD_ON + * + * Defines the event upon which the signal output on the FD pin is pulled low + * 00b: if the field is switched on + * 01b: by first valid start of communication (SoC) + * 10b: by selection of the tag + * 11b: (in pass-through mode NFC-->I2C) if the data is ready to be read from + * the I2C interface + * 11b: (in pass-through mode I2 + * + * See Section 8.4 of datasheet for more details + * + * Default : 00b + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[in] fd_on : @ref(nt3h2x11_fd_on) function to set + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_set_fd_on(const struct device *dev, enum nt3h2x11_csreg csreg, + enum nt3h2x11_fd_on fd_on); + +/** + * @brief Read FD_ON + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[out] *fd_on : Pointer to @ref(nt3h2x11_fd_on) + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_get_fd_on(const struct device *dev, enum nt3h2x11_csreg csreg, + enum nt3h2x11_fd_on *fd_on); + +/** + * @brief Set SRAM_MIRROR_ON_OFF + * + * 1b: SRAM mirror enabled and mirrored SRAM starts at page SRAM_MIRROR_BLOCK + * 0b: SRAM mirror disabled + * + * Default : 0b + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[in] enable : enable, 0=disable, 1=enable + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_set_srammirror_en(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t enable); + +/** + * @brief Read SRAM_MIRROR_ON_OFF + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[out] *enable : Pointer to enable, 0=disable, 1=enable + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_get_srammirror_en(const struct device *dev, enum nt3h2x11_csreg csreg, + uint8_t *enable); + +/** + * @brief Set TRANSFER_DIR + * + * Defines the data flow direction when pass-through mode is enabled + * 0b: from I2C to NFC interface + * 1b: from NFC to I2C interface + * In case the pass-through mode is NOT enabled, this bit should be set to 1b, + * otherwise there is no WRITE access from the NFC perspective + * + * If the transfer direction is already the desired direction nothing is done. + * If the Pthru is switched on, it will be switched off and back on after the + * direction change. + * + * Default : 1b + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[in] dir : @ref(nt3h2x11_transfer_dir) direction + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_set_transfer_dir(const struct device *dev, enum nt3h2x11_csreg csreg, + enum nt3h2x11_transfer_dir dir); + +/** + * @brief Read TRANSFER_DIR + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[out] *dir : Pointer to @ref(nt3h2x11_transfer_dir) direction + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_get_transfer_dir(const struct device *dev, enum nt3h2x11_csreg csreg, + enum nt3h2x11_transfer_dir *dir); + +/** + * @brief Set LAST_NDEF_BLOCK + * + * I2C block address of I2C block, which contains last byte(s) of stored NDEF + * message. An NFC read of the last page of this I2C block sets the register + * NDEF_DATA_READ to 1b and triggers field detection pin if FD_OFF is set to 10b. + * Valid range starts from 01h (NFC page 04h) up to 37h (NFC page DCh) for NTAG + * I2C plus 1k or up to 7Fh (NFC page FCh on Sector 1) for NTAG I2C plus 2k. + * + * Granularity is 4 pages(4bytes each), so block * 4 is the real page address. + * + * Max Block address is @ref(NT3H2X11_LAST_NDEF_BLOCK_MAX) + * + * Default : 0x00 + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[in] block : Block address + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_set_last_ndef_blk(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t block); + +/** + * @brief Read LAST_NDEF_BLOCK + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[out] *block : Pointer to Block address + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_get_last_ndef_blk(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t *block); + +/** + * @brief Set SRAM_MIRROR_BLOCK + * + * I2C block address of SRAM when mirrored into the User memory. + * Valid range starts from 01h (NFC page 04h) up to 34h (NFC page D0h) for NTAG + * I2C plus 1k or up to 7Ch (NFC page F0h on memory Sector 1) for NTAG I2C plus 2k + * + * Granularity is 4 pages(4bytes each), so block * 4 is the real page address. + * + * Default : 0xF8 + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[in] block : block to which the SRAM should be mirrored + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_set_srammirror_blk(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t block); + +/** + * @brief Read SRAM_MIRROR_BLOCK + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[out] *block : Pointer to block address + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_get_srammirror_blk(const struct device *dev, enum nt3h2x11_csreg csreg, + uint8_t *block); + +/** + * @brief Set the value of the watchdog timer + * + * Sets both WDT_LS and WDT_MS + * + * Default: 0x0848 + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[in] time : new time value of the watchdog timer + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_set_wdt(const struct device *dev, enum nt3h2x11_csreg csreg, uint16_t time); + +/** + * @brief Read the value of the watchdog timer + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[out] *time : Pointer to watchdog time + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_get_wdt(const struct device *dev, enum nt3h2x11_csreg csreg, uint16_t *time); + +/** + * @brief Set I2C_CLOCK_STR + * + * Enables (1b) or disable (0b) the I2C clock stretching + * + * Default : 1b + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[in] enable : enable, 0=disable, 1=enable + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_set_i2c_clkstr_en(const struct device *dev, enum nt3h2x11_csreg csreg, uint8_t enable); + +/** + * @brief Set I2C_CLOCK_STR + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] csreg : SESSION or CONFIG register-block + * @param[out] *enable : Pointer to enable, 0=disable, 1=enable + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_get_i2c_clkstr_en(const struct device *dev, enum nt3h2x11_csreg csreg, + uint8_t *enable); + +/******************* CONFIG ONLY OPERATIONS *******************/ + +/** + * @brief Read REG_LOCK_I2C + * + * I2C Configuration Lock Bit, + * 0b: Configuration bytes may be changed via I2C + * 1b: Configuration bytes cannot be changed via I2C + * + * Once set to 1b, cannot be reset to 0b anymore + * + * Default : 0b + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[out] *locked : Pointer to lock-bit + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_get_i2c_lock_config(const struct device *dev, uint8_t *locked); + +/** + * @brief Read REG_LOCK_NFC + * + * NFC Configuration Lock Bit, + * 0b: Configuration bytes may be changed via NFC + * 1b: Configuration bytes cannot be changed via NFC + * + * Once set to 1b, cannot be reset to 0b anymore + * + * Default : 0b + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[out] *locked : Pointer to lock-bit + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_get_nfc_lock_config(const struct device *dev, uint8_t *locked); + +/******************* SESSION ONLY OPERATIONS *******************/ + +/** + * @brief Read NEG_AUTH_REACHED + * + * Status bit to show the number of negative PWD_ AUTH attempts reached + * 0b: PWD_AUTH still possible + * 1b: PWD_AUTH locked + * + * Default : 0b + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[out] *enable : Pointer to enable, 0=disable, 1=enable + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_get_auth_en(const struct device *dev, uint8_t *enable); + +/** + * @brief Read NS_REG + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[out] *nsreg : Pointer to nsreg-register dump + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_get_nsreg(const struct device *dev, uint8_t *nsreg); + +/******************* GENERIC READ/WRITE *******************/ + +/** + * @brief Read X Blocks + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] block : Block-number to start read from + * @param[out] *buf : Pointer to buffer to which block-data is stored + * @param[in] count : Number of blocks to read + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_read_blocks(const struct device *dev, uint8_t block, uint8_t *buf, uint8_t count); + +/** + * @brief Write X Blocks + * + * Note: This function does not protect I2C address at reg 00 of block 00. + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] block : Block-number to start write from + * @param[in] *buf : Pointer to buffer of which data is written + * @param[in] count : Number of blocks to write + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_write_blocks(const struct device *dev, uint8_t block, uint8_t *buf, uint8_t count); + +/** + * @brief Read X Bytes + * + * Note: uses memory addresses instead of blocks. + * A single block consists of @ref(NT3H2X11_BYTES_BLK) bytes / memory addresses. + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] addr : Memory address to read from. + * @param[out] *buf : Buffer to write to + * @param[in] buf_len : Number of bytes to read + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_read_bytes(const struct device *dev, uint16_t addr, uint8_t *buf, uint16_t buf_len); + +/** + * @brief Write X Bytes + * + * Note: uses memory addresses instead of blocks. + * A single block consists of @ref(NT3H2X11_BYTES_BLK) bytes / memory addresses. + * + * Note: This function does not protect I2C address at reg 00 of block 00. + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] addr : Memory address to write to. + * @param[out] *buf : Buffer to write to + * @param[in] buf_len : Number of bytes to read + * @return : 0 on success, negative upon error. + */ +int nt3h2x11_write_bytes(const struct device *dev, uint16_t addr, uint8_t *buf, uint16_t buf_len); + +/******************* IRQ HANDLING *******************/ + +/** + * @brief Set application callback to receive interrupt-events + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] cb : Application callback for interrupts + */ +int nt3h2x11_irq_set_callback(const struct device *dev, nt3h2x11_irq_callback_t cb); + +/******************* DEVICE SETUP *******************/ + +/** + * @brief Force i2c address + * + * After updating the address successfully, device need too be reset. + * + * @param[in] *dev : Pointer to nt3h2x11 device + * @param[in] addr_old : Old (current) i2c address + * @param[in] addr_new : New i2c address + * @return : negative upon error. Infinite loop or reboot on + * success (depending on CONFIG_REBOOT). + */ +int nt3h2x11_set_i2c_addr(const struct device *dev, uint8_t addr_old, uint8_t addr_new); + +#endif /* ZEPHYR_INCLUDE_DRIVERS_NFC_NT3H2X11_H_ */ diff --git a/include/zephyr/nfc/nfc_tag.h b/include/zephyr/nfc/nfc_tag.h new file mode 100644 index 00000000000000..a8f3d1a9fc8954 --- /dev/null +++ b/include/zephyr/nfc/nfc_tag.h @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2023 Sendrato + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file NFC-tag subsystem + */ + +#ifndef ZEPHYR_INCLUDE_NFC_TAG_H_ +#define ZEPHYR_INCLUDE_NFC_TAG_H_ + +#include +#include +#include + +/** + * @brief NFC Tag Events + */ +enum nfc_tag_event { + /* not used */ + NFC_TAG_EVENT_NONE = 0, + /* External NFC field detected: tag is selected by reader */ + NFC_TAG_EVENT_FIELD_ON, + /* External NFC field has been removed / session ended */ + NFC_TAG_EVENT_FIELD_OFF, + /* When a reader has read all data */ + NFC_TAG_EVENT_READ_DONE, + /* When all data has been written to device */ + NFC_TAG_EVENT_WRITE_DONE, + /* NFC driver has stopped */ + NFC_TAG_EVENT_STOPPED, + /* When data is successfully sent to reader */ + NFC_TAG_EVENT_DATA_TRANSMITTED, + /* Data-block sent by reader */ + NFC_TAG_EVENT_DATA_IND, + /* Last-block of data which has been sent by reader */ + NFC_TAG_EVENT_DATA_IND_DONE, +}; + +/** + * @brief NFC tag types in which subsysten can operate. + */ +enum nfc_tag_type { + NFC_TAG_TYPE_UNDEF = 0, + NFC_TAG_TYPE_T2T, + NFC_TAG_TYPE_T4T +}; + +/** + * @brief NFC CMDs + */ +enum nfc_tag_cmd { + /* not used */ + NFC_TAG_CMD_NONE = 0, + /* External NFC field detected: tag is selected by reader */ + NFC_TAG_CMD_FIELD_ON, +}; + +/** + * @brief NFC driver callback + * + * @param[in] *dev : Pointer to NFC device which triggered interrupt. + * @param[in] event : @ref(nfc_tag_event) event. + * @param[in] *data : Pointer to databuf of event. Might be NULL. + * @param[in] data_len : Length of the databuf. Might be 0. + */ +typedef void (*nfc_tag_cb_t)(const struct device *dev, enum nfc_tag_event event, + const uint8_t *data, size_t data_len); + +/** DRIVER API TYPEDEFS **/ + +typedef int (*nfc_tag_init_t)(const struct device *dev, nfc_tag_cb_t cb); + +typedef int (*nfc_tag_set_type_t)(const struct device *dev, enum nfc_tag_type type); + +typedef int (*nfc_tag_get_type_t)(const struct device *dev, enum nfc_tag_type *type); + +typedef int (*nfc_tag_start_t)(const struct device *dev); + +typedef int (*nfc_tag_stop_t)(const struct device *dev); + +typedef int (*nfc_tag_set_ndef_t)(const struct device *dev, uint8_t *buf, uint16_t len); + +typedef int (*nfc_tag_cmd_t)(const struct device *dev, enum nfc_tag_cmd cmd, uint8_t *buf, + uint16_t *buf_len); + +struct nfc_tag_driver_api { + nfc_tag_init_t init; + nfc_tag_set_type_t set_type; + nfc_tag_get_type_t get_type; + nfc_tag_start_t start; + nfc_tag_stop_t stop; + nfc_tag_set_ndef_t set_ndef; + nfc_tag_cmd_t cmd; +}; + +/** + * @brief Initialize NFC Tag subsystem + * + * @param[in] *dev : Pointer to NFC device + * @param[in] cb : @ref(nfc_tag_cb_t) callback for event handling + * @return : 0 on success, negative upon error. + */ +static inline int nfc_tag_init(const struct device *dev, nfc_tag_cb_t cb) +{ + const struct nfc_tag_driver_api *api = (const struct nfc_tag_driver_api *)dev->api; + + return api->init(dev, cb); +} + +/** + * @brief Set specific NFC Tag mode + * + * @param[in] *dev : Pointer to NFC device + * @param[in] cb : @ref(nfc_tag_type) + * @return : 0 on success, negative upon error. + */ +static inline int nfc_tag_set_type(const struct device *dev, enum nfc_tag_type type) +{ + const struct nfc_tag_driver_api *api = (const struct nfc_tag_driver_api *)dev->api; + + return api->set_type(dev, type); +} + +/** + * @brief Read configured NFC Tag mode + * + * @param[in] *dev : Pointer to NFC device + * @param[in] cb : @ref(nfc_tag_type) + * @return : 0 on success, negative upon error. + */ +static inline int nfc_tag_get_type(const struct device *dev, enum nfc_tag_type *type) +{ + const struct nfc_tag_driver_api *api = (const struct nfc_tag_driver_api *)dev->api; + + return api->get_type(dev, type); +} + +/** + * @brief Activate NFC Tag Field + * + * @param[in] *dev : Pointer to NFC device + * @return : 0 on success, negative upon error. + */ +static inline int nfc_tag_start(const struct device *dev) +{ + const struct nfc_tag_driver_api *api = (const struct nfc_tag_driver_api *)dev->api; + + return api->start(dev); +} + +/** + * @brief Stop NFC Tag Field + * + * @param[in] *dev : Pointer to NFC device + * @return : 0 on success, negative upon error. + */ +static inline int nfc_tag_stop(const struct device *dev) +{ + const struct nfc_tag_driver_api *api = (const struct nfc_tag_driver_api *)dev->api; + + return api->stop(dev); +} + +/** + * @brief Write payload data NFC device. + * + * This data will be read by a NFC-reader upon a READ request. + * + * @param[in] *dev : Pointer to NFC device + * @param[in] *buf : Pointer to buffer to be written + * @param[in] buf_len : Buffer length. + * @return : 0 on success, negative upon error. + */ +static inline int nfc_tag_set_ndef(const struct device *dev, uint8_t *buf, uint16_t buf_len) +{ + const struct nfc_tag_driver_api *api = (const struct nfc_tag_driver_api *)dev->api; + + return api->set_ndef(dev, buf, buf_len); +} + +/** + * @brief Set driver specific setting. + * + * This a generic interface allowing customisation of the low-level driver, + * like setting of specific data-register. Available commands and their effect + * is driver-implementation depending. + * + * @param[in] *dev : Pointer to NFC device + * @param[in] cmd : @ref(enum nfc_tag_cmd) command + * @param[in,out] *buf : Pointer to buffer which can hold data to be written + * or allocated memory to store read-data. + * @param[in,out] *buf_len : Pointer to buffer size. When writing, it shall + * specify the length of @ref(buf). When reading it + * shall hold the length of the returned buf. + * @return : 0 on success, negative upon error. + */ +static inline int nfc_tag_cmd(const struct device *dev, enum nfc_tag_cmd cmd, uint8_t *buf, + uint16_t *buf_len) +{ + const struct nfc_tag_driver_api *api = (const struct nfc_tag_driver_api *)dev->api; + + return api->cmd(dev, cmd, buf, buf_len); +} + +#endif /* ZEPHYR_INCLUDE_NFC_TAG_H_ */ diff --git a/samples/drivers/nfc/CMakeLists.txt b/samples/drivers/nfc/CMakeLists.txt new file mode 100644 index 00000000000000..7c728573a582ff --- /dev/null +++ b/samples/drivers/nfc/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# Copyright (c) 2023 Sendrato +# +# SPDX-License-Identifier: Apache-2.0 +# + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(nfc) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/drivers/nfc/README.rst b/samples/drivers/nfc/README.rst new file mode 100644 index 00000000000000..cc08a0d7067cab --- /dev/null +++ b/samples/drivers/nfc/README.rst @@ -0,0 +1,50 @@ +.. zephyr:code-sample:: nfc + :name: NFC driver + :relevant-api: nfc + + Configure NFC driver as Tag2 + +Overview +******** + +This sample demonstrates how to setup and configure the NFC driver. +It creates 3 different ntag records. + +Building and Running +******************** + +The sample can be built and executed on boards supporting NFC. + +.. zephyr-app-commands:: + :zephyr-app: samples/drivers/nfc + :board: mg100 + :goals: build + :compact: + +Dependencies* +************* + +This depends on libraries from the nRF Connect SDK and nrfxlib. + +.. code-block:: none + + - name: nrf-sdk + url: https://github.com/nrfconnect/sdk-nrf.git + path: nrf + revision: v2.5.2 + import: + name-whitelist: + - nrfxlib + +Sample Output +============= + +.. code-block:: console + + *** Booting nRF Connect SDK zephyr-v3.5.0 *** + Starting NFC Text Record example + Device = ok: NFC_DEV + NFC configuration done (0) + NFC-CALLBACK EVENT: 1 + NFC-CALLBACK EVENT: 3 + NFC-CALLBACK EVENT: 2 diff --git a/samples/drivers/nfc/boards/dk6_qn9090.conf b/samples/drivers/nfc/boards/dk6_qn9090.conf new file mode 100644 index 00000000000000..568d33a163ef44 --- /dev/null +++ b/samples/drivers/nfc/boards/dk6_qn9090.conf @@ -0,0 +1,9 @@ +# +# Copyright (c) 2023 Sendrato +# +# SPDX-License-Identifier: Apache-2.0 +# + +# NFC driver selection for qn9090 DK6 development board +CONFIG_NFC_NT3H2X11=y +CONFIG_NFC_NT3H2X11_2K=y diff --git a/samples/drivers/nfc/boards/mg100.conf b/samples/drivers/nfc/boards/mg100.conf new file mode 100644 index 00000000000000..3f000ab9ea6d51 --- /dev/null +++ b/samples/drivers/nfc/boards/mg100.conf @@ -0,0 +1,11 @@ +# +# Copyright (c) 2023 Sendrato +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Enable NFC-wrapper for Nordic NFCT lib +CONFIG_NFC_NRFX=y + +# Setup NFC as Type 2 Tag +CONFIG_NFC_T2T_NRFXLIB=y diff --git a/samples/drivers/nfc/prj.conf b/samples/drivers/nfc/prj.conf new file mode 100644 index 00000000000000..8f853c0c08ec60 --- /dev/null +++ b/samples/drivers/nfc/prj.conf @@ -0,0 +1,9 @@ +# +# Copyright (c) 2023 Sendrato +# +# SPDX-License-Identifier: Apache-2.0 +# + +CONFIG_STDOUT_CONSOLE=y +CONFIG_LOG=y +CONFIG_NFC=y diff --git a/samples/drivers/nfc/src/main.c b/samples/drivers/nfc/src/main.c new file mode 100644 index 00000000000000..4735cb9e466408 --- /dev/null +++ b/samples/drivers/nfc/src/main.c @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2023 Sendrato + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#ifdef CONFIG_NFC_NRFX +#define DEV_PTR device_get_binding(CONFIG_NFC_NRFX_DRV_NAME) +#else +#define NFC_DEV nt3h2x11 +#define DEV_PTR DEVICE_DT_GET(DT_NODELABEL(NFC_DEV)) +#endif + +#define NDEF_MSG_BUF_SIZE (128u) + +#define FLAGS_MSG_BEGIN (0x80u) +#define FLAGS_MSG_END (0x40u) +#define FLAGS_CHUNK (0x20u) +#define FLAGS_SHORT_RECORD (0x10u) +#define FLAGS_ID_LENGTH (0x40u) + +#define TNF_EMPTY (0x00u) +#define TNF_WELLKNOWN (0x01u) +#define TNF_MIME (0x02u) +#define TNF_URI (0x03u) +#define TNF_EXTERNAL (0x04u) +#define TNF_UNKNOWN (0x05u) +#define TNF_UNCHANGED (0x06u) + +#define TYPE_WELLKNOWN_TXT {'T'} +#define TYPE_WELLKNOWN_URI {'U'} +#define TYPE_WELLKNOWN_SP {'S', 'p'} + +#define TYPE_MIME_HTML {'t', 'e', 'x', 't', '/', 'h', 't', 'm', 'l'} +#define TYPE_MIME_JSON {'t', 'e', 'x', 't', '/', 'j', 's', 'o', 'n'} + +/* + * Text data of `Well Known : text` consists of: + * - byte header + * - language code + * - text data + * + * The byte header is build up as: + * bit 7 : 0 = UTF8, 1 = UTF16 + * bit 6 : reserved + * bit 0-5 : length of language code + */ + +static const uint8_t txt_utf8_english[] = { + 0x02, + 'E', 'N', + 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!' +}; + +static const uint8_t txt_utf8_norwegian[] = { + 0x02, + 'N', 'O', + 'H', 'a', 'l', 'l', 'o', ' ', 'V', 'e', 'r', 'd', 'e', 'n', '!' +}; + +static const uint8_t txt_utf8_polish[] = { + 0x02, + 'P', 'L', + 'V', 'i', 't', 'a', 'j', ' ', 0xc5u, 0x9au, 'w', 'i', 'e', 'c', 'i', 'e', '!' +}; + +/* Payload: 3x Hello World in different languages */ +static const uint8_t txt_payload_cnt = 3u; + +/* Buffer used to hold an NFC NDEF message. */ +static uint8_t ndef_msg_buf[NDEF_MSG_BUF_SIZE]; + +/** + * @brief Callback from NFC driver when an event is triggered. + */ +static void nfc_callback(const struct device *dev, enum nfc_tag_event event, const uint8_t *data, + size_t data_len) +{ + ARG_UNUSED(dev); + ARG_UNUSED(data); + ARG_UNUSED(data_len); + + printk("NFC-CALLBACK EVENT: %d\n", event); +} + +/** + * @brief Function for encoding the NDEF text message. + */ +static int ndef_add(uint8_t *buf, uint8_t buf_len, uint8_t buf_idx, uint8_t ndef_idx, + uint8_t ndef_idx_max, uint8_t tnf, uint8_t *type, uint8_t type_len, + const uint8_t *record, uint32_t record_len) +{ + int rv = 0; + + if ((type_len + record_len + 6u) > (buf_len - buf_idx)) { + printk("Not enough memory in buffer"); + rv = -ENOMEM; + + } else { + uint8_t idx = buf_idx; + + /* Setup TNF + FLAGS */ + uint8_t hdr = tnf; + + if (ndef_idx == 0) { + hdr |= FLAGS_MSG_BEGIN; + } + if (ndef_idx == ndef_idx_max) { + hdr |= FLAGS_MSG_END; + } + + /* Setup Record Header */ + buf[idx + 0] = hdr; + buf[idx + 1] = type_len; + buf[idx + 2] = 0xFFu & (record_len >> 24); + buf[idx + 3] = 0xFFu & (record_len >> 16); + buf[idx + 4] = 0xFFu & (record_len >> 8); + buf[idx + 5] = 0xFFu & (record_len >> 0); + idx += 6; + + /* Setup type */ + (void)memcpy(&buf[idx], type, type_len); + idx += type_len; + + /* Setup payload data */ + (void)memcpy(&buf[idx], record, record_len); + idx += record_len; + + rv = idx; + } + + return rv; +} + +int main(void) +{ + int rv; + uint32_t len = sizeof(ndef_msg_buf); + const struct device *dev = DEV_PTR; + + printk("Starting NFC Text Record example\n"); + + if (dev == NULL) { + printk("Could not get %s device\n", STRINGIFY(NFC_DEV)); + return -ENODEV; + } + + printk("Device = ok: %s\n", STRINGIFY(NFC_DEV)); + + /* Set up NFC driver*/ + rv = nfc_tag_init(dev, nfc_callback); + if (rv != 0) { + printk("Cannot setup NFC driver!\n"); + } + + /* Set up Tag mode */ + if (rv == 0) { + rv = nfc_tag_set_type(dev, NFC_TAG_TYPE_T2T); + if (rv != 0) { + printk("Cannot setup NFC Tag mode! (%d)\n", rv); + } + } + + /* Setup welcome message */ + if (rv == 0) { + /* Byte-index in msg-buffer at which record is inserted */ + uint8_t buf_idx = 0u; + /* Type of record */ + uint8_t type_txt[] = TYPE_WELLKNOWN_TXT; + + /* Setup first record (at index 0)*/ + rv = ndef_add(ndef_msg_buf, sizeof(ndef_msg_buf), buf_idx, 0u, txt_payload_cnt - 1u, + TNF_WELLKNOWN, type_txt, sizeof(type_txt), txt_utf8_english, + sizeof(txt_utf8_english)); + + if (rv < 0) { + printk("Could not add english record"); + return -EINVAL; + } + + /* Setup second record (at index 1) */ + buf_idx = rv; + rv = ndef_add(ndef_msg_buf, sizeof(ndef_msg_buf), buf_idx, 1u, txt_payload_cnt - 1u, + TNF_WELLKNOWN, type_txt, sizeof(type_txt), txt_utf8_norwegian, + sizeof(txt_utf8_norwegian)); + + if (rv < 0) { + printk("Could not add norwegian record"); + return -EINVAL; + } + + /* Setup third record (at index 2) */ + buf_idx = rv; + rv = ndef_add(ndef_msg_buf, sizeof(ndef_msg_buf), buf_idx, 2u, txt_payload_cnt - 1u, + TNF_WELLKNOWN, type_txt, sizeof(type_txt), txt_utf8_polish, + sizeof(txt_utf8_polish)); + + if (rv < 0) { + printk("Could not add polish record"); + return -EINVAL; + } + + /* Setting up records was a success */ + rv = 0; + } + + /* Set created message as the NFC payload */ + if (rv == 0) { + rv = nfc_tag_set_ndef(dev, ndef_msg_buf, len); + if (rv != 0) { + printk("Cannot set payload! (%d)\n", rv); + } + } + + /* Start sensing NFC field */ + if (rv == 0) { + rv = nfc_tag_start(dev); + if (rv != 0) { + printk("Cannot start emulation! (%d)\n", rv); + } + } + + printk("NFC configuration done (%d)\n", rv); + + return 0; +}