Newer
Older
mbed-os / connectivity / drivers / nfc / TARGET_M24SR / include / nfc / m24sr_driver.h
/* mbed Microcontroller Library
 * SPDX-License-Identifier: BSD-3-Clause
 ******************************************************************************
 * @file    m24sr_driver.h
 * @author  ST Central Labs
 * @brief   This file provides a set of functions to interface with the M24SR
 *          device.
 ******************************************************************************
 * @attention
 *
 * <h2><center>&copy; COPYRIGHT(c) 2018 STMicroelectronics</center></h2>
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *   1. Redistributions of source code must retain the above copyright notice,
 *      this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright notice,
 *      this list of conditions and the following disclaimer in the documentation
 *      and/or other materials provided with the distribution.
 *   3. Neither the name of STMicroelectronics nor the names of its contributors
 *      may be used to endorse or promote products derived from this software
 *      without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************
 */

/*
 Based on:         X-CUBE-MEMS1/trunk/Drivers/BSP/Components/m24sr/m24sr.h
 Revision:         M24SR Driver V1.0.0
 */

#ifndef M24SR_H
#define M24SR_H

#include <stdint.h>
#include <mbed.h>
#include "I2C.h"
#include "NFCEEPROMDriver.h"
#include "EventQueue.h"

#if defined TARGET_DISCO_L475VG_IOT01A

#define M24SR_I2C_SDA_PIN     PB_11
#define M24SR_I2C_SCL_PIN     PB_10
#define M24SR_GPO_PIN         PE_4
#define M24SR_RF_DISABLE_PIN  PE_2

#elif MBED_CONF_X_NUCLEO_NFC01A1

#define M24SR_I2C_SDA_PIN     D14
#define M24SR_I2C_SCL_PIN     D15
#define M24SR_GPO_PIN         D12
#define M24SR_RF_DISABLE_PIN  D11

#else

#define M24SR_I2C_SDA_PIN     NC
#define M24SR_I2C_SCL_PIN     NC
#define M24SR_GPO_PIN         NC
#define M24SR_RF_DISABLE_PIN  NC

#endif

namespace mbed {
namespace nfc {
namespace vendor {
namespace ST {

#define OPEN_SESSION_RETRIES  5
#define CC_FILE_LENGTH        15
#define NDEF_FILE_HEADER_SIZE 2
#define MAX_NDEF_SIZE         0x1FFF

/**
 * User parameter used to invoke a command,
 * it is used to provide the data back with the response
 */
struct CommandData_t {
    uint8_t *data; /**< data */
    uint16_t length; /**< number of bytes in the data array */
    uint16_t offset; /**< offset parameter used in the read/write command */
};

/**
 * @brief APDU Command structure
 */
class C_APDU {
public:
    struct C_APDUHeader_t {
        uint8_t CLA; /**< Command class */
        uint8_t INS; /**< Operation code */
        uint8_t P1; /**< Selection Mode */
        uint8_t P2; /**< Selection Option */
    };

    struct C_APDUBody_t {
        uint8_t LC; /**< Data field length */
        const uint8_t *data; /**< Command parameters */
        uint8_t LE; /**< Expected length of data to be returned */
    };

    C_APDU(uint8_t cla, uint8_t ins, uint16_t p1p2, uint8_t length, const uint8_t *data, uint8_t expected)
    {
        header.CLA = cla;
        header.INS = ins;
        header.P1 = (uint8_t)((p1p2 & 0xFF00) >> 8);
        header.P2 = (uint8_t)(p1p2 & 0x00FF);
        body.LC = length;
        body.data = data;
        body.LE = expected;
    }

    C_APDUHeader_t header;
    C_APDUBody_t body;
};

/**
 * @brief SC response structure
 */
struct R_APDU {
    uint8_t *data; /**< Data returned from the card */ // pointer on the transceiver buffer = ReaderRecBuf[CR95HF_DATA_OFFSET ];
    uint8_t SW1; /**< Command Processing status */
    uint8_t SW2; /**< Command Processing qualification */
};

enum M24srError_t : uint16_t {
    M24SR_SUCCESS = 0,
    M24SR_ERROR = 0x6F00,
    M24SR_FILE_OVERFLOW_LE = 0x6280,
    M24SR_EOF = 0x6282,
    M24SR_PASSWORD_REQUIRED = 0x6300,
    M24SR_PASSWORD_INCORRECT = 0x63C0,
    M24SR_PASSWORD_INCORRECT1RETRY = 0x63C1,
    M24SR_PASSWORD_INCORRECT2RETRY = 0x63C2,
    M24SR_WRONG_LENGHT = 0x6700,
    M24SR_UNSUCESSFUL_UPDATING = 0x6581,
    M24SR_INCOPATIBLE_COMMAND = 0x6981,
    M24SR_SECURITY_UNSATISFIED = 0x6982,
    M24SR_REFERENCE_DATA_NOT_USABLE = 0x6984,

    M24SR_INCORRECT_PARAMETER = 0x6a80,
    M24SR_FILE_NOT_FOUND = 0x6a82,
    M24SR_FILE_OVERFLOW_LC = 0x6A84,

    M24SR_INCORRECT_P1_OR_P2 = 0x6A86,
    M24SR_RF_SESSION_KILLED = 0x6500,
    M24SR_INS_NOT_SUPPORTED = 0x6D00,
    M24SR_CLASS_NOT_SUPPORTED = 0x6E00,

