cancel
Showing results for 
Search instead for 
Did you mean: 

Unable to properly control ST25DV64KC read write permissions.

Jean4
Associate

Hi! I am trying to control the read write permission of my st25dv nfc tag with a nordic chip, but i am having a hard time doing that. I am able to write to it and everything using i2c and nfc, and with the following code 

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/i2c.h>
#include <string.h>

LOG_MODULE_REGISTER(st25dv_demo, LOG_LEVEL_INF);

#define ST25DV_ADDR 0x53
#define I2C_NODE DT_NODELABEL(i2c2)

#define EEPROM_START_ADDR 0x0000
#define EEPROM_LEN 16

#define I2C_PWD_REG  0x09B0
#define I2C_SSO_DYN  0x2000
#define RFA1SS_REG   0x0004
#define ENDA1_REG    0x0005
#define ENDA2_REG    0x0007
#define ENDA3_REG    0x0009

static const struct device *i2c_dev;

static int st25dv_write_sys(uint16_t reg, uint8_t data)
{
    uint8_t buf[3] = { reg >> 8, reg & 0xFF, data };
    return i2c_write(i2c_dev, buf, sizeof(buf), ST25DV_ADDR);
}

static int st25dv_open_session(const uint8_t *pwd)
{
    uint8_t buf[10] = { I2C_PWD_REG >> 8, I2C_PWD_REG & 0xFF };
    memcpy(&buf[2], pwd, 8);
    return i2c_write(i2c_dev, buf, sizeof(buf), ST25DV_ADDR);
}

static bool st25dv_is_session_open(void)
{
    uint8_t reg[2] = { I2C_SSO_DYN >> 8, I2C_SSO_DYN & 0xFF };
    uint8_t status;
    if (i2c_write_read(i2c_dev, ST25DV_ADDR, reg, sizeof(reg), &status, 1) < 0) {
        LOG_ERR("Failed to check session");
        return false;
    }
    return (status & 0x01);
}

static int st25dv_set_full_area1(void)
{
    // Set ENDA3 = 0xFF first (to max)
    if (st25dv_write_sys(ENDA3_REG, 0xFF)) return -1;
    k_msleep(5);

    // Set ENDA2 = 0xFF
    if (st25dv_write_sys(ENDA2_REG, 0xFF)) return -1;
    k_msleep(5);

    // Set ENDA1 = 0xFF (Area1 = entire memory)
    if (st25dv_write_sys(ENDA1_REG, 0xFF)) return -1;
    k_msleep(5);

    return 0;
}

static int st25dv_set_rf_write_protect(bool enable)
{
    if (!st25dv_is_session_open()) {
        LOG_ERR("Session not open");
        return -1;
    }

    // Set full Area1 before protection
    if (st25dv_set_full_area1() < 0) {
        LOG_ERR("Failed to set full Area1");
        return -1;
    }

    // Write RFA1SS: 0x05 = write-protected, read allowed
    return st25dv_write_sys(RFA1SS_REG, enable ? 0x05 : 0x00);
}

static int st25dv_write_eeprom(uint16_t addr, const uint8_t *data, size_t len)
{
    uint8_t buf[2 + EEPROM_LEN] = { addr >> 8, addr & 0xFF };
    memcpy(&buf[2], data, len);
    return i2c_write(i2c_dev, buf, len + 2, ST25DV_ADDR);
}

static int st25dv_read_eeprom(uint16_t addr, uint8_t *data, size_t len)
{
    uint8_t reg[2] = { addr >> 8, addr & 0xFF };
    return i2c_write_read(i2c_dev, ST25DV_ADDR, reg, sizeof(reg), data, len);
}

void main(void)
{
    i2c_dev = DEVICE_DT_GET(I2C_NODE);
    if (!device_is_ready(i2c_dev)) {
        LOG_ERR("I2C not ready");
        return;
    }

    uint8_t password[8] = {0}; // Default password (8 bytes of 0x00)
    uint8_t data[EEPROM_LEN];

    LOG_INF("ST25DV64KC Zephyr Example");

    // Open session
    LOG_INF("Opening I2C session...");
    st25dv_open_session(password);
    k_sleep(K_MSEC(10));

    LOG_INF("Session is %s", st25dv_is_session_open() ? "open" : "closed");

    // Enable RF write protection on Area 1
    LOG_INF("Enabling RF write protection on full memory...");
    if (st25dv_set_rf_write_protect(true) == 0)
        LOG_INF("RF write protection enabled");

    // Read back EEPROM to confirm it’s still accessible via I2C
    LOG_INF("Reading EEPROM Area 1 via I2C...");
    memset(data, 0, EEPROM_LEN);
    st25dv_read_eeprom(EEPROM_START_ADDR, data, EEPROM_LEN);

    for (int i = 0; i < EEPROM_LEN; i++)
        printk("0x%02X ", data[i]);
    printk("\n");
}

I am able to enable write protection, but apparently that affects also i2c because I am unable to set a new NDEF message after that. All i want is to allow the user to enter something in the tag, once that is confirmed, the MCU enables write protection over RF but still allows read. That's basically it. This is the code with which i am trying to turn on read only, but it doesn't seem to be working 

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/i2c.h>
#include <string.h>

