/* mbed Microcontroller Library * Copyright (c) 2018 ARM Limited * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "NFCEEPROM.h" #include "ndef/ndef.h" using namespace mbed; using namespace mbed::nfc; NFCEEPROM::NFCEEPROM(NFCEEPROMDriver *driver, events::EventQueue *queue, const Span<uint8_t> &ndef_buffer) : NFCTarget(ndef_buffer), _delegate(NULL), _driver(driver), _event_queue(queue), _initialized(false), _current_op(nfc_eeprom_idle), _ndef_buffer_reader { nullptr, 0, nullptr }, _ndef_buffer_read_sz(0), _eeprom_address(0), _operation_result(NFC_ERR_UNKNOWN) { _driver->set_delegate(this); _driver->set_event_queue(queue); } nfc_err_t NFCEEPROM::initialize() { MBED_ASSERT(_initialized == false); // Initialize should only be called once // Initialize driver _driver->reset(); _initialized = true; return NFC_OK; } void NFCEEPROM::set_delegate(NFCEEPROM::Delegate *delegate) { _delegate = delegate; } void NFCEEPROM::write_ndef_message() { MBED_ASSERT(_initialized == true); if (_current_op != nfc_eeprom_idle) { if (_delegate != NULL) { _delegate->on_ndef_message_written(NFC_ERR_BUSY); } return; } // First update NDEF message if required ndef_msg_encode(ndef_message()); _current_op = nfc_eeprom_write_start_session; // Retrieve reader ac_buffer_dup(&_ndef_buffer_reader, ac_buffer_builder_buffer(ndef_msg_buffer_builder(ndef_message()))); // Check that NDEF message is not too big if (ac_buffer_reader_readable(&_ndef_buffer_reader) > _driver->read_max_size()) { handle_error(NFC_ERR_BUFFER_TOO_SMALL); return; } // Reset EEPROM address _eeprom_address = 0; // Go through the steps! _driver->start_session(); // 1 - Start session // 2 - Write bytes (can be repeated) // 3 - Set NDEF message size // 4 - End session } void NFCEEPROM::read_ndef_message() { MBED_ASSERT(_initialized == true); if (_current_op != nfc_eeprom_idle) { if (_delegate != NULL) { _delegate->on_ndef_message_written(NFC_ERR_BUSY); } return; } _current_op = nfc_eeprom_read_start_session; // Reset EEPROM address _eeprom_address = 0; // Go through the steps! _driver->start_session(); // 1 - Start session // 2 - Get NDEF message size // 3 - Read bytes (can be repeated) // 4 - End session } void NFCEEPROM::erase_ndef_message() { // We don't want to take any risks, so erase the whole address space // And set the message size to 0 MBED_ASSERT(_initialized == true); if (_current_op != nfc_eeprom_idle) { if (_delegate != NULL) { _delegate->on_ndef_message_erased(NFC_ERR_BUSY); } return; } _current_op = nfc_eeprom_erase_start_session; // Reset EEPROM address _eeprom_address = 0; // Go through the steps! _driver->start_session(); // 1 - Start session // 2 - Set addressable size to the max // 3 - Erase bytes (can be repeated) // 4 - Set addressable size to 0 // 5 - End session } void NFCEEPROM::on_session_started(bool success) { switch (_current_op) { case nfc_eeprom_write_start_session: if (!success) { handle_error(NFC_ERR_CONTROLLER); // An EEPROM is not really a controller but close enough return; } _current_op = nfc_eeprom_write_write_size; _driver->write_size(ac_buffer_reader_readable(&_ndef_buffer_reader)); break; case nfc_eeprom_read_start_session: if (!success) { handle_error(NFC_ERR_CONTROLLER); return; } _current_op = nfc_eeprom_read_read_size; _driver->read_size(); break; case nfc_eeprom_erase_start_session: if (!success) { handle_error(NFC_ERR_CONTROLLER); return; } _current_op = nfc_eeprom_erase_write_max_size; _driver->write_size(_driver->read_max_size()); break; default: // Should not happen, state machine is broken or driver is doing something wrong handle_error(NFC_ERR_UNKNOWN); return; } } void NFCEEPROM::on_session_ended(bool success) { switch (_current_op) { case nfc_eeprom_write_end_session: if (!success) { handle_error(NFC_ERR_CONTROLLER); return; } _current_op = nfc_eeprom_idle; if (_delegate != NULL) { _delegate->on_ndef_message_written(_operation_result); } break; case nfc_eeprom_read_end_session: if (!success) { handle_error(NFC_ERR_CONTROLLER); return; } _current_op = nfc_eeprom_idle; // Try to parse the NDEF message ndef_msg_decode(ndef_message()); if (_delegate != NULL) { _delegate->on_ndef_message_read(_operation_result); } break; case nfc_eeprom_erase_end_session: if (!success) { handle_error(NFC_ERR_CONTROLLER); return; } _current_op = nfc_eeprom_idle; if (_delegate != NULL) { _delegate->on_ndef_message_erased(_operation_result); } break; default: // Should not happen, state machine is broken or driver is doing something wrong handle_error(NFC_ERR_UNKNOWN); return; } } void NFCEEPROM::on_bytes_read(size_t count) { switch (_current_op) { case nfc_eeprom_read_read_bytes: { if (count == 0) { handle_error(NFC_ERR_CONTROLLER); return; } // Discard bytes that were actually read and update address _eeprom_address += count; ac_buffer_builder_t *buffer_builder = ndef_msg_buffer_builder(ndef_message()); ac_buffer_builder_write_n_skip(buffer_builder, count); // Continue reading _event_queue->call(this, &NFCEEPROM::continue_read); break; } default: // Should not happen, state machine is broken or driver is doing something wrong handle_error(NFC_ERR_UNKNOWN); return; } } void NFCEEPROM::on_bytes_written(size_t count) { switch (_current_op) { case nfc_eeprom_write_write_bytes: if (count == 0) { handle_error(NFC_ERR_CONTROLLER); return; } // Skip bytes that were actually written and update address _eeprom_address += count; ac_buffer_read_n_skip(&_ndef_buffer_reader, count); // Continue writing _event_queue->call(this, &NFCEEPROM::continue_write); break; default: // Should not happen, state machine is broken or driver is doing something wrong handle_error(NFC_ERR_UNKNOWN); return; } } void NFCEEPROM::on_size_written(bool success) { switch (_current_op) { case nfc_eeprom_write_write_size: if (!success) { handle_error(NFC_ERR_CONTROLLER); return; } _current_op = nfc_eeprom_write_write_bytes; continue_write(); break; case nfc_eeprom_erase_write_max_size: if (!success) { handle_error(NFC_ERR_CONTROLLER); return; } // Start erasing bytes _current_op = nfc_eeprom_erase_erase_bytes; continue_erase(); break; case nfc_eeprom_erase_write_0_size: if (!success) { handle_error(NFC_ERR_CONTROLLER); return; } // End session _current_op = nfc_eeprom_erase_end_session; _operation_result = NFC_OK; _driver->end_session(); break; default: // Should not happen, state machine is broken or driver is doing something wrong handle_error(NFC_ERR_UNKNOWN); return; } } void NFCEEPROM::on_size_read(bool success, size_t size) { switch (_current_op) { case nfc_eeprom_read_read_size: { if (!success) { handle_error(NFC_ERR_CONTROLLER); return; } // Reset NDEF message buffer builder ac_buffer_builder_t *buffer_builder = ndef_msg_buffer_builder(ndef_message()); ac_buffer_builder_reset(buffer_builder); // Check that we have a big enough buffer to read the message if (size > ac_buffer_builder_writable(buffer_builder)) { // Not enough space, close session _current_op = nfc_eeprom_read_end_session; _operation_result = NFC_ERR_BUFFER_TOO_SMALL; _driver->end_session(); return; } // Save size and reset address _eeprom_address = 0; _ndef_buffer_read_sz = size; // Start reading bytes _current_op = nfc_eeprom_read_read_bytes; _event_queue->call(this, &NFCEEPROM::continue_read); break; } default: // Should not happen, state machine is broken or driver is doing something wrong handle_error(NFC_ERR_UNKNOWN); return; } } void NFCEEPROM::on_bytes_erased(size_t count) { switch (_current_op) { case nfc_eeprom_erase_erase_bytes: if (count == 0) { handle_error(NFC_ERR_CONTROLLER); return; } // Update address _eeprom_address += count; // Continue erasing _event_queue->call(this, &NFCEEPROM::continue_erase); break; default: // Should not happen, state machine is broken or driver is doing something wrong handle_error(NFC_ERR_UNKNOWN); return; } } void NFCEEPROM::continue_write() { if (ac_buffer_reader_readable(&_ndef_buffer_reader) > 0) { // Continue writing _driver->write_bytes(_eeprom_address, ac_buffer_reader_current_buffer_pointer(&_ndef_buffer_reader), ac_buffer_reader_current_buffer_length(&_ndef_buffer_reader)); } else { // we are done _current_op = nfc_eeprom_write_end_session; _operation_result = NFC_OK; _driver->end_session(); } } void NFCEEPROM::continue_erase() { if (_eeprom_address < _driver->read_max_size()) { // Continue erasing _driver->erase_bytes(_eeprom_address, _driver->read_max_size() - _eeprom_address); } else { // Now update size _current_op = nfc_eeprom_erase_write_0_size; _driver->write_size(0); } } void NFCEEPROM::continue_read() { if (_eeprom_address < _ndef_buffer_read_sz) { // Continue reading ac_buffer_builder_t *buffer_builder = ndef_msg_buffer_builder(ndef_message()); _driver->read_bytes(_eeprom_address, ac_buffer_builder_write_position(buffer_builder), _ndef_buffer_read_sz - _eeprom_address); } else { // Done, close session _current_op = nfc_eeprom_read_end_session; _operation_result = NFC_OK; _driver->end_session(); } } void NFCEEPROM::handle_error(nfc_err_t ret) { // Save & reset current op nfc_eeprom_operation_t last_op = _current_op; _current_op = nfc_eeprom_idle; if (_delegate != NULL) { if (last_op <= nfc_eeprom_write_end_session) { _delegate->on_ndef_message_written(ret); } else if (last_op <= nfc_eeprom_read_end_session) { _delegate->on_ndef_message_read(ret); } else if (last_op <= nfc_eeprom_erase_end_session) { _delegate->on_ndef_message_erased(ret); } } } NFCNDEFCapable::Delegate *NFCEEPROM::ndef_capable_delegate() { return _delegate; }