    M24SR_IO_ERROR_I2CTIMEOUT = 0x0011,
    M24SR_IO_ERROR_CRC = 0x0012,
    M24SR_IO_ERROR_NACK = 0x0013,
    M24SR_IO_ERROR_PARAMETER = 0x0014,
    M24SR_IO_ERROR_NBATEMPT = 0x0015,
    M24SR_IO_NOACKNOWLEDGE = 0x0016,
    M24SR_IO_PIN_NOT_CONNECTED = 0x0017
};

/**
 * @brief GPO state
 */
enum NfcGpoState_t {
    HIGH_IMPEDANCE = 0,
    SESSION_OPENED = 1,
    WIP = 2,
    I2C_ANSWER_READY = 3,
    INTERRUPT = 4,
    STATE_CONTROL = 5
};

/**
 * Possible password to set.
 */
enum PasswordType_t {
    READ_PASSWORD = 0x01, /**< Password to use before reading the tag */
    WRITE_PASSWORD = 0x02, /**< Password to use before writing the tag */
    I2C_PASSWORD = 0x03, /**< Root password, used only through nfc */
};

/**
 * Command that the component can accept
 */
enum Command_t {
    NONE,
    DESELECT,
    SELECT_APPLICATION,
    SELECT_CC_FILE,
    SELECT_NDEF_FILE,
    SELECT_SYSTEM_FILE,
    READ,
    UPDATE,
    VERIFY,
    MANAGE_I2C_GPO,
    MANAGE_RF_GPO,
    CHANGE_REFERENCE_DATA,
    ENABLE_VERIFICATION_REQUIREMENT,
    DISABLE_VERIFICATION_REQUIREMENT,
    ENABLE_PERMANET_STATE,
    DISABLE_PERMANET_STATE,
};

/**
 * Communication mode used by this device
 */
enum Communication_t {
    SYNC, /**< SYNC wait the command response before returning */
    ASYNC /**< ASYNC use a callback to notify the end of a command */
};

/**
 * Class representing a M24SR component.
 * This component has two operation modes, sync or async.
 * In sync mode each function call returns only after the command has completed.
 * In async mode each function call returns immediately and the answer will be notified
 * through a callback.
 * The default behaviour is sync mode.
 */
class M24srDriver : public NFCEEPROMDriver {
public:
    /**
     * Object that contains all the callbacks fired by this class, each command has its own callback.
     * The callback default implementation is an empty function.
     */
    class Callbacks {
    public:
        /** called when get_session completes */
        virtual void on_session_open(M24srDriver *nfc, M24srError_t status)
        {
            (void) nfc;
            (void) status;
        }

        /** called when deselect completes */
        virtual void on_deselect(M24srDriver *nfc, M24srError_t status)
        {
            (void) nfc;
            (void) status;
        }

        /** called when select_application completes */
        virtual void on_selected_application(M24srDriver *nfc, M24srError_t status)
        {
            (void) nfc;
            (void) status;
        }

        /** called when select_cc_file completes */
        virtual void on_selected_cc_file(M24srDriver *nfc, M24srError_t status)
        {
            (void) nfc;
            (void) status;
        }

        /** called when select_ndef_file completes */
        virtual void on_selected_ndef_file(M24srDriver *nfc, M24srError_t status)
        {
            (void) nfc;
            (void) status;
        }

        /** called when select_system_file completes */
        virtual void on_selected_system_file(M24srDriver *nfc, M24srError_t status)
        {
            (void) nfc;
            (void) status;
        }

        /** called when read_binary completes */
        virtual void on_read_byte(M24srDriver *nfc, M24srError_t status, uint16_t offset, uint8_t *bytes_read,
                                  uint16_t read_count)
        {
            (void) nfc;
            (void) status;
            (void) offset;
            (void) bytes_read;
            (void) read_count;
        }

        /** called when update_binary completes */
        virtual void on_updated_binary(M24srDriver *nfc, M24srError_t status, uint16_t offset, uint8_t *bytes_written,
                                       uint16_t write_count)
        {
            (void) nfc;
            (void) status;
            (void) bytes_written;
            (void) write_count;
            (void) offset;
        }

        /** called when verify completes */
        virtual void on_verified(M24srDriver *nfc, M24srError_t status, PasswordType_t password_type, const uint8_t *pwd)
        {
            (void) nfc;
            (void) status;
            (void) password_type;
            (void) pwd;
        }

        /** called when manage_i2c_gpo completes */
        virtual void on_manage_i2c_gpo(M24srDriver *nfc, M24srError_t status, NfcGpoState_t new_status)
        {
            (void) nfc;
            (void) status;
            (void) new_status;
        }

        /** called when manage_rf_gpo completes */
        virtual void on_manage_rf_gpo(M24srDriver *nfc, M24srError_t status, NfcGpoState_t new_status)
        {
            (void) nfc;
            (void) status;
            (void) new_status;
        }

        /** called when change_reference_data completes */
        virtual void on_change_reference_data(M24srDriver *nfc, M24srError_t status, PasswordType_t type,
                                              const uint8_t *data)
        {
            (void) nfc;
            (void) status;
            (void) type;
            (void) data;
        }

        /** called when enable_verification_requirement completes */
        virtual void on_enable_verification_requirement(M24srDriver *nfc, M24srError_t status, PasswordType_t type)
        {
            (void) nfc;
            (void) status;
            (void) type;
        }

        /** called when disable_verification_requirement completes */
        virtual void on_disable_verification_requirement(M24srDriver *nfc, M24srError_t status, PasswordType_t type)
        {
            (void) nfc;
            (void) status;
            (void) type;
        }

        /** called when enable_permanent_state completes */
        virtual void on_enable_permanent_state(M24srDriver *nfc, M24srError_t status, PasswordType_t type)
        {
            (void) nfc;
            (void) status;
            (void) type;
        }

        /** called when disable_permanent_state completes */
        virtual void on_disable_permanent_state(M24srDriver *nfc, M24srError_t status, PasswordType_t type)
        {
            (void) nfc;
            (void) status;
            (void) type;
        }

        /** called when read_id completes */
        virtual void on_read_id(M24srDriver *nfc, M24srError_t status, uint8_t *id)
        {
            (void) nfc;
            (void) status;
            (void) id;
        }