LOG_MODULE_REGISTER(st25dv_demo, LOG_LEVEL_INF);

#define ST25DV_ADDR         0x53
#define I2C_NODE            DT_NODELABEL(i2c2)
#define EEPROM_START_ADDR   0x0000

static const struct device *i2c_dev;

// Write single byte to system register
static int st25dv_write_sys(uint16_t reg, uint8_t data)
{
    uint8_t buf[3] = { reg >> 8, reg & 0xFF, data };
    return i2c_write(i2c_dev, buf, sizeof(buf), ST25DV_ADDR);
}

// Open session with 8-byte password
static int st25dv_open_session(const uint8_t *pwd)
{
    uint8_t buf[10] = { 0x09, 0xB0 }; // I2C_PWD_REG high byte, low byte
    memcpy(&buf[2], pwd, 8);
    return i2c_write(i2c_dev, buf, sizeof(buf), ST25DV_ADDR);
}

// Check if session is open
static bool st25dv_is_session_open(void)
{
    uint8_t reg[2] = { 0x20, 0x00 }; // I2C_SSO_DYN
    uint8_t status;
    if (i2c_write_read(i2c_dev, ST25DV_ADDR, reg, sizeof(reg), &status, 1) < 0) {
        LOG_ERR("Failed to check session");
        return false;
    }
    return (status & 0x01);
}

// Enable RF write protection on area 1
static int st25dv_set_rf_write_protect(bool enable)
{
    if (!st25dv_is_session_open()) {
        LOG_ERR("Session not open");
        return -1;
    }
    return st25dv_write_sys(0x0004, enable ? 0x01 : 0x00); // RFA1SS_REG
}

// Write bytes to EEPROM starting at addr
static int st25dv_write_eeprom(uint16_t addr, const uint8_t *data, size_t len)
{
    uint8_t buf[2 + 64] = {0};
    buf[0] = addr >> 8;
    buf[1] = addr & 0xFF;
    memcpy(&buf[2], data, len);
    return i2c_write(i2c_dev, buf, len + 2, ST25DV_ADDR);
}

void main(void)
{
    i2c_dev = DEVICE_DT_GET(I2C_NODE);
    if (!device_is_ready(i2c_dev)) {
        LOG_ERR("I2C not ready");
        return;
    }

    LOG_INF("ST25DV64KC Zephyr Example");

    const uint8_t password[8] = { 0x00 }; // Default password

    // Capability Container (CC) bytes (8 bytes)
const uint8_t cc[8] = {
    0xE1, 0x40, 0x06, 0x0E,  // CC bytes: Magic, version, size, RW flag (read-only)
    0x00, 0x00, 0x00, 0x00   // Reserved bytes
};

    // NDEF message TLV (21 bytes)
    const uint8_t ndef_msg[21] = {
        0x03, 0x11,             // NDEF TLV, length=17 bytes
        0xD1, 0x01, 0x0D,       // NDEF Record Header, type length=1, payload length=13
        0x54,                   // Type = 'T' (Text)
        0x02,                   // Status byte: UTF-8 + lang length=2
        0x65, 0x6E,             // 'en' language code
        'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd',
        0xFE                    // Terminator TLV
    };

    LOG_INF("Opening I2C session...");
    if (st25dv_open_session(password) < 0) {
        LOG_ERR("Failed to open session");
        return;
    }
    k_sleep(K_MSEC(10));
    if (!st25dv_is_session_open()) {
        LOG_ERR("Session not open");
        return;
    }
    LOG_INF("Session is open");

    // Write CC to address 0x0000
    LOG_INF("Writing Capability Container (CC)...");
    if (st25dv_write_eeprom(0x0000, cc, sizeof(cc)) < 0) {
        LOG_ERR("Failed to write CC");
        return;
    }
    k_msleep(20);

    // Write NDEF message starting at 0x0008
    LOG_INF("Writing NDEF message...");
    if (st25dv_write_eeprom(0x0008, ndef_msg, sizeof(ndef_msg)) < 0) {
        LOG_ERR("Failed to write NDEF message");
        return;
    }
    k_msleep(20);

    // Enable RF write protection
    LOG_INF("Enabling RF write protection...");
    if (st25dv_set_rf_write_protect(true) < 0) {
        LOG_ERR("Failed to enable RF write protection");
        return;
    }
    k_msleep(50);


    LOG_INF("NDEF message successfully written and tag is read-only over RF.");
}
 
Any help would be appreciated.
1 REPLY 1
Rene Lenerve
ST Employee

Hi Jean4,

Reading your code I'm not sure that you are changing the I²C device code while switching between system and user area. Please refer to the Datasheet section 6.3 Device addressing and note the 2 addresses for user and system memory in the table.

Another point I noticed, is the CC file in Type V can have a length of 4 or 8 bytes. in your case you have defined a message len (byte 2 of CC file)  of 6 bytes so that implies the CC file length is of 4 bytes only.

ReneLenerve_0-1752744830777.png

I hope this can help you.

Kind Regards.