        /** called when enable_read_password completes */
        virtual void on_enable_read_password(M24srDriver *nfc, M24srError_t status, const uint8_t *new_password)
        {
            (void) nfc;
            (void) status;
            (void) new_password;
        }

        /** called when oenable_write_password completes */
        virtual void on_enable_write_password(M24srDriver *nfc, M24srError_t status, const uint8_t *new_password)
        {
            (void) nfc;
            (void) status;
            (void) new_password;
        }

        /** called when disable_read_password completes */
        virtual void on_disable_read_password(M24srDriver *nfc, M24srError_t status)
        {
            (void) nfc;
            (void) status;
        }

        /** called when disable_write_password completes */
        virtual void on_disable_write_password(M24srDriver *nfc, M24srError_t status)
        {
            (void) nfc;
            (void) status;
        }

        /** called when disable_all_password completes */
        virtual void on_disable_all_password(M24srDriver *nfc, M24srError_t status)
        {
            (void) nfc;
            (void) status;
        }

        /** called when enable_read_only completes */
        virtual void on_enable_read_only(M24srDriver *nfc, M24srError_t status)
        {
            (void) nfc;
            (void) status;
        }

        /** called when enable_write_only completes */
        virtual void on_enable_write_only(M24srDriver *nfc, M24srError_t status)
        {
            (void) nfc;
            (void) status;
        }

        /** called when disable_read_only completes */
        virtual void on_disable_read_only(M24srDriver *nfc, M24srError_t status)
        {
            (void) nfc;
            (void) status;
        }

        /** called when disable_write_only completes */
        virtual void on_disable_write_only(M24srDriver *nfc, M24srError_t status)
        {
            (void) nfc;
            (void) status;
        }

        virtual ~Callbacks() { }
    };

public:
    /** Create the driver, default pin names will be used appropriate for the board.
     *  @param i2c_data_pin I2C data pin name.
     *  @param i2c_clock_pin I2C clock pin name.
     *  @param gpo_pin I2C GPO pin name.
     *  @param rf_disable_pin pin name for breaking the RF connection.
     */
    M24srDriver(PinName i2c_data_pin = M24SR_I2C_SDA_PIN, PinName i2c_clock_pin = M24SR_I2C_SCL_PIN,
                PinName gpo_pin = M24SR_GPO_PIN, PinName rf_disable_pin = M24SR_RF_DISABLE_PIN);

    virtual ~M24srDriver() { }

    /** @see NFCEEPROMDriver::reset
     */
    virtual void reset()
    {
        set_callback(&_default_cb);
        init();
        manage_i2c_gpo(I2C_ANSWER_READY);
    }

    /** @see NFCEEPROMDriver::get_max_size
     */
    virtual size_t read_max_size()
    {
        return MAX_NDEF_SIZE;
    }

    /** @see NFCEEPROMDriver::start_session
     */
    virtual void start_session(bool force = true)
    {
        if (_is_session_open) {
            delegate()->on_session_started(true);
            return;
        }

        set_callback(&_open_session_cb);

        get_session(force);
    }

    /** @see NFCEEPROMDriver::end_session
     */
    virtual void end_session()
    {
        set_callback(&_close_session_cb);
        deselect();
    }

    /** @see NFCEEPROMDriver::read_bytes
     */
    virtual void read_bytes(uint32_t address, uint8_t *bytes, size_t count)
    {
        if (!_is_session_open) {
            delegate()->on_bytes_read(0);
            return;
        }

        if (address > _ndef_size) {
            delegate()->on_bytes_read(0);
            return;
        }

        set_callback(&_read_byte_cb);

        if (count > _max_read_bytes) {
            count = _max_read_bytes;
        }

        if (address + count > _ndef_size) {
            count = _ndef_size - address;
        }

        if (count == 0) {
            delegate()->on_bytes_read(0);
            return;
        }

        /* offset by ndef file size*/
        address += NDEF_FILE_HEADER_SIZE;

        read_binary((uint16_t) address, (uint8_t) count, bytes);
    }

    /** @see NFCEEPROMDriver::write_bytes
     */
    virtual void write_bytes(uint32_t address, const uint8_t *bytes, size_t count)
    {
        if (!_is_session_open) {
            delegate()->on_bytes_written(0);
            return;
        }

        if (address > _ndef_size) {
            delegate()->on_bytes_written(0);
            return;
        }

        if (bytes) {
            set_callback(&_write_byte_cb);
        } else {
            set_callback(&_erase_bytes_cb);
        }

        if (count > _max_write_bytes) {
            count = _max_write_bytes;
        }

        if (address + count > _ndef_size) {
            count = _ndef_size - address;
        }

        if (count == 0) {
            delegate()->on_bytes_written(0);
            return;
        }

        /* offset by ndef file size*/
        address += NDEF_FILE_HEADER_SIZE;

        update_binary((uint16_t) address, (uint8_t) count, bytes);
    }

    /** @see NFCEEPROMDriver::set_size
     */
    virtual void write_size(size_t count)
    {
        if (!_is_session_open) {
            delegate()->on_size_read(false, 0);
            return;
        }

        if (count > MAX_NDEF_SIZE - NDEF_FILE_HEADER_SIZE) {
            delegate()->on_size_read(false, 0);
            return;
        }

        set_callback(&_set_size_cb);

        _ndef_size = (uint16_t)count;

        /* NDEF file size is BE */
        uint8_t *bytes = (uint8_t *)&_ndef_size;
        _ndef_size_buffer[0] = bytes[1];
        _ndef_size_buffer[1] = bytes[0];

        update_binary(0, NDEF_FILE_HEADER_SIZE, (const uint8_t *)&_ndef_size_buffer);
    }

    /** @see NFCEEPROMDriver::get_size
     */
    virtual void read_size()
    {
        if (!_is_session_open) {
            delegate()->on_size_read(false, 0);
            return;
        }

        set_callback(&_get_size_cb);

        read_binary(0, NDEF_FILE_HEADER_SIZE, (uint8_t *)&_ndef_size_buffer);
    }

    /** @see NFCEEPROMDriver::erase_bytes
     */
    virtual void erase_bytes(uint32_t address, size_t size)
    {
        write_bytes(address, NULL, size);
    }

private:
    /**
     * Change the function to call when a command ends.
     * @param commandCallback Object containing the callback, if NULL it will use empty callback
     */
    void set_callback(Callbacks *callback)
    {
        if (callback) {
            _command_cb = callback;
        } else {
            _command_cb = &_default_cb;
        }
    }

    /**
     * get the callback object to use
     * @return callback object to use
     */
    Callbacks *get_callback()
    {
        /* this allows for two levels of operation, the previous command will continue
         * when this set of callbacks has finished */
        if (_subcommand_cb) {
            return _subcommand_cb;
        }
        return _command_cb;
    }

    void nfc_interrupt_callback()
    {
        if (_communication_type == ASYNC) {
            event_queue()->call(this, &M24srDriver::manage_event);
        }
    }

    /**
     * Enable the request of a password before reading the tag.
     * @param current_write_password Current password
     * @param new_password Password to request before reading the tag.
     * @return return M24SR_SUCCESS if no errors
     * @note The password must have a length of 16 chars.
     */
    M24srError_t enable_read_password(const uint8_t *current_write_password, const uint8_t *new_password)
    {
        _subcommand_cb = &_change_password_request_status_cb;
        _change_password_request_status_cb.set_task(READ_PASSWORD, new_password);

        return verify(WRITE_PASSWORD, current_write_password);
    }

    /**
     * Disable the request of a password before reading the tag.
     * @param current_write_password Current password
     * @return return M24SR_SUCCESS if no errors
     * @note The password must have a length of 16 chars.
     */
    M24srError_t disable_read_password(const uint8_t *current_write_password)
    {
        _subcommand_cb = &_change_password_request_status_cb;
        _change_password_request_status_cb.set_task(READ_PASSWORD, NULL);

        return verify(WRITE_PASSWORD, current_write_password);
    }

    /**
     * Enable the request of a password before writing to the tag.
     * @param current_write_password Current password
     * @param new_password Password to request before reading the tag.
     * @return return M24SR_SUCCESS if no errors
     * @note The password must have a length of 16 chars.
     */
    M24srError_t enable_write_password(const uint8_t *current_write_password, uint8_t *new_password)
    {
        _subcommand_cb = &_change_password_request_status_cb;
        _change_password_request_status_cb.set_task(WRITE_PASSWORD, new_password);

        return verify(WRITE_PASSWORD, current_write_password);
    }

    /**
     * Disable the request of a password before writing the tag.
     * @param current_write_password Current password.
     * @return return M24SR_SUCCESS if no errors
     * @note The password must have a length of 16 chars.
     */
    M24srError_t disable_write_password(const uint8_t *current_write_password)
    {
        _subcommand_cb = &_change_password_request_status_cb;
        _change_password_request_status_cb.set_task(WRITE_PASSWORD, NULL);

        return verify(WRITE_PASSWORD, current_write_password);
    }

    /**
     * @brief This function disables both read and write passwords.
     * @param super_user_password I2C super user password.
     * @return return M24SR_SUCCESS if no errors
     * @note The password must have a length of 16 chars.
     */
    M24srError_t disable_all_password(const uint8_t *super_user_password)
    {
        _subcommand_cb = &_remove_password_cb;
        return verify(I2C_PASSWORD, super_user_password);
    }

    /**
     * @brief This function enables read only mode.
     * @param current_write_password Write password is needed to enable read only mode.
     * @return return M24SR_SUCCESS if no errors
     * @note The password must have a length of 16 chars.
     */
    M24srError_t enable_read_only(const uint8_t *current_write_password)
    {
        _subcommand_cb = &_change_access_state_cb;
        _change_access_state_cb.change_access_state(ChangeAccessStateCallback::WRITE, false);

        return verify(WRITE_PASSWORD, current_write_password);
    }

    /**
     * @brief This function disables read only mode.
     * @param current_write_password Write password is needed to disable read only mode.
     * @return return M24SR_SUCCESS if no errors
     * @note The password must have a length of 16 chars.
     */
    M24srError_t disable_read_only(const uint8_t *current_write_password)
    {
        _subcommand_cb = &_change_access_state_cb;
        _change_access_state_cb.change_access_state(ChangeAccessStateCallback::WRITE, true);

        return verify(I2C_PASSWORD, current_write_password);
    }

    /**
     * @brief This function enables write only mode.
     * @param current_write_password Write password is needed to enable write only mode.
     * @return return M24SR_SUCCESS if no errors
     * @note The password must have a length of 16 chars.
     */
    M24srError_t enable_write_only(const uint8_t *current_write_password)
    {
        _subcommand_cb = &_change_access_state_cb;
        _change_access_state_cb.change_access_state(ChangeAccessStateCallback::READ, false);

        return verify(WRITE_PASSWORD, current_write_password);
    }

    /**
     * @brief This function disables write only mode.
     * @param current_write_password Write password is needed to disable write only mode.
     * @return return M24SR_SUCCESS if no errors
     * @note The password must have a length of 16 chars.
     */
    M24srError_t disable_write_only(const uint8_t *current_write_password)
    {
        _subcommand_cb = &_change_access_state_cb;
        _change_access_state_cb.change_access_state(ChangeAccessStateCallback::READ, true);

        return verify(I2C_PASSWORD, current_write_password);
    }

private:
    M24srError_t init();
    M24srError_t read_id(uint8_t *nfc_id);
    M24srError_t get_session(bool force = false);

    M24srError_t deselect();
    M24srError_t receive_deselect();

    M24srError_t select_application();
    M24srError_t receive_select_application();

    M24srError_t select_cc_file();
    M24srError_t receive_select_cc_file();

    M24srError_t select_ndef_file(uint16_t ndef_file_id);
    M24srError_t receive_select_ndef_file();

    M24srError_t select_system_file();
    M24srError_t receive_select_system_file();

    M24srError_t read_binary(uint16_t offset, uint8_t length, uint8_t *buffer);
    M24srError_t st_read_binary(uint16_t offset, uint8_t length, uint8_t *buffer);
    M24srError_t receive_read_binary();

    M24srError_t update_binary(uint16_t offset, uint8_t length, const uint8_t *data);
    M24srError_t receive_update_binary();

    M24srError_t verify(PasswordType_t password_type, const uint8_t *password);
    M24srError_t receive_verify();

    M24srError_t change_reference_data(PasswordType_t password_type, const uint8_t *password);
    M24srError_t receive_change_reference_data();

    M24srError_t enable_verification_requirement(PasswordType_t password_type);
    M24srError_t receive_enable_verification_requirement();

    M24srError_t disable_verification_requirement(PasswordType_t password_type);
    M24srError_t receive_disable_verification_requirement();

    M24srError_t enable_permanent_state(PasswordType_t password_type);
    M24srError_t receive_enable_permanent_state();

    M24srError_t disable_permanent_state(PasswordType_t password_type);
    M24srError_t receive_disable_permanent_state();

    M24srError_t send_interrupt();
    M24srError_t state_control(bool gpo_reset);

    M24srError_t manage_i2c_gpo(NfcGpoState_t gpo_i2c_config);
    M24srError_t manage_rf_gpo(NfcGpoState_t gpo_rf_config);

    M24srError_t rf_config(bool enable);
    M24srError_t send_fwt_extension(uint8_t fwt_byte);

    M24srError_t send_receive_i2c(uint16_t length, uint8_t *command);

    /**
     * Function to call when the component fire an interrupt.
     * @return last operation status
     */
    M24srError_t manage_event();

    /**
     * Send a command to the component.
     * @param length Length of the command.
     * @param command Buffer containing the command.
     * @return M24SR_SUCCESS if no errors
     */
    M24srError_t io_send_i2c_command(uint8_t length, const uint8_t *command);

    /**
     * Read a command response.
     * @param length Number of bytes to read.
     * @param command Buffer to store the response into.
     * @return M24SR_SUCCESS if no errors
     */
    M24srError_t io_receive_i2c_response(uint8_t length, uint8_t *command);

    /**
     * Do an active polling on the I2C bus until the answer is ready.
     * @return M24SR_SUCCESS if no errors
     */
    M24srError_t io_poll_i2c();

    bool manage_sync_communication(M24srError_t *status);

private:
    /**
     * @brief This class permits to enable/disable the password request to read/write into the tag
     */
    class ChangePasswordRequestStatusCallback : public Callbacks {
    public:
        /**
         * Build the chain of callbacks.
         */
        ChangePasswordRequestStatusCallback()
            : _new_password(NULL),
              _type(I2C_PASSWORD),
              _enable(false) { }

        /* This class is equivalent to calling the methods:
         *
         * To enable the request:
         * - Verify
         * - change_reference_data
         * - enable_permanent_state
         *
         * To disable the request:
         * - verify
         * - disable_verification_requirement
         */

        /**
         * Set the password to enable/disable.
         * @param type Type of password to enable/disable.
         * @param new_password Array of 16bytes with the new password, if null the request will be disabled.
         */
        void set_task(PasswordType_t type, const uint8_t *new_password)
        {
            _new_password = new_password;
            _type = type;
            _enable = (new_password != NULL);
        }

        virtual void on_verified(M24srDriver *nfc, M24srError_t status, PasswordType_t, const uint8_t *)
        {
            if (status != M24SR_SUCCESS) {
                return on_finish_command(nfc, status);
            }
            if (_enable) {
                nfc->change_reference_data(_type, _new_password);
            } else {
                nfc->disable_verification_requirement(_type);
            }
        }

        virtual void on_disable_verification_requirement(M24srDriver *nfc, M24srError_t status, PasswordType_t)
        {
            on_finish_command(nfc, status);
        }

        virtual void on_change_reference_data(M24srDriver *nfc, M24srError_t status, PasswordType_t type, const uint8_t *)
        {
            if (status == M24SR_SUCCESS) {
                nfc->enable_permanent_state(type);
            } else {
                on_finish_command(nfc, status);
            }
        }

        virtual void on_enable_permanent_state(M24srDriver *nfc, M24srError_t status, PasswordType_t)
        {
            on_finish_command(nfc, status);
        }

    private:
        /**
         * Remove the private callbacks and call the user callback.
         * @param nfc Object triggering the command.
         * @param status Command status.
         */
        void on_finish_command(M24srDriver *nfc, M24srError_t status)
        {
            nfc->_subcommand_cb = NULL;

            if (_enable) {
                if (_type == READ_PASSWORD) {
                    nfc->get_callback()->on_enable_read_password(nfc, status, _new_password);
                } else {
                    nfc->get_callback()->on_enable_write_password(nfc, status, _new_password);
                }
            } else {
                if (_type == READ_PASSWORD) {
                    nfc->get_callback()->on_disable_read_password(nfc, status);
                } else {
                    nfc->get_callback()->on_disable_write_password(nfc, status);
                }
            }
        }

    private:
        const uint8_t *_new_password;
        PasswordType_t _type;
        bool _enable;
    };

    /**
     * @brief This class permits to disable all the password requests to read/write into the tag.
     */
    class RemoveAllPasswordCallback : public Callbacks {
    public:
        /**
         * Build the chain of callbacks.
         */
        RemoveAllPasswordCallback()
            : _password(NULL) { }

        /* This class is equivalent to calling the methods:
         * - verify(i2c)
         * - disable_permanent_state(Read)
         * - disable_permanent_state(write)
         * - disable_verification_requirement(Read)
         * - disable_verification_requirement(write)
         * - change_reference_data(Read)
         * - change_reference_data(write)
         */

        virtual void on_verified(M24srDriver *nfc, M24srError_t status, PasswordType_t, const uint8_t *data)
        {
            if (status != M24SR_SUCCESS) {
                return on_finish_command(nfc, status);
            }
            _password = data;
            nfc->disable_permanent_state(READ_PASSWORD);
        }

        virtual void on_disable_permanent_state(M24srDriver *nfc, M24srError_t status, PasswordType_t type)
        {
            if (status != M24SR_SUCCESS) {
                return on_finish_command(nfc, status);
            }
            if (type == READ_PASSWORD) {
                nfc->disable_permanent_state(WRITE_PASSWORD);
            } else {
                nfc->disable_verification_requirement(READ_PASSWORD);
            }
        }

        virtual void on_disable_verification_requirement(M24srDriver *nfc, M24srError_t status, PasswordType_t type)
        {
            if (status != M24SR_SUCCESS) {
                return on_finish_command(nfc, status);
            }
            if (type == READ_PASSWORD) {
                nfc->disable_verification_requirement(WRITE_PASSWORD);
            } else {
                nfc->change_reference_data(READ_PASSWORD, _password);
            }
        }

        virtual void on_change_reference_data(M24srDriver *nfc, M24srError_t status, PasswordType_t type,
                                              const uint8_t *data)
        {
            if (status != M24SR_SUCCESS) {
                return on_finish_command(nfc, status);
            }
            if (type == READ_PASSWORD) {
                nfc->change_reference_data(WRITE_PASSWORD, data);
            } else {
                on_finish_command(nfc, status);
            }
        }

    private:
        /**
         * Remove the private callback and call the onDisableAllPassword callback.
         * @param nfc Object triggering the command.
         * @param status Command status.
         */
        void on_finish_command(M24srDriver *nfc, M24srError_t status)
        {
            nfc->_subcommand_cb = NULL;
            _password = NULL;
            nfc->get_callback()->on_disable_all_password(nfc, status);
        }

    private:
        /**
         * Store the default password used for open a super user session
         * it will be set as default read/write password
         */
        const uint8_t *_password;
    };

    /**
     * @brief This class permits to set the tag as read/write only.
     */
    class ChangeAccessStateCallback : public Callbacks {
    public:
        enum AccessType_t {
            WRITE,
            READ
        };

        /**
         * Build the chain of callbacks.
         */
        ChangeAccessStateCallback()
            : _type(WRITE),
              _enable(false) { }

        /* This class is equivalent to calling the methods:
         * - verify(i2c)
         * - enable_permanent_state(Read/write)
         * or:
         * - verify(i2c)
         * - disable_permanent_state</li>
         * - disable_verification_requirement(Read/write)
         */

        /**
         * Set the access to enable/disable an access type.
         * @param type Access type.
         * @param enable True to enable the state, False to disable it.
         */
        void change_access_state(AccessType_t type, bool enable)
        {
            _type = type;
            _enable = enable;
        }

        virtual void on_verified(M24srDriver *nfc, M24srError_t status, PasswordType_t, const uint8_t *)
        {
            if (status != M24SR_SUCCESS) {
                return on_finish_command(nfc, status);
            }

            if (_enable) {
                nfc->disable_permanent_state(_type == WRITE ? WRITE_PASSWORD : READ_PASSWORD);
            } else {
                nfc->enable_permanent_state(_type == WRITE ? WRITE_PASSWORD : READ_PASSWORD);
            }

        }

        virtual void on_disable_permanent_state(M24srDriver *nfc, M24srError_t status, PasswordType_t type)
        {
            if (status != M24SR_SUCCESS) {
                return on_finish_command(nfc, status);
            }

            nfc->disable_verification_requirement(type);
        }

        virtual void on_disable_verification_requirement(M24srDriver *nfc, M24srError_t status, PasswordType_t)
        {
            on_finish_command(nfc, status);
        }

        virtual void on_enable_permanent_state(M24srDriver *nfc, M24srError_t status, PasswordType_t)
        {
            on_finish_command(nfc, status);
        }

    private:
        /**
         * Remove the private callback and call the user callback.
         * @param nfc Object triggering the command.
         * @param status Command status.
         */
        void on_finish_command(M24srDriver *nfc, M24srError_t status)
        {
            nfc->_subcommand_cb = NULL;
            if (_enable) {
                if (_type == READ) {
                    //enable read = disable write only
                    nfc->get_callback()->on_disable_write_only(nfc, status);
                } else {
                    //enable write = disable read only
                    nfc->get_callback()->on_disable_read_only(nfc, status);
                }
            } else {
                if (_type == WRITE) {
                    //disable write = enable read only
                    nfc->get_callback()->on_enable_read_only(nfc, status);
                } else {
                    nfc->get_callback()->on_enable_write_only(nfc, status);
                }
            }
        }

    private:
        AccessType_t _type;
        bool _enable;
    };

    /**
     * @brief Object with the callback used to send a ManageGPO command.
     */
    class ManageGPOCallback : public Callbacks {

    public:
        /**
         * Build the chain of callbacks.
         * @param parent Parent component to run the command on.
         */
        ManageGPOCallback()
            : _new_gpo_config(HIGH_IMPEDANCE),
              _read_gpo_config(0),
              _change_i2c_gpo(true) { }

        /* This class is equivalent to calling the methods:
         * - selected_application
         * - select_system_file
         * - read_binary
         * - verify
         * - update_binary
         */

        /**
         * Command parameters.
         * @param i2cGpo true to change the i2c gpo, false for the rf gpo.
         * @param new_config new gpo function.
         */
        void set_new_gpo_config(bool i2cGpo, NfcGpoState_t new_config)
        {
            _new_gpo_config = new_config;
            _change_i2c_gpo = i2cGpo;
        }

        virtual void on_selected_application(M24srDriver *nfc, M24srError_t status)
        {
            if (status == M24SR_SUCCESS) {
                nfc->select_system_file();
            } else {
                on_finish_command(nfc, status);
            }
        }

        virtual void on_selected_system_file(M24srDriver *nfc, M24srError_t status)
        {
            if (status == M24SR_SUCCESS) {
                nfc->read_binary(0x0004, 0x01, &_read_gpo_config);
            } else {
                on_finish_command(nfc, status);
            }
        }

        virtual void on_read_byte(M24srDriver *nfc, M24srError_t status, uint16_t, uint8_t *, uint16_t)
        {
            if (status == M24SR_SUCCESS) {
                nfc->verify(I2C_PASSWORD, default_password);
            } else {
                on_finish_command(nfc, status);
            }
        }

        virtual void on_verified(M24srDriver *nfc, M24srError_t status, PasswordType_t, const uint8_t *)
        {
            if (status != M24SR_SUCCESS) {
                return on_finish_command(nfc, status);
            }

            if (_change_i2c_gpo) {
                _read_gpo_config = (_read_gpo_config & 0xF0) | (uint8_t) _new_gpo_config;
            } else {
                _read_gpo_config = (_read_gpo_config & 0x0F) | (((uint8_t) _new_gpo_config) << 4);
            }

            nfc->update_binary(0x0004, 0x01, &_read_gpo_config);
        }

        virtual void on_updated_binary(M24srDriver *nfc, M24srError_t status, uint16_t, uint8_t *, uint16_t)
        {

            if (status == M24SR_SUCCESS) {
                if (_new_gpo_config == I2C_ANSWER_READY) {
                    nfc->_communication_type = ASYNC;
                } else {
                    nfc->_communication_type = SYNC;
                }
            }
            on_finish_command(nfc, status);
        }

    private:
        /**
         * Remove the private callback and call the user callback.
         * @param nfc Object where the command was send to.
         * @param status Command status.
         */
        void on_finish_command(M24srDriver *nfc, M24srError_t status)
        {
            nfc->_subcommand_cb = NULL;
            if (_change_i2c_gpo) {
                nfc->_command_cb->on_manage_i2c_gpo(nfc, status, _new_gpo_config);
            } else {
                nfc->_command_cb->on_manage_rf_gpo(nfc, status, _new_gpo_config);
            }
        }

    private:
        /** new gpo function that this class has to write */
        NfcGpoState_t _new_gpo_config;

        /** variable where storeing the read gpo configuration */
        uint8_t _read_gpo_config;

        /** true to change the i2c gpo, false to change the rf gpo */
        bool _change_i2c_gpo;
    };

    /**
     * @brief Object with the callback used to read the component ID
     */
    class ReadIDCallback : public Callbacks {
    public:
        /**
         * Build the chain of callbacks.
         * @param parent object where to send the command to.
         */
        ReadIDCallback() : _id(NULL) { }

        /* This class is equivalent to calling the methods:
         * - select_application
         * - select_system_file
         * - read_binary
         */

        /**
         * Set the variable containing the result
         * @param idPtr
         */
        void set_task(uint8_t *id)
        {
            _id = id;
        }

        virtual void on_selected_application(M24srDriver *nfc, M24srError_t status)
        {
            if (status == M24SR_SUCCESS) {
                nfc->select_system_file();
            } else {
                on_finish_command(nfc, status);
            }

        }

        virtual void on_selected_system_file(M24srDriver *nfc, M24srError_t status)
        {
            if (status == M24SR_SUCCESS) {
                nfc->read_binary(0x0011, 0x01, _id);
            } else {
                on_finish_command(nfc, status);
            }
        }

        virtual void on_read_byte(M24srDriver *nfc, M24srError_t status, uint16_t, uint8_t *, uint16_t)
        {
            on_finish_command(nfc, status);
        }

    private:
        /**
         * Remove the private callback and call the user onReadId function.
         * @param nfc Object where the command was send.
         * @param status Command status.
         */
        void on_finish_command(M24srDriver *nfc, M24srError_t status)
        {
            nfc->_subcommand_cb = NULL;
            nfc->get_callback()->on_read_id(nfc, status, _id);
        }

    private:
        /** pointer to read id */
        uint8_t *_id;
    };

    /**
     * Class containing the callback needed to open a session and read the max
     * read/write size
     */
    class OpenSessionCallBack : public Callbacks {
    public:
        OpenSessionCallBack()
            : _retries(OPEN_SESSION_RETRIES) { }

        void on_session_open(M24srDriver *nfc, M24srError_t status)
        {
            if (status == M24SR_SUCCESS) {
                nfc->select_application();
            } else {
                nfc->delegate()->on_session_started(false);
            }
        }

        void on_selected_application(M24srDriver *nfc, M24srError_t status)
        {
            if (status == M24SR_SUCCESS) {
                nfc->select_cc_file();
            } else {
                if (_retries == 0) {
                    nfc->delegate()->on_session_started(false);
                } else {
                    _retries--;
                    nfc->select_application();
                }
            }
        }

        void on_selected_cc_file(M24srDriver *nfc, M24srError_t status)
        {
            if (status == M24SR_SUCCESS) {
                nfc->read_binary(0x0000, CC_FILE_LENGTH, CCFile);
            } else {
                nfc->delegate()->on_session_started(false);
            }
        }

        void on_read_byte(M24srDriver *nfc, M24srError_t status, uint16_t, uint8_t *bytes_read,
                          uint16_t read_count)
        {
            if (status != M24SR_SUCCESS || read_count != CC_FILE_LENGTH) {
                nfc->delegate()->on_session_started(false);
            }
            uint16_t ndef_file_id = (uint16_t)((bytes_read[0x09] << 8) | bytes_read[0x0A]);
            nfc->_max_read_bytes = (uint16_t)((bytes_read[0x03] << 8) | bytes_read[0x04]);
            nfc->_max_write_bytes = (uint16_t)((bytes_read[0x05] << 8) | bytes_read[0x06]);
            nfc->select_ndef_file(ndef_file_id);
        }

        void on_selected_ndef_file(M24srDriver *nfc, M24srError_t status)
        {
            nfc->_is_session_open = (status == M24SR_SUCCESS);
            nfc->delegate()->on_session_started(nfc->_is_session_open);
        }

    private:
        /** number of trials done for open the session */
        uint32_t _retries;

        /** buffer where read the CC file */
        uint8_t CCFile[15];
    };

    /**
     * Class containing the callback needed to close a session
     */
    class CloseSessionCallBack : public Callbacks {
    public:
        CloseSessionCallBack() { }

        virtual void on_deselect(M24srDriver *nfc, M24srError_t status)
        {
            if (status == M24SR_SUCCESS) {
                nfc->_is_session_open = false;
                nfc->delegate()->on_session_ended(true);
            } else {
                nfc->delegate()->on_session_ended(false);
            }
        }
    };

    /**
     * Class containing the callback needed to write a buffer
     */
    class WriteByteCallback : public Callbacks {
    public:
        WriteByteCallback() { }

        virtual void on_updated_binary(M24srDriver *nfc, M24srError_t status, uint16_t offset, uint8_t *bytes_written,
                                       uint16_t write_count)
        {
            if (status != M24SR_SUCCESS) {
                nfc->delegate()->on_bytes_written(0);
                return;
            }

            nfc->delegate()->on_bytes_written(write_count);
        }
    };

    /**
     * Class containing the callback needed to read a buffer
     */
    class ReadByteCallback : public Callbacks {
    public:
        ReadByteCallback() { }

        virtual void on_read_byte(M24srDriver *nfc, M24srError_t status, uint16_t offset, uint8_t *bytes_read,
                                  uint16_t read_count)
        {
            if (status != M24SR_SUCCESS) {
                nfc->delegate()->on_bytes_read(0);
                return;
            }

            nfc->delegate()->on_bytes_read(read_count);
        }
    };

    class SetSizeCallback : public Callbacks {
    public:
        SetSizeCallback() { }

        virtual void on_updated_binary(M24srDriver *nfc, M24srError_t status, uint16_t offset, uint8_t *bytes_written,
                                       uint16_t write_count)
        {
            if (status != M24SR_SUCCESS) {
                nfc->delegate()->on_size_written(false);
                return;
            }

            nfc->delegate()->on_size_written(true);
        }
    };

    class GetSizeCallback : public Callbacks {
    public:
        GetSizeCallback() { }

        virtual void on_read_byte(M24srDriver *nfc, M24srError_t status, uint16_t offset, uint8_t *bytes_read,
                                  uint16_t read_count)
        {
            if (status != M24SR_SUCCESS) {
                nfc->delegate()->on_size_read(false, 0);
                return;
            }

            /* NDEF file size is BE */
            nfc->_ndef_size = (((uint16_t) nfc->_ndef_size_buffer[0]) << 8 | nfc->_ndef_size_buffer[1]);

            nfc->delegate()->on_size_read(true, nfc->_ndef_size);
        }
    };

    class EraseBytesCallback : public Callbacks {
    public:
        EraseBytesCallback() { }

        virtual void on_updated_binary(M24srDriver *nfc, M24srError_t status, uint16_t offset, uint8_t *bytes_written,
                                       uint16_t write_count)
        {
            if (status != M24SR_SUCCESS) {
                nfc->delegate()->on_bytes_erased(0);
                return;
            }

            nfc->delegate()->on_bytes_erased(write_count);
        }
    };

private:
    /** Default password used to change the write/read permission */
    static const uint8_t default_password[16];

    I2C _i2c_channel;

    /** Interrupt object fired when the gpo status changes */
    InterruptIn _gpo_event_interrupt;
    DigitalIn _gpo_pin;
    DigitalOut _rf_disable_pin;

    /** object containing the callbacks to use*/
    Callbacks *_command_cb;

    /**
     * Object with private callbacks used to hide high level commands each
     * calling multiple low level commands. This callbacks object has
     * higher priority comparing to the user callbacks.
     */
    Callbacks *_subcommand_cb;

    Callbacks _default_cb;
    ManageGPOCallback _manage_gpo_cb;
    ReadIDCallback _read_id_cb;
    ChangePasswordRequestStatusCallback _change_password_request_status_cb;
    RemoveAllPasswordCallback _remove_password_cb;
    ChangeAccessStateCallback _change_access_state_cb;
    OpenSessionCallBack _open_session_cb;
    CloseSessionCallBack _close_session_cb;
    WriteByteCallback _write_byte_cb;
    ReadByteCallback _read_byte_cb;
    SetSizeCallback _set_size_cb;
    GetSizeCallback _get_size_cb;
    EraseBytesCallback _erase_bytes_cb;


    uint8_t _buffer[0xFF];

    /** Type of communication being used (SYNC, ASYNC) */
    Communication_t _communication_type;

    Command_t _last_command;
    CommandData_t _last_command_data;

    /** Buffer used to build the command to send to the chip. */
    uint16_t _ndef_size;
    uint8_t _ndef_size_buffer[NDEF_FILE_HEADER_SIZE];
    uint8_t _max_read_bytes;
    uint8_t _max_write_bytes;
    uint8_t _did_byte;

    bool _is_session_open;
};

} //ST
} //vendor
} //nfc
} //mbed

#endif // M24SR_H

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/