From aa1e9acdb1c31e2cff55ecd57d34db8b88ff6fd1 Mon Sep 17 00:00:00 2001 From: Conrad Braam Date: Thu, 3 Jan 2019 17:50:05 +0000 Subject: [PATCH 01/14] Simple commit wo history, review rework done, internal docs removed --- TEST_APPS/device/nfcapp/SmartPoster.cpp | 220 ++++++++++++ TEST_APPS/device/nfcapp/SmartPoster.h | 139 ++++++++ TEST_APPS/device/nfcapp/main.cpp | 150 ++++++++ TEST_APPS/device/nfcapp/mbed_app.json | 16 + TEST_APPS/device/nfcapp/nfccommands.cpp | 231 +++++++++++++ TEST_APPS/device/nfcapp/nfccommands.h | 80 +++++ TEST_APPS/device/nfcapp/nfcprocess.h | 118 +++++++ TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp | 183 ++++++++++ TEST_APPS/device/nfcapp/nfcprocessCtrl.h | 96 ++++++ TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp | 132 ++++++++ TEST_APPS/device/nfcapp/nfcprocessEeprom.h | 70 ++++ TEST_APPS/device/nfcapp/nfctestshim.cpp | 295 ++++++++++++++++ TEST_APPS/device/nfcapp/nfctestshim.h | 128 +++++++ TEST_APPS/device/nfcapp/uart.cpp | 43 +++ TEST_APPS/icetea_plugins/nfc_test_parsers.py | 75 ++++ TEST_APPS/icetea_plugins/plugins_to_load.py | 5 +- TEST_APPS/testcases/nfc/README.md | 319 ++++++++++++++++++ TEST_APPS/testcases/nfc/__init__.py | 0 .../testcases/nfc/img/creamscone-mobile.png | Bin 0 -> 35996 bytes TEST_APPS/testcases/nfc/img/inter-op-view.png | Bin 0 -> 28534 bytes .../testcases/nfc/img/simple-overview.png | Bin 0 -> 38235 bytes TEST_APPS/testcases/nfc/mobileapi.md | 94 ++++++ TEST_APPS/testcases/nfc/nfc_clf_wrapper.py | 133 ++++++++ TEST_APPS/testcases/nfc/nfc_cli_helper.py | 70 ++++ TEST_APPS/testcases/nfc/nfc_messages.py | 72 ++++ TEST_APPS/testcases/nfc/test_nfc.py | 293 ++++++++++++++++ TEST_APPS/testcases/nfc/test_self.py | 188 +++++++++++ 27 files changed, 3149 insertions(+), 1 deletion(-) create mode 100644 TEST_APPS/device/nfcapp/SmartPoster.cpp create mode 100644 TEST_APPS/device/nfcapp/SmartPoster.h create mode 100644 TEST_APPS/device/nfcapp/main.cpp create mode 100644 TEST_APPS/device/nfcapp/mbed_app.json create mode 100644 TEST_APPS/device/nfcapp/nfccommands.cpp create mode 100644 TEST_APPS/device/nfcapp/nfccommands.h create mode 100644 TEST_APPS/device/nfcapp/nfcprocess.h create mode 100644 TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp create mode 100644 TEST_APPS/device/nfcapp/nfcprocessCtrl.h create mode 100644 TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp create mode 100644 TEST_APPS/device/nfcapp/nfcprocessEeprom.h create mode 100644 TEST_APPS/device/nfcapp/nfctestshim.cpp create mode 100644 TEST_APPS/device/nfcapp/nfctestshim.h create mode 100644 TEST_APPS/device/nfcapp/uart.cpp create mode 100644 TEST_APPS/icetea_plugins/nfc_test_parsers.py create mode 100644 TEST_APPS/testcases/nfc/README.md create mode 100644 TEST_APPS/testcases/nfc/__init__.py create mode 100644 TEST_APPS/testcases/nfc/img/creamscone-mobile.png create mode 100644 TEST_APPS/testcases/nfc/img/inter-op-view.png create mode 100644 TEST_APPS/testcases/nfc/img/simple-overview.png create mode 100644 TEST_APPS/testcases/nfc/mobileapi.md create mode 100644 TEST_APPS/testcases/nfc/nfc_clf_wrapper.py create mode 100644 TEST_APPS/testcases/nfc/nfc_cli_helper.py create mode 100644 TEST_APPS/testcases/nfc/nfc_messages.py create mode 100644 TEST_APPS/testcases/nfc/test_nfc.py create mode 100644 TEST_APPS/testcases/nfc/test_self.py diff --git a/TEST_APPS/device/nfcapp/SmartPoster.cpp b/TEST_APPS/device/nfcapp/SmartPoster.cpp new file mode 100644 index 0000000000..5fb7f3ccb0 --- /dev/null +++ b/TEST_APPS/device/nfcapp/SmartPoster.cpp @@ -0,0 +1,220 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2018 ARM Limited + * + * 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 "SmartPoster.h" + +#include "nfc/ndef/common/Text.h" +#include "nfc/ndef/common/URI.h" +#include "nfc/ndef/common/Mime.h" +#include "nfc/ndef/MessageBuilder.h" +#include "nfc/ndef/common/util.h" + +using mbed::Span; + +using mbed::nfc::ndef::MessageBuilder; +using mbed::nfc::ndef::RecordType; +using mbed::nfc::ndef::Record; +using mbed::nfc::ndef::RecordID; +using mbed::nfc::ndef::RecordPayload; +using mbed::nfc::ndef::common::span_from_cstr; +using mbed::nfc::ndef::common::Mime; +using mbed::nfc::ndef::common::Text; +using mbed::nfc::ndef::common::URI; + +// todo: this class probably needs to be in the nfc module itself + +namespace { +static RecordType smart_poster_record_type() { + return RecordType(RecordType::well_known_type, span_from_cstr("Sp")); +} + +static RecordType action_record_type() { + return RecordType(RecordType::well_known_type, span_from_cstr("act")); +} + +static RecordType size_record_type() { + return RecordType(RecordType::well_known_type, span_from_cstr("s")); +} + +static RecordType type_record_type() { + return RecordType(RecordType::well_known_type, span_from_cstr("T")); +} + +static size_t compute_record_size(const RecordType& type, + const RecordPayload& payload) { + return MessageBuilder::compute_record_size( + Record(type, payload, RecordID(), false, false)); +} + +} // end of anonymous namespace + +SmartPoster::SmartPoster(const URI &uri) : + _uri(uri), _action(), _resource_size(0), _action_set(false), _resource_size_set( + false) { +} + +void SmartPoster::set_title(const Text &text) { + _title = text; +} + +void SmartPoster::set_icon(const Mime &icon) { + _icon = icon; +} + +void SmartPoster::set_action(action_t action) { + _action = action; + _action_set = true; +} + +void SmartPoster::set_resource_size(uint32_t size) { + _resource_size = size; + _resource_size_set = true; +} + +void SmartPoster::set_resource_type(Span &type) { + _type.set_text(Text::UTF8, Span(), type); +} + +bool SmartPoster::append_record(MessageBuilder &ndef_builder, + bool is_last_record) const { + if (_uri.get_uri_field().empty()) { + return false; + } + + struct PayloadBuilder: MessageBuilder::PayloadBuilder { + PayloadBuilder(const SmartPoster &sp) : + sp(sp) { + } + + virtual size_t size() const { + return sp.get_uri_record_size() + sp.get_title_record_size() + + sp.get_icon_record_size() + sp.get_action_record_size() + + sp.get_resource_size_record_size() + + sp.get_type_record_size(); + } + + virtual void build(const Span &buffer) const { + MessageBuilder smart_poster_builder(buffer); + sp.append_title(smart_poster_builder); + sp.append_icon(smart_poster_builder); + sp.append_resource_size(smart_poster_builder); + sp.append_type(smart_poster_builder); + sp.append_action(smart_poster_builder); + sp.append_uri(smart_poster_builder); + } + + const SmartPoster &sp; + }; + + bool result = ndef_builder.append_record(smart_poster_record_type(), + PayloadBuilder(*this), is_last_record); + return result; +} + +void SmartPoster::append_uri(MessageBuilder& builder) const { + _uri.append_as_record(builder, true); +} + +size_t SmartPoster::get_uri_record_size() const { + return _uri.get_record_size(); +} + +void SmartPoster::append_title(MessageBuilder& builder) const { + if (_title.get_text().empty()) { + return; + } + _title.append_as_record(builder); +} + +size_t SmartPoster::get_title_record_size() const { + if (_title.get_text().empty()) { + return 0; + } + + return _title.get_record_size(); +} + +void SmartPoster::append_icon(MessageBuilder& builder) const { + if (_icon.get_mime_content().empty()) { + return; + } + _icon.append_as_record(builder); +} + +size_t SmartPoster::get_icon_record_size() const { + if (_icon.get_mime_content().empty()) { + return 0; + } + + return _icon.get_record_size(); +} + +void SmartPoster::append_action(MessageBuilder& builder) const { + if (!_action_set) { + return; + } + + const uint8_t action_value[1] = { _action }; + builder.append_record(action_record_type(), action_value); +} + +size_t SmartPoster::get_action_record_size() const { + if (!_action_set) { + return 0; + } + + const uint8_t action_value[1] = { _action }; + + return compute_record_size(action_record_type(), action_value); +} + +void SmartPoster::append_resource_size(MessageBuilder& builder) const { + if (!_resource_size_set) { + return; + } + + uint8_t value[4]; + std::reverse_copy(&_resource_size, &_resource_size + 4, value); + + builder.append_record(size_record_type(), value); +} + +size_t SmartPoster::get_resource_size_record_size() const { + if (!_resource_size_set) { + return 0; + } + + uint8_t value[4]; + + return compute_record_size(size_record_type(), value); +} + +void SmartPoster::append_type(MessageBuilder& builder) const { + if (_type.get_text().empty()) { + return; + } + + builder.append_record(type_record_type(), _type.get_text()); +} + +size_t SmartPoster::get_type_record_size() const { + if (_type.get_text().empty()) { + return 0; + } + + return compute_record_size(type_record_type(), _type.get_text()); +} + diff --git a/TEST_APPS/device/nfcapp/SmartPoster.h b/TEST_APPS/device/nfcapp/SmartPoster.h new file mode 100644 index 0000000000..bba5838545 --- /dev/null +++ b/TEST_APPS/device/nfcapp/SmartPoster.h @@ -0,0 +1,139 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2018 ARM Limited + * + * 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. + */ + +#ifndef SMARTPOSTER_H_ +#define SMARTPOSTER_H_ + +#include "nfc/ndef/common/Text.h" +#include "nfc/ndef/common/URI.h" +#include "nfc/ndef/common/Mime.h" +#include "nfc/ndef/MessageBuilder.h" + +/** + * Smart poster object. + * + * A smart poster is one of the basic use case of NFC. It encapsulates a URI to + * a resource and meta-data of the resource. + * + * Meta-data are optional, they can be: + * - title: name of the resource + * - icon: image/media associated to the resource + * - action: Action the peer should execute upon reception of the smart poster + * - size: The size of the resource. + * - type: Mime type of the resource. + * + * @note It obeys to value semantic and can be copied around. + */ +class SmartPoster { +public: + typedef mbed::nfc::ndef::common::Mime Mime; + typedef mbed::nfc::ndef::common::Text Text; + typedef mbed::nfc::ndef::common::URI URI; + typedef mbed::nfc::ndef::MessageBuilder MessageBuilder; + + /** + * Type of actions that should be executed upon smart poster reception. + */ + enum action_t { + EXECUTE, //!< EXECUTE + SAVE, //!< SAVE + EDIT //!< EDIT + }; + + /** + * Construct a smart poster. + * + * @param uri The URI to the resource. + */ + SmartPoster(const URI &uri); + + /** + * Set the title of the resource. + * + * @param text The title of the resource to set. + */ + void set_title(const Text &text); + + /** + * Set the icon of the resource. + * + * @param icon The icon to set. + */ + void set_icon(const Mime &icon); + + /** + * Set the action to trigger upon smart poster reception. + * + * @param action The action to do upon reception. + */ + void set_action(action_t action); + + /** + * Set the size of the resource. + * + * @param size The size of the resource. + */ + void set_resource_size(uint32_t size); + + /** + * Set the type of the resource. + * + * @param resource_type The type of the resource pointed by the URI. + */ + void set_resource_type(mbed::Span &resource_type); + + /** + * Append the smart poster as a ndef record. + * + * @param ndef_builder The message builder where the record is appended. + * @param is_last_record Indicates if this message is the last one. + * + * @return true if the message has been appended to the builder or false + * otherwise. + */ + bool append_record(MessageBuilder &ndef_builder, bool is_last_record) const; + +private: + void append_uri(MessageBuilder &builder) const; + size_t get_uri_record_size() const; + + void append_title(MessageBuilder &builder) const; + size_t get_title_record_size() const; + + void append_icon(MessageBuilder &builder) const; + size_t get_icon_record_size() const; + + void append_action(MessageBuilder &builder) const; + size_t get_action_record_size() const; + + void append_resource_size(MessageBuilder &builder) const; + size_t get_resource_size_record_size() const; + + void append_type(MessageBuilder &builder) const; + size_t get_type_record_size() const; + + URI _uri; + Text _title; + Mime _icon; + action_t _action; + uint32_t _resource_size; + Text _type; + + bool _action_set :1; + bool _resource_size_set :1; +}; + +#endif /* SMARTPOSTER_H_ */ diff --git a/TEST_APPS/device/nfcapp/main.cpp b/TEST_APPS/device/nfcapp/main.cpp new file mode 100644 index 0000000000..4398b0145c --- /dev/null +++ b/TEST_APPS/device/nfcapp/main.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2018 ARM Limited. All rights reserved. + * 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 +#include +#include +#include +#include +#include +#include "mbed.h" +#include "mbed_events.h" +#include "mbed-client-cli/ns_cmdline.h" +#include "NFCEEPROMDriver.h" +#include "nfctestshim.h" +#include "nfccommands.h" +#include "smartposter.h" + +#if MBED_CONF_NFCEEPROM +using mbed::nfc::NFCEEPROM; +using mbed::nfc::NFCEEPROMDriver; +#else +#ifndef TARGET_PN512 +#warning [NOT_SUPPORTED] NFC not supported for this target +#endif + +#include "nfc/controllers/PN512Driver.h" +#include "nfc/controllers/PN512SPITransportDriver.h" + +#include "nfc/NFCRemoteInitiator.h" +#include "nfc/NFCController.h" + +using mbed::nfc::NFCRemoteInitiator; +using mbed::nfc::NFCController; +using mbed::nfc::nfc_rf_protocols_bitmask_t; +#endif // MBED_CONF_NFCEEPROM + +using mbed::Span; +using mbed::nfc::ndef::MessageBuilder; +using mbed::nfc::ndef::common::Text; +using mbed::nfc::ndef::common::URI; +using mbed::nfc::ndef::common::span_from_cstr; + + +void wrap_printf(const char *f, va_list a) { + vprintf(f, a); +} + +const char *errorcodes = // descriptions from nfc/stack/nfc_errors.h + " 0 NFC_OK \n" + " 1 NFC_ERR_UNKNOWN\n" + " 2 NFC_ERR_LENGTH \n" + " 3 NFC_ERR_NOT_FOUND\n" + " 4 NFC_ERR_UNSUPPORTED\n" + " 5 NFC_ERR_PARAMS \n" + " 6 NFC_ERR_BUFFER_TOO_SMALL\n" + " 7 NFC_ERR_TIMEOUT\n" + " 8 NFC_ERR_CRC\n" + " 9 NFC_ERR_NOPEER \n" + "10 NFC_ERR_PARITY \n" + "11 NFC_ERR_FIELD\n" + "12 NFC_ERR_COLLISION\n" + "13 NFC_ERR_WRONG_COMM \n" + "14 NFC_ERR_PROTOCOL \n" + "15 NFC_ERR_BUSY \n" + "16 NFC_ERR_CONTROLLER \n" + "17 NFC_ERR_HALTED \n" + "18 NFC_ERR_MAC\n" + "19 NFC_ERR_UNDERFLOW\n" + "20 NFC_ERR_DISCONNECTED \n" + "21 NFC_ERR_ABORTED\n"; + +// for easy manual UI interaction +int seteasy(int argc, char *argv[]) { + const char msg[][20] = + { "echo off", "set --retcode true", "set --vt100 off" }; + for (size_t i = 0; i < (sizeof(msg) / sizeof(msg[0])); i++) { + cmd_exe((char*) msg[i]); + } + return (CMDLINE_RETCODE_SUCCESS); +} + +int main(int argc, char *argv[]) { + cmd_init(&wrap_printf); + cmd_add("getlastnfcerror", HandleTestCommand::cmd_get_last_nfc_error, + "last NFC error code", errorcodes); + cmd_add("setlastnfcerror", HandleTestCommand::cmd_set_last_nfc_error, + "self-test", "for self-test only"); + cmd_add("initnfc", HandleTestCommand::cmd_init_nfc, "init NFC driver", + "call first"); + cmd_add("init", HandleTestCommand::cmd_init_nfc, "alias initnfc", + "call first"); + cmd_add("setsmartposter", HandleTestCommand::cmd_set_smartposter, + "send smartposter NDEF", ""); + cmd_add("iseeprom", HandleTestCommand::cmd_get_conf_nfceeprom, + "get NFC configEEPROM present", + "true if config exists, else false"); + cmd_add("readmessage", HandleTestCommand::cmd_read_message, + "read EEPROM else return last message", "returns hex dump"); + cmd_add("read", HandleTestCommand::cmd_read_message, "alias readmessage", + "returns hex dump"); + cmd_add("erase", HandleTestCommand::cmd_erase, + "erase EEPROM or clear last message", "erase entire flash/buffer"); + cmd_add("writelong", HandleTestCommand::cmd_write_long_ndef_message, + "fill T NDEF with pattern up to ", + "writelong [messagetorepeat='thequickbrownfoxisadog']"); + cmd_add("start", HandleTestCommand::cmd_start_discovery, + "start discovery [auto=autorestart|man=manual]", + "loop restarts by default, man to disable"); + cmd_add("stop", HandleTestCommand::cmd_stop_discovery, "stop discovery", + "[wait=0] with optional wait for session end"); + + cmd_add("getprotocols", HandleTestCommand::cmd_get_supported_rf_protocols, + "get supported protocols", "returns CSV list, see setprotocols"); + cmd_add("setprotocols", HandleTestCommand::cmd_configure_rf_protocols, + "set rf protocols", "-p [t1t]/[t2t]/[t3t]/[isodep]/[nfcdep]/[t5t]"); + cmd_add("easy", seteasy, "Use human readable terminal output", + "echo off,vt100 off,return-codes visible"); + +#if MBED_CONF_NFCEEPROM + cmd_printf("MBED NFC EEPROM defined\r\n"); +#else + cmd_printf("MBED NFC Controller tests\r\n"); +#endif + +#ifdef TARGET_M24SR + cmd_printf("Using driver:M24SR\r\n"); +#endif +#ifdef TARGET_PN512 + cmd_printf("Using driver:PN512\r\n"); +#endif + + int c; + HandleTestCommand handleCommands; // starts handling nfc messages + while ((c = getc(stdin)) != EOF) { + cmd_char_input(c); + } + return 0; +} diff --git a/TEST_APPS/device/nfcapp/mbed_app.json b/TEST_APPS/device/nfcapp/mbed_app.json new file mode 100644 index 0000000000..1629135ab4 --- /dev/null +++ b/TEST_APPS/device/nfcapp/mbed_app.json @@ -0,0 +1,16 @@ +{ + "target_overrides": { + "DISCO_L475VG_IOT01A": { + "target.extra_labels_add": ["M24SR"], + "MBED_NFC_M24SR.nfceeprom": true + }, + "NUCLEO_F401RE": { + "target.extra_labels_add": ["PN512"] + }, + "NUCLEO_F746ZG": { + "target.extra_labels_add": ["M24SR"], + "MBED_NFC_M24SR.X_NUCLEO_NFC01A1": true, + "MBED_NFC_M24SR.nfceeprom": true + } + } +} diff --git a/TEST_APPS/device/nfcapp/nfccommands.cpp b/TEST_APPS/device/nfcapp/nfccommands.cpp new file mode 100644 index 0000000000..2462b40c71 --- /dev/null +++ b/TEST_APPS/device/nfcapp/nfccommands.cpp @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2018 ARM Limited. All rights reserved. + * 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 +#include +#include +#include "mbed.h" +#include "mbed_events.h" +#include "mbed-client-cli/ns_cmdline.h" +#include "rtos\Thread.h" + +#include "nfctestshim.h" + +#include "nfccommands.h" + +events::EventQueue nfcQueue; +Thread nfcThread; +NFCTestShim * pNFC_Test_Shim = NULL; + +NFCTestShim* new_testshim() { +#if MBED_CONF_NFCEEPROM + mbed::nfc::NFCEEPROMDriver& eeprom_driver = get_eeprom_driver(nfcQueue); + + return ( (NFCTestShim *)(new NFCProcessEEPROM(nfcQueue, eeprom_driver)) ); +#else + return ((NFCTestShim *) (new NFCProcessController(nfcQueue))); +#endif // EEPROM + +} + +void nfcRoutine() { + nfcQueue.dispatch_forever(); +} + +HandleTestCommand::HandleTestCommand() { + osStatus status = nfcThread.start(callback(&nfcRoutine)); + MBED_ASSERT(status == osOK); +} + +int HandleTestCommand::cmd_set_last_nfc_error(int argc, char *argv[]) { + if (argc <= 1) { + cmd_printf("setlastnfcerror() invalid parameter(s)\r\n"); + return (CMDLINE_RETCODE_INVALID_PARAMETERS); + } else { + int value = strtol(argv[1], NULL, 10); + nfcQueue.call(NFCTestShim::cmd_set_last_nfc_error, value); + } + return (CMDLINE_RETCODE_EXCUTING_CONTINUE); +} + +int HandleTestCommand::cmd_init_nfc(int argc, char *argv[]) { + + if (pNFC_Test_Shim) { + cmd_printf("WARN init called again!\r\n"); // only legal here, if eeprom driver stops talking + } else { + pNFC_Test_Shim = new_testshim(); + } + nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_init); + return (CMDLINE_RETCODE_EXCUTING_CONTINUE); +} + +int HandleTestCommand::cmd_read_message(int argc, char *argv[]) { + nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_read_nfceeprom); + + return (CMDLINE_RETCODE_EXCUTING_CONTINUE); +} + +int HandleTestCommand::cmd_set_smartposter(int argc, char *argv[]) { + if (argc <= 1) { + cmd_printf("setlastnfcerror() invalid parameter(s)\r\n"); + return (CMDLINE_RETCODE_INVALID_PARAMETERS); + } else { + // parse arg and queue it up + char * uri = (char*) malloc(strlen(argv[1]) + 1); + if (uri) { + strcpy(uri, argv[1]); + nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_set_smartposter, + uri); // called thread must free + } + else { + cmd_printf("WARN out of memory!\r\n"); + return (CMDLINE_RETCODE_FAIL); + } + } + return (CMDLINE_RETCODE_EXCUTING_CONTINUE); +} + +// todo: jira IOTPAN-295 +int HandleTestCommand::cmd_erase(int argc, char *argv[]) { + nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_erase); + return (CMDLINE_RETCODE_EXCUTING_CONTINUE); +} + +int HandleTestCommand::cmd_write_long_ndef_message(int argc, char *argv[]) { + size_t length, idx, sourceLength; + static const char alphabet[] = "thequickbrownfoxjumpedoverthelazydog"; + char *data; + const char *sourceMessage; + + // expect 2 or 3 args " [optional-text]" + if (argc < 2) { + cmd_printf("supply length of message\r\n"); + return (CMDLINE_RETCODE_INVALID_PARAMETERS); + } + + int converted = sscanf(argv[1], "%d", &length); + if (1 != converted) { + cmd_printf("Cannot convert value to int\r\n"); + return (CMDLINE_RETCODE_INVALID_PARAMETERS); + } + data = (char*) malloc(length + 1); + if (!data) { + cmd_printf("WARN out of memory!\r\n"); + return (CMDLINE_RETCODE_FAIL); + } + if (argc > 2) { + // user provided text to copy into text NDEF record + sourceMessage = argv[2]; + } else { + // use our internal default message to copy into the text NDEF + sourceMessage = alphabet; + } + sourceLength = strlen(sourceMessage); + for (idx = 0; idx < length; idx++) { + data[idx] = sourceMessage[idx % sourceLength]; + } + data[length] = '\0'; + + // method must release buffer + nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_write_long, data); + return (CMDLINE_RETCODE_EXCUTING_CONTINUE); +} + +int HandleTestCommand::cmd_start_discovery(int argc, char *argv[]) { + if ((argc > 1) && (0 == strcmp(argv[1], "man"))) { + cmd_printf("User must restart discovery manually()\r\n"); + nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_start_discovery, false); + } else { + cmd_printf("App will restart discovery loop on auto()\r\n"); + nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_start_discovery, true); + } + return (CMDLINE_RETCODE_EXCUTING_CONTINUE); +} + +int HandleTestCommand::cmd_stop_discovery(int argc, char *argv[]) { + nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_stop_discovery); + return (CMDLINE_RETCODE_EXCUTING_CONTINUE); +} + +int HandleTestCommand::cmd_get_supported_rf_protocols(int argc, char *argv[]) { + nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_get_rf_protocols); + return (CMDLINE_RETCODE_EXCUTING_CONTINUE); +} + +bool HandleTestCommand::set_protocol_target( + nfc_rf_protocols_bitmask_t & bitmask, const char *protocolName) { + bool parsed = false; + if (0 == strcmp(protocolName, "t1t")) { + parsed = bitmask.target_t1t = true; + } + if (0 == strcmp(protocolName, "t2t")) { + parsed = bitmask.target_t2t = true; + } + if (0 == strcmp(protocolName, "t3t")) { + parsed = bitmask.target_t3t = true; + } + if (0 == strcmp(protocolName, "t5t")) { + parsed = bitmask.target_t5t = true; + } + if (0 == strcmp(protocolName, "isodep")) { + parsed = bitmask.target_iso_dep = true; + } + if (0 == strcmp(protocolName, "nfcdep")) { + parsed = bitmask.target_nfc_dep = true; + } + return (parsed); +} + +int HandleTestCommand::cmd_configure_rf_protocols(int argc, char *argv[]) { + nfc_rf_protocols_bitmask_t protocols = { 0 }; + + int argindex = argc; + while (argindex > 1) { + if (!set_protocol_target(protocols, argv[argindex - 1])) { + cmd_printf("Unknown protocol %s", argv[argindex - 1]); + return (CMDLINE_RETCODE_INVALID_PARAMETERS); + } + argindex--; + } + nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_configure_rf_protocols, + protocols); + return (CMDLINE_RETCODE_EXCUTING_CONTINUE); +} + +// todo: implement +int cmd_start_stop_discovery_wait_tag(int argc, char *argv[]) { + + return (CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED); +} + +///////////////////////////////////////////////////////////////////// +// boilerplate only + +int cmd_is_iso7816_supported(int argc, char *argv[]) { + return (CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED); +} + +int cmd_add_iso7816_application(int argc, char *argv[]) { + return (CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED); +} + +int cmd_set_tagtype(int argc, char *argv[]) { + return (CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED); +} + +int cmd_get_tagtype(int argc, char *argv[]) { + return (CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED); +} + diff --git a/TEST_APPS/device/nfcapp/nfccommands.h b/TEST_APPS/device/nfcapp/nfccommands.h new file mode 100644 index 0000000000..1b99f23335 --- /dev/null +++ b/TEST_APPS/device/nfcapp/nfccommands.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018 ARM Limited. All rights reserved. + * 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. + */ + +#ifndef _NFCCOMMANDS_H_INCLUDED +#define _NFCCOMMANDS_H_INCLUDED + +#if MBED_CONF_NFCEEPROM +#include "nfcProcessEeprom.h" +#else +#include "nfcProcessCtrl.h" +#endif + +extern events::EventQueue nfcQueue; + +// see https://support.microsoft.com/en-my/help/208427/maximum-url-length-is-2-083-characters-in-internet-explorer +#define MAX_URL_LENGTH 2000 + +class HandleTestCommand { +public: + // start thread and handle queue + HandleTestCommand(); + /* set corresponding mask bit on, return false if the supplied string cannot parse */ + static bool set_protocol_target(nfc_rf_protocols_bitmask_t & bitmask, const char *protocolName); + + /* return and clear the last result code. Type "help getlastnfcerror" for a list of error codes */ + static int cmd_get_last_nfc_error(int argc, char *argv[]) { + nfcQueue.call(NFCTestShim::cmd_get_last_nfc_error); + return (CMDLINE_RETCODE_EXCUTING_CONTINUE); + } + + /* internal function to test getlastnfcerror */ + static int cmd_set_last_nfc_error(int argc, char *argv[]); + + /* compile time flag */ + static int cmd_get_conf_nfceeprom(int argc, char *argv[]) { + nfcQueue.call(NFCTestShim::cmd_get_conf_nfceeprom); + return (CMDLINE_RETCODE_EXCUTING_CONTINUE); + } + + /* must be called before invoking any other calls */ + static int cmd_init_nfc(int argc, char *argv[]); + /* write a smartposter url, 'Sp' NDEF to the target */ + static int cmd_set_smartposter(int argc, char *argv[]); + /* erase EEPROM */ + static int cmd_erase(int argc, char *argv[]); + + // controller driver methods: + static int cmd_get_supported_rf_protocols(int argc, char *argv[]); + static int cmd_configure_rf_protocols(int argc, char *argv[]); + static int cmd_start_discovery(int argc, char *argv[]); + static int cmd_stop_discovery(int argc, char *argv[]); + + /* read raw EEPROM contents */ + static int cmd_read_message(int argc, char *argv[]); + /* write a text 'T' NDEF message to the target */ + static int cmd_write_long_ndef_message(int argc, char *argv[]); + +}; + + +// un-implemented or sparse support in drivers, so not covered +int cmd_is_iso7816_supported(int argc, char *argv[]); +int cmd_add_iso7816_application(int argc, char *argv[]); +int cmd_set_tagtype(int argc, char *argv[]); +int cmd_get_tagtype(int argc, char *argv[]); + +#endif // _NFCCOMMANDS_H_INCLUDED diff --git a/TEST_APPS/device/nfcapp/nfcprocess.h b/TEST_APPS/device/nfcapp/nfcprocess.h new file mode 100644 index 0000000000..834dd4d937 --- /dev/null +++ b/TEST_APPS/device/nfcapp/nfcprocess.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2018 ARM Limited. All rights reserved. + * 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. + */ + +#ifndef _NFCPROCESS_H_INCLUDED +#define _NFCPROCESS_H_INCLUDED + +#include +#include +#include +#include + +#include "nfc/ndef/MessageBuilder.h" +#include "nfc/ndef/common/URI.h" +#include "nfc/ndef/common/util.h" +#include "nfctestshim.h" + +#if MBED_CONF_NFCEEPROM + #include "NFCEEPROM.h" + #include "EEPROMDriver.h" +#else + #include "nfc/nfcdefinitions.h" + #ifdef TARGET_PN512 + #include "nfc/controllers/PN512Driver.h" + #include "nfc/controllers/PN512SPITransportDriver.h" + #endif + #include "nfc/NFCRemoteInitiator.h" + #include "nfc/NFCController.h" + #include "nfc/ndef/common/util.h" + +#endif // MBED_CONF_NFCEEPROM + +using mbed::nfc::ndef::MessageBuilder; +using mbed::nfc::ndef::common::URI; +using mbed::nfc::ndef::common::span_from_cstr; +using mbed::Span; + + +#if MBED_CONF_NFCEEPROM + + using mbed::nfc::NFCEEPROM; + using mbed::nfc::NFCEEPROMDriver; + +class NFCProcessEEPROM : NFCTestShim , mbed::nfc::NFCEEPROM::Delegate +{ +public: + NFCProcessEEPROM(events::EventQueue& queue, NFCEEPROMDriver& eeprom_driver) ; + nfc_err_t init(); + void queue_write_call(); + void queue_write_long_call(); + void queue_read_call(); + void queue_erase_call(); + +private: + virtual void on_ndef_message_written(nfc_err_t result); + virtual void on_ndef_message_read(nfc_err_t result); + virtual void parse_ndef_message(const Span &buffer); + virtual size_t build_ndef_message(const Span &buffer); + virtual void on_ndef_message_erased(nfc_err_t result); +private: + uint8_t _ndef_buffer[0x2000]; // if this buffer is smaller than the EEPROM, the driver may crash see IOTPAN-297 + NFCEEPROM _eeprom; + EventQueue& _queue; +}; + +#else // NFC Controller + +class NFCProcessController : NFCTestShim, NFCRemoteInitiator::Delegate, NFCController::Delegate { +public: + NFCProcessController(events::EventQueue &queue); + + nfc_err_t init(); + nfc_err_t start_discovery(); + nfc_err_t stop_discovery(); + void set_discovery_restart_auto() {_discovery_restart = true;}; + void set_discovery_restart_manual(){_discovery_restart = false;}; + nfc_rf_protocols_bitmask_t get_rf_protocols(); + nfc_err_t set_rf_protocols(nfc_rf_protocols_bitmask_t protocols); + + virtual void parse_ndef_message(const Span &buffer); + virtual size_t build_ndef_message(const Span &buffer); + const char *str_discovery_terminated_reason(nfc_discovery_terminated_reason_t reason); + + +private: + // these events are handled, to restart discovery + virtual void on_connected(); + virtual void on_disconnected(); + virtual void on_discovery_terminated(nfc_discovery_terminated_reason_t reason); + virtual void on_nfc_initiator_discovered(const SharedPtr &nfc_initiator); + +private: + bool _discovery_restart; + uint8_t _ndef_buffer[1024]; + mbed::nfc::PN512SPITransportDriver _pn512_transport; + mbed::nfc::PN512Driver _pn512_driver; +protected: + EventQueue& _queue; +private: + NFCController _nfc_controller; + SharedPtr _nfc_remote_initiator; +}; +#endif // TARGET_M24SR / else TARGET_PN512 + + +#endif // _NFCPROCESS_H_INCLUDED diff --git a/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp b/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp new file mode 100644 index 0000000000..3b1dfa0ae8 --- /dev/null +++ b/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2018 ARM Limited. All rights reserved. + * 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 +#include +#include +#include +#include "mbed.h" +#include "mbed_events.h" +#include "mbed-client-cli/ns_cmdline.h" + +#include "nfc/stack/nfc_errors.h" + +#include "nfc/ndef/common/Text.h" +#include "nfc/ndef/common/URI.h" +#include "nfc/ndef/common/Mime.h" +#include "nfc/ndef/MessageBuilder.h" +#include "nfc/ndef/common/util.h" + + +#include "nfcProcessCtrl.h" +#include "SmartPoster.h" + +using mbed::Span; +#if ! MBED_CONF_NFCEEPROM +using mbed::nfc::nfc_rf_protocols_bitmask_t; +using mbed::nfc::ndef::MessageBuilder; +using mbed::nfc::ndef::RecordType; +using mbed::nfc::ndef::Record; +using mbed::nfc::ndef::RecordID; +using mbed::nfc::ndef::RecordPayload; +using mbed::nfc::ndef::common::span_from_cstr; +using mbed::nfc::ndef::common::Mime; +using mbed::nfc::ndef::common::Text; +using mbed::nfc::ndef::common::URI; +using mbed::nfc::NFCController; + +//class NFCProcessController : NFCRemoteInitiator::Delegate, NFCController::Delegate { + +NFCProcessController::NFCProcessController(events::EventQueue &queue) : + // pins: mosi, miso, sclk, ssel, irq, rst + _pn512_transport(D11, D12, D13, D10, A1, A0), _pn512_driver( + &_pn512_transport), _queue(queue), _nfc_controller( + &_pn512_driver, &queue, _ndef_buffer) { +} + +/** + * Initialise and configure the NFC controller. + * + * @return NFC_OK in case of success or a meaningful error code in case of + * failure. + */ +nfc_err_t NFCProcessController::init() { + cmd_printf("init()\r\n"); + + // register callbacks + _nfc_controller.set_delegate(this); + return _nfc_controller.initialize(); +} + +/** + * Start the discovery of peers. + * + * @return NFC_OK in case of success or a meaningful error code in case of + * failure. + */ +nfc_err_t NFCProcessController::start_discovery() { + cmd_printf("start_discovery()\r\n"); + + return _nfc_controller.start_discovery(); +} + +/** + * Stop discovery. + * + * @return NFC_OK in case of success or a meaningful error code in case of + * failure. + */ +nfc_err_t NFCProcessController::stop_discovery() { + cmd_printf("stop_discovery()\r\n"); + return _nfc_controller.cancel_discovery(); +} + +nfc_rf_protocols_bitmask_t NFCProcessController::get_rf_protocols() { + cmd_printf("get_supported_rf_protocols()\r\n"); + return _nfc_controller.get_supported_rf_protocols(); +} + +nfc_err_t NFCProcessController::set_rf_protocols( + nfc_rf_protocols_bitmask_t protocols) { + cmd_printf("configure_rf_protocols()\r\n"); + + return _nfc_controller.configure_rf_protocols(protocols); +} + +/* ------------------------------------------------------------------------ + * Implementation of NFCRemoteInitiator::Delegate + */ +void NFCProcessController::on_connected() { + cmd_printf("on_connected()\r\n"); +} + +void NFCProcessController::on_disconnected() { + cmd_printf("on_disconnected()\r\n"); + + // reset the state of the remote initiator + _nfc_remote_initiator->set_delegate(NULL); + _nfc_remote_initiator.reset(); + + // restart peer discovery + _nfc_controller.start_discovery(); +} + +void NFCProcessController::parse_ndef_message( + const Span &buffer) { + size_t len = buffer.size(); + // copy remotely written message into our dummy buffer + if (len <= sizeof(_ndef_write_buffer)) { + cmd_printf("Store remote ndef message of size %d\r\n", len); + memcpy(_ndef_write_buffer, buffer.data(), len); + _ndef_write_buffer_used = len; + } else { + cmd_printf("Remote ndef message of size %d too large!\r\n", len); + } +} + +size_t NFCProcessController::build_ndef_message(const Span &buffer) { + cmd_printf("Copying message %d bytes to query buffer\r\n", + _ndef_write_buffer_used); + memcpy(buffer.data(), _ndef_write_buffer, _ndef_write_buffer_used); + for (size_t k = 0; k < _ndef_write_buffer_used; k++) { + cmd_printf("%02x ", buffer[k]); + } + return _ndef_write_buffer_used; +} + +const char *NFCProcessController::str_discovery_terminated_reason( + nfc_discovery_terminated_reason_t reason) { + static const char* reasons[4] = { "completed", "cancelled", "rf error"}; + switch (reason) { + case nfc_discovery_terminated_completed : + case nfc_discovery_terminated_canceled: + case nfc_discovery_terminated_rf_error: + return reasons[reason]; + } + return "unexpected!"; +} + + +void NFCProcessController::on_discovery_terminated( + nfc_discovery_terminated_reason_t reason) { + cmd_printf("on_discovery_terminated(%s)\r\n", + str_discovery_terminated_reason(reason)); + if (reason != nfc_discovery_terminated_completed + && this->_discovery_restart) { + start_discovery(); + } +} + +void NFCProcessController::on_nfc_initiator_discovered( + const SharedPtr &nfc_initiator) { + cmd_printf("on_nfc_initiator_discovered()\r\n"); + + // setup the local remote initiator + _nfc_remote_initiator = nfc_initiator; + _nfc_remote_initiator->set_delegate(this); + _nfc_remote_initiator->connect(); +} + +#endif // #if ! MBED_CONF_NFCEEPROM + diff --git a/TEST_APPS/device/nfcapp/nfcprocessCtrl.h b/TEST_APPS/device/nfcapp/nfcprocessCtrl.h new file mode 100644 index 0000000000..44b6c6326d --- /dev/null +++ b/TEST_APPS/device/nfcapp/nfcprocessCtrl.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2018 ARM Limited. All rights reserved. + * 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. + */ + +#ifndef _NFCPROCESS_H_INCLUDED +#define _NFCPROCESS_H_INCLUDED + +#include +#include +#include +#include + +#include "nfc/ndef/MessageBuilder.h" +#include "nfc/ndef/common/URI.h" +#include "nfc/ndef/common/util.h" +#include "nfctestshim.h" + +#if !MBED_CONF_NFCEEPROM + +#include "nfc/nfcdefinitions.h" +#ifdef TARGET_PN512 +#include "nfc/controllers/PN512Driver.h" +#include "nfc/controllers/PN512SPITransportDriver.h" +#endif +#include "nfc/NFCRemoteInitiator.h" +#include "nfc/NFCController.h" +#include "nfc/ndef/common/util.h" + + +using mbed::nfc::ndef::MessageBuilder; +using mbed::nfc::ndef::common::URI; +using mbed::nfc::ndef::common::span_from_cstr; +using mbed::Span; + + + +class NFCProcessController: NFCTestShim, + NFCRemoteInitiator::Delegate, + NFCController::Delegate { +public: + NFCProcessController(events::EventQueue &queue); + + nfc_err_t init(); + nfc_err_t start_discovery(); + nfc_err_t stop_discovery(); + nfc_rf_protocols_bitmask_t get_rf_protocols(); + nfc_err_t set_rf_protocols(nfc_rf_protocols_bitmask_t protocols); + + virtual void parse_ndef_message(const Span &buffer); + virtual size_t build_ndef_message(const Span &buffer); + const char *str_discovery_terminated_reason( + nfc_discovery_terminated_reason_t reason); + +private: + // these events are handled, to restart discovery + /** + * Implementation of NFCRemoteEndpoint::Delegate */ + virtual void on_connected(); + /** + * Implementation of NFCRemoteEndpoint::Delegate */ + virtual void on_disconnected(); + /** + * Implementation of NFCController::Delegate */ + virtual void on_discovery_terminated( + nfc_discovery_terminated_reason_t reason); + /** + * Implementation of NFCController::Delegate */ + virtual void on_nfc_initiator_discovered( + const SharedPtr &nfc_initiator); + +private: + + + mbed::nfc::PN512SPITransportDriver _pn512_transport; + mbed::nfc::PN512Driver _pn512_driver; +protected: + EventQueue& _queue; +private: + NFCController _nfc_controller; + SharedPtr _nfc_remote_initiator; +}; +#endif // Controller + +#endif // _NFCPROCESS_H_INCLUDED diff --git a/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp b/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp new file mode 100644 index 0000000000..31db64eca2 --- /dev/null +++ b/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2018 ARM Limited. All rights reserved. + * 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 +#include +#include +#include +#include "mbed.h" +#include "mbed_events.h" +#include "mbed-client-cli/ns_cmdline.h" + +#include "nfc/stack/nfc_errors.h" +#include "nfc/ndef/common/Text.h" +#include "nfc/ndef/common/URI.h" +#include "nfc/ndef/common/Mime.h" +#include "nfc/ndef/MessageBuilder.h" +#include "nfc/ndef/common/util.h" +#if MBED_CONF_NFCEEPROM +#include "NFCEEPROMDriver.h" + +#include "nfcProcessEeprom.h" +#include "SmartPoster.h" + +using mbed::Span; +using mbed::nfc::ndef::MessageBuilder; +using mbed::nfc::ndef::RecordType; +using mbed::nfc::ndef::Record; +using mbed::nfc::ndef::RecordID; +using mbed::nfc::ndef::RecordPayload; +using mbed::nfc::ndef::common::span_from_cstr; +using mbed::nfc::ndef::common::Mime; +using mbed::nfc::ndef::common::Text; +using mbed::nfc::ndef::common::URI; + +// implements : mbed::nfc::NFCEEPROM::Delegate +NFCProcessEEPROM::NFCProcessEEPROM(events::EventQueue& queue, NFCEEPROMDriver& eeprom_driver) : +_eeprom(&eeprom_driver, &queue, _ndef_buffer), +_queue(queue) +{} + +nfc_err_t NFCProcessEEPROM::init() { + nfc_err_t err = _eeprom.initialize(); + if (err != NFC_OK) { + cmd_printf("NFCProcessEEPROM::init() (error: %d)!\r\n", err); + _queue.break_dispatch(); + } else { + cmd_printf("NFCProcessEEPROM::init() OK\r\n"); + } + _eeprom.set_delegate(this); + return(err); +} + +void NFCProcessEEPROM::queue_write_call() { + cmd_printf("NFCProcessEEPROM::queue_write_call() entry\r\n"); + _queue.call(&_eeprom, &NFCEEPROM::write_ndef_message); +} + +void NFCProcessEEPROM::queue_read_call() { + cmd_printf("NFCProcessEEPROM::queue_read_call() entry\r\n"); + _queue.call(&_eeprom, &NFCEEPROM::read_ndef_message); +} + +void NFCProcessEEPROM::queue_erase_call() { + cmd_printf("NFCProcessEEPROM::queue_erase_call() entry\r\n"); + _queue.call(&_eeprom, &NFCEEPROM::erase_ndef_message); +} + +void NFCProcessEEPROM::on_ndef_message_written(nfc_err_t result) { + // todo: de-duplicate this code + set_last_nfc_error(result); + if (result == NFC_OK) { + cmd_printf("message written successfully\r\n"); + } else { + cmd_printf("Failed to write (error: %d)!\r\n", result); + } + // complete the async test method here + cmd_ready(CMDLINE_RETCODE_SUCCESS); +} + +void NFCProcessEEPROM::on_ndef_message_read(nfc_err_t result) { + set_last_nfc_error(result); + if (result == NFC_OK) { + cmd_printf("message read successfully\r\n"); + } else { + cmd_printf("Failed to read (error: %d)!\r\n", result); + } + // complete the async test method here + cmd_ready(CMDLINE_RETCODE_SUCCESS); +} + +void NFCProcessEEPROM::on_ndef_message_erased(nfc_err_t result) +{ + // todo : de-duplicate/template this callback handler + set_last_nfc_error(result); + if (result == NFC_OK) { + cmd_printf("message erased successfully\r\n"); + } else { + cmd_printf("Failed to erase (error: %d)!\r\n", result); + } + // complete the async test method here + cmd_ready(CMDLINE_RETCODE_SUCCESS); +} + +void NFCProcessEEPROM::parse_ndef_message(const Span &buffer) { + cmd_printf("Received an ndef message of size %d\r\n", buffer.size()); + print_ndef_message(buffer, buffer.size()); +} + +size_t NFCProcessEEPROM::build_ndef_message(const Span &buffer) { + cmd_printf("Copying ndef message %d bytes into buffer\r\n", _ndef_write_buffer_used); + // make a copy into our buffer + memcpy(buffer.data(), _ndef_write_buffer, _ndef_write_buffer_used); + for (size_t k=0; k<_ndef_write_buffer_used; k++ ) { + cmd_printf("%02x ", buffer[k] ); + } + return _ndef_write_buffer_used; +} + +#endif // MBED_CONF_NFCEEPROM + diff --git a/TEST_APPS/device/nfcapp/nfcprocessEeprom.h b/TEST_APPS/device/nfcapp/nfcprocessEeprom.h new file mode 100644 index 0000000000..d0ae7f616d --- /dev/null +++ b/TEST_APPS/device/nfcapp/nfcprocessEeprom.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018 ARM Limited. All rights reserved. + * 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. + */ + +#ifndef _NFCPROCESS_H_INCLUDED +#define _NFCPROCESS_H_INCLUDED + +#include +#include +#include +#include + +#include "nfc/ndef/MessageBuilder.h" +#include "nfc/ndef/common/URI.h" +#include "nfc/ndef/common/util.h" +#include "nfctestshim.h" + +#if MBED_CONF_NFCEEPROM +#include "NFCEEPROM.h" +#include "EEPROMDriver.h" + +#endif // MBED_CONF_NFCEEPROM + +using mbed::nfc::ndef::MessageBuilder; +using mbed::nfc::ndef::common::URI; +using mbed::nfc::ndef::common::span_from_cstr; +using mbed::Span; + +#if MBED_CONF_NFCEEPROM + +using mbed::nfc::NFCEEPROM; +using mbed::nfc::NFCEEPROMDriver; + +class NFCProcessEEPROM : NFCTestShim , mbed::nfc::NFCEEPROM::Delegate +{ +public: + NFCProcessEEPROM(events::EventQueue& queue, NFCEEPROMDriver& eeprom_driver); + nfc_err_t init(); + void queue_write_call(); + void queue_write_long_call(); + void queue_read_call(); + void queue_erase_call(); + +private: + virtual void on_ndef_message_written(nfc_err_t result); + virtual void on_ndef_message_read(nfc_err_t result); + virtual void parse_ndef_message(const Span &buffer); + virtual size_t build_ndef_message(const Span &buffer); + virtual void on_ndef_message_erased(nfc_err_t result); +private: + NFCEEPROM _eeprom; + EventQueue& _queue; +}; + +#endif // eeprom + + +#endif // _NFCPROCESS_H_INCLUDED diff --git a/TEST_APPS/device/nfcapp/nfctestshim.cpp b/TEST_APPS/device/nfcapp/nfctestshim.cpp new file mode 100644 index 0000000000..389eebceec --- /dev/null +++ b/TEST_APPS/device/nfcapp/nfctestshim.cpp @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2018 ARM Limited. All rights reserved. + * 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 +#include +#include +#include +#include "mbed.h" +#include "mbed-client-cli/ns_cmdline.h" +#include "nfc/ndef/common/Text.h" +#include "nfc/ndef/common/URI.h" +#include "nfc/ndef/common/Mime.h" +#include "nfc/ndef/MessageBuilder.h" +#include "nfc/ndef/common/util.h" +#include "nfc/nfcdefinitions.h" + +#include "NFCEEPROMDriver.h" +#include "nfcCommands.h" +#include "nfctestshim.h" +#include "SmartPoster.h" + +using mbed::Span; +using mbed::nfc::ndef::MessageBuilder; +using mbed::nfc::ndef::RecordType; +using mbed::nfc::ndef::common::span_from_cstr; +using mbed::nfc::ndef::common::Text; +using mbed::nfc::ndef::common::URI; +using mbed::nfc::nfc_rf_protocols_bitmask_t; + +// statics +char NFCTestShim::long_string[0x2000]; + +int NFCTestShim::last_nfc_error = 0; +#if MBED_CONF_NFCEEPROM +int NFCTestShim::using_eeprom = true; +#else +int NFCTestShim::using_eeprom = false; +#endif + +NFCTestShim::NFCTestShim() : + _ndef_write_buffer_used(0), ndef_poster_message(_ndef_write_buffer), _discovery_restart( + true) // on disconnect, will restart discovery +{ +} + +// The last failed NFC API call status, gets cleared upon reading it. +void NFCTestShim::get_last_nfc_error() { + int last = last_nfc_error; + last_nfc_error = 0; + // return data to the plugin framework + cmd_printf("{{lastnfcerror=%d}}\r\n", last); + cmd_ready(CMDLINE_RETCODE_SUCCESS); +} + +void NFCTestShim::set_last_nfc_error(int err) { + last_nfc_error = err; + cmd_printf("\r\n{{lastnfcerror=%d}}\r\n", last_nfc_error); +} + +// if an NFC EEPROM driver is configured +void NFCTestShim::get_conf_nfceeprom() { + set_last_nfc_error(NFC_OK); + cmd_printf("{{iseeprom=%s}}\r\n", (using_eeprom ? "true" : "false")); + cmd_ready(CMDLINE_RETCODE_SUCCESS); +} + +void NFCTestShim::print_ndef_message(const Span &buffer, + size_t length) { + cmd_printf("{{nfcmessage="); + for (size_t k = 0; k < length; k++) { + cmd_printf("%02x ", buffer.data()[k]); + } + cmd_printf("}}\r\n"); +} + +void NFCTestShim::cmd_init() { + nfc_err_t ret = init(); + set_last_nfc_error(ret); + + if (NFC_OK != ret) { + cmd_ready(CMDLINE_RETCODE_FAIL); + } else { + cmd_ready(CMDLINE_RETCODE_SUCCESS); + } +} + +void NFCTestShim::cmd_get_rf_protocols() { +#if MBED_CONF_NFCEEPROM + cmd_printf("EEPROM cannot get protocol()\r\n"); + set_last_nfc_error(NFC_ERR_UNSUPPORTED); + cmd_ready(CMDLINE_RETCODE_INVALID_PARAMETERS); + +#else + nfc_rf_protocols_bitmask_t protocols = + ((NFCProcessController*) this)->get_rf_protocols(); + protocols.target_t1t = true; + protocols.target_t2t = true; + protocols.target_t3t = true; + protocols.target_t5t = true; + protocols.target_nfc_dep = true; + static char strSupported[7 * 6 + 1] = ""; + if (protocols.target_t1t) { + strcat(strSupported, "t1t,"); + } + if (protocols.target_t2t) { + strcat(strSupported, "t2t,"); + } + if (protocols.target_t3t) { + strcat(strSupported, "t3t,"); + } + if (protocols.target_iso_dep) { + strcat(strSupported, "isodep,"); + } + if (protocols.target_nfc_dep) { + strcat(strSupported, "nfcdep,"); + } + if (protocols.target_t5t) { + strcat(strSupported, "t5t,"); + } + if (strlen(strSupported)) { + strSupported[strlen(strSupported) - 1] = '\0'; // strip trailing comma + } + cmd_printf("{{protocols=%s}}", strSupported); + set_last_nfc_error(NFC_OK); + cmd_ready(CMDLINE_RETCODE_SUCCESS); +#endif +} + +void NFCTestShim::cmd_configure_rf_protocols( + nfc_rf_protocols_bitmask_t protocols) { +#if MBED_CONF_NFCEEPROM + cmd_printf("EEPROM cannot set protocol()\r\n"); + set_last_nfc_error(NFC_ERR_UNSUPPORTED); + cmd_ready(CMDLINE_RETCODE_INVALID_PARAMETERS); +#else + + nfc_err_t err = ((NFCProcessController*) this)->set_rf_protocols(protocols); + set_last_nfc_error(err); + if (NFC_OK != err) { + cmd_ready(CMDLINE_RETCODE_FAIL); + } else { + cmd_ready(CMDLINE_RETCODE_SUCCESS); + } +#endif +} + +// RETURNS: ICETEA error code asynchronously NFC error is set +// {{bytes=XX XX XX XX.. }} are returned +void NFCTestShim::cmd_read_nfceeprom() { +#if MBED_CONF_NFCEEPROM + ((NFCProcessEEPROM*)this)->queue_read_call(); + cmd_printf("NFCTestShim::read_nfceeprom() exit\r\n"); + +#else + // returns last message "written", since we cannot read + print_ndef_message(_ndef_write_buffer, _ndef_write_buffer_used); + set_last_nfc_error(NFC_OK); + + cmd_printf("NFCTestShim::read_nfceeprom()\r\n"); + cmd_ready(CMDLINE_RETCODE_SUCCESS); +#endif +} + +void NFCTestShim::cmd_erase() { + +#if MBED_CONF_NFCEEPROM + ((NFCProcessEEPROM*)this)->queue_erase_call(); + +#else + cmd_printf("erase %d bytes, last msg\r\n", + (sizeof(_ndef_write_buffer) / sizeof(uint8_t))); + _ndef_write_buffer_used = 0; + memset(_ndef_write_buffer, 0, sizeof(_ndef_write_buffer) / sizeof(uint8_t)); + set_last_nfc_error(NFC_OK); // effectively a no-op + cmd_ready(CMDLINE_RETCODE_SUCCESS); +#endif +} + +// populate buffer with really long message - length checks to be done by driver only +void NFCTestShim::cmd_write_long(char *data) { + MessageBuilder builder(ndef_poster_message); + + strcpy(NFCTestShim::long_string, data); //max_ndef - header - overheads + Text text(Text::UTF8, span_from_cstr("en-US"), + span_from_cstr((const char*) (NFCTestShim::long_string))); + + text.append_as_record(builder, true); + _ndef_write_buffer_used = builder.get_message().size(); + cmd_printf("Composed NDEF message %d bytes\r\n", _ndef_write_buffer_used); + +#if MBED_CONF_NFCEEPROM + ((NFCProcessEEPROM*)this)->queue_write_call(); +#else + // not on a wire, so the caller will store the message in a buffer + set_last_nfc_error(NFC_OK); + cmd_ready(CMDLINE_RETCODE_SUCCESS); +#endif + + cmd_printf("NFCTestShim::write_long() exit\r\n"); + free(data); +} + +// PARAM: uri - this method must free the passed pointer +// RETURNS: ICETEA error code asynchronously NFC error is set +// An interesting side use case would be to prompt to install an app from the appstore using the tag +void NFCTestShim::cmd_set_smartposter(char *cmdUri) { + MessageBuilder builder(ndef_poster_message); + + uint8_t smart_poster_buffer[1024]; + MessageBuilder smart_poster_builder(smart_poster_buffer); + + char* urlbegin = strstr(cmdUri, "."); + urlbegin++; + URI uri(URI::HTTPS_WWW, span_from_cstr(urlbegin)); + uri.append_as_record(smart_poster_builder, true); + + builder.append_record( + RecordType(RecordType::well_known_type, span_from_cstr("Sp")), + smart_poster_builder.get_message(), true); + + _ndef_write_buffer_used = builder.get_message().size(); + cmd_printf("Composed NDEF message %d bytes\r\n", _ndef_write_buffer_used); + +#if MBED_CONF_NFCEEPROM + ((NFCProcessEEPROM*)this)->queue_write_call(); +#else + // not on a wire, so the call just stores the message in a buffer + set_last_nfc_error(NFC_OK); + cmd_ready(CMDLINE_RETCODE_SUCCESS); +#endif + cmd_printf("NFCTestShim::setsmartposter() exit\r\n"); + free(cmdUri); +} + +// disabled in EEPROMs, overridden if controller present +void NFCTestShim::cmd_start_discovery(bool manual) { +#if MBED_CONF_NFCEEPROM + cmd_printf("EEPROM cannot start_discovery()\r\n"); + set_last_nfc_error(NFC_ERR_UNSUPPORTED); + cmd_ready(CMDLINE_RETCODE_INVALID_PARAMETERS); + +#else + + // todo: remove hard coded + nfc_rf_protocols_bitmask_t protocols = { 0 }; + protocols.target_iso_dep = 1; + + nfc_err_t err = ((NFCProcessController*) this)->set_rf_protocols(protocols); + + if (manual) { + this->set_discovery_restart_manual(); + } else { + this->set_discovery_restart_auto(); + } + err = this->start_discovery(); + set_last_nfc_error(err); + if (NFC_OK != err) { + cmd_ready(CMDLINE_RETCODE_FAIL); + } else { + cmd_ready(CMDLINE_RETCODE_SUCCESS); + } +#endif +} + +// disabled in EEPROMs, overridden if controller present +void NFCTestShim::cmd_stop_discovery() { + +#if MBED_CONF_NFCEEPROM + cmd_printf("EEPROM cannot stop_discovery()\r\n"); + set_last_nfc_error(NFC_ERR_UNSUPPORTED); + cmd_ready(CMDLINE_RETCODE_INVALID_PARAMETERS); +#else + nfc_err_t err = this->stop_discovery(); + set_last_nfc_error(err); + if (NFC_OK != err) { + cmd_ready(CMDLINE_RETCODE_FAIL); + } else { + cmd_ready(CMDLINE_RETCODE_SUCCESS); + } + +#endif +} + diff --git a/TEST_APPS/device/nfcapp/nfctestshim.h b/TEST_APPS/device/nfcapp/nfctestshim.h new file mode 100644 index 0000000000..8a0b65b79f --- /dev/null +++ b/TEST_APPS/device/nfcapp/nfctestshim.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2018 ARM Limited. All rights reserved. + * 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. + */ + +#ifndef _NFCTESTSHIM_H_INCLUDED +#define _NFCTESTSHIM_H_INCLUDED + +#include +#include +#include +#include + +#include "mbed_config.h" +#include "nfc/ndef/MessageBuilder.h" +#include "nfc/ndef/common/URI.h" +#include "nfc/ndef/common/util.h" +#include "nfc/nfcdefinitions.h" + +// all targets that have an EEPROM +#if MBED_CONF_NFCEEPROM +#define TEST_NFCEEPROM_TARGET +#endif + +// all targets that have a controller +#if defined (TARGET_PN512) +#define TEST_NFCCONTRL_TARGET +#endif + +#if MBED_CONF_NFCEEPROM +#include "NFCEEPROM.h" +#include "EEPROMDriver.h" + +#else +#ifdef TARGET_PN512 +#include "nfc/controllers/PN512Driver.h" +#include "nfc/controllers/PN512SPITransportDriver.h" +#endif +#include "nfc/NFCRemoteInitiator.h" +#include "nfc/NFCController.h" + +using mbed::Span; +using mbed::nfc::NFCRemoteInitiator; +using mbed::nfc::NFCController; +#endif // TEST_EEPROM_TARGET + +using mbed::nfc::ndef::MessageBuilder; +using mbed::nfc::ndef::common::URI; +using mbed::nfc::ndef::common::span_from_cstr; +using mbed::nfc::nfc_rf_protocols_bitmask_t; + +class NFCTestShim { +public: + NFCTestShim(); + + static void cmd_get_last_nfc_error() { + get_last_nfc_error(); + } + ; + static void cmd_set_last_nfc_error(int err) { + set_last_nfc_error(err); + cmd_ready (CMDLINE_RETCODE_SUCCESS); + } + ; + static void cmd_get_conf_nfceeprom() { + get_conf_nfceeprom(); + } + ; + static void get_last_nfc_error(); + static void set_last_nfc_error(int err); + static void get_conf_nfceeprom(); + static void print_ndef_message(const Span &buffer, + size_t length); + + void cmd_init(); + virtual nfc_err_t init() = 0; + + void cmd_set_smartposter(char *cmdUri); + void cmd_erase(); + void cmd_write_long(char *data); + void cmd_read_nfceeprom(); + void cmd_start_discovery(bool manual = false); + void cmd_stop_discovery(); + void cmd_configure_rf_protocols(nfc_rf_protocols_bitmask_t protocols); + void cmd_get_rf_protocols(); + +protected: + // implement/declare EEPROM and Controller model underlying common BH and delegate specializations + virtual nfc_err_t set_rf_protocols(nfc_rf_protocols_bitmask_t protocols) {return NFC_ERR_UNSUPPORTED ;}; + virtual nfc_err_t start_discovery() {return NFC_ERR_UNSUPPORTED ;}; + virtual nfc_err_t stop_discovery() {return NFC_ERR_UNSUPPORTED ;}; + void set_discovery_restart_auto() { + _discovery_restart = true; + }; + void set_discovery_restart_manual() { + _discovery_restart = false; + }; + + +protected: + size_t _ndef_write_buffer_used; + Span ndef_poster_message; // message to build and send + uint8_t _ndef_write_buffer[0x2000]; // if this buffer is smaller than the EEPROM, the driver may crash see IOTPAN-297 + uint8_t _ndef_buffer[0x2000]; // driver buffer + bool _discovery_restart; + +private: + static int last_nfc_error; + + static int using_eeprom; + static char long_string[0x2000]; +}; + +// forward declare single instance +extern NFCTestShim * pNFC_Test_Shim; + +#endif // _NFCTESTSHIM_H_INCLUDED diff --git a/TEST_APPS/device/nfcapp/uart.cpp b/TEST_APPS/device/nfcapp/uart.cpp new file mode 100644 index 0000000000..361c51c2b7 --- /dev/null +++ b/TEST_APPS/device/nfcapp/uart.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 ARM Limited. All rights reserved. + * 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 +#include +#include "mbed.h" + +/** + * Macros for setting console flow control. + */ +#define CONSOLE_FLOWCONTROL_RTS 1 +#define CONSOLE_FLOWCONTROL_CTS 2 +#define CONSOLE_FLOWCONTROL_RTSCTS 3 +#define mbed_console_concat_(x) CONSOLE_FLOWCONTROL_##x +#define mbed_console_concat(x) mbed_console_concat_(x) +#define CONSOLE_FLOWCONTROL mbed_console_concat(MBED_CONF_TARGET_CONSOLE_UART_FLOW_CONTROL) + +#define SERIAL_CONSOLE_BAUD_RATE 115200 + +FileHandle *mbed::mbed_override_console(int) { + static UARTSerial console(STDIO_UART_TX, STDIO_UART_RX, + SERIAL_CONSOLE_BAUD_RATE); +#if CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTS + console.set_flow_control(SerialBase::RTS, STDIO_UART_RTS, NC); +#elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_CTS + console.set_flow_control(SerialBase::CTS, NC, STDIO_UART_CTS); +#elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTSCTS + console.set_flow_control(SerialBase::RTSCTS, STDIO_UART_RTS, STDIO_UART_CTS); +#endif + return &console; +} diff --git a/TEST_APPS/icetea_plugins/nfc_test_parsers.py b/TEST_APPS/icetea_plugins/nfc_test_parsers.py new file mode 100644 index 0000000000..9738252fe2 --- /dev/null +++ b/TEST_APPS/icetea_plugins/nfc_test_parsers.py @@ -0,0 +1,75 @@ +""" +Copyright 2018 ARM Limited +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. +""" + + +from icetea_lib.Plugin.PluginBase import PluginBase +import re +import os + +class NfcTestParsers(PluginBase): + # constructor + def __init__(self): + super(NfcTestParsers, self).__init__() + + def get_parsers(self): + return { + 'getlastnfcerror': self.trace_parser, + 'setlastnfcerror': self.trace_parser, + 'iseeprom': self.trace_parser, + 'initnfc': self.trace_parser, # all commands that return an NFC error code + 'readmessage' : self.trace_parser, + 'erase' : self.trace_parser, + 'writelong' : self.trace_parser, + 'stop' : self.trace_parser, + 'start' : self.trace_parser, + 'setsmartposter': self.trace_parser, + 'getprotocols': self.trace_parser, + 'setprotocols': self.trace_parser + } + + def trace_parser(self, response): + results = {'iseeprom': None, # 'true' if EEPROM + 'lastnfcerror':None, # 0=OK >0 = error + 'nfcmessage':None, # NDEF array of bytes + 'protocols':None} # csv list + respLines = response.lines + for line in respLines: + try: + value = PluginBase.find_one(line, "{{lastnfcerror=([0-9]+)}}") + if value is not False: + results['lastnfcerror'] = int(value) + # iseeprom + value = PluginBase.find_one(line, "{{iseeprom=([\w]+)}}") + if value is not False: + if ("TRUE" == value.upper() or "1" == value): + results['iseeprom'] = True + else: + results['iseeprom'] = False + # nfcmessage (hex data dumps) + data = PluginBase.find_one(line, "{{nfcmessage=([0-9a-f\s]*)}}") + if data is not False: + value = [] + for byte in data.split(' '): + if bool(byte): + value.append( int(byte, 16)) + results['nfcmessage'] = value + # t1t,t2t,t3t,isodep,nfcdef,t5t + value = PluginBase.find_one(line, "{{protocols=(([\w]*,?)*)}}") + if value is not False: + results['protocols'] = value # a csv list + except re.error as e: # the framework gobbles errors in the plugin + print("Regex error",e,"occured in",os.path.basename(__file__), "!!") + raise e + return results diff --git a/TEST_APPS/icetea_plugins/plugins_to_load.py b/TEST_APPS/icetea_plugins/plugins_to_load.py index 1322fbcc46..3cd4358450 100644 --- a/TEST_APPS/icetea_plugins/plugins_to_load.py +++ b/TEST_APPS/icetea_plugins/plugins_to_load.py @@ -13,7 +13,10 @@ See the License for the specific language governing permissions and limitations under the License. """ from ip_test_parsers import IpTestParsers +from nfc_test_parsers import NfcTestParsers + plugins_to_load = { - "ip_test_parsers": IpTestParsers + "ip_test_parsers": IpTestParsers, + "nfc_test_parsers": NfcTestParsers } diff --git a/TEST_APPS/testcases/nfc/README.md b/TEST_APPS/testcases/nfc/README.md new file mode 100644 index 0000000000..136305e899 --- /dev/null +++ b/TEST_APPS/testcases/nfc/README.md @@ -0,0 +1,319 @@ +# NFC tests. +Internal document. + +A CI test suite for NFC component. These tests validate card mbed emulation cases. The key use case is a NFC smart poster supporting comissioning workflow. +The SUT (system under test) is the NFC target. Tests exercise the framework and NDEF transactions when a NFC controller driver is used, or when the stack is configured for an NFC EEPROM chip in the system integration. + +This project is called CreamScone, which is an ice tea framework based cli-driven python test. + + +- [NFC tests . CONFIDENTIAL](#nfc-tests-confidential) +- [Overview](#overview) +- [NFC System Testing high level design](#nfc-system-testing-high-level-design) +- [Low level design](#low-level-design) +- [User Guide](#user-guide) + - [Test cases](#test-cases) + - [cli commands](#cli-commands) + - [How to](#how-to) + - [Alternate NFC drivers note:](#alternate-nfc-drivers-note:) + - [Running the tests](#running-the-tests) + + + +# Overview +A set of tests run in CI, which can provide: +- Internal confidence +- Faster iterations +- More efficient work +- Clear escalation path + +A [Ice-tea](https://github.com/ARMmbed/mbed-os-5-docs/blob/development/docs/tools/testing/testing_icetea.md) based test suite. In it's simplest form, the suite merely drives API's for the NFC tag reader/writer, and validates a tag simulation running on an idle target, allows test cases in discovery, connection and read/write NDEF records. + +In order to mitigate the costs associated with system testing, use existing frameworks or parts and make it easy to test each individually. The [nfcpy](https://nfcpy.readthedocs.io/) Python library is used as the core of the *CreamScone* component which uses a PN53* device [SCL3711 usb reader](https://www.identiv.com/products/smart-card-readers/rfid-nfc-contactless/scl3711/) over USB to read the mbed simulated tag. This library is used to drive host interactions because it is portable (windows/GNULinux.) Remote NFC interactions will raise events in the mbed application. Connection and read/write events which get handled in user application on the target get wired up to asyncronously return responses and the data values (NDEF messages) to the ice tea framework. These events and data are thus tested/checked in the code (python) running on host. The target test app wraps the API, allowing many new test-scenarios to be written purely in Python. + +**NFC compliance** + +This suite only assists in NFC forum compliance. Developers must self certify using test tools from a provider to uncover early issues and get an external test vendor to achieve certification. + +**Mobiles and inter-op** + +(Unimplemented) Describe test procedures using a mobile phone app for Android and for IOS. + +![Basic Overview](img/simple-overview.png) + +Because the comissioning workflow application quality is the end goal, the NFC suite includes learnings to design the CI setup needed for future system testing that bring a mobile phone into the test-rig. The use of a mobile and bluetooth pairing as well as the continous integration system is not included. + + + +# NFC System Testing high level design +Mitigate risks identified, to the product from an internal view to supporting releases, and from customer development and production risks. In summary: +- Architecture risks and Api breaks +- Partner cannot NFC forum Certify +- Partner driver has bugs +- Code regressions in O/S +- Arm mbed provided driver or HAL has bugs +- Security vulnerabilities + +In precis, “Empower engineers to efficiently ship quality code with confidence.” + +**Design requirements:** +- Identify and use tools to allow running in CI system, on many targets/configurations +- Be portable (can run in the CI system) using NFC explorer boards in lab for correctly co-located targets. +- Be able to set up and run locally in development + +# Low level design +**components** + +An icetea suite [test_nfc.py](TEST_APPS\testcases\nfc\test_nfc.py) + +Commandline (serial port) driven app [ice_device.py](TEST_APPS\testcases\nfc\ice_device.py) aka _'CreamScone'_ which allows manual interactions with the driver. + +An icetea plugin [nfc_test_parsers.py](TEST_APPS\icetea_plugins\nfc_test_parsers.py) + +MbedOS cli test app [main.cpp](TEST_APPS\device\nfcapp\main.cpp). The CLI commands return results asyncronously for most commands which get passed to and handled on a driver thread. + +The SUT target is rebooted between tests, since test modify the target hardware state. + +**Future:** A complete inter-op ready design expands to also include a switch-box to allow the reader to connect to NFC enabled targets nearby using flying cables and a sticky-back antenna. The switch allows selecting either alternative tags, or NFC peers. The switch can be controlled using GPIO either driven from spare IO pins on the target DUT itself (preferred option), or perhaps from a Raspberry pi. + +![inter-op](img/inter-op-view.png) + +![future](img/creamscone-mobile.png) + + +**Reference:** + +https://github.com/ARMmbed/mbed-os/blob/master/docs/design-documents/nfc/nfc_design.md + +https://github.com/ARMmbed/mbed-os/tree/master/features/nfc/nfc + +https://github.com/ARMmbed/mbed-os-example-nfc + +# User Guide +This section covers the test case specification and how to run the test suite. + +## Test cases +CLI commands used by each test case describe the steps in a test. +** Basic local only cases ** +- test_nfc_error_codes : inintnfc , setlastnfcerror \ , getlastnfcerror + - Verify that the test CLI engine can initialize the stack, and can return NFC codes +- test_nfc_eeprom : iseeprom + - prints "true" if the target has an EEPROM configured stack, else prints "false" diagnostic only +- test_nfc_get_controller_protocols + - set nfc protocols supported +- test_nfc_set_controller_protocols + - get nfc protocols supported +- test_nfc_setsmartposter : setsmartposter \<-u> \ + - Sets a smartposter message, does not verify over wireless! (Only https protocol tested.) +- test_nfc_erase : initnfc, erase, readmessage + - Erase entire EEPROM, (behaviour for controller stack is null) +- test_nfc_write_long : initnfc, erase, writelong \, readmessage + - Write a very large text T record, and verify expected length written +- test_nfc_reprogrammed : iseeprom, initnfc, erase, wirelessly reprogram, wirelessly verify + - Use a reader/writer to program the tag using the default M24SR chip password +** End-to-End cases ** +- test_nfce2e_target_found + - tag can actually be detected wireless +test_nfce2e_type4_found + - correct tag detected +- test_nfce2e_discovery_loop + - Start or Stop discovery loop or disable depending on stack +test_nfce2e_read_stress + - read large message from device +test_nfce2e_reprogrammed + - modify large message from device +test_nfce2e_reprogrammed_stress + - write moderate message wirelessly +test_nfce2e_smartposter + - as with the basic test, but wirelessly + +** unimplemented due to limited support ** +- test_nfc_iso7816_supported +- test_nfc_add_iso7816_application +- test_nfc_set_tagtype +- test_nfc_get_tagtype + + +## cli commands +cli commands take parameters, its possible to type help at the cli for a list of commands. +``` +mbed sterm --baudrate 115200 +help +... +getlastnfcerror last NFC error code +setlastnfcerror self-test +initnfc init NFC driver +setsmartposter send smartposter NDEF +iseeprom NFC configEEPROM present +readmessage read EEPROM else return last message +erase erase EEPROM or clear last message +writelong fill entire FILE with pattern +... +``` +Note: Most commands also return a NFC status value (type "getlastnfcerror help" in console) which allow us to build negative test cases. +Note: Some commands only apply to NFC controllers, these commands fail with the appropriate not-supported code NFC_ERR_UNSUPPORTED and additionally return -2 error code to ice-tea. + +**unimplemented CLI commands** +commands that were not implemented in the test app +- set/get tag type +- get/set iso7816 app + +**Excluded** +- power consumption +- throughput +- memory consumption + +## How to +**Wirring diagram for NFC Explorer with PN512** + +If using the Raspbery Pi explorer (PN512) board, use this pinout mapping diagram to connect the shield to the reference target. In this case a ST NucleoF401RE pinout is shown. +``` + Nucleo F401RE Explore NFC + (Arduino header) (pin1 on shield shown with a <|) + +-------+ +-------+ +--------+ + | [NC] | | [B8] | |[ 2][ 1]| + | [IOREF| | [B9] | |[ 4][ 3]| + | [RST] | | [AVDD]| |[ 6][ 5]| +1<---+ [3V3] | | [GND] | |[ 8][ 7]| + | [5V] | | [A5] +--->23 |[10][ 9]| + | [GND] | | [A6] +--->21 |[12][11]| +25<--+ [GND] | | [A7] +--->19 |[14][13]| + | [VIN] | | [B6] +--->3 |[16][15]| + | | | [C7] | |[18][17]| +26<--+ [A0] | | [A9] | |[20][19]| +16<--+ [A1] | | [A9] | |[22][21]| + | ... | | | |[24][23]| + | | | [A8] | |[26][25]| + +-------+ | ... | +--------+ + | | + | | + +-------+ + +Patch using jumper wires to the +indicated pins on the Shield. + +``` +Schematic (https://www.element14.com/community/docs/DOC-76384/l/explore-nfc-board-schematic) +To change pinouts, if your reference design or shield pins differ for the PN512 controller driver, open nfcProcessCtrl.cpp and find the code +``` +NFCProcessController::NFCProcessController(events::EventQueue &queue) : + // pins: mosi, miso, sclk, ssel, irq, rst + _pn512_transport(D11, D12, D13, D10, A1, A0), _pn512_driver( + &_pn512_transport), _queue(queue), _nfc_controller( + &_pn512_driver, &queue, _ndef_buffer) { +} +``` +modify pins as needed. + +**Compilation target drivers** + +If using the EEPROM driver, the mbed_app.json will contain +``` + "target_overrides": { + "DISCO_L475VG_IOT01A": { + "target.extra_labels_add": ["M24SR"] + } + } +... +``` +If using the Explorer Shield or PN512 driver mbed_app.json will add +``` + "target_overrides": { + "NUCLEO_F401RE": { + "target.extra_labels_add": ["PN512"] + } + } +... +``` +## Alternate NFC drivers note: + +Please see the example json file .\TEST_APPS\testcases\nfc\mbed_app.json . The test does not check that you have any needed shield installed, so if it "hangs" at the point the "initnfc" command is used, the driver or shield may be the fault. The test assumes that MBED_CONF_NFCEEPROM is set to 1, if not it assumes that a controller driver is in use. To add support for other then PN512 and M24SR, it is required to add the support. + +If the driver you add is for Eeprom, open nfccommands.cpp and find the code and modify line as shown +++ +``` +NFCTestShim* new_testshim() { +#if MBED_CONF_NFCEEPROM +--- mbed::nfc::NFCEEPROMDriver& eeprom_driver = get_eeprom_driver(nfcQueue); + ++++ mbed::nfc::NFCEEPROMDriver& eeprom_driver = get_myeeprom_driver(nfcQueue); + + return ( (NFCTestShim *)(new NFCProcessEEPROM(nfcQueue, eeprom_driver)) ); +#else + return ((NFCTestShim *) (new NFCProcessController(nfcQueue))); +#endif // EEPROM +``` + +If the driver you add is a controller driver, open nfcProcessCtrl.cpp and find the code +``` +NFCProcessController::NFCProcessController(events::EventQueue &queue) : + // pins: mosi, miso, sclk, ssel, irq, rst + _pn512_transport(D11, D12, D13, D10, A1, A0), _pn512_driver( + &_pn512_transport), _queue(queue), _nfc_controller( + &_pn512_driver, &queue, _ndef_buffer) { +} +``` +1. You will want to replace with a reference to the desired controller driver. Likewise this is where pinout hanges if using a supplied controller driver have to be made. +2. Search for occurences of guard macro #ifdef TARGET_PN512 , and add guard code for your specific controller driver. + +## Running the tests +1. Wire an [explorer shield](https://cpc.farnell.com/nxp/explore-nfc/add-on-board-nfc-for-raspberry/dp/SC13404) up to and compile the target application. +2. Provision the target, and verify that it responds with an _`action NDEF record http://www.mbed.com`_ by using a mobile phone to scan over the antenna. +3. Install python (2.7) and install the nfcpy library, [see](https://nfcpy.readthedocs.io/en/latest/topics/get-started.html) . NFC reader can be connected to a serial port, or more commonly a USB dongle. Verify the dongle is functioning. +4. Place the scanner near the explorer shield. Run various test program commands like so: +- python ice_device -command describe + +**run the suite** +In a working folder, run + +`git clone https://github.com/ARMmbed/mbed-os.git` + + + +If using discovery and the M24SR driver, you need to: +`git clone https://github.com/ARMmbed/mbed-nfc-m24sr.git' + + +And copy the files something like this: +`xcopy ..\mbed-nfc-m24sr\*.* .\eeprom_driver\' + +To run the End2End tests, type: +`mbed test --icetea -n test_nfce2e` + +To run only the standalone (readerless tests if you do not have a card reader), type: +`mbed test --icetea -n test_nfc_eeprom,test_nfc_error_codes,test_nfc_setsmartposter,test_nfc_erase,test_nfc_write_long` + +**Using the app standalone** + +The target app can accept commandline inputs over serial. +``` +mbed sterm --baudrate 115200 +``` + +Note: If the target uses an EEPROM, it need not be powered/running, to be read, mbedOS is not running at that point. + +**Device API error codes** +You can issue the command "getlastnfcerror help" to see a list of error codes that are returned by most commands. +``` +#define NFC_OK 0 ///< No error +#define NFC_ERR_UNKNOWN 1 ///< Unknown error +#define NFC_ERR_LENGTH 2 ///< Length of parameter is wrong +#define NFC_ERR_NOT_FOUND 3 ///< Could not find item +#define NFC_ERR_UNSUPPORTED 4 ///< This action is not supported +#define NFC_ERR_PARAMS 5 ///< These parameters are not correct +#define NFC_ERR_BUFFER_TOO_SMALL 6 ///< The buffer is too small to store all data (buffer overflow) +#define NFC_ERR_TIMEOUT 7 ///< Timeout +#define NFC_ERR_CRC 8 ///< Checksum does not match +#define NFC_ERR_NOPEER 9 ///< No target/initiator in vicinity +#define NFC_ERR_PARITY 10 ///< Parity error +#define NFC_ERR_FIELD 11 ///< No RF field detected (or RF field lost) +#define NFC_ERR_COLLISION 12 ///< Collision detected +#define NFC_ERR_WRONG_COMM 13 ///< Communication error +#define NFC_ERR_PROTOCOL 14 ///< Protocol is not conformant +#define NFC_ERR_BUSY 15 ///< Resource is busy +#define NFC_ERR_CONTROLLER 16 ///< Controller failure +#define NFC_ERR_HALTED 17 ///< Target has been halted +#define NFC_ERR_MAC 18 ///< MAC does not match +#define NFC_ERR_UNDERFLOW 19 ///< Could not send data in time +#define NFC_ERR_DISCONNECTED 20 ///< Link has disconnected +#define NFC_ERR_ABORTED 21 ///< Command was aborted +``` diff --git a/TEST_APPS/testcases/nfc/__init__.py b/TEST_APPS/testcases/nfc/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/TEST_APPS/testcases/nfc/img/creamscone-mobile.png b/TEST_APPS/testcases/nfc/img/creamscone-mobile.png new file mode 100644 index 0000000000000000000000000000000000000000..114a622099f57563e128627590bd49379b21a323 GIT binary patch literal 35996 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 literal 0 HcmV?d00001 diff --git a/TEST_APPS/testcases/nfc/img/simple-overview.png b/TEST_APPS/testcases/nfc/img/simple-overview.png new file mode 100644 index 0000000000000000000000000000000000000000..3c02dd9f86d5267a0d4c62cb5cc82a9cddc6d04d GIT binary patch literal 38235 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 _{#mJ9~g+>D;_Gsoog|+U$#uy3R(phm14%v zpO5P#$g!*%L3Re;biZ4M2u><^bYBQ0hV_taDyBI#Y?}J;u&tXvG0yX;Fbl^OOxQjU(An>;9PV6;v3=*V zDUn=sL=FLpPzYvV9)33tTz9#qXdxNbC_DMGjJ1%h zC^#$YCXV)oUkrppBEMdELG2p2b1lX;ox@hpm7XqqOI4X& z5lb_%{3zqSrIxOk!y#0Mu0=M$2_?5}?&o43`BifH09#3!o`zzn8Q1X-Q(#~yT=j{S z+_@HG+i@-hrAtGUWzt|-3cUc~8xwd1B6Ja@D7yr`N~B0%(F>_fBm6rx!CB%(6*}bYnS`Tt1s!3p>sZlG}M8ph(du zZ$se_wjbv*SV1uip8@#1hEMDBcrh*Z8DUZKhQ0lwqoZ5%bi=e`Z-=A12fiKM-5sLX z1z#OqU1G;gnC|ZAaCC?r_*IOM`xH7{_-1`N#n*i^APHHRL#8gMJdw{Fn2#&vAX--@ z-;#<=F4*tKCIAS-%-J>pS?RhrldV>vN-jb*FZ26s zCJBpRNeo|Emx-m4F(a1DWMWasi9#X?Ik99iWf++lrVZn@)albv5BPf=4Mugr<393< z^Bywo*b52K;ed%IdW^mvhvfE<4#y4$EQJq%Oy~z>W2VE=-PPTN=67{FK>n@{NFtB` zAPoup8_>}uK-BK)5;_FWVCEm#rGcO&qqaFP5U@dNm;hl(3IR=F zIk_P%^WiEzP7*hU)ga=+y0A(t8O1byWnwY-P8un28_b3ra2+j=M$g2&(Igm*W_w{0 zGfcy)5xEhOE&7xZCi6bXF$`Gl^Ll)~KBEIdy4Y=qUdik3?smh!?(ROX2i1jej)5HM z=yu$+*U_=pvG=CEd))^7IZE;VISys}^*OxY;S2xeFDIrF*uV->Bm=HEwuF()4FE>x z3M8{A4BC~=k~IofY33@fiF>6R6qaBr1@b}&O~n>q1vnT&)+0$Yo2<`Hip-GHE*zcA zkU26Te+(EgOw(LAz3lHG0UzM{`}p^P@FD(z0QccM=-A|cd^ZyX-;;1sa9V|vtq6Cn z#n`rU_z)DlP{|AnXSvz(Cc69zC15n9Hn2TKAyB1AV*`Fe?gwOt#E8o6ey{{qMG3%b zvT_+13S+Pqe&Rd^Mrt3xA})euWeOMsrjZn^!A=DaRQU*4vMyafiq-T-+nBs_58lCP z1s>e4b7%rwXyjU>kpth&ibW`P`N4%)xajhiB)xcHvgVq^LLn?g7s$G78Fdc!1TluB zssab2_s*7pyUA?`Ugqw;hAegfD3;O$k~0w&vt-FZEC1oB1lqm9`4-4I_r{Mot-%}Y zf1Go@$T`R1aOwkhj>D-h+&K=XzHsL_och9@<8bN=caFoUFWfl}r@nCKIGp;zo#Sxo z3wMsgsW03)4yV3w=Qy1D!ky!A>I-*{!>K3P?EfEQ0oo*5H76Ya0000 + +- [NFC test SDK comparison](#nfc-test-sdk-comparison) +- [Overview](#overview) +- [Comparison](#comparison) + - [IOS (objective C)](#ios-objective-c) + - [Android (Java)](#android-java) + - [pynfc (python)](#pynfc-python) +- [Observe](#observe) + + + +# Overview +A comparison which analyses NFC use cases on mobile, as background to the test case design/implementation in the comissioning workflow :NFC-Bluetooth-pairing application. +- Analyse the [Apple API](https://developer.apple.com/documentation/corenfc) +- Analyse the [Android API](https://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc#java) +- Python test [pynfc modules](https://nfcpy.readthedocs.io/en/latest/modules/index.html) + + +# Comparison +From the lowest level, each programmer interface has a definition for Errors, Link, NDEF, Tags and Sessions. +Note: Comparisons are high level and use past experience and old docs. + + +## IOS (objective C) +1. Errors: + are a struct with 3 members:
+ int:value,
struct:string:usererror
struct:string:localized +1. isodep: + + not supported +1. NDEF support parameters with + + payload
and Typename, +4. Tags: + + Are an Object representing tag +5. Sessions are managed using + + Delegate &callbacks + +## Android (Java) +1. Errors: + + thrown as IOException with cause and message, there are no returned error datas +2. isodep: + + get/set communication parameters and RAW. +3. NDEF: + + Includes SNEP +4. Tags : + + (3)A,(3)B,(4)F,V and isoDep layer +5. Sessions + + Intents and Actions, runtime registration of PendingIntent() allows hooking using tag filters + +## pynfc (python) +1. Errors : + + raises Exceptions nfc.clf.Error and others per class +2. isodep: + + get/set communication parameters and RAW. +3. NDEF: + + full implementation and types. Includes SNEP +4. Tags : + + 1,2,(3)A,(3)B,(4)F isoDep layer +5. Sessions : + + using delegate class callbacks + +# Observe +Negative test cases would be better designed around the user cases, than around the implementations, base error conditions at the API layer look more like +- UnsupportedTarget +- Communication +- Protocol +- (w) Transmission +- (w) Timeout +- (w) BrokenLink +- ValueError + +Valuable test data cases shall be for valid and boundary cases for the smartposter NDEF record: +- uri – URI string ASCII only +- title – Smart poster title(s), (additional internationalizations with IANA codes not tested) +- icons – omitted +- action – The recommended action , a string diff --git a/TEST_APPS/testcases/nfc/nfc_clf_wrapper.py b/TEST_APPS/testcases/nfc/nfc_clf_wrapper.py new file mode 100644 index 0000000000..a552d2138a --- /dev/null +++ b/TEST_APPS/testcases/nfc/nfc_clf_wrapper.py @@ -0,0 +1,133 @@ +""" +Copyright 2018 ARM Limited +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. +""" + +import time +import nfc +from nfc.clf import RemoteTarget +import logging + +logprefixnfc = "NFCPY: " + +""" + Wrap calls to nfcpi testing module, handle loading the driver +""" + + +def command_is(string, command): + return string.split(' ')[0] == command + + +def debug_nfc_data(key, value): + """ + print useful data values for the host/user {{in between}} easy to spot brackets. + """ + text = "{{%s=%s}}" % (key, value) + logger.info(text) + + +logging.basicConfig(level=logging.DEBUG) # read commandline value? +logger = logging.getLogger() + + +class NfcWrapper: + """ + Finds the NFC reader USB front-end and prepares it for use. + """ + def __init__(self): + # will need a parameter here to help with libusb detection? + # clf.open("usb:04e6:5591") will open the SCL3711-NFC + logger.info("Initializing the NFC tag reader...") + self.clf = nfc.ContactlessFrontend() + if (self.clf.open("usb") ): # the NFC reader was not detected on any USB port! + logger.info("NFC Frontend found OK") + else: + logger.error("The NFC reader was not detected on any USB port!") + self.clfResponse = None + + def clf_response(self): + return self.clfResponse + + def parse(self,line): + logging.debug(line) + parseok = False + # find command and call the needed nfcWrapper method + if command_is(line, "ping"): + self.pong() + parseok = True + if command_is(line, "connect"): + detectedTag = self.connect() + debug_nfc_data("Connectedtag", detectedTag) + parseok = True + if command_is(line, "mute"): + detectedTag = self.mute() + parseok = True + if command_is(line, "disconnect"): + self.disconnect() + parseok = True + return parseok + + """return the detected tag, else timeout after interval""" + def sense(self, target_options = ("106A","106B","212F")): + logging.info(logprefixnfc + "detecting tags with options " + target_options) + # todo filter using the target_options + targets = self.clf.sense(RemoteTarget('106A'), RemoteTarget('106B'), RemoteTarget('212F')) + self.clfResponse = targets + return targets + + def connect(self, target_options = ("106A","106B","212F")): + # todo: decide on tag types to allow/filter + after5s = lambda: time.time() - started > 5 + started = time.time() + tag = self.clf.connect( rdwr={'on-connect': lambda tag: False}, + llcp={}, terminate = after5s) + self.clfResponse = tag + if tag: # None if timeout expires + logging.info(logprefixnfc + str(tag)) + return tag + + def mute(self): + """turn off the reader radio""" + if self.clf.device: + logging.info(logprefixnfc + "radio mute" + self.clf.device.product_name) + self.clf.device.mute() + + def disconnect(self): + logging.info(logprefixnfc + "close frontend.") + self.clf.close() + +""" +Handle interactions with the NFC reader, and singleton +""" +class ContactlessCommandRunner(): + + """ + Lazy initialization singleton to open the reader once only - else when the framework scans for + tests, it causes us to open the reader. This breaks the Windows driver. + """ + def __getattr__(self, name): + if name == 'nfc': + + if ContactlessCommandRunner.__nfc_wrapper is None: + ContactlessCommandRunner.__nfc_wrapper = NfcWrapper() + return ContactlessCommandRunner.__nfc_wrapper + + __nfc_wrapper = None + + # plumbing, calls a static instance for the reader object. + def parse(self, line): + return self.nfc.parse(line) + + def clf_response(self): + return self.nfc.clf_response() diff --git a/TEST_APPS/testcases/nfc/nfc_cli_helper.py b/TEST_APPS/testcases/nfc/nfc_cli_helper.py new file mode 100644 index 0000000000..e0eeb1067b --- /dev/null +++ b/TEST_APPS/testcases/nfc/nfc_cli_helper.py @@ -0,0 +1,70 @@ +""" +Copyright 2018 ARM Limited +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. +""" + +# ice-tea cli commands decorator class + + +from nfc_messages import NfcErrors +import logging +import icetea_lib.tools.asserts as asserts + +class CliHelper(): + """ + Helper method, checks the nfc SDK error-code for you, makes writing a negative test much easier + Example: + if (target_is_eeprom): + nfc_command("dev1", "start", expected_retcode=-2, expected_nfc_error= NfcErrors.nfc_err_unsupported) + else: + nfc_command("dev1", "start") + """ + + def nfc_command(self, k, cmd, # pylint: disable=invalid-name + wait=True, + timeout=10, + expected_retcode=0, + asynchronous=False, + report_cmd_fail=True, + expected_nfc_error=NfcErrors.nfc_ok): + response = self.command(k, cmd, wait, timeout, expected_retcode, asynchronous, report_cmd_fail) + asserts.assertEqual(int(response.parsed['lastnfcerror']), expected_nfc_error.value) + return response + + @staticmethod + def command_is(string, command): + return string.split(' ')[0] == command + + @staticmethod + def debug_nfc_data(key, value): + """ + print useful data values for the host/user {{in between}} easy to spot brackets. + """ + text = "{{%s=%s}}" % (key, value) + logging.Logger.info(text) + + def assert_binary_equal(self, left, right, length): + i = 0 + while i < length: + asserts.assertEqual(left[i], ord(right[i]), ("Missmatch @offset %d 0x%x <> 0x%x" % (i, left[i], ord(right[i]))) ) + i = i + 1 + + def assert_text_equal(self, left, right, length): + """ + Asserts if the 2 buffers (Text) differ + """ + i = 0 + while i < length: + asserts.assertEqual(ord(left[i]), ord(right[i]), ("Missmatch @offset %d %d <> %d" % (i, ord(left[i]), ord(right[i]))) ) + i = i + 1 + diff --git a/TEST_APPS/testcases/nfc/nfc_messages.py b/TEST_APPS/testcases/nfc/nfc_messages.py new file mode 100644 index 0000000000..b416506976 --- /dev/null +++ b/TEST_APPS/testcases/nfc/nfc_messages.py @@ -0,0 +1,72 @@ +import nfc +from enum import Enum +import logging + + +class NfcErrors(Enum): + nfc_ok = 0 + nfc_err_unknown = 1 + nfc_err_length = 2 + nfc_err_not_found = 3 + nfc_err_unsupported = 4 + nfc_err_params = 5 + nfc_err_buffer_too_small= 6 + nfc_err_timeout = 7 + nfc_err_crc = 8 + nfc_err_nopeer = 9 + nfc_err_parity = 10 + nfc_err_field = 11 + nfc_err_collision = 12 + nfc_err_wrong_comm = 13 + nfc_err_protocol = 14 + nfc_err_busy = 15 + nfc_err_controller = 16 + nfc_err_halted = 17 + nfc_err_mac = 18 + nfc_err_underflow = 19 + nfc_err_disconnected = 20 + nfc_err_aborted = 21 + + +''' +return a 'T'ext text ndef record +''' +def make_textrecord(text, language='en-US'): + return nfc.ndef.Message(nfc.ndef.TextRecord(text, language)) + +''' +Return an NDEF message +resource -- url +titles -- list of : colon delimited titles where an optional language code precedes the title - +if lang codes are omitted, 'en' is assumed +action -- one of default/save/exec/edit +''' +def make_smartposter(resource, titles, action = 'default'): + record = nfc.ndef.SmartPosterRecord(resource) + for title in titles: + lang, text = title.split(':', 1) if ':' in title else ('en', title) + record.title[lang] = text + if not action in ('default', 'exec', 'save', 'edit'): + logging.error("action not one of 'default', 'exec', 'save', 'edit'") + return + record.action = action + + return nfc.ndef.Message(record) + + +''' +Program the provided NDEF messsage into the tag (authentication is not required) +''' +def program_remote_tag(message, tag): + if not tag.ndef.is_writeable: + logging.error("This Tag is not writeable.") + return False + tag.ndef.message = message + logging.info("Programmed tag OK.") + return True + +''' +Builds a long string by repeating a shorter string up to the required length +''' +def repeat_string_to_length(string_to_expand, length): + return (string_to_expand * ((length/len(string_to_expand))+1))[:length] diff --git a/TEST_APPS/testcases/nfc/test_nfc.py b/TEST_APPS/testcases/nfc/test_nfc.py new file mode 100644 index 0000000000..9655aebb22 --- /dev/null +++ b/TEST_APPS/testcases/nfc/test_nfc.py @@ -0,0 +1,293 @@ +""" +Copyright 2018 ARM Limited +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. +""" + + +from icetea_lib.bench import Bench +from nfc_clf_wrapper import ContactlessCommandRunner +import nfc_messages +import time +from mbed_clitest.tools.tools import test_case +import icetea_lib.tools.asserts as asserts +from nfc_messages import NfcErrors +from nfc_cli_helper import CliHelper + + +class CreamSconeTests(Bench, CliHelper): + """ + This test wrapper requires a usb connected contactless card reader dongle, and allows E2E testing. + See readme file for details + """ + def __init__(self, **kwargs): + testcase_args = { + 'title':"NFC tests with a reader", + 'status':"development", + 'purpose':"NFC e2e", + 'component':["NFC"], + 'type':"regression", + 'requirements':{ + "duts": { + '*': { + "count": 1, + "type": "hardware", + "application": { + "name": "TEST_APPS-device-nfcapp" + } + }, + "1": {"nick": "dev1"} + } + } + } + testcase_args.update(kwargs) + Bench.__init__(self, **testcase_args) + + def setup(self): + try: + self.clf = ContactlessCommandRunner() + self.clf.parse("mute") + except: + raise asserts.TestStepFail("Could not find NFC reader") + + def teardown(self): + self.logger.info("Test teardown: Reboot target...") + self.reset_dut() + + + +@test_case(CreamSconeTests) +def test_nfce2e_target_found(self): + """ + smoke - Our emulated tag is detectable + """ + + response = self.nfc_command("dev1", "iseeprom") + eeprom = response.parsed['iseeprom'] + self.nfc_command("dev1", "initnfc") + if not eeprom: + self.nfc_command("dev1", "start") + + self.clf.parse("connect") + tag = self.clf.clf_response() + asserts.assertNotNone(tag, "Could not connect to any tag") + + +@test_case(CreamSconeTests) +def test_nfce2e_type4_found(self): + """ + check - Type 4 tag is detected wirelessly + NOTE: If the tage emulation does not default to type4, this test needs to be modified + +++ self.nfc_command("dev1", "setprotocols t4t") + or the assertion be adapated according to support desired level + """ + + response = self.nfc_command("dev1", "iseeprom") + eeprom = response.parsed['iseeprom'] + self.nfc_command("dev1", "initnfc") + if not eeprom: + self.nfc_command("dev1", "start") + + self.clf.parse("connect") + tag = self.clf.clf_response() + asserts.assertNotNone(tag, "Could not connect to any tag") + + asserts.assertEqual(tag.type, 'Type4Tag', "Tag of type Type4Tag not found") + + +@test_case(CreamSconeTests) +def test_nfce2e_smartposter(self): + """ + check - Tag can be set and read via contactless + """ + expectedURI = "https://www.mbed.com" # ensure that these differ per test case + + self.nfc_command("dev1", "initnfc") + self.nfc_command("dev1", "erase") + response = self.nfc_command("dev1", "iseeprom") + eeprom = response.parsed['iseeprom'] + + self.logger.info("Target includes NFCEEPROM: %s" % eeprom) + if not eeprom: + self.nfc_command("dev1", "start") + + # write poster tag to target + self.command("dev1", "setsmartposter %s" % expectedURI) + + self.clf.parse("connect") + tag = self.clf.clf_response() + asserts.assertNotNone(tag, "Could not connect to any tag") + asserts.assertEqual(1, len(tag.ndef.records), "expected number NDEF records") + + asserts.assertEqual(tag.ndef.records[0].__class__.__name__, "SmartposterRecord", "expected SmartposterRecord") + asserts.assertEqual(expectedURI, tag.ndef.records[0].uri_records[0].uri, "expected exact URI") + + +@test_case(CreamSconeTests) +def test_nfce2e_reprogrammed(self): + """ + check - Tag can be programmed from a remote and read via contactless + """ + expectedURI = "https://www.google.com" + + response = self.nfc_command("dev1", "iseeprom") + eeprom = response.parsed['iseeprom'] + self.logger.info("Target includes NFCEEPROM: %s" % eeprom) + + self.nfc_command("dev1", "initnfc") + if not eeprom: + self.nfc_command("dev1", "start") + self.nfc_command("dev1", "erase") + + # program a poster tag to target + self.clf.parse("connect") + tag = self.clf.clf_response() + asserts.assertNotNone(tag, "Could not connect to any tag") + smartposter = nfc_messages.make_smartposter(expectedURI, ["en-US:Other search engines exist"]) + nfc_messages.program_remote_tag(smartposter, tag) + self.logger.info("Remote programmed %d bytes Smartposter" % len(str(smartposter))) + + # read device + self.clf.parse("connect") + tag = self.clf.clf_response() + asserts.assertNotNone(tag, "Could not re-connect to any tag") + + asserts.assertEqual(tag.ndef.records[0].__class__.__name__, "SmartposterRecord", "expected SmartposterRecord") + asserts.assertEqual(expectedURI, tag.ndef.records[0].uri_records[0].uri, "expected exact URI") + self.clf.parse("mute") # disable radio + + # verify in target + response = self.nfc_command("dev1", "readmessage") + + # check contents + expected_message = str(smartposter) + asserts.assertEqual(len(response.parsed['nfcmessage']), len(expected_message)) + self.assert_binary_equal(response.parsed['nfcmessage'], expected_message, len(expected_message)) + + +@test_case(CreamSconeTests) +def test_nfce2e_read_stress(self): + """ + check - Large record can be read via contactless + """ + messageRep = 'thequickbrownfoxjumpedoverthelazydog' # repeating message written + textLength = 2050 # 2K < x < 4K + + # calculate actual message to compare to using the library + expected_text = nfc_messages.repeat_string_to_length(messageRep, textLength) + message = nfc_messages.make_textrecord( expected_text ) + + response = self.nfc_command("dev1", "iseeprom") + eeprom = response.parsed['iseeprom'] + self.logger.info("Target includes NFCEEPROM: %s" % eeprom) + + self.nfc_command("dev1", "initnfc") + if not eeprom: + self.nfc_command("dev1", "start") + self.nfc_command("dev1", "erase") + self.nfc_command("dev1", "writelong %d %s" % (textLength,messageRep)) + + self.clf.parse("connect") + tag = self.clf.clf_response() + asserts.assertNotNone(tag, "Could not connect to any tag") + + # assert that read the eeprom contents gives correct data and length + asserts.assertEqual(tag.ndef.records[0].__class__.__name__, "TextRecord", "expected TextRecord") + asserts.assertEqual(len(tag.ndef.records[0].text), len(expected_text)) + self.assert_text_equal(tag.ndef.records[0].text, expected_text, len(expected_text)) + + +@test_case(CreamSconeTests) +def test_nfce2e_reprogrammed_stress(self): + """ + check - Large record can be programmed from a remote and read via contactless + """ + messageRep = 'thequickbrownfoxjumpedoverthelazydog' # repeating message written + textLength = 2050 # 2K < x < 4K + + # calculate actual message to compare to using the library + message = nfc_messages.make_textrecord( nfc_messages.repeat_string_to_length(messageRep, textLength)) + expected_message = str(message) + + response = self.nfc_command("dev1", "iseeprom") + eeprom = response.parsed['iseeprom'] + self.logger.info("Target includes NFCEEPROM: %s" % eeprom) + + self.nfc_command("dev1", "initnfc") + if not eeprom: + self.nfc_command("dev1", "start") + self.nfc_command("dev1", "erase") + + # program a large tag to target wirelessly + self.clf.parse("connect") + tag = self.clf.clf_response() + asserts.assertNotNone(tag, "Could not connect to any tag") + nfc_messages.program_remote_tag(message, tag) + self.logger.info("%d bytes chunk of data written to tag remotely" % len(str(message))) + + # read device + self.clf.parse("connect") + tag = self.clf.clf_response() + asserts.assertNotNone(tag, "Could not re-connect to any tag") + self.clf.parse("mute") # disable the reader radio + + # verify in target + response = self.nfc_command("dev1", "readmessage") + asserts.assertEqual(len(response.parsed['nfcmessage']), len(expected_message)) + self.assert_binary_equal(response.parsed['nfcmessage'], expected_message, len(expected_message)) + + +@test_case(CreamSconeTests) +def test_nfce2e_discovery_loop(self): + """ + check - Controller discovery loop stop/start + fails : blocked by an issue + """ + expectedURI = "https://www.nasa.com" # ensure that these differ per test case + + response = self.command("dev1", "iseeprom") # will hold result from the init lib call + eeprom = response.parsed['iseeprom'] + self.logger.info("Target includes NFCEEPROM: %s" % eeprom) + + self.nfc_command("dev1", "initnfc") # this NOT automatically start discovery at the same time, the test command "start" must be used on a controller. (Eeeproms always have the loop enabled.) + # By default, the test app automatically starts discovery loop again after a reader disconnects from the controller. + # Automatic resume after disconnect can be turned off by using command "start man" , the default is "start auto" . + + if not eeprom: + self.clf.parse("connect") + tag = self.clf.clf_response() + asserts.assertNone(tag, "post-init: Tag discovery loop should be stopped!") + self.nfc_command("dev1", "stop") + time.sleep(1) + + self.clf.parse("connect") + tag = self.clf.clf_response() + asserts.assertNone(tag, "post-stop: Tag discovery loop should be stopped!") + self.nfc_command("dev1", "start") + time.sleep(1) + + self.clf.parse("connect") + tag = self.clf.clf_response() + asserts.assertNotNone(tag, "Could not connect to any tag") + + self.clf.parse("mute") + self.nfc_command("dev1", "stop") + time.sleep(10) + self.clf.parse("connect") + tag = self.clf.clf_response() + # test blocked by issue raised IOTPAN313 NFC Controller discovery can stop but cannot restart - PN512 + asserts.assertNone(tag, "post-restart: Tag discovery loop should be stopped!") + + else: + # eeprom, so not supported + self.nfc_command("dev1", "start", expected_retcode=-2, expected_nfc_error= NfcErrors.nfc_err_unsupported ) + self.nfc_command("dev1", "stop", expected_retcode=-2, expected_nfc_error= NfcErrors.nfc_err_unsupported ) diff --git a/TEST_APPS/testcases/nfc/test_self.py b/TEST_APPS/testcases/nfc/test_self.py new file mode 100644 index 0000000000..8ae5d9e1a2 --- /dev/null +++ b/TEST_APPS/testcases/nfc/test_self.py @@ -0,0 +1,188 @@ +""" +Copyright 2018 ARM Limited +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. +""" + +import time +from icetea_lib.bench import Bench +from mbed_clitest.tools.tools import test_case +import icetea_lib.tools.asserts as asserts +import nfc_messages +from nfc_messages import NfcErrors +from nfc_cli_helper import CliHelper + + +""" +Standalone (no NFC reader needed) tests, which cover API with no end-to-end checks. +""" +class CreamSconeSelfTests(Bench, CliHelper): + def __init__(self, **kwargs): + testcase_args = { + 'title':"NFC tests with no reader", + 'status':"development", + 'purpose':"NFC target-only checks", + 'component':["NFC"], + 'type':"smoke", + 'requirements':{ + "duts": { + '*': { + "count": 1, + "type": "hardware", + "application": { + "name": "TEST_APPS-device-nfcapp" + } + }, + "1": {"nick": "dev1"} + } + } + } + testcase_args.update(kwargs) + Bench.__init__(self, **testcase_args) + + def setup(self): + pass + + def teardown(self): + self.logger.info("Test teardown: Reboot target...") + self.reset_dut() + + +""" +smoke - target app is running, and can exchange simple values +""" +@test_case(CreamSconeSelfTests) +def test_nfc_error_codes(self): + wally = NfcErrors.nfc_err_not_found + for x in range(0, 3): + self.nfc_command("dev1", "setlastnfcerror %d" % wally.value, expected_nfc_error=wally) + self.nfc_command("dev1", "getlastnfcerror", expected_nfc_error=wally) + + self.nfc_command("dev1", "setlastnfcerror %d" % 0) + self.nfc_command("dev1", "getlastnfcerror") + + +""" +smoke - target app reports if NFC eeprom driver present +""" +@test_case(CreamSconeSelfTests) +def test_nfc_eeprom(self): + + response = self.command("dev1", "iseeprom") # will hold result from the init lib call + self.logger.info("Target includes NFCEEPROM: %s" % response.parsed['iseeprom']) + +""" +check - Assert discovery can be started/stopped +""" +@test_case(CreamSconeSelfTests) +def test_nfc_discovery(self): + + self.nfc_command("dev1", "initnfc") + + response = self.nfc_command("dev1", "iseeprom") # will hold result from the init lib call + eeprom = response.parsed['iseeprom'] + self.logger.info("Target includes NFCEEPROM: %s" % eeprom) + if not eeprom: + self.nfc_command("dev1", "start") + self.nfc_command("dev1", "stop") + self.nfc_command("dev1", "start") + self.nfc_command("dev1", "stop") + else: + # eeprom, so not supported + self.nfc_command("dev1", "start", expected_retcode=-2, expected_nfc_error = NfcErrors.nfc_err_unsupported ) + self.nfc_command("dev1", "stop", expected_retcode=-2 , expected_nfc_error= NfcErrors.nfc_err_unsupported ) + +""" +check - Create a SmartPoster but does not read it back +""" +@test_case(CreamSconeSelfTests) +def test_nfc_setsmartposter(self): + + self.nfc_command("dev1", "initnfc") + self.nfc_command("dev1", "setsmartposter -u https://www.mbed.com") + +@test_case(CreamSconeSelfTests) +def test_nfc_erase(self): + self.nfc_command("dev1", "initnfc") + response = self.nfc_command("dev1", "iseeprom") + eeprom = response.parsed['iseeprom'] + if eeprom: + self.logger.info("Target includes NFCEEPROM: %s" % eeprom) + + self.nfc_command("dev1", "erase", timeout=30) + response = self.nfc_command("dev1", "readmessage") + asserts.assertEqual(response.parsed['nfcmessage'] is None, True) + +''' +check - Build a long message by copying a string to stress the driver with a nominal buffer. Verify contents of entire message +can be read back. +''' +@test_case(CreamSconeSelfTests) +def test_nfc_write_long(self): + messageRep = 'thequickbrownfoxjumpedoverthelazydog' # repeating message written + textLength = 200 # 2K < x < 4K + # calculate actual message to compare to using the library + message = nfc_messages.make_textrecord( nfc_messages.repeat_string_to_length(messageRep, textLength)) + expected_message = str(message) + + self.nfc_command("dev1", "initnfc") + response = self.nfc_command("dev1", "iseeprom") + eeprom = response.parsed['iseeprom'] + if eeprom: + self.logger.info("Target includes NFCEEPROM: %s" % eeprom) + + self.nfc_command("dev1", "erase") + self.nfc_command("dev1", "writelong %d %s" % (textLength,messageRep)) + response = self.nfc_command("dev1", "readmessage") + # assert that read the eeprom contents gives textlength bytes (including framing bytes which will vary) + asserts.assertEqual(len(response.parsed['nfcmessage']), len(expected_message)) + i = 0 + # assert that read the eeprom contents gives thequickbrownfoxjumpedoverthelazydog repeated in loop + while i < len(response.parsed['nfcmessage']): + asserts.assertEqual(response.parsed['nfcmessage'][i], ord(expected_message[i])) + i = i + 1 + +''' +check - Query supported protocols if we have a controller +''' +@test_case(CreamSconeSelfTests) +def test_nfc_get_controller_protocols(self): + self.nfc_command("dev1", "initnfc") + + response = self.nfc_command("dev1", "iseeprom") + eeprom = response.parsed['iseeprom'] + if eeprom: + self.logger.info("Test ignore - target includes NFCEEPROM: %s" % eeprom) + else: + response = self.nfc_command("dev1", "getprotocols") + self.logger.info("Protocols = %s" % response.parsed['protocols']) + + +''' +Set used protocols if we have an controller +''' +@test_case(CreamSconeSelfTests) +def test_nfc_set_controller_protocols(self): + self.nfc_command("dev1", "initnfc") + + response = self.nfc_command("dev1", "iseeprom") + eeprom = response.parsed['iseeprom'] + if eeprom: + self.logger.info("Test ignore - target includes NFCEEPROM: %s" % eeprom) + else: + response = self.nfc_command("dev1", "setprotocols t1t") + response = self.nfc_command("dev1", "setprotocols t2t") + response = self.nfc_command("dev1", "setprotocols t3t") + response = self.nfc_command("dev1", "setprotocols isodep") + response = self.nfc_command("dev1", "setprotocols nfcdep") + response = self.nfc_command("dev1", "setprotocols t5t") + response = self.nfc_command("dev1", "setprotocols t1t t2t t3t isodep nfcdep t5t") From 260e6d1c8f8ea9502b34e422adc8ed39337e1b6d Mon Sep 17 00:00:00 2001 From: Conrad Braam Date: Fri, 4 Jan 2019 09:24:15 +0000 Subject: [PATCH 02/14] astyle,comments,removed headers, fixed get_rf_protocols,copyright headers --- TEST_APPS/device/nfcapp/SmartPoster.cpp | 95 ++++++++----- TEST_APPS/device/nfcapp/SmartPoster.h | 4 +- TEST_APPS/device/nfcapp/main.cpp | 82 +++++------ TEST_APPS/device/nfcapp/nfccommands.cpp | 94 ++++++++----- TEST_APPS/device/nfcapp/nfccommands.h | 8 +- TEST_APPS/device/nfcapp/nfcprocess.h | 45 +++--- TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp | 61 ++++---- TEST_APPS/device/nfcapp/nfcprocessCtrl.h | 18 +-- TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp | 37 +++-- TEST_APPS/device/nfcapp/nfcprocessEeprom.h | 9 +- TEST_APPS/device/nfcapp/nfctestshim.cpp | 140 +++++++++++-------- TEST_APPS/device/nfcapp/nfctestshim.h | 80 +++++------ TEST_APPS/device/nfcapp/uart.cpp | 16 ++- TEST_APPS/testcases/nfc/nfc_clf_wrapper.py | 4 +- TEST_APPS/testcases/nfc/nfc_cli_helper.py | 4 +- TEST_APPS/testcases/nfc/nfc_messages.py | 17 +++ TEST_APPS/testcases/nfc/test_nfc.py | 4 +- TEST_APPS/testcases/nfc/test_self.py | 4 +- 18 files changed, 408 insertions(+), 314 deletions(-) diff --git a/TEST_APPS/device/nfcapp/SmartPoster.cpp b/TEST_APPS/device/nfcapp/SmartPoster.cpp index 5fb7f3ccb0..da2732d762 100644 --- a/TEST_APPS/device/nfcapp/SmartPoster.cpp +++ b/TEST_APPS/device/nfcapp/SmartPoster.cpp @@ -37,76 +37,91 @@ using mbed::nfc::ndef::common::URI; // todo: this class probably needs to be in the nfc module itself namespace { -static RecordType smart_poster_record_type() { +static RecordType smart_poster_record_type() +{ return RecordType(RecordType::well_known_type, span_from_cstr("Sp")); } -static RecordType action_record_type() { +static RecordType action_record_type() +{ return RecordType(RecordType::well_known_type, span_from_cstr("act")); } -static RecordType size_record_type() { +static RecordType size_record_type() +{ return RecordType(RecordType::well_known_type, span_from_cstr("s")); } -static RecordType type_record_type() { +static RecordType type_record_type() +{ return RecordType(RecordType::well_known_type, span_from_cstr("T")); } -static size_t compute_record_size(const RecordType& type, - const RecordPayload& payload) { +static size_t compute_record_size(const RecordType &type, + const RecordPayload &payload) +{ return MessageBuilder::compute_record_size( - Record(type, payload, RecordID(), false, false)); + Record(type, payload, RecordID(), false, false)); } } // end of anonymous namespace SmartPoster::SmartPoster(const URI &uri) : - _uri(uri), _action(), _resource_size(0), _action_set(false), _resource_size_set( - false) { + _uri(uri), _action(), _resource_size(0), _action_set(false), _resource_size_set( + false) +{ } -void SmartPoster::set_title(const Text &text) { +void SmartPoster::set_title(const Text &text) +{ _title = text; } -void SmartPoster::set_icon(const Mime &icon) { +void SmartPoster::set_icon(const Mime &icon) +{ _icon = icon; } -void SmartPoster::set_action(action_t action) { +void SmartPoster::set_action(action_t action) +{ _action = action; _action_set = true; } -void SmartPoster::set_resource_size(uint32_t size) { +void SmartPoster::set_resource_size(uint32_t size) +{ _resource_size = size; _resource_size_set = true; } -void SmartPoster::set_resource_type(Span &type) { +void SmartPoster::set_resource_type(Span &type) +{ _type.set_text(Text::UTF8, Span(), type); } bool SmartPoster::append_record(MessageBuilder &ndef_builder, - bool is_last_record) const { + bool is_last_record) const +{ if (_uri.get_uri_field().empty()) { return false; } struct PayloadBuilder: MessageBuilder::PayloadBuilder { PayloadBuilder(const SmartPoster &sp) : - sp(sp) { + sp(sp) + { } - virtual size_t size() const { + virtual size_t size() const + { return sp.get_uri_record_size() + sp.get_title_record_size() - + sp.get_icon_record_size() + sp.get_action_record_size() - + sp.get_resource_size_record_size() - + sp.get_type_record_size(); + + sp.get_icon_record_size() + sp.get_action_record_size() + + sp.get_resource_size_record_size() + + sp.get_type_record_size(); } - virtual void build(const Span &buffer) const { + virtual void build(const Span &buffer) const + { MessageBuilder smart_poster_builder(buffer); sp.append_title(smart_poster_builder); sp.append_icon(smart_poster_builder); @@ -120,26 +135,30 @@ bool SmartPoster::append_record(MessageBuilder &ndef_builder, }; bool result = ndef_builder.append_record(smart_poster_record_type(), - PayloadBuilder(*this), is_last_record); + PayloadBuilder(*this), is_last_record); return result; } -void SmartPoster::append_uri(MessageBuilder& builder) const { +void SmartPoster::append_uri(MessageBuilder &builder) const +{ _uri.append_as_record(builder, true); } -size_t SmartPoster::get_uri_record_size() const { +size_t SmartPoster::get_uri_record_size() const +{ return _uri.get_record_size(); } -void SmartPoster::append_title(MessageBuilder& builder) const { +void SmartPoster::append_title(MessageBuilder &builder) const +{ if (_title.get_text().empty()) { return; } _title.append_as_record(builder); } -size_t SmartPoster::get_title_record_size() const { +size_t SmartPoster::get_title_record_size() const +{ if (_title.get_text().empty()) { return 0; } @@ -147,14 +166,16 @@ size_t SmartPoster::get_title_record_size() const { return _title.get_record_size(); } -void SmartPoster::append_icon(MessageBuilder& builder) const { +void SmartPoster::append_icon(MessageBuilder &builder) const +{ if (_icon.get_mime_content().empty()) { return; } _icon.append_as_record(builder); } -size_t SmartPoster::get_icon_record_size() const { +size_t SmartPoster::get_icon_record_size() const +{ if (_icon.get_mime_content().empty()) { return 0; } @@ -162,7 +183,8 @@ size_t SmartPoster::get_icon_record_size() const { return _icon.get_record_size(); } -void SmartPoster::append_action(MessageBuilder& builder) const { +void SmartPoster::append_action(MessageBuilder &builder) const +{ if (!_action_set) { return; } @@ -171,7 +193,8 @@ void SmartPoster::append_action(MessageBuilder& builder) const { builder.append_record(action_record_type(), action_value); } -size_t SmartPoster::get_action_record_size() const { +size_t SmartPoster::get_action_record_size() const +{ if (!_action_set) { return 0; } @@ -181,7 +204,8 @@ size_t SmartPoster::get_action_record_size() const { return compute_record_size(action_record_type(), action_value); } -void SmartPoster::append_resource_size(MessageBuilder& builder) const { +void SmartPoster::append_resource_size(MessageBuilder &builder) const +{ if (!_resource_size_set) { return; } @@ -192,7 +216,8 @@ void SmartPoster::append_resource_size(MessageBuilder& builder) const { builder.append_record(size_record_type(), value); } -size_t SmartPoster::get_resource_size_record_size() const { +size_t SmartPoster::get_resource_size_record_size() const +{ if (!_resource_size_set) { return 0; } @@ -202,7 +227,8 @@ size_t SmartPoster::get_resource_size_record_size() const { return compute_record_size(size_record_type(), value); } -void SmartPoster::append_type(MessageBuilder& builder) const { +void SmartPoster::append_type(MessageBuilder &builder) const +{ if (_type.get_text().empty()) { return; } @@ -210,7 +236,8 @@ void SmartPoster::append_type(MessageBuilder& builder) const { builder.append_record(type_record_type(), _type.get_text()); } -size_t SmartPoster::get_type_record_size() const { +size_t SmartPoster::get_type_record_size() const +{ if (_type.get_text().empty()) { return 0; } diff --git a/TEST_APPS/device/nfcapp/SmartPoster.h b/TEST_APPS/device/nfcapp/SmartPoster.h index bba5838545..d12e979a58 100644 --- a/TEST_APPS/device/nfcapp/SmartPoster.h +++ b/TEST_APPS/device/nfcapp/SmartPoster.h @@ -132,8 +132,8 @@ private: uint32_t _resource_size; Text _type; - bool _action_set :1; - bool _resource_size_set :1; + bool _action_set : 1; + bool _resource_size_set : 1; }; #endif /* SMARTPOSTER_H_ */ diff --git a/TEST_APPS/device/nfcapp/main.cpp b/TEST_APPS/device/nfcapp/main.cpp index 4398b0145c..b38a2ee9cf 100644 --- a/TEST_APPS/device/nfcapp/main.cpp +++ b/TEST_APPS/device/nfcapp/main.cpp @@ -18,14 +18,12 @@ #include #include #include -#include -#include "mbed.h" +#include +#include "platform/Span.h" #include "mbed_events.h" #include "mbed-client-cli/ns_cmdline.h" -#include "NFCEEPROMDriver.h" -#include "nfctestshim.h" -#include "nfccommands.h" -#include "smartposter.h" +#include "nfcTestShim.h" +#include "nfcCommands.h" #if MBED_CONF_NFCEEPROM using mbed::nfc::NFCEEPROM; @@ -35,63 +33,51 @@ using mbed::nfc::NFCEEPROMDriver; #warning [NOT_SUPPORTED] NFC not supported for this target #endif -#include "nfc/controllers/PN512Driver.h" -#include "nfc/controllers/PN512SPITransportDriver.h" - -#include "nfc/NFCRemoteInitiator.h" -#include "nfc/NFCController.h" - -using mbed::nfc::NFCRemoteInitiator; -using mbed::nfc::NFCController; -using mbed::nfc::nfc_rf_protocols_bitmask_t; #endif // MBED_CONF_NFCEEPROM -using mbed::Span; -using mbed::nfc::ndef::MessageBuilder; -using mbed::nfc::ndef::common::Text; -using mbed::nfc::ndef::common::URI; -using mbed::nfc::ndef::common::span_from_cstr; - -void wrap_printf(const char *f, va_list a) { +void wrap_printf(const char *f, va_list a) +{ vprintf(f, a); } const char *errorcodes = // descriptions from nfc/stack/nfc_errors.h - " 0 NFC_OK \n" - " 1 NFC_ERR_UNKNOWN\n" - " 2 NFC_ERR_LENGTH \n" - " 3 NFC_ERR_NOT_FOUND\n" - " 4 NFC_ERR_UNSUPPORTED\n" - " 5 NFC_ERR_PARAMS \n" - " 6 NFC_ERR_BUFFER_TOO_SMALL\n" - " 7 NFC_ERR_TIMEOUT\n" - " 8 NFC_ERR_CRC\n" - " 9 NFC_ERR_NOPEER \n" - "10 NFC_ERR_PARITY \n" - "11 NFC_ERR_FIELD\n" - "12 NFC_ERR_COLLISION\n" - "13 NFC_ERR_WRONG_COMM \n" - "14 NFC_ERR_PROTOCOL \n" - "15 NFC_ERR_BUSY \n" - "16 NFC_ERR_CONTROLLER \n" - "17 NFC_ERR_HALTED \n" - "18 NFC_ERR_MAC\n" - "19 NFC_ERR_UNDERFLOW\n" - "20 NFC_ERR_DISCONNECTED \n" - "21 NFC_ERR_ABORTED\n"; + " 0 NFC_OK \n" + " 1 NFC_ERR_UNKNOWN\n" + " 2 NFC_ERR_LENGTH \n" + " 3 NFC_ERR_NOT_FOUND\n" + " 4 NFC_ERR_UNSUPPORTED\n" + " 5 NFC_ERR_PARAMS \n" + " 6 NFC_ERR_BUFFER_TOO_SMALL\n" + " 7 NFC_ERR_TIMEOUT\n" + " 8 NFC_ERR_CRC\n" + " 9 NFC_ERR_NOPEER \n" + "10 NFC_ERR_PARITY \n" + "11 NFC_ERR_FIELD\n" + "12 NFC_ERR_COLLISION\n" + "13 NFC_ERR_WRONG_COMM \n" + "14 NFC_ERR_PROTOCOL \n" + "15 NFC_ERR_BUSY \n" + "16 NFC_ERR_CONTROLLER \n" + "17 NFC_ERR_HALTED \n" + "18 NFC_ERR_MAC\n" + "19 NFC_ERR_UNDERFLOW\n" + "20 NFC_ERR_DISCONNECTED \n" + "21 NFC_ERR_ABORTED\n"; // for easy manual UI interaction -int seteasy(int argc, char *argv[]) { +int seteasy(int argc, char *argv[]) +{ const char msg[][20] = - { "echo off", "set --retcode true", "set --vt100 off" }; + { "echo off", "set --retcode true", "set --vt100 off" }; for (size_t i = 0; i < (sizeof(msg) / sizeof(msg[0])); i++) { - cmd_exe((char*) msg[i]); + cmd_exe((char *) msg[i]); } return (CMDLINE_RETCODE_SUCCESS); } -int main(int argc, char *argv[]) { +int main(int argc, char *argv[]) +{ cmd_init(&wrap_printf); cmd_add("getlastnfcerror", HandleTestCommand::cmd_get_last_nfc_error, "last NFC error code", errorcodes); diff --git a/TEST_APPS/device/nfcapp/nfccommands.cpp b/TEST_APPS/device/nfcapp/nfccommands.cpp index 2462b40c71..b6bf082235 100644 --- a/TEST_APPS/device/nfcapp/nfccommands.cpp +++ b/TEST_APPS/device/nfcapp/nfccommands.cpp @@ -16,40 +16,46 @@ #include #include #include -#include "mbed.h" #include "mbed_events.h" #include "mbed-client-cli/ns_cmdline.h" #include "rtos\Thread.h" +#include "nfcTestShim.h" +#include "nfcCommands.h" -#include "nfctestshim.h" - -#include "nfccommands.h" +#if MBED_CONF_NFCEEPROM +#include "NFCEEPROMDriver.h" +#include "nfcProcessEeprom.h" +#endif events::EventQueue nfcQueue; -Thread nfcThread; -NFCTestShim * pNFC_Test_Shim = NULL; +rtos::Thread nfcThread; +NFCTestShim *pNFC_Test_Shim = NULL; -NFCTestShim* new_testshim() { +NFCTestShim *new_testshim() +{ #if MBED_CONF_NFCEEPROM - mbed::nfc::NFCEEPROMDriver& eeprom_driver = get_eeprom_driver(nfcQueue); + mbed::nfc::NFCEEPROMDriver &eeprom_driver = get_eeprom_driver(nfcQueue); - return ( (NFCTestShim *)(new NFCProcessEEPROM(nfcQueue, eeprom_driver)) ); + return ((NFCTestShim *)(new NFCProcessEEPROM(nfcQueue, eeprom_driver))); #else - return ((NFCTestShim *) (new NFCProcessController(nfcQueue))); + return ((NFCTestShim *)(new NFCProcessController(nfcQueue))); #endif // EEPROM } -void nfcRoutine() { +void nfcRoutine() +{ nfcQueue.dispatch_forever(); } -HandleTestCommand::HandleTestCommand() { - osStatus status = nfcThread.start(callback(&nfcRoutine)); +HandleTestCommand::HandleTestCommand() +{ + osStatus status = nfcThread.start(mbed::callback(&nfcRoutine)); MBED_ASSERT(status == osOK); } -int HandleTestCommand::cmd_set_last_nfc_error(int argc, char *argv[]) { +int HandleTestCommand::cmd_set_last_nfc_error(int argc, char *argv[]) +{ if (argc <= 1) { cmd_printf("setlastnfcerror() invalid parameter(s)\r\n"); return (CMDLINE_RETCODE_INVALID_PARAMETERS); @@ -60,7 +66,8 @@ int HandleTestCommand::cmd_set_last_nfc_error(int argc, char *argv[]) { return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } -int HandleTestCommand::cmd_init_nfc(int argc, char *argv[]) { +int HandleTestCommand::cmd_init_nfc(int argc, char *argv[]) +{ if (pNFC_Test_Shim) { cmd_printf("WARN init called again!\r\n"); // only legal here, if eeprom driver stops talking @@ -71,39 +78,42 @@ int HandleTestCommand::cmd_init_nfc(int argc, char *argv[]) { return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } -int HandleTestCommand::cmd_read_message(int argc, char *argv[]) { +int HandleTestCommand::cmd_read_message(int argc, char *argv[]) +{ nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_read_nfceeprom); return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } -int HandleTestCommand::cmd_set_smartposter(int argc, char *argv[]) { +int HandleTestCommand::cmd_set_smartposter(int argc, char *argv[]) +{ if (argc <= 1) { cmd_printf("setlastnfcerror() invalid parameter(s)\r\n"); return (CMDLINE_RETCODE_INVALID_PARAMETERS); } else { // parse arg and queue it up - char * uri = (char*) malloc(strlen(argv[1]) + 1); + char *uri = (char *) malloc(strlen(argv[1]) + 1); if (uri) { strcpy(uri, argv[1]); nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_set_smartposter, - uri); // called thread must free - } - else { + uri); // called thread must free + } else { cmd_printf("WARN out of memory!\r\n"); return (CMDLINE_RETCODE_FAIL); - } + } } return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } // todo: jira IOTPAN-295 -int HandleTestCommand::cmd_erase(int argc, char *argv[]) { +int HandleTestCommand::cmd_erase(int argc, char *argv[]) +{ nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_erase); return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } -int HandleTestCommand::cmd_write_long_ndef_message(int argc, char *argv[]) { +int HandleTestCommand::cmd_write_long_ndef_message(int argc, char *argv[]) +{ size_t length, idx, sourceLength; static const char alphabet[] = "thequickbrownfoxjumpedoverthelazydog"; char *data; @@ -120,8 +130,8 @@ int HandleTestCommand::cmd_write_long_ndef_message(int argc, char *argv[]) { cmd_printf("Cannot convert value to int\r\n"); return (CMDLINE_RETCODE_INVALID_PARAMETERS); } - data = (char*) malloc(length + 1); - if (!data) { + data = (char *) malloc(length + 1); + if (!data) { cmd_printf("WARN out of memory!\r\n"); return (CMDLINE_RETCODE_FAIL); } @@ -143,7 +153,8 @@ int HandleTestCommand::cmd_write_long_ndef_message(int argc, char *argv[]) { return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } -int HandleTestCommand::cmd_start_discovery(int argc, char *argv[]) { +int HandleTestCommand::cmd_start_discovery(int argc, char *argv[]) +{ if ((argc > 1) && (0 == strcmp(argv[1], "man"))) { cmd_printf("User must restart discovery manually()\r\n"); nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_start_discovery, false); @@ -154,18 +165,21 @@ int HandleTestCommand::cmd_start_discovery(int argc, char *argv[]) { return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } -int HandleTestCommand::cmd_stop_discovery(int argc, char *argv[]) { +int HandleTestCommand::cmd_stop_discovery(int argc, char *argv[]) +{ nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_stop_discovery); return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } -int HandleTestCommand::cmd_get_supported_rf_protocols(int argc, char *argv[]) { +int HandleTestCommand::cmd_get_supported_rf_protocols(int argc, char *argv[]) +{ nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_get_rf_protocols); return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } bool HandleTestCommand::set_protocol_target( - nfc_rf_protocols_bitmask_t & bitmask, const char *protocolName) { + nfc_rf_protocols_bitmask_t &bitmask, const char *protocolName) +{ bool parsed = false; if (0 == strcmp(protocolName, "t1t")) { parsed = bitmask.target_t1t = true; @@ -188,7 +202,8 @@ bool HandleTestCommand::set_protocol_target( return (parsed); } -int HandleTestCommand::cmd_configure_rf_protocols(int argc, char *argv[]) { +int HandleTestCommand::cmd_configure_rf_protocols(int argc, char *argv[]) +{ nfc_rf_protocols_bitmask_t protocols = { 0 }; int argindex = argc; @@ -200,12 +215,13 @@ int HandleTestCommand::cmd_configure_rf_protocols(int argc, char *argv[]) { argindex--; } nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_configure_rf_protocols, - protocols); + protocols); return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } // todo: implement -int cmd_start_stop_discovery_wait_tag(int argc, char *argv[]) { +int cmd_start_stop_discovery_wait_tag(int argc, char *argv[]) +{ return (CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED); } @@ -213,19 +229,23 @@ int cmd_start_stop_discovery_wait_tag(int argc, char *argv[]) { ///////////////////////////////////////////////////////////////////// // boilerplate only -int cmd_is_iso7816_supported(int argc, char *argv[]) { +int cmd_is_iso7816_supported(int argc, char *argv[]) +{ return (CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED); } -int cmd_add_iso7816_application(int argc, char *argv[]) { +int cmd_add_iso7816_application(int argc, char *argv[]) +{ return (CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED); } -int cmd_set_tagtype(int argc, char *argv[]) { +int cmd_set_tagtype(int argc, char *argv[]) +{ return (CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED); } -int cmd_get_tagtype(int argc, char *argv[]) { +int cmd_get_tagtype(int argc, char *argv[]) +{ return (CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED); } diff --git a/TEST_APPS/device/nfcapp/nfccommands.h b/TEST_APPS/device/nfcapp/nfccommands.h index 1b99f23335..7477a4bc82 100644 --- a/TEST_APPS/device/nfcapp/nfccommands.h +++ b/TEST_APPS/device/nfcapp/nfccommands.h @@ -33,10 +33,11 @@ public: // start thread and handle queue HandleTestCommand(); /* set corresponding mask bit on, return false if the supplied string cannot parse */ - static bool set_protocol_target(nfc_rf_protocols_bitmask_t & bitmask, const char *protocolName); + static bool set_protocol_target(nfc_rf_protocols_bitmask_t &bitmask, const char *protocolName); /* return and clear the last result code. Type "help getlastnfcerror" for a list of error codes */ - static int cmd_get_last_nfc_error(int argc, char *argv[]) { + static int cmd_get_last_nfc_error(int argc, char *argv[]) + { nfcQueue.call(NFCTestShim::cmd_get_last_nfc_error); return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } @@ -45,7 +46,8 @@ public: static int cmd_set_last_nfc_error(int argc, char *argv[]); /* compile time flag */ - static int cmd_get_conf_nfceeprom(int argc, char *argv[]) { + static int cmd_get_conf_nfceeprom(int argc, char *argv[]) + { nfcQueue.call(NFCTestShim::cmd_get_conf_nfceeprom); return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } diff --git a/TEST_APPS/device/nfcapp/nfcprocess.h b/TEST_APPS/device/nfcapp/nfcprocess.h index 834dd4d937..987ae1058b 100644 --- a/TEST_APPS/device/nfcapp/nfcprocess.h +++ b/TEST_APPS/device/nfcapp/nfcprocess.h @@ -25,20 +25,20 @@ #include "nfc/ndef/MessageBuilder.h" #include "nfc/ndef/common/URI.h" #include "nfc/ndef/common/util.h" -#include "nfctestshim.h" +#include "nfcTestShim.h" #if MBED_CONF_NFCEEPROM - #include "NFCEEPROM.h" - #include "EEPROMDriver.h" +#include "NFCEEPROM.h" +#include "EEPROMDriver.h" #else - #include "nfc/nfcdefinitions.h" - #ifdef TARGET_PN512 - #include "nfc/controllers/PN512Driver.h" - #include "nfc/controllers/PN512SPITransportDriver.h" - #endif - #include "nfc/NFCRemoteInitiator.h" - #include "nfc/NFCController.h" - #include "nfc/ndef/common/util.h" +#include "nfc/nfcdefinitions.h" +#ifdef TARGET_PN512 +#include "nfc/controllers/PN512Driver.h" +#include "nfc/controllers/PN512SPITransportDriver.h" +#endif +#include "nfc/NFCRemoteInitiator.h" +#include "nfc/NFCController.h" +#include "nfc/ndef/common/util.h" #endif // MBED_CONF_NFCEEPROM @@ -50,13 +50,12 @@ using mbed::Span; #if MBED_CONF_NFCEEPROM - using mbed::nfc::NFCEEPROM; - using mbed::nfc::NFCEEPROMDriver; +using mbed::nfc::NFCEEPROM; +using mbed::nfc::NFCEEPROMDriver; -class NFCProcessEEPROM : NFCTestShim , mbed::nfc::NFCEEPROM::Delegate -{ +class NFCProcessEEPROM : NFCTestShim, mbed::nfc::NFCEEPROM::Delegate { public: - NFCProcessEEPROM(events::EventQueue& queue, NFCEEPROMDriver& eeprom_driver) ; + NFCProcessEEPROM(events::EventQueue &queue, NFCEEPROMDriver &eeprom_driver) ; nfc_err_t init(); void queue_write_call(); void queue_write_long_call(); @@ -72,7 +71,7 @@ private: private: uint8_t _ndef_buffer[0x2000]; // if this buffer is smaller than the EEPROM, the driver may crash see IOTPAN-297 NFCEEPROM _eeprom; - EventQueue& _queue; + EventQueue &_queue; }; #else // NFC Controller @@ -84,8 +83,14 @@ public: nfc_err_t init(); nfc_err_t start_discovery(); nfc_err_t stop_discovery(); - void set_discovery_restart_auto() {_discovery_restart = true;}; - void set_discovery_restart_manual(){_discovery_restart = false;}; + void set_discovery_restart_auto() + { + _discovery_restart = true; + }; + void set_discovery_restart_manual() + { + _discovery_restart = false; + }; nfc_rf_protocols_bitmask_t get_rf_protocols(); nfc_err_t set_rf_protocols(nfc_rf_protocols_bitmask_t protocols); @@ -107,7 +112,7 @@ private: mbed::nfc::PN512SPITransportDriver _pn512_transport; mbed::nfc::PN512Driver _pn512_driver; protected: - EventQueue& _queue; + EventQueue &_queue; private: NFCController _nfc_controller; SharedPtr _nfc_remote_initiator; diff --git a/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp b/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp index 3b1dfa0ae8..2e703744b3 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp +++ b/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp @@ -17,7 +17,6 @@ #include #include #include -#include "mbed.h" #include "mbed_events.h" #include "mbed-client-cli/ns_cmdline.h" @@ -50,10 +49,11 @@ using mbed::nfc::NFCController; //class NFCProcessController : NFCRemoteInitiator::Delegate, NFCController::Delegate { NFCProcessController::NFCProcessController(events::EventQueue &queue) : - // pins: mosi, miso, sclk, ssel, irq, rst - _pn512_transport(D11, D12, D13, D10, A1, A0), _pn512_driver( - &_pn512_transport), _queue(queue), _nfc_controller( - &_pn512_driver, &queue, _ndef_buffer) { + // pins: mosi, miso, sclk, ssel, irq, rst + _pn512_transport(D11, D12, D13, D10, A1, A0), _pn512_driver( + &_pn512_transport), _queue(queue), _nfc_controller( + &_pn512_driver, &queue, _ndef_buffer) +{ } /** @@ -62,7 +62,8 @@ NFCProcessController::NFCProcessController(events::EventQueue &queue) : * @return NFC_OK in case of success or a meaningful error code in case of * failure. */ -nfc_err_t NFCProcessController::init() { +nfc_err_t NFCProcessController::init() +{ cmd_printf("init()\r\n"); // register callbacks @@ -76,7 +77,8 @@ nfc_err_t NFCProcessController::init() { * @return NFC_OK in case of success or a meaningful error code in case of * failure. */ -nfc_err_t NFCProcessController::start_discovery() { +nfc_err_t NFCProcessController::start_discovery() +{ cmd_printf("start_discovery()\r\n"); return _nfc_controller.start_discovery(); @@ -88,31 +90,37 @@ nfc_err_t NFCProcessController::start_discovery() { * @return NFC_OK in case of success or a meaningful error code in case of * failure. */ -nfc_err_t NFCProcessController::stop_discovery() { +nfc_err_t NFCProcessController::stop_discovery() +{ cmd_printf("stop_discovery()\r\n"); return _nfc_controller.cancel_discovery(); } -nfc_rf_protocols_bitmask_t NFCProcessController::get_rf_protocols() { +nfc_rf_protocols_bitmask_t NFCProcessController::get_rf_protocols() +{ cmd_printf("get_supported_rf_protocols()\r\n"); return _nfc_controller.get_supported_rf_protocols(); } nfc_err_t NFCProcessController::set_rf_protocols( - nfc_rf_protocols_bitmask_t protocols) { + nfc_rf_protocols_bitmask_t protocols) +{ cmd_printf("configure_rf_protocols()\r\n"); return _nfc_controller.configure_rf_protocols(protocols); } + /* ------------------------------------------------------------------------ * Implementation of NFCRemoteInitiator::Delegate */ -void NFCProcessController::on_connected() { +void NFCProcessController::on_connected() +{ cmd_printf("on_connected()\r\n"); } -void NFCProcessController::on_disconnected() { +void NFCProcessController::on_disconnected() +{ cmd_printf("on_disconnected()\r\n"); // reset the state of the remote initiator @@ -124,7 +132,8 @@ void NFCProcessController::on_disconnected() { } void NFCProcessController::parse_ndef_message( - const Span &buffer) { + const Span &buffer) +{ size_t len = buffer.size(); // copy remotely written message into our dummy buffer if (len <= sizeof(_ndef_write_buffer)) { @@ -136,9 +145,10 @@ void NFCProcessController::parse_ndef_message( } } -size_t NFCProcessController::build_ndef_message(const Span &buffer) { +size_t NFCProcessController::build_ndef_message(const Span &buffer) +{ cmd_printf("Copying message %d bytes to query buffer\r\n", - _ndef_write_buffer_used); + _ndef_write_buffer_used); memcpy(buffer.data(), _ndef_write_buffer, _ndef_write_buffer_used); for (size_t k = 0; k < _ndef_write_buffer_used; k++) { cmd_printf("%02x ", buffer[k]); @@ -147,22 +157,24 @@ size_t NFCProcessController::build_ndef_message(const Span &buffer) { } const char *NFCProcessController::str_discovery_terminated_reason( - nfc_discovery_terminated_reason_t reason) { - static const char* reasons[4] = { "completed", "cancelled", "rf error"}; + nfc_discovery_terminated_reason_t reason) +{ + static const char *reasons[4] = { "completed", "cancelled", "rf error"}; switch (reason) { - case nfc_discovery_terminated_completed : - case nfc_discovery_terminated_canceled: - case nfc_discovery_terminated_rf_error: - return reasons[reason]; + case nfc_discovery_terminated_completed : + case nfc_discovery_terminated_canceled: + case nfc_discovery_terminated_rf_error: + return reasons[reason]; } return "unexpected!"; } void NFCProcessController::on_discovery_terminated( - nfc_discovery_terminated_reason_t reason) { + nfc_discovery_terminated_reason_t reason) +{ cmd_printf("on_discovery_terminated(%s)\r\n", - str_discovery_terminated_reason(reason)); + str_discovery_terminated_reason(reason)); if (reason != nfc_discovery_terminated_completed && this->_discovery_restart) { start_discovery(); @@ -170,7 +182,8 @@ void NFCProcessController::on_discovery_terminated( } void NFCProcessController::on_nfc_initiator_discovered( - const SharedPtr &nfc_initiator) { + const SharedPtr &nfc_initiator) +{ cmd_printf("on_nfc_initiator_discovered()\r\n"); // setup the local remote initiator diff --git a/TEST_APPS/device/nfcapp/nfcprocessCtrl.h b/TEST_APPS/device/nfcapp/nfcprocessCtrl.h index 44b6c6326d..2ffda6049e 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessCtrl.h +++ b/TEST_APPS/device/nfcapp/nfcprocessCtrl.h @@ -21,11 +21,11 @@ #include #include #include - +#include "mbed_events.h" #include "nfc/ndef/MessageBuilder.h" #include "nfc/ndef/common/URI.h" #include "nfc/ndef/common/util.h" -#include "nfctestshim.h" +#include "nfcTestShim.h" #if !MBED_CONF_NFCEEPROM @@ -39,6 +39,8 @@ #include "nfc/ndef/common/util.h" +using mbed::nfc::NFCRemoteInitiator; +using mbed::nfc::NFCController; using mbed::nfc::ndef::MessageBuilder; using mbed::nfc::ndef::common::URI; using mbed::nfc::ndef::common::span_from_cstr; @@ -47,8 +49,8 @@ using mbed::Span; class NFCProcessController: NFCTestShim, - NFCRemoteInitiator::Delegate, - NFCController::Delegate { + NFCRemoteInitiator::Delegate, + NFCController::Delegate { public: NFCProcessController(events::EventQueue &queue); @@ -61,7 +63,7 @@ public: virtual void parse_ndef_message(const Span &buffer); virtual size_t build_ndef_message(const Span &buffer); const char *str_discovery_terminated_reason( - nfc_discovery_terminated_reason_t reason); + nfc_discovery_terminated_reason_t reason); private: // these events are handled, to restart discovery @@ -74,11 +76,11 @@ private: /** * Implementation of NFCController::Delegate */ virtual void on_discovery_terminated( - nfc_discovery_terminated_reason_t reason); + nfc_discovery_terminated_reason_t reason); /** * Implementation of NFCController::Delegate */ virtual void on_nfc_initiator_discovered( - const SharedPtr &nfc_initiator); + const SharedPtr &nfc_initiator); private: @@ -86,7 +88,7 @@ private: mbed::nfc::PN512SPITransportDriver _pn512_transport; mbed::nfc::PN512Driver _pn512_driver; protected: - EventQueue& _queue; + EventQueue &_queue; private: NFCController _nfc_controller; SharedPtr _nfc_remote_initiator; diff --git a/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp b/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp index 31db64eca2..d19b0a8e12 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp +++ b/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp @@ -17,7 +17,6 @@ #include #include #include -#include "mbed.h" #include "mbed_events.h" #include "mbed-client-cli/ns_cmdline.h" @@ -45,12 +44,13 @@ using mbed::nfc::ndef::common::Text; using mbed::nfc::ndef::common::URI; // implements : mbed::nfc::NFCEEPROM::Delegate -NFCProcessEEPROM::NFCProcessEEPROM(events::EventQueue& queue, NFCEEPROMDriver& eeprom_driver) : -_eeprom(&eeprom_driver, &queue, _ndef_buffer), -_queue(queue) +NFCProcessEEPROM::NFCProcessEEPROM(events::EventQueue &queue, NFCEEPROMDriver &eeprom_driver) : + _eeprom(&eeprom_driver, &queue, _ndef_buffer), + _queue(queue) {} -nfc_err_t NFCProcessEEPROM::init() { +nfc_err_t NFCProcessEEPROM::init() +{ nfc_err_t err = _eeprom.initialize(); if (err != NFC_OK) { cmd_printf("NFCProcessEEPROM::init() (error: %d)!\r\n", err); @@ -59,25 +59,29 @@ nfc_err_t NFCProcessEEPROM::init() { cmd_printf("NFCProcessEEPROM::init() OK\r\n"); } _eeprom.set_delegate(this); - return(err); + return (err); } -void NFCProcessEEPROM::queue_write_call() { +void NFCProcessEEPROM::queue_write_call() +{ cmd_printf("NFCProcessEEPROM::queue_write_call() entry\r\n"); _queue.call(&_eeprom, &NFCEEPROM::write_ndef_message); } -void NFCProcessEEPROM::queue_read_call() { +void NFCProcessEEPROM::queue_read_call() +{ cmd_printf("NFCProcessEEPROM::queue_read_call() entry\r\n"); _queue.call(&_eeprom, &NFCEEPROM::read_ndef_message); } -void NFCProcessEEPROM::queue_erase_call() { +void NFCProcessEEPROM::queue_erase_call() +{ cmd_printf("NFCProcessEEPROM::queue_erase_call() entry\r\n"); _queue.call(&_eeprom, &NFCEEPROM::erase_ndef_message); } -void NFCProcessEEPROM::on_ndef_message_written(nfc_err_t result) { +void NFCProcessEEPROM::on_ndef_message_written(nfc_err_t result) +{ // todo: de-duplicate this code set_last_nfc_error(result); if (result == NFC_OK) { @@ -89,7 +93,8 @@ void NFCProcessEEPROM::on_ndef_message_written(nfc_err_t result) { cmd_ready(CMDLINE_RETCODE_SUCCESS); } -void NFCProcessEEPROM::on_ndef_message_read(nfc_err_t result) { +void NFCProcessEEPROM::on_ndef_message_read(nfc_err_t result) +{ set_last_nfc_error(result); if (result == NFC_OK) { cmd_printf("message read successfully\r\n"); @@ -113,17 +118,19 @@ void NFCProcessEEPROM::on_ndef_message_erased(nfc_err_t result) cmd_ready(CMDLINE_RETCODE_SUCCESS); } -void NFCProcessEEPROM::parse_ndef_message(const Span &buffer) { +void NFCProcessEEPROM::parse_ndef_message(const Span &buffer) +{ cmd_printf("Received an ndef message of size %d\r\n", buffer.size()); print_ndef_message(buffer, buffer.size()); } -size_t NFCProcessEEPROM::build_ndef_message(const Span &buffer) { +size_t NFCProcessEEPROM::build_ndef_message(const Span &buffer) +{ cmd_printf("Copying ndef message %d bytes into buffer\r\n", _ndef_write_buffer_used); // make a copy into our buffer memcpy(buffer.data(), _ndef_write_buffer, _ndef_write_buffer_used); - for (size_t k=0; k<_ndef_write_buffer_used; k++ ) { - cmd_printf("%02x ", buffer[k] ); + for (size_t k = 0; k < _ndef_write_buffer_used; k++) { + cmd_printf("%02x ", buffer[k]); } return _ndef_write_buffer_used; } diff --git a/TEST_APPS/device/nfcapp/nfcprocessEeprom.h b/TEST_APPS/device/nfcapp/nfcprocessEeprom.h index d0ae7f616d..bfb81a6616 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessEeprom.h +++ b/TEST_APPS/device/nfcapp/nfcprocessEeprom.h @@ -25,7 +25,7 @@ #include "nfc/ndef/MessageBuilder.h" #include "nfc/ndef/common/URI.h" #include "nfc/ndef/common/util.h" -#include "nfctestshim.h" +#include "nfcTestShim.h" #if MBED_CONF_NFCEEPROM #include "NFCEEPROM.h" @@ -43,10 +43,9 @@ using mbed::Span; using mbed::nfc::NFCEEPROM; using mbed::nfc::NFCEEPROMDriver; -class NFCProcessEEPROM : NFCTestShim , mbed::nfc::NFCEEPROM::Delegate -{ +class NFCProcessEEPROM : NFCTestShim, mbed::nfc::NFCEEPROM::Delegate { public: - NFCProcessEEPROM(events::EventQueue& queue, NFCEEPROMDriver& eeprom_driver); + NFCProcessEEPROM(events::EventQueue &queue, NFCEEPROMDriver &eeprom_driver); nfc_err_t init(); void queue_write_call(); void queue_write_long_call(); @@ -61,7 +60,7 @@ private: virtual void on_ndef_message_erased(nfc_err_t result); private: NFCEEPROM _eeprom; - EventQueue& _queue; + events::EventQueue &_queue; }; #endif // eeprom diff --git a/TEST_APPS/device/nfcapp/nfctestshim.cpp b/TEST_APPS/device/nfcapp/nfctestshim.cpp index 389eebceec..0915bcd440 100644 --- a/TEST_APPS/device/nfcapp/nfctestshim.cpp +++ b/TEST_APPS/device/nfcapp/nfctestshim.cpp @@ -17,7 +17,6 @@ #include #include #include -#include "mbed.h" #include "mbed-client-cli/ns_cmdline.h" #include "nfc/ndef/common/Text.h" #include "nfc/ndef/common/URI.h" @@ -28,7 +27,7 @@ #include "NFCEEPROMDriver.h" #include "nfcCommands.h" -#include "nfctestshim.h" +#include "nfcTestShim.h" #include "SmartPoster.h" using mbed::Span; @@ -40,44 +39,54 @@ using mbed::nfc::ndef::common::URI; using mbed::nfc::nfc_rf_protocols_bitmask_t; // statics -char NFCTestShim::long_string[0x2000]; +namespace { +char long_string[0x2000]; -int NFCTestShim::last_nfc_error = 0; +int last_nfc_error = 0; #if MBED_CONF_NFCEEPROM -int NFCTestShim::using_eeprom = true; +int using_eeprom = true; #else -int NFCTestShim::using_eeprom = false; +int using_eeprom = false; #endif +} NFCTestShim::NFCTestShim() : - _ndef_write_buffer_used(0), ndef_poster_message(_ndef_write_buffer), _discovery_restart( - true) // on disconnect, will restart discovery + _ndef_write_buffer_used(0), ndef_poster_message(_ndef_write_buffer), + _discovery_restart(true) // on disconnect, will restart discovery { } -// The last failed NFC API call status, gets cleared upon reading it. -void NFCTestShim::get_last_nfc_error() { - int last = last_nfc_error; - last_nfc_error = 0; - // return data to the plugin framework +/** \brief The last failed NFC API call status, gets cleared upon reading it. + * \return void The NFC error is set asyncronously by sending text back over serial + */ +void NFCTestShim::get_last_nfc_error() +{ + int last = ::last_nfc_error; + ::last_nfc_error = 0; + // return data as text to the plugin framework cmd_printf("{{lastnfcerror=%d}}\r\n", last); cmd_ready(CMDLINE_RETCODE_SUCCESS); } -void NFCTestShim::set_last_nfc_error(int err) { - last_nfc_error = err; - cmd_printf("\r\n{{lastnfcerror=%d}}\r\n", last_nfc_error); +void NFCTestShim::set_last_nfc_error(int err) +{ + ::last_nfc_error = err; + cmd_printf("\r\n{{lastnfcerror=%d}}\r\n", ::last_nfc_error); } // if an NFC EEPROM driver is configured -void NFCTestShim::get_conf_nfceeprom() { +void NFCTestShim::get_conf_nfceeprom() +{ set_last_nfc_error(NFC_OK); - cmd_printf("{{iseeprom=%s}}\r\n", (using_eeprom ? "true" : "false")); + // return data as text to the plugin framework + cmd_printf("{{iseeprom=%s}}\r\n", (::using_eeprom ? "true" : "false")); cmd_ready(CMDLINE_RETCODE_SUCCESS); } void NFCTestShim::print_ndef_message(const Span &buffer, - size_t length) { + size_t length) +{ + // return data as text to the plugin framework cmd_printf("{{nfcmessage="); for (size_t k = 0; k < length; k++) { cmd_printf("%02x ", buffer.data()[k]); @@ -85,7 +94,8 @@ void NFCTestShim::print_ndef_message(const Span &buffer, cmd_printf("}}\r\n"); } -void NFCTestShim::cmd_init() { +void NFCTestShim::cmd_init() +{ nfc_err_t ret = init(); set_last_nfc_error(ret); @@ -96,20 +106,16 @@ void NFCTestShim::cmd_init() { } } -void NFCTestShim::cmd_get_rf_protocols() { +void NFCTestShim::cmd_get_rf_protocols() +{ #if MBED_CONF_NFCEEPROM cmd_printf("EEPROM cannot get protocol()\r\n"); set_last_nfc_error(NFC_ERR_UNSUPPORTED); cmd_ready(CMDLINE_RETCODE_INVALID_PARAMETERS); #else - nfc_rf_protocols_bitmask_t protocols = - ((NFCProcessController*) this)->get_rf_protocols(); - protocols.target_t1t = true; - protocols.target_t2t = true; - protocols.target_t3t = true; - protocols.target_t5t = true; - protocols.target_nfc_dep = true; + nfc_rf_protocols_bitmask_t protocols = get_rf_protocols(); + static char strSupported[7 * 6 + 1] = ""; if (protocols.target_t1t) { strcat(strSupported, "t1t,"); @@ -132,6 +138,7 @@ void NFCTestShim::cmd_get_rf_protocols() { if (strlen(strSupported)) { strSupported[strlen(strSupported) - 1] = '\0'; // strip trailing comma } + // return data as text to the plugin framework cmd_printf("{{protocols=%s}}", strSupported); set_last_nfc_error(NFC_OK); cmd_ready(CMDLINE_RETCODE_SUCCESS); @@ -139,14 +146,15 @@ void NFCTestShim::cmd_get_rf_protocols() { } void NFCTestShim::cmd_configure_rf_protocols( - nfc_rf_protocols_bitmask_t protocols) { + nfc_rf_protocols_bitmask_t protocols) +{ #if MBED_CONF_NFCEEPROM cmd_printf("EEPROM cannot set protocol()\r\n"); set_last_nfc_error(NFC_ERR_UNSUPPORTED); cmd_ready(CMDLINE_RETCODE_INVALID_PARAMETERS); #else - nfc_err_t err = ((NFCProcessController*) this)->set_rf_protocols(protocols); + nfc_err_t err = set_rf_protocols(protocols); set_last_nfc_error(err); if (NFC_OK != err) { cmd_ready(CMDLINE_RETCODE_FAIL); @@ -156,11 +164,15 @@ void NFCTestShim::cmd_configure_rf_protocols( #endif } -// RETURNS: ICETEA error code asynchronously NFC error is set -// {{bytes=XX XX XX XX.. }} are returned -void NFCTestShim::cmd_read_nfceeprom() { +/** \brief Copy data from the Controller buffer, or if EEPROM will initiate a read of the + * eeprom contents which get dumped as a string {{bytes=XX XX XX XX.. }} and parsed by + * the framework + * \return void An ICETEA error code and NFC error is set asyncronously + */ +void NFCTestShim::cmd_read_nfceeprom() +{ #if MBED_CONF_NFCEEPROM - ((NFCProcessEEPROM*)this)->queue_read_call(); + ((NFCProcessEEPROM *)this)->queue_read_call(); cmd_printf("NFCTestShim::read_nfceeprom() exit\r\n"); #else @@ -173,35 +185,41 @@ void NFCTestShim::cmd_read_nfceeprom() { #endif } -void NFCTestShim::cmd_erase() { +void NFCTestShim::cmd_erase() +{ #if MBED_CONF_NFCEEPROM - ((NFCProcessEEPROM*)this)->queue_erase_call(); + ((NFCProcessEEPROM *)this)->queue_erase_call(); #else cmd_printf("erase %d bytes, last msg\r\n", - (sizeof(_ndef_write_buffer) / sizeof(uint8_t))); + sizeof(_ndef_write_buffer)); _ndef_write_buffer_used = 0; - memset(_ndef_write_buffer, 0, sizeof(_ndef_write_buffer) / sizeof(uint8_t)); + memset(_ndef_write_buffer, 0, sizeof(_ndef_write_buffer)); set_last_nfc_error(NFC_OK); // effectively a no-op cmd_ready(CMDLINE_RETCODE_SUCCESS); #endif } -// populate buffer with really long message - length checks to be done by driver only -void NFCTestShim::cmd_write_long(char *data) { +/** \brief Writes a Text T record buffer with really long message - length checks to be done by driver only. + * If an NFC controller, no write to the chip happens, we copy the data into a Controller buffer + * \param uri This method must free the passed in pointer + * \return void An ICETEA error code and NFC error is set asyncronously + */ +void NFCTestShim::cmd_write_long(char *data) +{ MessageBuilder builder(ndef_poster_message); - strcpy(NFCTestShim::long_string, data); //max_ndef - header - overheads + strcpy(::long_string, data); //max_ndef - header - overheads Text text(Text::UTF8, span_from_cstr("en-US"), - span_from_cstr((const char*) (NFCTestShim::long_string))); + span_from_cstr((const char *)(::long_string))); text.append_as_record(builder, true); _ndef_write_buffer_used = builder.get_message().size(); cmd_printf("Composed NDEF message %d bytes\r\n", _ndef_write_buffer_used); #if MBED_CONF_NFCEEPROM - ((NFCProcessEEPROM*)this)->queue_write_call(); + ((NFCProcessEEPROM *)this)->queue_write_call(); #else // not on a wire, so the caller will store the message in a buffer set_last_nfc_error(NFC_OK); @@ -212,29 +230,31 @@ void NFCTestShim::cmd_write_long(char *data) { free(data); } -// PARAM: uri - this method must free the passed pointer -// RETURNS: ICETEA error code asynchronously NFC error is set -// An interesting side use case would be to prompt to install an app from the appstore using the tag -void NFCTestShim::cmd_set_smartposter(char *cmdUri) { +/** \brief Write a URI Use case would be to prompt to install an app from the appstore using the tag + * \param uri This method must free the passed in pointer + * \return void An ICETEA error code and NFC error is set asyncronously + */ +void NFCTestShim::cmd_set_smartposter(char *cmdUri) +{ MessageBuilder builder(ndef_poster_message); uint8_t smart_poster_buffer[1024]; MessageBuilder smart_poster_builder(smart_poster_buffer); - char* urlbegin = strstr(cmdUri, "."); + char *urlbegin = strstr(cmdUri, "."); urlbegin++; URI uri(URI::HTTPS_WWW, span_from_cstr(urlbegin)); uri.append_as_record(smart_poster_builder, true); builder.append_record( - RecordType(RecordType::well_known_type, span_from_cstr("Sp")), - smart_poster_builder.get_message(), true); + RecordType(RecordType::well_known_type, span_from_cstr("Sp")), + smart_poster_builder.get_message(), true); _ndef_write_buffer_used = builder.get_message().size(); cmd_printf("Composed NDEF message %d bytes\r\n", _ndef_write_buffer_used); #if MBED_CONF_NFCEEPROM - ((NFCProcessEEPROM*)this)->queue_write_call(); + ((NFCProcessEEPROM *)this)->queue_write_call(); #else // not on a wire, so the call just stores the message in a buffer set_last_nfc_error(NFC_OK); @@ -245,7 +265,8 @@ void NFCTestShim::cmd_set_smartposter(char *cmdUri) { } // disabled in EEPROMs, overridden if controller present -void NFCTestShim::cmd_start_discovery(bool manual) { +void NFCTestShim::cmd_start_discovery(bool manual) +{ #if MBED_CONF_NFCEEPROM cmd_printf("EEPROM cannot start_discovery()\r\n"); set_last_nfc_error(NFC_ERR_UNSUPPORTED); @@ -253,18 +274,18 @@ void NFCTestShim::cmd_start_discovery(bool manual) { #else - // todo: remove hard coded + // todo: Jira logged. remove hard coded protocol nfc_rf_protocols_bitmask_t protocols = { 0 }; protocols.target_iso_dep = 1; - nfc_err_t err = ((NFCProcessController*) this)->set_rf_protocols(protocols); + nfc_err_t err = set_rf_protocols(protocols); if (manual) { - this->set_discovery_restart_manual(); + set_discovery_restart_manual(); } else { - this->set_discovery_restart_auto(); + set_discovery_restart_auto(); } - err = this->start_discovery(); + err = start_discovery(); set_last_nfc_error(err); if (NFC_OK != err) { cmd_ready(CMDLINE_RETCODE_FAIL); @@ -275,14 +296,15 @@ void NFCTestShim::cmd_start_discovery(bool manual) { } // disabled in EEPROMs, overridden if controller present -void NFCTestShim::cmd_stop_discovery() { +void NFCTestShim::cmd_stop_discovery() +{ #if MBED_CONF_NFCEEPROM cmd_printf("EEPROM cannot stop_discovery()\r\n"); set_last_nfc_error(NFC_ERR_UNSUPPORTED); cmd_ready(CMDLINE_RETCODE_INVALID_PARAMETERS); #else - nfc_err_t err = this->stop_discovery(); + nfc_err_t err = stop_discovery(); set_last_nfc_error(err); if (NFC_OK != err) { cmd_ready(CMDLINE_RETCODE_FAIL); diff --git a/TEST_APPS/device/nfcapp/nfctestshim.h b/TEST_APPS/device/nfcapp/nfctestshim.h index 8a0b65b79f..2c485c1aec 100644 --- a/TEST_APPS/device/nfcapp/nfctestshim.h +++ b/TEST_APPS/device/nfcapp/nfctestshim.h @@ -26,62 +26,36 @@ #include "nfc/ndef/MessageBuilder.h" #include "nfc/ndef/common/URI.h" #include "nfc/ndef/common/util.h" -#include "nfc/nfcdefinitions.h" +#include "nfc/NFCDefinitions.h" -// all targets that have an EEPROM -#if MBED_CONF_NFCEEPROM -#define TEST_NFCEEPROM_TARGET -#endif -// all targets that have a controller -#if defined (TARGET_PN512) -#define TEST_NFCCONTRL_TARGET -#endif - -#if MBED_CONF_NFCEEPROM -#include "NFCEEPROM.h" -#include "EEPROMDriver.h" - -#else -#ifdef TARGET_PN512 -#include "nfc/controllers/PN512Driver.h" -#include "nfc/controllers/PN512SPITransportDriver.h" -#endif -#include "nfc/NFCRemoteInitiator.h" -#include "nfc/NFCController.h" - -using mbed::Span; -using mbed::nfc::NFCRemoteInitiator; -using mbed::nfc::NFCController; -#endif // TEST_EEPROM_TARGET - -using mbed::nfc::ndef::MessageBuilder; -using mbed::nfc::ndef::common::URI; -using mbed::nfc::ndef::common::span_from_cstr; using mbed::nfc::nfc_rf_protocols_bitmask_t; class NFCTestShim { public: NFCTestShim(); - static void cmd_get_last_nfc_error() { + static void cmd_get_last_nfc_error() + { get_last_nfc_error(); } ; - static void cmd_set_last_nfc_error(int err) { + static void cmd_set_last_nfc_error(int err) + { set_last_nfc_error(err); - cmd_ready (CMDLINE_RETCODE_SUCCESS); + cmd_ready(CMDLINE_RETCODE_SUCCESS); } ; - static void cmd_get_conf_nfceeprom() { + static void cmd_get_conf_nfceeprom() + { get_conf_nfceeprom(); } ; static void get_last_nfc_error(); static void set_last_nfc_error(int err); static void get_conf_nfceeprom(); - static void print_ndef_message(const Span &buffer, - size_t length); + static void print_ndef_message(const mbed::Span &buffer, + size_t length); void cmd_init(); virtual nfc_err_t init() = 0; @@ -97,32 +71,42 @@ public: protected: // implement/declare EEPROM and Controller model underlying common BH and delegate specializations - virtual nfc_err_t set_rf_protocols(nfc_rf_protocols_bitmask_t protocols) {return NFC_ERR_UNSUPPORTED ;}; - virtual nfc_err_t start_discovery() {return NFC_ERR_UNSUPPORTED ;}; - virtual nfc_err_t stop_discovery() {return NFC_ERR_UNSUPPORTED ;}; - void set_discovery_restart_auto() { + virtual nfc_err_t set_rf_protocols(nfc_rf_protocols_bitmask_t protocols) + { + return NFC_ERR_UNSUPPORTED ; + }; + virtual nfc_rf_protocols_bitmask_t get_rf_protocols() + { + + }; + virtual nfc_err_t start_discovery() + { + return NFC_ERR_UNSUPPORTED ; + }; + virtual nfc_err_t stop_discovery() + { + return NFC_ERR_UNSUPPORTED ; + }; + void set_discovery_restart_auto() + { _discovery_restart = true; }; - void set_discovery_restart_manual() { + void set_discovery_restart_manual() + { _discovery_restart = false; }; protected: size_t _ndef_write_buffer_used; - Span ndef_poster_message; // message to build and send + mbed::Span ndef_poster_message; // message to build and send uint8_t _ndef_write_buffer[0x2000]; // if this buffer is smaller than the EEPROM, the driver may crash see IOTPAN-297 uint8_t _ndef_buffer[0x2000]; // driver buffer bool _discovery_restart; -private: - static int last_nfc_error; - - static int using_eeprom; - static char long_string[0x2000]; }; // forward declare single instance -extern NFCTestShim * pNFC_Test_Shim; +extern NFCTestShim *pNFC_Test_Shim; #endif // _NFCTESTSHIM_H_INCLUDED diff --git a/TEST_APPS/device/nfcapp/uart.cpp b/TEST_APPS/device/nfcapp/uart.cpp index 361c51c2b7..fa6bdf99ae 100644 --- a/TEST_APPS/device/nfcapp/uart.cpp +++ b/TEST_APPS/device/nfcapp/uart.cpp @@ -15,7 +15,8 @@ */ #include #include -#include "mbed.h" +#include "platform/FileHandle.h" +#include "drivers/UARTSerial.h" /** * Macros for setting console flow control. @@ -29,15 +30,16 @@ #define SERIAL_CONSOLE_BAUD_RATE 115200 -FileHandle *mbed::mbed_override_console(int) { - static UARTSerial console(STDIO_UART_TX, STDIO_UART_RX, - SERIAL_CONSOLE_BAUD_RATE); +mbed::FileHandle *mbed::mbed_override_console(int) +{ + static mbed::UARTSerial console(STDIO_UART_TX, STDIO_UART_RX, + SERIAL_CONSOLE_BAUD_RATE); #if CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTS - console.set_flow_control(SerialBase::RTS, STDIO_UART_RTS, NC); + mbed::console.set_flow_control(SerialBase::RTS, STDIO_UART_RTS, NC); #elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_CTS - console.set_flow_control(SerialBase::CTS, NC, STDIO_UART_CTS); + mbed::console.set_flow_control(SerialBase::CTS, NC, STDIO_UART_CTS); #elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTSCTS - console.set_flow_control(SerialBase::RTSCTS, STDIO_UART_RTS, STDIO_UART_CTS); + mbed::console.set_flow_control(SerialBase::RTSCTS, STDIO_UART_RTS, STDIO_UART_CTS); #endif return &console; } diff --git a/TEST_APPS/testcases/nfc/nfc_clf_wrapper.py b/TEST_APPS/testcases/nfc/nfc_clf_wrapper.py index a552d2138a..c90493095b 100644 --- a/TEST_APPS/testcases/nfc/nfc_clf_wrapper.py +++ b/TEST_APPS/testcases/nfc/nfc_clf_wrapper.py @@ -1,5 +1,7 @@ """ -Copyright 2018 ARM Limited +Copyright (c) 2017, Arm Limited and affiliates. +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 diff --git a/TEST_APPS/testcases/nfc/nfc_cli_helper.py b/TEST_APPS/testcases/nfc/nfc_cli_helper.py index e0eeb1067b..bd6e695c1a 100644 --- a/TEST_APPS/testcases/nfc/nfc_cli_helper.py +++ b/TEST_APPS/testcases/nfc/nfc_cli_helper.py @@ -1,5 +1,7 @@ """ -Copyright 2018 ARM Limited +Copyright (c) 2017, Arm Limited and affiliates. +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 diff --git a/TEST_APPS/testcases/nfc/nfc_messages.py b/TEST_APPS/testcases/nfc/nfc_messages.py index b416506976..38e4eace47 100644 --- a/TEST_APPS/testcases/nfc/nfc_messages.py +++ b/TEST_APPS/testcases/nfc/nfc_messages.py @@ -1,3 +1,20 @@ +""" +Copyright (c) 2017, Arm Limited and affiliates. +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. +""" + import nfc from enum import Enum import logging diff --git a/TEST_APPS/testcases/nfc/test_nfc.py b/TEST_APPS/testcases/nfc/test_nfc.py index 9655aebb22..14b907a8d3 100644 --- a/TEST_APPS/testcases/nfc/test_nfc.py +++ b/TEST_APPS/testcases/nfc/test_nfc.py @@ -1,5 +1,7 @@ """ -Copyright 2018 ARM Limited +Copyright (c) 2017, Arm Limited and affiliates. +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 diff --git a/TEST_APPS/testcases/nfc/test_self.py b/TEST_APPS/testcases/nfc/test_self.py index 8ae5d9e1a2..916dacee1f 100644 --- a/TEST_APPS/testcases/nfc/test_self.py +++ b/TEST_APPS/testcases/nfc/test_self.py @@ -1,5 +1,7 @@ """ -Copyright 2018 ARM Limited +Copyright (c) 2017, Arm Limited and affiliates. +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 From fa29270b2e21c117c23d245185c652e1195e7302 Mon Sep 17 00:00:00 2001 From: Conrad Braam Date: Fri, 4 Jan 2019 18:06:09 +0000 Subject: [PATCH 03/14] SPDX updated, review refactoring, doc errors --- TEST_APPS/device/nfcapp/SmartPoster.cpp | 16 +-- TEST_APPS/device/nfcapp/SmartPoster.h | 15 ++- TEST_APPS/device/nfcapp/main.cpp | 14 ++- TEST_APPS/device/nfcapp/nfccommands.h | 8 ++ TEST_APPS/device/nfcapp/nfcprocess.h | 123 ------------------- TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp | 5 +- TEST_APPS/device/nfcapp/nfcprocessCtrl.h | 10 +- TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp | 3 +- TEST_APPS/device/nfcapp/nfcprocessEeprom.h | 9 +- TEST_APPS/device/nfcapp/nfctestshim.cpp | 5 +- TEST_APPS/device/nfcapp/nfctestshim.h | 20 ++- TEST_APPS/testcases/nfc/README.md | 100 ++++++++------- 12 files changed, 107 insertions(+), 221 deletions(-) delete mode 100644 TEST_APPS/device/nfcapp/nfcprocess.h diff --git a/TEST_APPS/device/nfcapp/SmartPoster.cpp b/TEST_APPS/device/nfcapp/SmartPoster.cpp index da2732d762..c09cf8488a 100644 --- a/TEST_APPS/device/nfcapp/SmartPoster.cpp +++ b/TEST_APPS/device/nfcapp/SmartPoster.cpp @@ -1,19 +1,19 @@ -/* mbed Microcontroller Library - * Copyright (c) 2018-2018 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. +/* Copyright (c) 2018 ARM Limited. All rights reserved. + * 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 + * 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. + * 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 "SmartPoster.h" #include "nfc/ndef/common/Text.h" diff --git a/TEST_APPS/device/nfcapp/SmartPoster.h b/TEST_APPS/device/nfcapp/SmartPoster.h index d12e979a58..065abdceac 100644 --- a/TEST_APPS/device/nfcapp/SmartPoster.h +++ b/TEST_APPS/device/nfcapp/SmartPoster.h @@ -1,15 +1,14 @@ -/* mbed Microcontroller Library - * Copyright (c) 2018-2018 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. +/* Copyright (c) 2018 ARM Limited. All rights reserved. + * 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 + * 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. + * 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. */ diff --git a/TEST_APPS/device/nfcapp/main.cpp b/TEST_APPS/device/nfcapp/main.cpp index b38a2ee9cf..2578fd7c24 100644 --- a/TEST_APPS/device/nfcapp/main.cpp +++ b/TEST_APPS/device/nfcapp/main.cpp @@ -75,7 +75,19 @@ int seteasy(int argc, char *argv[]) } return (CMDLINE_RETCODE_SUCCESS); } - +/** + * This test app can be used standalone interactively with at 115200 baud terminal. It is designed to work with the + * IceTea test framework https://os.mbed.com/docs/latest/tools/icetea-testing-applications.html . This app does + * not run test cases, it wraps functions in the API in ways that let us test multiple cases and scenarios. + * Test commands are routed in the CommandHandler class to an eventqueue, so it interfaces to the driver + * on a separate driver thread, which means test functions complete asynchronously. + * The driver is only called in a thread in the shim or wrapper class called NFCTestShim, which handles driver + * callbacks and completes the async part of a test function. NFCTestShim has specializations for NFC controllers + * and EEPROMS. The classes NFCProcessController and NFCProcessEEPROM respectively implement the needed specialization. + * + * If using an NFC EEPROM, an extra library is needed. Please see the documentation in the README.MD for instructions + * on how to modify this test for new drivers/targets, and the steps to run this test suite. + */ int main(int argc, char *argv[]) { cmd_init(&wrap_printf); diff --git a/TEST_APPS/device/nfcapp/nfccommands.h b/TEST_APPS/device/nfcapp/nfccommands.h index 7477a4bc82..f081bedbc0 100644 --- a/TEST_APPS/device/nfcapp/nfccommands.h +++ b/TEST_APPS/device/nfcapp/nfccommands.h @@ -28,6 +28,14 @@ extern events::EventQueue nfcQueue; // see https://support.microsoft.com/en-my/help/208427/maximum-url-length-is-2-083-characters-in-internet-explorer #define MAX_URL_LENGTH 2000 +/** + * HandleTestCommand turns all the typed-in/serial commands into function calls sent via a eventqueue to the driver + * shim/wrapper class. Most application level code that is agnostic of EEPROM/controller drivers lives in this class. + * Methods with cmd_ prefix map to the serial commands, and are building blocks for test cases and scenarios. The + * first function a test must call is typically the initnfc command. Commands report back a test verdict, and a + * NFC status code. The test verdict is always success unless the command is not allowed. Tests much check the + * NFC error code for NFC_OK or zero; this pattern allows us to write negative tests which expect a specific NFC error. + */ class HandleTestCommand { public: // start thread and handle queue diff --git a/TEST_APPS/device/nfcapp/nfcprocess.h b/TEST_APPS/device/nfcapp/nfcprocess.h deleted file mode 100644 index 987ae1058b..0000000000 --- a/TEST_APPS/device/nfcapp/nfcprocess.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2018 ARM Limited. All rights reserved. - * 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. - */ - -#ifndef _NFCPROCESS_H_INCLUDED -#define _NFCPROCESS_H_INCLUDED - -#include -#include -#include -#include - -#include "nfc/ndef/MessageBuilder.h" -#include "nfc/ndef/common/URI.h" -#include "nfc/ndef/common/util.h" -#include "nfcTestShim.h" - -#if MBED_CONF_NFCEEPROM -#include "NFCEEPROM.h" -#include "EEPROMDriver.h" -#else -#include "nfc/nfcdefinitions.h" -#ifdef TARGET_PN512 -#include "nfc/controllers/PN512Driver.h" -#include "nfc/controllers/PN512SPITransportDriver.h" -#endif -#include "nfc/NFCRemoteInitiator.h" -#include "nfc/NFCController.h" -#include "nfc/ndef/common/util.h" - -#endif // MBED_CONF_NFCEEPROM - -using mbed::nfc::ndef::MessageBuilder; -using mbed::nfc::ndef::common::URI; -using mbed::nfc::ndef::common::span_from_cstr; -using mbed::Span; - - -#if MBED_CONF_NFCEEPROM - -using mbed::nfc::NFCEEPROM; -using mbed::nfc::NFCEEPROMDriver; - -class NFCProcessEEPROM : NFCTestShim, mbed::nfc::NFCEEPROM::Delegate { -public: - NFCProcessEEPROM(events::EventQueue &queue, NFCEEPROMDriver &eeprom_driver) ; - nfc_err_t init(); - void queue_write_call(); - void queue_write_long_call(); - void queue_read_call(); - void queue_erase_call(); - -private: - virtual void on_ndef_message_written(nfc_err_t result); - virtual void on_ndef_message_read(nfc_err_t result); - virtual void parse_ndef_message(const Span &buffer); - virtual size_t build_ndef_message(const Span &buffer); - virtual void on_ndef_message_erased(nfc_err_t result); -private: - uint8_t _ndef_buffer[0x2000]; // if this buffer is smaller than the EEPROM, the driver may crash see IOTPAN-297 - NFCEEPROM _eeprom; - EventQueue &_queue; -}; - -#else // NFC Controller - -class NFCProcessController : NFCTestShim, NFCRemoteInitiator::Delegate, NFCController::Delegate { -public: - NFCProcessController(events::EventQueue &queue); - - nfc_err_t init(); - nfc_err_t start_discovery(); - nfc_err_t stop_discovery(); - void set_discovery_restart_auto() - { - _discovery_restart = true; - }; - void set_discovery_restart_manual() - { - _discovery_restart = false; - }; - nfc_rf_protocols_bitmask_t get_rf_protocols(); - nfc_err_t set_rf_protocols(nfc_rf_protocols_bitmask_t protocols); - - virtual void parse_ndef_message(const Span &buffer); - virtual size_t build_ndef_message(const Span &buffer); - const char *str_discovery_terminated_reason(nfc_discovery_terminated_reason_t reason); - - -private: - // these events are handled, to restart discovery - virtual void on_connected(); - virtual void on_disconnected(); - virtual void on_discovery_terminated(nfc_discovery_terminated_reason_t reason); - virtual void on_nfc_initiator_discovered(const SharedPtr &nfc_initiator); - -private: - bool _discovery_restart; - uint8_t _ndef_buffer[1024]; - mbed::nfc::PN512SPITransportDriver _pn512_transport; - mbed::nfc::PN512Driver _pn512_driver; -protected: - EventQueue &_queue; -private: - NFCController _nfc_controller; - SharedPtr _nfc_remote_initiator; -}; -#endif // TARGET_M24SR / else TARGET_PN512 - - -#endif // _NFCPROCESS_H_INCLUDED diff --git a/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp b/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp index 2e703744b3..c2d65635c8 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp +++ b/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp @@ -49,10 +49,11 @@ using mbed::nfc::NFCController; //class NFCProcessController : NFCRemoteInitiator::Delegate, NFCController::Delegate { NFCProcessController::NFCProcessController(events::EventQueue &queue) : + NFCTestShim(queue), // pins: mosi, miso, sclk, ssel, irq, rst _pn512_transport(D11, D12, D13, D10, A1, A0), _pn512_driver( - &_pn512_transport), _queue(queue), _nfc_controller( - &_pn512_driver, &queue, _ndef_buffer) + &_pn512_transport), + _nfc_controller(&_pn512_driver, &queue, _ndef_buffer) { } diff --git a/TEST_APPS/device/nfcapp/nfcprocessCtrl.h b/TEST_APPS/device/nfcapp/nfcprocessCtrl.h index 2ffda6049e..d0fa549d09 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessCtrl.h +++ b/TEST_APPS/device/nfcapp/nfcprocessCtrl.h @@ -47,7 +47,10 @@ using mbed::nfc::ndef::common::span_from_cstr; using mbed::Span; - +/** + * Wrapper class handles calls and callbacks for NFC controller drivers. Note, that users must call "start" + * in order to start the discovery loop for controllers. An internal buffer stores the NFC message and records. + */ class NFCProcessController: NFCTestShim, NFCRemoteInitiator::Delegate, NFCController::Delegate { @@ -83,13 +86,8 @@ private: const SharedPtr &nfc_initiator); private: - - mbed::nfc::PN512SPITransportDriver _pn512_transport; mbed::nfc::PN512Driver _pn512_driver; -protected: - EventQueue &_queue; -private: NFCController _nfc_controller; SharedPtr _nfc_remote_initiator; }; diff --git a/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp b/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp index d19b0a8e12..ce27a79bad 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp +++ b/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp @@ -45,8 +45,7 @@ using mbed::nfc::ndef::common::URI; // implements : mbed::nfc::NFCEEPROM::Delegate NFCProcessEEPROM::NFCProcessEEPROM(events::EventQueue &queue, NFCEEPROMDriver &eeprom_driver) : - _eeprom(&eeprom_driver, &queue, _ndef_buffer), - _queue(queue) + NFCTestShim(queue), _eeprom(&eeprom_driver, &queue, _ndef_buffer) {} nfc_err_t NFCProcessEEPROM::init() diff --git a/TEST_APPS/device/nfcapp/nfcprocessEeprom.h b/TEST_APPS/device/nfcapp/nfcprocessEeprom.h index bfb81a6616..0e9649f9b6 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessEeprom.h +++ b/TEST_APPS/device/nfcapp/nfcprocessEeprom.h @@ -31,18 +31,16 @@ #include "NFCEEPROM.h" #include "EEPROMDriver.h" -#endif // MBED_CONF_NFCEEPROM - using mbed::nfc::ndef::MessageBuilder; using mbed::nfc::ndef::common::URI; using mbed::nfc::ndef::common::span_from_cstr; using mbed::Span; - -#if MBED_CONF_NFCEEPROM - using mbed::nfc::NFCEEPROM; using mbed::nfc::NFCEEPROMDriver; +/** + * Wrapper class handles events specific to the EEPROM driver. + */ class NFCProcessEEPROM : NFCTestShim, mbed::nfc::NFCEEPROM::Delegate { public: NFCProcessEEPROM(events::EventQueue &queue, NFCEEPROMDriver &eeprom_driver); @@ -60,7 +58,6 @@ private: virtual void on_ndef_message_erased(nfc_err_t result); private: NFCEEPROM _eeprom; - events::EventQueue &_queue; }; #endif // eeprom diff --git a/TEST_APPS/device/nfcapp/nfctestshim.cpp b/TEST_APPS/device/nfcapp/nfctestshim.cpp index 0915bcd440..60073d9bb2 100644 --- a/TEST_APPS/device/nfcapp/nfctestshim.cpp +++ b/TEST_APPS/device/nfcapp/nfctestshim.cpp @@ -50,9 +50,10 @@ int using_eeprom = false; #endif } -NFCTestShim::NFCTestShim() : +NFCTestShim::NFCTestShim(events::EventQueue &queue) : _ndef_write_buffer_used(0), ndef_poster_message(_ndef_write_buffer), - _discovery_restart(true) // on disconnect, will restart discovery + _discovery_restart(true), // on disconnect, will restart discovery + _queue(queue) { } diff --git a/TEST_APPS/device/nfcapp/nfctestshim.h b/TEST_APPS/device/nfcapp/nfctestshim.h index 2c485c1aec..efe8fa1910 100644 --- a/TEST_APPS/device/nfcapp/nfctestshim.h +++ b/TEST_APPS/device/nfcapp/nfctestshim.h @@ -33,24 +33,21 @@ using mbed::nfc::nfc_rf_protocols_bitmask_t; class NFCTestShim { public: - NFCTestShim(); + NFCTestShim(events::EventQueue &queue); static void cmd_get_last_nfc_error() { get_last_nfc_error(); - } - ; + }; static void cmd_set_last_nfc_error(int err) { set_last_nfc_error(err); cmd_ready(CMDLINE_RETCODE_SUCCESS); - } - ; + }; static void cmd_get_conf_nfceeprom() { get_conf_nfceeprom(); - } - ; + }; static void get_last_nfc_error(); static void set_last_nfc_error(int err); static void get_conf_nfceeprom(); @@ -77,7 +74,8 @@ protected: }; virtual nfc_rf_protocols_bitmask_t get_rf_protocols() { - + nfc_rf_protocols_bitmask_t none; + return none; }; virtual nfc_err_t start_discovery() { @@ -96,13 +94,13 @@ protected: _discovery_restart = false; }; - protected: size_t _ndef_write_buffer_used; mbed::Span ndef_poster_message; // message to build and send uint8_t _ndef_write_buffer[0x2000]; // if this buffer is smaller than the EEPROM, the driver may crash see IOTPAN-297 - uint8_t _ndef_buffer[0x2000]; // driver buffer - bool _discovery_restart; + uint8_t _ndef_buffer[0x2000]; // driver I/O buffer + bool _discovery_restart; // default true, restart discovery loop again on remote disconnect + events::EventQueue &_queue; }; diff --git a/TEST_APPS/testcases/nfc/README.md b/TEST_APPS/testcases/nfc/README.md index 136305e899..c5baeafb00 100644 --- a/TEST_APPS/testcases/nfc/README.md +++ b/TEST_APPS/testcases/nfc/README.md @@ -1,13 +1,13 @@ # NFC tests. -Internal document. -A CI test suite for NFC component. These tests validate card mbed emulation cases. The key use case is a NFC smart poster supporting comissioning workflow. +A CI test suite for NFC component. These tests validate card mbed emulation cases. The key use case is an NFC smart poster supporting comissioning workflow. The SUT (system under test) is the NFC target. Tests exercise the framework and NDEF transactions when a NFC controller driver is used, or when the stack is configured for an NFC EEPROM chip in the system integration. This project is called CreamScone, which is an ice tea framework based cli-driven python test. -- [NFC tests . CONFIDENTIAL](#nfc-tests-confidential) + +- [NFC tests.](#nfc-tests) - [Overview](#overview) - [NFC System Testing high level design](#nfc-system-testing-high-level-design) - [Low level design](#low-level-design) @@ -15,10 +15,10 @@ This project is called CreamScone, which is an ice tea framework based cli-drive - [Test cases](#test-cases) - [cli commands](#cli-commands) - [How to](#how-to) - - [Alternate NFC drivers note:](#alternate-nfc-drivers-note:) - [Running the tests](#running-the-tests) - +- [Alternate NFC drivers note:](#alternate-nfc-drivers-note) + # Overview A set of tests run in CI, which can provide: @@ -29,7 +29,7 @@ A set of tests run in CI, which can provide: A [Ice-tea](https://github.com/ARMmbed/mbed-os-5-docs/blob/development/docs/tools/testing/testing_icetea.md) based test suite. In it's simplest form, the suite merely drives API's for the NFC tag reader/writer, and validates a tag simulation running on an idle target, allows test cases in discovery, connection and read/write NDEF records. -In order to mitigate the costs associated with system testing, use existing frameworks or parts and make it easy to test each individually. The [nfcpy](https://nfcpy.readthedocs.io/) Python library is used as the core of the *CreamScone* component which uses a PN53* device [SCL3711 usb reader](https://www.identiv.com/products/smart-card-readers/rfid-nfc-contactless/scl3711/) over USB to read the mbed simulated tag. This library is used to drive host interactions because it is portable (windows/GNULinux.) Remote NFC interactions will raise events in the mbed application. Connection and read/write events which get handled in user application on the target get wired up to asyncronously return responses and the data values (NDEF messages) to the ice tea framework. These events and data are thus tested/checked in the code (python) running on host. The target test app wraps the API, allowing many new test-scenarios to be written purely in Python. +In order to mitigate the costs associated with system testing, use existing frameworks or parts and make it easy to test each individually. The [nfcpy](https://nfcpy.readthedocs.io/) Python library is used as the core of the *CreamScone* component which uses a PN53* device [SCL3711 usb reader](https://www.identiv.com/products/smart-card-readers/rfid-nfc-contactless/scl3711/) over USB to read the mbed simulated tag. This library is used to drive host interactions because it is portable (windows/GNULinux.) Remote NFC interactions will raise events in the mbed application. Connection and read/write events which get handled in user application on the target get wired up to asynchronously return responses and the data values (NDEF messages) to the ice tea framework. These events and data are thus tested/checked in the code (python) running on host. The target test app wraps the API, allowing many new test-scenarios to be written purely in Python. **NFC compliance** @@ -37,7 +37,7 @@ This suite only assists in NFC forum compliance. Developers must self certify us **Mobiles and inter-op** -(Unimplemented) Describe test procedures using a mobile phone app for Android and for IOS. +Not in scope. Test procedures using a mobile phone app for Android and for IOS were not in scope for this test suite. ![Basic Overview](img/simple-overview.png) @@ -46,7 +46,7 @@ Because the comissioning workflow application quality is the end goal, the NFC s # NFC System Testing high level design -Mitigate risks identified, to the product from an internal view to supporting releases, and from customer development and production risks. In summary: +Mitigate risks identified, to the product from an internal view to supporting releases. Help customers develop a driver or a design, and reduce their production risks. In summary: - Architecture risks and Api breaks - Partner cannot NFC forum Certify - Partner driver has bugs @@ -54,9 +54,9 @@ Mitigate risks identified, to the product from an internal view to supporting re - Arm mbed provided driver or HAL has bugs - Security vulnerabilities -In precis, “Empower engineers to efficiently ship quality code with confidence.” +In short, “Empower engineers to efficiently ship quality code with confidence.” -**Design requirements:** +**Design requirements: ** - Identify and use tools to allow running in CI system, on many targets/configurations - Be portable (can run in the CI system) using NFC explorer boards in lab for correctly co-located targets. - Be able to set up and run locally in development @@ -70,18 +70,18 @@ Commandline (serial port) driven app [ice_device.py](TEST_APPS\testcases\nfc\ice An icetea plugin [nfc_test_parsers.py](TEST_APPS\icetea_plugins\nfc_test_parsers.py) -MbedOS cli test app [main.cpp](TEST_APPS\device\nfcapp\main.cpp). The CLI commands return results asyncronously for most commands which get passed to and handled on a driver thread. +MbedOS cli test app [main.cpp](TEST_APPS\device\nfcapp\main.cpp). The CLI commands return results asynchronously for most commands which get passed to and handled on a driver thread. The SUT target is rebooted between tests, since test modify the target hardware state. -**Future:** A complete inter-op ready design expands to also include a switch-box to allow the reader to connect to NFC enabled targets nearby using flying cables and a sticky-back antenna. The switch allows selecting either alternative tags, or NFC peers. The switch can be controlled using GPIO either driven from spare IO pins on the target DUT itself (preferred option), or perhaps from a Raspberry pi. +**Future: ** A complete inter-op ready design expands to also include a switch-box to allow the reader to connect to NFC enabled targets nearby using flying cables and a sticky-back antenna. The switch allows selecting either alternative tags, or NFC peers. The switch can be controlled using GPIO either driven from spare IO pins on the target DUT itself (preferred option), or perhaps from a Raspberry pi. ![inter-op](img/inter-op-view.png) ![future](img/creamscone-mobile.png) -**Reference:** +**Reference: ** https://github.com/ARMmbed/mbed-os/blob/master/docs/design-documents/nfc/nfc_design.md @@ -153,6 +153,7 @@ writelong fill entire FILE with pattern Note: Most commands also return a NFC status value (type "getlastnfcerror help" in console) which allow us to build negative test cases. Note: Some commands only apply to NFC controllers, these commands fail with the appropriate not-supported code NFC_ERR_UNSUPPORTED and additionally return -2 error code to ice-tea. + **unimplemented CLI commands** commands that were not implemented in the test app - set/get tag type @@ -225,11 +226,38 @@ If using the Explorer Shield or PN512 driver mbed_app.json will add } ... ``` -## Alternate NFC drivers note: -Please see the example json file .\TEST_APPS\testcases\nfc\mbed_app.json . The test does not check that you have any needed shield installed, so if it "hangs" at the point the "initnfc" command is used, the driver or shield may be the fault. The test assumes that MBED_CONF_NFCEEPROM is set to 1, if not it assumes that a controller driver is in use. To add support for other then PN512 and M24SR, it is required to add the support. +## Running the tests +1. Wire an [explorer shield](https://cpc.farnell.com/nxp/explore-nfc/add-on-board-nfc-for-raspberry/dp/SC13404) up to and compile the target application. +2. Flash the binary to target and verify that it responds with an _`action NDEF record http://www.mbed.com`_ by using a mobile phone to scan over the antenna. +3. Install python (2.7) and install the nfcpy library, [see](https://nfcpy.readthedocs.io/en/latest/topics/get-started.html) . NFC reader can be connected to a serial port, or more commonly a USB dongle. Verify the dongle is functioning. +4. Place the scanner near the explorer shield. Run various test program commands like so: +- python ice_device -command describe -If the driver you add is for Eeprom, open nfccommands.cpp and find the code and modify line as shown +++ +**run the suite** +In a working folder, run + +`git clone https://github.com/ARMmbed/mbed-os.git` + + +If using the EEPROM driver, ( ST Discovery and the M24SR driver), you need the driver: +`git clone https://github.com/ARMmbed/mbed-nfc-m24sr.git' + + +And copy the files into your mbed root: +`xcopy ..\mbed-nfc-m24sr\*.* .\eeprom_driver\' + +To run the End2End tests, type: +`mbed test --icetea -n test_nfce2e` + +To run only the standalone (readerless tests if you do not have a card reader), type: +`mbed test --icetea -n test_nfc_eeprom,test_nfc_error_codes,test_nfc_setsmartposter,test_nfc_erase,test_nfc_write_long` + +# Alternate NFC drivers note: + +Please see the example json file .\TEST_APPS\testcases\nfc\mbed_app.json . The test does not check that you have any needed shield installed, so if it "hangs" at the point the "initnfc" command is used, the driver or shield may be the fault. The test assumes that MBED_CONF_NFCEEPROM is set to 1, if not it assumes that a NFC Controller driver is in use. To test drivers other than PN512 and M24SR, it is required to make test code changes that reference the driver. The driver can be instantiated once only. + +If the new driver you add is for Eeprom, open nfccommands.cpp and find the code and modify line as shown +++ ``` NFCTestShim* new_testshim() { #if MBED_CONF_NFCEEPROM @@ -243,7 +271,7 @@ NFCTestShim* new_testshim() { #endif // EEPROM ``` -If the driver you add is a controller driver, open nfcProcessCtrl.cpp and find the code +If the driver you add is a Controller driver, open nfcProcessCtrl.cpp and find the code ``` NFCProcessController::NFCProcessController(events::EventQueue &queue) : // pins: mosi, miso, sclk, ssel, irq, rst @@ -252,46 +280,14 @@ NFCProcessController::NFCProcessController(events::EventQueue &queue) : &_pn512_driver, &queue, _ndef_buffer) { } ``` -1. You will want to replace with a reference to the desired controller driver. Likewise this is where pinout hanges if using a supplied controller driver have to be made. -2. Search for occurences of guard macro #ifdef TARGET_PN512 , and add guard code for your specific controller driver. +1. You will want to replace this and reference the desired controller driver. Likewise, this code is where pinout changes have to be made if using the supplied Controller driver. +2. Search for occurences of guard macros `#ifdef TARGET_PN512` , and `#endif`. Add a new guard macro and code for your specific controller driver at the same point. -## Running the tests -1. Wire an [explorer shield](https://cpc.farnell.com/nxp/explore-nfc/add-on-board-nfc-for-raspberry/dp/SC13404) up to and compile the target application. -2. Provision the target, and verify that it responds with an _`action NDEF record http://www.mbed.com`_ by using a mobile phone to scan over the antenna. -3. Install python (2.7) and install the nfcpy library, [see](https://nfcpy.readthedocs.io/en/latest/topics/get-started.html) . NFC reader can be connected to a serial port, or more commonly a USB dongle. Verify the dongle is functioning. -4. Place the scanner near the explorer shield. Run various test program commands like so: -- python ice_device -command describe - -**run the suite** -In a working folder, run - -`git clone https://github.com/ARMmbed/mbed-os.git` - - - -If using discovery and the M24SR driver, you need to: -`git clone https://github.com/ARMmbed/mbed-nfc-m24sr.git' - - -And copy the files something like this: -`xcopy ..\mbed-nfc-m24sr\*.* .\eeprom_driver\' - -To run the End2End tests, type: -`mbed test --icetea -n test_nfce2e` - -To run only the standalone (readerless tests if you do not have a card reader), type: -`mbed test --icetea -n test_nfc_eeprom,test_nfc_error_codes,test_nfc_setsmartposter,test_nfc_erase,test_nfc_write_long` - -**Using the app standalone** - -The target app can accept commandline inputs over serial. -``` -mbed sterm --baudrate 115200 -``` Note: If the target uses an EEPROM, it need not be powered/running, to be read, mbedOS is not running at that point. **Device API error codes** + You can issue the command "getlastnfcerror help" to see a list of error codes that are returned by most commands. ``` #define NFC_OK 0 ///< No error From 7095b82126b51ea4bde6df7760f694dd851a47b9 Mon Sep 17 00:00:00 2001 From: Conrad Braam Date: Wed, 9 Jan 2019 16:20:06 +0000 Subject: [PATCH 04/14] removed 'using nn' +listed issues +uri types --- TEST_APPS/device/nfcapp/mbed_app.json | 3 + TEST_APPS/device/nfcapp/nfccommands.cpp | 3 + TEST_APPS/device/nfcapp/nfccommands.h | 2 +- TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp | 2 +- TEST_APPS/device/nfcapp/nfcprocessCtrl.h | 27 +++---- TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp | 4 +- TEST_APPS/device/nfcapp/nfcprocessEeprom.h | 15 ++-- TEST_APPS/device/nfcapp/nfctestshim.cpp | 74 ++++++++++++++++++-- TEST_APPS/device/nfcapp/nfctestshim.h | 16 ++--- TEST_APPS/icetea_plugins/nfc_test_parsers.py | 6 +- TEST_APPS/testcases/nfc/README.md | 63 +++++++++++------ TEST_APPS/testcases/nfc/nfc_cli_helper.py | 22 ++++-- TEST_APPS/testcases/nfc/test_nfc.py | 17 ++--- TEST_APPS/testcases/nfc/test_self.py | 73 ++++++++++++++++--- 14 files changed, 236 insertions(+), 91 deletions(-) diff --git a/TEST_APPS/device/nfcapp/mbed_app.json b/TEST_APPS/device/nfcapp/mbed_app.json index 1629135ab4..0eb15da3ff 100644 --- a/TEST_APPS/device/nfcapp/mbed_app.json +++ b/TEST_APPS/device/nfcapp/mbed_app.json @@ -1,4 +1,7 @@ { + "config": { + "TEST_NDEF_MSG_MAX" : 8192 + }, "target_overrides": { "DISCO_L475VG_IOT01A": { "target.extra_labels_add": ["M24SR"], diff --git a/TEST_APPS/device/nfcapp/nfccommands.cpp b/TEST_APPS/device/nfcapp/nfccommands.cpp index b6bf082235..e21aa14ba2 100644 --- a/TEST_APPS/device/nfcapp/nfccommands.cpp +++ b/TEST_APPS/device/nfcapp/nfccommands.cpp @@ -27,6 +27,8 @@ #include "nfcProcessEeprom.h" #endif +using mbed::nfc::nfc_rf_protocols_bitmask_t; + events::EventQueue nfcQueue; rtos::Thread nfcThread; NFCTestShim *pNFC_Test_Shim = NULL; @@ -87,6 +89,7 @@ int HandleTestCommand::cmd_read_message(int argc, char *argv[]) int HandleTestCommand::cmd_set_smartposter(int argc, char *argv[]) { + // args are "setsmartposter", "" if (argc <= 1) { cmd_printf("setlastnfcerror() invalid parameter(s)\r\n"); return (CMDLINE_RETCODE_INVALID_PARAMETERS); diff --git a/TEST_APPS/device/nfcapp/nfccommands.h b/TEST_APPS/device/nfcapp/nfccommands.h index f081bedbc0..700567f055 100644 --- a/TEST_APPS/device/nfcapp/nfccommands.h +++ b/TEST_APPS/device/nfcapp/nfccommands.h @@ -41,7 +41,7 @@ public: // start thread and handle queue HandleTestCommand(); /* set corresponding mask bit on, return false if the supplied string cannot parse */ - static bool set_protocol_target(nfc_rf_protocols_bitmask_t &bitmask, const char *protocolName); + static bool set_protocol_target(mbed::nfc::nfc_rf_protocols_bitmask_t &bitmask, const char *protocolName); /* return and clear the last result code. Type "help getlastnfcerror" for a list of error codes */ static int cmd_get_last_nfc_error(int argc, char *argv[]) diff --git a/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp b/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp index c2d65635c8..949419017f 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp +++ b/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp @@ -183,7 +183,7 @@ void NFCProcessController::on_discovery_terminated( } void NFCProcessController::on_nfc_initiator_discovered( - const SharedPtr &nfc_initiator) + const SharedPtr &nfc_initiator) { cmd_printf("on_nfc_initiator_discovered()\r\n"); diff --git a/TEST_APPS/device/nfcapp/nfcprocessCtrl.h b/TEST_APPS/device/nfcapp/nfcprocessCtrl.h index d0fa549d09..24736d07c0 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessCtrl.h +++ b/TEST_APPS/device/nfcapp/nfcprocessCtrl.h @@ -38,33 +38,24 @@ #include "nfc/NFCController.h" #include "nfc/ndef/common/util.h" - -using mbed::nfc::NFCRemoteInitiator; -using mbed::nfc::NFCController; -using mbed::nfc::ndef::MessageBuilder; -using mbed::nfc::ndef::common::URI; -using mbed::nfc::ndef::common::span_from_cstr; -using mbed::Span; - - /** * Wrapper class handles calls and callbacks for NFC controller drivers. Note, that users must call "start" * in order to start the discovery loop for controllers. An internal buffer stores the NFC message and records. */ class NFCProcessController: NFCTestShim, - NFCRemoteInitiator::Delegate, - NFCController::Delegate { + mbed::nfc::NFCRemoteInitiator::Delegate, + mbed::nfc::NFCController::Delegate { public: NFCProcessController(events::EventQueue &queue); nfc_err_t init(); nfc_err_t start_discovery(); nfc_err_t stop_discovery(); - nfc_rf_protocols_bitmask_t get_rf_protocols(); - nfc_err_t set_rf_protocols(nfc_rf_protocols_bitmask_t protocols); + mbed::nfc::nfc_rf_protocols_bitmask_t get_rf_protocols(); + nfc_err_t set_rf_protocols(mbed::nfc::nfc_rf_protocols_bitmask_t protocols); - virtual void parse_ndef_message(const Span &buffer); - virtual size_t build_ndef_message(const Span &buffer); + virtual void parse_ndef_message(const mbed::Span &buffer); + virtual size_t build_ndef_message(const mbed::Span &buffer); const char *str_discovery_terminated_reason( nfc_discovery_terminated_reason_t reason); @@ -83,13 +74,13 @@ private: /** * Implementation of NFCController::Delegate */ virtual void on_nfc_initiator_discovered( - const SharedPtr &nfc_initiator); + const SharedPtr &nfc_initiator); private: mbed::nfc::PN512SPITransportDriver _pn512_transport; mbed::nfc::PN512Driver _pn512_driver; - NFCController _nfc_controller; - SharedPtr _nfc_remote_initiator; + mbed::nfc::NFCController _nfc_controller; + SharedPtr _nfc_remote_initiator; }; #endif // Controller diff --git a/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp b/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp index ce27a79bad..c9abf9cb45 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp +++ b/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp @@ -42,10 +42,12 @@ using mbed::nfc::ndef::common::span_from_cstr; using mbed::nfc::ndef::common::Mime; using mbed::nfc::ndef::common::Text; using mbed::nfc::ndef::common::URI; +using mbed::nfc::NFCEEPROM; +using mbed::nfc::NFCEEPROMDriver; // implements : mbed::nfc::NFCEEPROM::Delegate NFCProcessEEPROM::NFCProcessEEPROM(events::EventQueue &queue, NFCEEPROMDriver &eeprom_driver) : - NFCTestShim(queue), _eeprom(&eeprom_driver, &queue, _ndef_buffer) + NFCTestShim(queue), _eeprom(&eeprom_driver, &queue, _ndef_buffer) {} nfc_err_t NFCProcessEEPROM::init() diff --git a/TEST_APPS/device/nfcapp/nfcprocessEeprom.h b/TEST_APPS/device/nfcapp/nfcprocessEeprom.h index 0e9649f9b6..a4af6517a3 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessEeprom.h +++ b/TEST_APPS/device/nfcapp/nfcprocessEeprom.h @@ -31,19 +31,13 @@ #include "NFCEEPROM.h" #include "EEPROMDriver.h" -using mbed::nfc::ndef::MessageBuilder; -using mbed::nfc::ndef::common::URI; -using mbed::nfc::ndef::common::span_from_cstr; -using mbed::Span; -using mbed::nfc::NFCEEPROM; -using mbed::nfc::NFCEEPROMDriver; /** * Wrapper class handles events specific to the EEPROM driver. */ class NFCProcessEEPROM : NFCTestShim, mbed::nfc::NFCEEPROM::Delegate { public: - NFCProcessEEPROM(events::EventQueue &queue, NFCEEPROMDriver &eeprom_driver); + NFCProcessEEPROM(events::EventQueue &queue, mbed::nfc::NFCEEPROMDriver &eeprom_driver); nfc_err_t init(); void queue_write_call(); void queue_write_long_call(); @@ -53,14 +47,13 @@ public: private: virtual void on_ndef_message_written(nfc_err_t result); virtual void on_ndef_message_read(nfc_err_t result); - virtual void parse_ndef_message(const Span &buffer); - virtual size_t build_ndef_message(const Span &buffer); + virtual void parse_ndef_message(const mbed::Span &buffer); + virtual size_t build_ndef_message(const mbed::Span &buffer); virtual void on_ndef_message_erased(nfc_err_t result); private: - NFCEEPROM _eeprom; + mbed::nfc::NFCEEPROM _eeprom; }; #endif // eeprom - #endif // _NFCPROCESS_H_INCLUDED diff --git a/TEST_APPS/device/nfcapp/nfctestshim.cpp b/TEST_APPS/device/nfcapp/nfctestshim.cpp index 60073d9bb2..b0c5793272 100644 --- a/TEST_APPS/device/nfcapp/nfctestshim.cpp +++ b/TEST_APPS/device/nfcapp/nfctestshim.cpp @@ -38,9 +38,48 @@ using mbed::nfc::ndef::common::Text; using mbed::nfc::ndef::common::URI; using mbed::nfc::nfc_rf_protocols_bitmask_t; + // statics namespace { -char long_string[0x2000]; +char long_string[MBED_CONF_APP_TEST_NDEF_MSG_MAX]; + +char const *uri_prefix_string[] = { "", + "http://www.", + "https://www.", + "http://", + "https://", + "tel:", + "mailto:", + "ftp://anonymous:anonymous@", + "ftp://ftp.", + "ftps://", + "sftp://", + "smb://", + "nfs://", + "ftp://", + "dav://", + "news:", + "telnet://", + "imap:", + "rstp://", + "urn:", + "pop:", + "sip:", + "sips:", + "tftp:", + "btspp://", + "btl2cap://", + "btgoep://", + "tcpobex://", + "irdaobex://", + "file://", + "urn:epc:id:", + "urn:epc:tag:", + "urn:epc:pat:", + "urn:epc:raw:", + "urn:epc:", + "urn:nfc:" + }; int last_nfc_error = 0; #if MBED_CONF_NFCEEPROM @@ -95,6 +134,31 @@ void NFCTestShim::print_ndef_message(const Span &buffer, cmd_printf("}}\r\n"); } +URI::uri_identifier_code_t NFCTestShim::get_ndef_record_type(char const *url) +{ + size_t i; + int len, bestLen = -1, index = -1; + // find largest matching prefix + for (i = 1; i < sizeof(uri_prefix_string) / sizeof(uri_prefix_string[0]); i++) { + len = strlen(uri_prefix_string[i]); + if (0 == strncmp(uri_prefix_string[i], url, len)) { + if (len > bestLen) { + index = i; + bestLen = len; + } + } + } + return (URI::uri_identifier_code_t)index; +} + +char const *NFCTestShim::get_ndef_record_type_prefix(URI::uri_identifier_code_t id) +{ + if ((id < 1) | (id > sizeof(uri_prefix_string) / sizeof(uri_prefix_string[0]))) { + return (""); // unknown case + } + return (::uri_prefix_string[(int)id]); +} + void NFCTestShim::cmd_init() { nfc_err_t ret = init(); @@ -242,9 +306,11 @@ void NFCTestShim::cmd_set_smartposter(char *cmdUri) uint8_t smart_poster_buffer[1024]; MessageBuilder smart_poster_builder(smart_poster_buffer); - char *urlbegin = strstr(cmdUri, "."); - urlbegin++; - URI uri(URI::HTTPS_WWW, span_from_cstr(urlbegin)); + URI::uri_identifier_code_t uri_id = get_ndef_record_type(cmdUri); + char *urlbegin = cmdUri + strlen(get_ndef_record_type_prefix(uri_id)); + URI uri(uri_id, span_from_cstr(urlbegin)); + cmd_printf("{{uri_id=%d}}\r\n", (int)uri_id); + uri.append_as_record(smart_poster_builder, true); builder.append_record( diff --git a/TEST_APPS/device/nfcapp/nfctestshim.h b/TEST_APPS/device/nfcapp/nfctestshim.h index efe8fa1910..bdcb52352c 100644 --- a/TEST_APPS/device/nfcapp/nfctestshim.h +++ b/TEST_APPS/device/nfcapp/nfctestshim.h @@ -29,8 +29,6 @@ #include "nfc/NFCDefinitions.h" -using mbed::nfc::nfc_rf_protocols_bitmask_t; - class NFCTestShim { public: NFCTestShim(events::EventQueue &queue); @@ -53,6 +51,8 @@ public: static void get_conf_nfceeprom(); static void print_ndef_message(const mbed::Span &buffer, size_t length); + static mbed::nfc::ndef::common::URI::uri_identifier_code_t get_ndef_record_type(char const *url); + static char const *get_ndef_record_type_prefix(mbed::nfc::ndef::common::URI::uri_identifier_code_t id); void cmd_init(); virtual nfc_err_t init() = 0; @@ -63,18 +63,18 @@ public: void cmd_read_nfceeprom(); void cmd_start_discovery(bool manual = false); void cmd_stop_discovery(); - void cmd_configure_rf_protocols(nfc_rf_protocols_bitmask_t protocols); + void cmd_configure_rf_protocols(mbed::nfc::nfc_rf_protocols_bitmask_t protocols); void cmd_get_rf_protocols(); protected: // implement/declare EEPROM and Controller model underlying common BH and delegate specializations - virtual nfc_err_t set_rf_protocols(nfc_rf_protocols_bitmask_t protocols) + virtual nfc_err_t set_rf_protocols(mbed::nfc::nfc_rf_protocols_bitmask_t protocols) { return NFC_ERR_UNSUPPORTED ; }; - virtual nfc_rf_protocols_bitmask_t get_rf_protocols() + virtual mbed::nfc::nfc_rf_protocols_bitmask_t get_rf_protocols() { - nfc_rf_protocols_bitmask_t none; + mbed::nfc::nfc_rf_protocols_bitmask_t none; return none; }; virtual nfc_err_t start_discovery() @@ -97,8 +97,8 @@ protected: protected: size_t _ndef_write_buffer_used; mbed::Span ndef_poster_message; // message to build and send - uint8_t _ndef_write_buffer[0x2000]; // if this buffer is smaller than the EEPROM, the driver may crash see IOTPAN-297 - uint8_t _ndef_buffer[0x2000]; // driver I/O buffer + uint8_t _ndef_write_buffer[MBED_CONF_APP_TEST_NDEF_MSG_MAX]; // if this buffer is smaller than the EEPROM, the driver may crash see IOTPAN-297 + uint8_t _ndef_buffer[MBED_CONF_APP_TEST_NDEF_MSG_MAX]; // driver I/O buffer bool _discovery_restart; // default true, restart discovery loop again on remote disconnect events::EventQueue &_queue; diff --git a/TEST_APPS/icetea_plugins/nfc_test_parsers.py b/TEST_APPS/icetea_plugins/nfc_test_parsers.py index 9738252fe2..bfbfbaa393 100644 --- a/TEST_APPS/icetea_plugins/nfc_test_parsers.py +++ b/TEST_APPS/icetea_plugins/nfc_test_parsers.py @@ -43,7 +43,8 @@ class NfcTestParsers(PluginBase): results = {'iseeprom': None, # 'true' if EEPROM 'lastnfcerror':None, # 0=OK >0 = error 'nfcmessage':None, # NDEF array of bytes - 'protocols':None} # csv list + 'protocols':None, # csv list + 'uri_id':None} # nfc URI type identifier respLines = response.lines for line in respLines: try: @@ -69,6 +70,9 @@ class NfcTestParsers(PluginBase): value = PluginBase.find_one(line, "{{protocols=(([\w]*,?)*)}}") if value is not False: results['protocols'] = value # a csv list + value = PluginBase.find_one(line, "{{uri_id=([0-9]+)}}") + if value is not False: + results['uri_id'] = int(value) except re.error as e: # the framework gobbles errors in the plugin print("Regex error",e,"occured in",os.path.basename(__file__), "!!") raise e diff --git a/TEST_APPS/testcases/nfc/README.md b/TEST_APPS/testcases/nfc/README.md index c5baeafb00..43e64a20f1 100644 --- a/TEST_APPS/testcases/nfc/README.md +++ b/TEST_APPS/testcases/nfc/README.md @@ -9,7 +9,7 @@ This project is called CreamScone, which is an ice tea framework based cli-drive - [NFC tests.](#nfc-tests) - [Overview](#overview) -- [NFC System Testing high level design](#nfc-system-testing-high-level-design) +- [System Test high level design](#system-test-high-level-design) - [Low level design](#low-level-design) - [User Guide](#user-guide) - [Test cases](#test-cases) @@ -17,6 +17,7 @@ This project is called CreamScone, which is an ice tea framework based cli-drive - [How to](#how-to) - [Running the tests](#running-the-tests) - [Alternate NFC drivers note:](#alternate-nfc-drivers-note) +- [Known issues](#known-issues) @@ -45,7 +46,7 @@ Because the comissioning workflow application quality is the end goal, the NFC s -# NFC System Testing high level design +# System Test high level design Mitigate risks identified, to the product from an internal view to supporting releases. Help customers develop a driver or a design, and reduce their production risks. In summary: - Architecture risks and Api breaks - Partner cannot NFC forum Certify @@ -62,36 +63,43 @@ In short, “Empower engineers to efficiently ship quality code with confidence. - Be able to set up and run locally in development # Low level design -**components** +**Components** -An icetea suite [test_nfc.py](TEST_APPS\testcases\nfc\test_nfc.py) +API standalone Self tests [test_self.py](TEST_APPS\testcases\nfc\test_self.py) -Commandline (serial port) driven app [ice_device.py](TEST_APPS\testcases\nfc\ice_device.py) aka _'CreamScone'_ which allows manual interactions with the driver. -An icetea plugin [nfc_test_parsers.py](TEST_APPS\icetea_plugins\nfc_test_parsers.py) +API E2E (wireless) tests [test_nfc.py](TEST_APPS\testcases\nfc\test_nfc.py) + +Commandline (serial port) driven [target app](TEST_APPS\devices\nfcapp\main.cpp) aka _'CreamScone'_ which allows manual interactions with the driver. The app will send all API return data over serial link. + +An icetea plugin [nfc_test_parsers.py](TEST_APPS\icetea_plugins\nfc_test_parsers.py) which parses API responses over the serial port into python variables. MbedOS cli test app [main.cpp](TEST_APPS\device\nfcapp\main.cpp). The CLI commands return results asynchronously for most commands which get passed to and handled on a driver thread. -The SUT target is rebooted between tests, since test modify the target hardware state. - -**Future: ** A complete inter-op ready design expands to also include a switch-box to allow the reader to connect to NFC enabled targets nearby using flying cables and a sticky-back antenna. The switch allows selecting either alternative tags, or NFC peers. The switch can be controlled using GPIO either driven from spare IO pins on the target DUT itself (preferred option), or perhaps from a Raspberry pi. +**Future: ** A complete inter-op ready design intended to include a switch-box to allow the reader to connect to NFC enabled targets nearby using flying cables and a sticky-back antenna. The switch allows selecting either alternative tags, or NFC peers. The switch can be controlled using GPIO either driven from spare IO pins on the target DUT itself (preferred option), or perhaps from a Raspberry pi. ![inter-op](img/inter-op-view.png) ![future](img/creamscone-mobile.png) -**Reference: ** +** Reference: ** -https://github.com/ARMmbed/mbed-os/blob/master/docs/design-documents/nfc/nfc_design.md +[ARMmbed NFC design](https://github.com/ARMmbed/mbed-os/blob/master/docs/design-documents/nfc/nfc_design.md) -https://github.com/ARMmbed/mbed-os/tree/master/features/nfc/nfc +[ARMmbed NFC code](https://github.com/ARMmbed/mbed-os/tree/master/features/nfc/nfc) -https://github.com/ARMmbed/mbed-os-example-nfc +[ARMmbed NFC example application](https://github.com/ARMmbed/mbed-os-example-nfc/) + +[Python NFC library](https://nfcpy.readthedocs.io/en/latest/topics/get-started.html) + +[NFC forum](https://nfc-forum.org/) # User Guide This section covers the test case specification and how to run the test suite. +The SUT target is rebooted between tests, since tests modify the target hardware state. + ## Test cases CLI commands used by each test case describe the steps in a test. ** Basic local only cases ** @@ -196,7 +204,7 @@ indicated pins on the Shield. ``` Schematic (https://www.element14.com/community/docs/DOC-76384/l/explore-nfc-board-schematic) To change pinouts, if your reference design or shield pins differ for the PN512 controller driver, open nfcProcessCtrl.cpp and find the code -``` +```json NFCProcessController::NFCProcessController(events::EventQueue &queue) : // pins: mosi, miso, sclk, ssel, irq, rst _pn512_transport(D11, D12, D13, D10, A1, A0), _pn512_driver( @@ -209,7 +217,7 @@ modify pins as needed. **Compilation target drivers** If using the EEPROM driver, the mbed_app.json will contain -``` +```json "target_overrides": { "DISCO_L475VG_IOT01A": { "target.extra_labels_add": ["M24SR"] @@ -218,7 +226,7 @@ If using the EEPROM driver, the mbed_app.json will contain ... ``` If using the Explorer Shield or PN512 driver mbed_app.json will add -``` +```json "target_overrides": { "NUCLEO_F401RE": { "target.extra_labels_add": ["PN512"] @@ -248,17 +256,17 @@ And copy the files into your mbed root: `xcopy ..\mbed-nfc-m24sr\*.* .\eeprom_driver\' To run the End2End tests, type: -`mbed test --icetea -n test_nfce2e` +`mbed test --icetea --app-config .\TEST_APPS\device\nfcapp\mbed_app.json -n test_nfce2e` To run only the standalone (readerless tests if you do not have a card reader), type: -`mbed test --icetea -n test_nfc_eeprom,test_nfc_error_codes,test_nfc_setsmartposter,test_nfc_erase,test_nfc_write_long` +`mbed test --icetea --app-config .\TEST_APPS\device\nfcapp\mbed_app.json -n test_nfc_eeprom,test_nfc_error_codes,test_nfc_setsmartposter,test_nfc_erase,test_nfc_write_long` # Alternate NFC drivers note: Please see the example json file .\TEST_APPS\testcases\nfc\mbed_app.json . The test does not check that you have any needed shield installed, so if it "hangs" at the point the "initnfc" command is used, the driver or shield may be the fault. The test assumes that MBED_CONF_NFCEEPROM is set to 1, if not it assumes that a NFC Controller driver is in use. To test drivers other than PN512 and M24SR, it is required to make test code changes that reference the driver. The driver can be instantiated once only. If the new driver you add is for Eeprom, open nfccommands.cpp and find the code and modify line as shown +++ -``` +```C++ NFCTestShim* new_testshim() { #if MBED_CONF_NFCEEPROM --- mbed::nfc::NFCEEPROMDriver& eeprom_driver = get_eeprom_driver(nfcQueue); @@ -272,7 +280,7 @@ NFCTestShim* new_testshim() { ``` If the driver you add is a Controller driver, open nfcProcessCtrl.cpp and find the code -``` +```C++ NFCProcessController::NFCProcessController(events::EventQueue &queue) : // pins: mosi, miso, sclk, ssel, irq, rst _pn512_transport(D11, D12, D13, D10, A1, A0), _pn512_driver( @@ -289,7 +297,7 @@ Note: If the target uses an EEPROM, it need not be powered/running, to be read, **Device API error codes** You can issue the command "getlastnfcerror help" to see a list of error codes that are returned by most commands. -``` +```C++ #define NFC_OK 0 ///< No error #define NFC_ERR_UNKNOWN 1 ///< Unknown error #define NFC_ERR_LENGTH 2 ///< Length of parameter is wrong @@ -313,3 +321,16 @@ You can issue the command "getlastnfcerror help" to see a list of error codes th #define NFC_ERR_DISCONNECTED 20 ///< Link has disconnected #define NFC_ERR_ABORTED 21 ///< Command was aborted ``` +# Known issues + +1. The test app defines large buffer to store the maximum realistic message of 8K by default. For targets with limited memory (< ~32K) will need to modify the app config. Open mbed_app.config and modify the setting ` "TEST_NDEF_MSG_MAX" : 8192` to suit by overriding it on specific targets. The test cases (python code) which stress read/write will need updates if the buffer is reduced to 2K by editing test_nfc.py and modifying the line(s) to fall within the new macro value. +```python + # Values > 4 k incur large time costs + STRESS_BUFFLEN = 2050 +``` + +2. The test app and the supplied drivers only support Type4 tags. The test app thus does not exercise the different protocols and always sets iso-dep level functionality (Type4) for NFC Controller initialization. + +1. Test test_nfce2e_discovery_loop fails on NFC controller. The NFC controller driver discovery loop cannot be stopped manually. No major functionality is lost, it only prevents a complete disable of NFC at runtime. A bug ticket #IOTPAN-313 was logged to fix the stop function. The Controller still restarts discovery loop normally under app control after a peer disconnects. + +1. The smartposter NDEF record wrapper class `smartposter.h` is also provided as part of the NFC examples. The examples are not needed to compile the test app, but this example class may be usefull to customers. This file may thus move into the NFC component in future. diff --git a/TEST_APPS/testcases/nfc/nfc_cli_helper.py b/TEST_APPS/testcases/nfc/nfc_cli_helper.py index bd6e695c1a..a54e2464b5 100644 --- a/TEST_APPS/testcases/nfc/nfc_cli_helper.py +++ b/TEST_APPS/testcases/nfc/nfc_cli_helper.py @@ -22,9 +22,12 @@ from nfc_messages import NfcErrors import logging import icetea_lib.tools.asserts as asserts +# Values > 4 k incur large time costs +STRESS_BUFFLEN = 2050 # constant value for large buffers, this needs to be less than the target supported size. + class CliHelper(): """ - Helper method, checks the nfc SDK error-code for you, makes writing a negative test much easier + Helper methods, checks the nfc SDK error-code for you, makes writing a negative test much easier Example: if (target_is_eeprom): nfc_command("dev1", "start", expected_retcode=-2, expected_nfc_error= NfcErrors.nfc_err_unsupported) @@ -39,6 +42,9 @@ class CliHelper(): asynchronous=False, report_cmd_fail=True, expected_nfc_error=NfcErrors.nfc_ok): + """ + By default will assert if the NFC result code is non-zero. + """ response = self.command(k, cmd, wait, timeout, expected_retcode, asynchronous, report_cmd_fail) asserts.assertEqual(int(response.parsed['lastnfcerror']), expected_nfc_error.value) return response @@ -50,23 +56,25 @@ class CliHelper(): @staticmethod def debug_nfc_data(key, value): """ - print useful data values for the host/user {{in between}} easy to spot brackets. + print useful data values for the host/user with a >> preamble to make it easy to spot """ - text = "{{%s=%s}}" % (key, value) + text = ">> %s=%s" % (key, value) logging.Logger.info(text) - def assert_binary_equal(self, left, right, length): + def assert_binary_equal(self, left, right, left_length, right_length): + asserts.assertEqual(left_length, right_length, "Buffers are not same length") i = 0 - while i < length: + while i < left_length: asserts.assertEqual(left[i], ord(right[i]), ("Missmatch @offset %d 0x%x <> 0x%x" % (i, left[i], ord(right[i]))) ) i = i + 1 - def assert_text_equal(self, left, right, length): + def assert_text_equal(self, left, right, left_length, right_length): """ Asserts if the 2 buffers (Text) differ """ + asserts.assertEqual(left_length, right_length, "Buffers are not same length") i = 0 - while i < length: + while i < left_length: asserts.assertEqual(ord(left[i]), ord(right[i]), ("Missmatch @offset %d %d <> %d" % (i, ord(left[i]), ord(right[i]))) ) i = i + 1 diff --git a/TEST_APPS/testcases/nfc/test_nfc.py b/TEST_APPS/testcases/nfc/test_nfc.py index 14b907a8d3..7fabf8dae0 100644 --- a/TEST_APPS/testcases/nfc/test_nfc.py +++ b/TEST_APPS/testcases/nfc/test_nfc.py @@ -24,6 +24,7 @@ from mbed_clitest.tools.tools import test_case import icetea_lib.tools.asserts as asserts from nfc_messages import NfcErrors from nfc_cli_helper import CliHelper +from nfc_cli_helper import STRESS_BUFFLEN class CreamSconeTests(Bench, CliHelper): @@ -75,6 +76,9 @@ def test_nfce2e_target_found(self): response = self.nfc_command("dev1", "iseeprom") eeprom = response.parsed['iseeprom'] + + # Invokes icetea command, this method also checks the NFC api result expected_nfc_error=NFC_OK + # Tester can supply the expected_nfc_error=NFC_XXX parameter, to override. self.nfc_command("dev1", "initnfc") if not eeprom: self.nfc_command("dev1", "start") @@ -172,8 +176,7 @@ def test_nfce2e_reprogrammed(self): # check contents expected_message = str(smartposter) - asserts.assertEqual(len(response.parsed['nfcmessage']), len(expected_message)) - self.assert_binary_equal(response.parsed['nfcmessage'], expected_message, len(expected_message)) + self.assert_binary_equal(response.parsed['nfcmessage'], expected_message, len(response.parsed['nfcmessage']), len(expected_message)) @test_case(CreamSconeTests) @@ -182,7 +185,7 @@ def test_nfce2e_read_stress(self): check - Large record can be read via contactless """ messageRep = 'thequickbrownfoxjumpedoverthelazydog' # repeating message written - textLength = 2050 # 2K < x < 4K + textLength = STRESS_BUFFLEN # 2K < x < 4K # calculate actual message to compare to using the library expected_text = nfc_messages.repeat_string_to_length(messageRep, textLength) @@ -204,8 +207,7 @@ def test_nfce2e_read_stress(self): # assert that read the eeprom contents gives correct data and length asserts.assertEqual(tag.ndef.records[0].__class__.__name__, "TextRecord", "expected TextRecord") - asserts.assertEqual(len(tag.ndef.records[0].text), len(expected_text)) - self.assert_text_equal(tag.ndef.records[0].text, expected_text, len(expected_text)) + self.assert_text_equal(tag.ndef.records[0].text, expected_text, len(tag.ndef.records[0].text), len(expected_text)) @test_case(CreamSconeTests) @@ -214,7 +216,7 @@ def test_nfce2e_reprogrammed_stress(self): check - Large record can be programmed from a remote and read via contactless """ messageRep = 'thequickbrownfoxjumpedoverthelazydog' # repeating message written - textLength = 2050 # 2K < x < 4K + textLength = STRESS_BUFFLEN # 2K < x < 4K # calculate actual message to compare to using the library message = nfc_messages.make_textrecord( nfc_messages.repeat_string_to_length(messageRep, textLength)) @@ -244,8 +246,7 @@ def test_nfce2e_reprogrammed_stress(self): # verify in target response = self.nfc_command("dev1", "readmessage") - asserts.assertEqual(len(response.parsed['nfcmessage']), len(expected_message)) - self.assert_binary_equal(response.parsed['nfcmessage'], expected_message, len(expected_message)) + self.assert_binary_equal(response.parsed['nfcmessage'], expected_message, len(response.parsed['nfcmessage']), len(expected_message)) @test_case(CreamSconeTests) diff --git a/TEST_APPS/testcases/nfc/test_self.py b/TEST_APPS/testcases/nfc/test_self.py index 916dacee1f..17e2bbf5b0 100644 --- a/TEST_APPS/testcases/nfc/test_self.py +++ b/TEST_APPS/testcases/nfc/test_self.py @@ -22,7 +22,7 @@ import icetea_lib.tools.asserts as asserts import nfc_messages from nfc_messages import NfcErrors from nfc_cli_helper import CliHelper - +from nfc_cli_helper import STRESS_BUFFLEN """ Standalone (no NFC reader needed) tests, which cover API with no end-to-end checks. @@ -110,7 +110,7 @@ check - Create a SmartPoster but does not read it back def test_nfc_setsmartposter(self): self.nfc_command("dev1", "initnfc") - self.nfc_command("dev1", "setsmartposter -u https://www.mbed.com") + self.nfc_command("dev1", "setsmartposter https://www.mbed.com") @test_case(CreamSconeSelfTests) def test_nfc_erase(self): @@ -131,7 +131,7 @@ can be read back. @test_case(CreamSconeSelfTests) def test_nfc_write_long(self): messageRep = 'thequickbrownfoxjumpedoverthelazydog' # repeating message written - textLength = 200 # 2K < x < 4K + textLength = STRESS_BUFFLEN # 2K < x < 4K # calculate actual message to compare to using the library message = nfc_messages.make_textrecord( nfc_messages.repeat_string_to_length(messageRep, textLength)) expected_message = str(message) @@ -146,12 +146,7 @@ def test_nfc_write_long(self): self.nfc_command("dev1", "writelong %d %s" % (textLength,messageRep)) response = self.nfc_command("dev1", "readmessage") # assert that read the eeprom contents gives textlength bytes (including framing bytes which will vary) - asserts.assertEqual(len(response.parsed['nfcmessage']), len(expected_message)) - i = 0 - # assert that read the eeprom contents gives thequickbrownfoxjumpedoverthelazydog repeated in loop - while i < len(response.parsed['nfcmessage']): - asserts.assertEqual(response.parsed['nfcmessage'][i], ord(expected_message[i])) - i = i + 1 + self.assert_binary_equal(response.parsed['nfcmessage'], expected_message, len(response.parsed['nfcmessage']), len(expected_message)) ''' check - Query supported protocols if we have a controller @@ -170,7 +165,8 @@ def test_nfc_get_controller_protocols(self): ''' -Set used protocols if we have an controller +check - Can set used protocols if we have a controller +Note: Currently only support Typ4 tags in PN512 driver ''' @test_case(CreamSconeSelfTests) def test_nfc_set_controller_protocols(self): @@ -188,3 +184,60 @@ def test_nfc_set_controller_protocols(self): response = self.nfc_command("dev1", "setprotocols nfcdep") response = self.nfc_command("dev1", "setprotocols t5t") response = self.nfc_command("dev1", "setprotocols t1t t2t t3t isodep nfcdep t5t") + +''' +check - SmartPoster URI forms are supported (in the test-app) +''' +@test_case(CreamSconeSelfTests) +def test_nfc_check_smartposter_uri_forms(self): + def enum(**enums): + return type('Enum', (), enums) + + IDS = enum(NA=0x00, # Not applicable + HTTP_WWW=0x01, # http://www. + HTTPS_WWW=0x02, # https://www. + HTTP=0x03, # http:// + HTTPS=0x04, # https:// + TEL=0x05, # tel: + MAILTO=0x06, # mailto: + FTP_ANONYMOUS=0x07, # ftp://anonymous:anonymous@ + FTP_FTP=0x08, # ftp://ftp. + FTPS=0x09, # ftps:// + SFTP=0x0A, # sftp:// + SMB=0x0B, # smb:// + NFS=0x0C, # nfs:// + FTP=0x0D, # ftp:// + DAV=0x0E, # dav:// + NEWS=0x0F, # news: + TELNET=0x10, # telnet:// + IMAP=0x11, # imap: + RSTP=0x12, # rstp:// + URN=0x13, # urn: + POP=0x14, # pop: + SIP=0x15, # sip: + SIPS=0x16, # sips: + TFTP=0x17, # tftp: + BTSPP=0x18, # btspp:// + BTL2CAP=0x19, # btl2cap:// + BTGOEP=0x1A, # btgoep:// + TCPOBEX=0x1B, # tcpobex:// + IRDAOBEX=0x1C, # irdaobex:// + FILE=0x1D, # file:// + URN_EPC_ID=0x1E, # urn:epc:id: + URN_EPC_TAG=0x1F, # urn:epc:tag: + URN_EPC_PAT=0x20, # urn:epc:pat: + URN_EPC_RAW=0x21, # urn:epc:raw: + URN_EPC=0x22, # urn:epc: + URN_NFC=0x23, # urn:nfc: + ) + self.nfc_command("dev1", "initnfc") + result = self.nfc_command("dev1", "setsmartposter https://www.mbed.com") + asserts.assertEqual(result.parsed['uri_id'], IDS.HTTPS_WWW, "uri type expected HTTPS_WWW") + result = self.nfc_command("dev1", "setsmartposter http://www.mbed.com") + asserts.assertEqual(result.parsed['uri_id'], IDS.HTTP_WWW) + result = self.nfc_command("dev1", "setsmartposter https://www.topleveldomain") + asserts.assertEqual(result.parsed['uri_id'], IDS.HTTPS_WWW) + result = self.nfc_command("dev1", "setsmartposter tel:555-5551234") + asserts.assertEqual(result.parsed['uri_id'], IDS.TEL) + result = self.nfc_command("dev1", "setsmartposter ftp://www.mbed.com/files/") + asserts.assertEqual(result.parsed['uri_id'], IDS.FTP ) From c36f5406ae8cd9bdc5bfdcd3dcff22813f7eba57 Mon Sep 17 00:00:00 2001 From: Conrad Braam Date: Tue, 15 Jan 2019 17:33:35 +0000 Subject: [PATCH 05/14] Merged buffer size in config --- TEST_APPS/device/nfcapp/main.cpp | 10 ++- TEST_APPS/device/nfcapp/mbed_app.json | 2 +- TEST_APPS/device/nfcapp/nfccommands.cpp | 19 +++- TEST_APPS/device/nfcapp/nfccommands.h | 27 +++--- TEST_APPS/device/nfcapp/nfcprocessCtrl.h | 5 ++ TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp | 11 ++- TEST_APPS/device/nfcapp/nfcprocessEeprom.h | 3 + TEST_APPS/device/nfcapp/nfctestshim.cpp | 16 ++-- TEST_APPS/device/nfcapp/nfctestshim.h | 10 ++- TEST_APPS/icetea_plugins/nfc_test_parsers.py | 92 ++++++++++++++++---- TEST_APPS/testcases/nfc/README.md | 14 +-- TEST_APPS/testcases/nfc/nfc_cli_helper.py | 14 +-- TEST_APPS/testcases/nfc/nfc_messages.py | 28 +++--- TEST_APPS/testcases/nfc/test_nfc.py | 47 +++++----- TEST_APPS/testcases/nfc/test_self.py | 28 ++++-- 15 files changed, 221 insertions(+), 105 deletions(-) diff --git a/TEST_APPS/device/nfcapp/main.cpp b/TEST_APPS/device/nfcapp/main.cpp index 2578fd7c24..4d5a3fef8c 100644 --- a/TEST_APPS/device/nfcapp/main.cpp +++ b/TEST_APPS/device/nfcapp/main.cpp @@ -65,7 +65,7 @@ const char *errorcodes = // descriptions from nfc/stack/nfc_errors.h "20 NFC_ERR_DISCONNECTED \n" "21 NFC_ERR_ABORTED\n"; -// for easy manual UI interaction +/** Disables VT100 etc. for easy manual UI interaction */ int seteasy(int argc, char *argv[]) { const char msg[][20] = @@ -95,15 +95,17 @@ int main(int argc, char *argv[]) "last NFC error code", errorcodes); cmd_add("setlastnfcerror", HandleTestCommand::cmd_set_last_nfc_error, "self-test", "for self-test only"); + cmd_add("iseeprom", HandleTestCommand::cmd_get_conf_nfceeprom, + "get NFC configEEPROM present", + "true if config exists, else false"); cmd_add("initnfc", HandleTestCommand::cmd_init_nfc, "init NFC driver", "call first"); + cmd_add("getmaxndef", HandleTestCommand::cmd_get_max_ndef, "get max NDEF record target supports", + "returns the eeprom size, or max buffer if a controller"); cmd_add("init", HandleTestCommand::cmd_init_nfc, "alias initnfc", "call first"); cmd_add("setsmartposter", HandleTestCommand::cmd_set_smartposter, "send smartposter NDEF", ""); - cmd_add("iseeprom", HandleTestCommand::cmd_get_conf_nfceeprom, - "get NFC configEEPROM present", - "true if config exists, else false"); cmd_add("readmessage", HandleTestCommand::cmd_read_message, "read EEPROM else return last message", "returns hex dump"); cmd_add("read", HandleTestCommand::cmd_read_message, "alias readmessage", diff --git a/TEST_APPS/device/nfcapp/mbed_app.json b/TEST_APPS/device/nfcapp/mbed_app.json index 0eb15da3ff..a0faf90cf9 100644 --- a/TEST_APPS/device/nfcapp/mbed_app.json +++ b/TEST_APPS/device/nfcapp/mbed_app.json @@ -1,6 +1,6 @@ { "config": { - "TEST_NDEF_MSG_MAX" : 8192 + "TEST_NDEF_MSG_MAX" : 1024 }, "target_overrides": { "DISCO_L475VG_IOT01A": { diff --git a/TEST_APPS/device/nfcapp/nfccommands.cpp b/TEST_APPS/device/nfcapp/nfccommands.cpp index e21aa14ba2..27941d1f53 100644 --- a/TEST_APPS/device/nfcapp/nfccommands.cpp +++ b/TEST_APPS/device/nfcapp/nfccommands.cpp @@ -68,6 +68,17 @@ int HandleTestCommand::cmd_set_last_nfc_error(int argc, char *argv[]) return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } + +int HandleTestCommand::cmd_get_max_ndef(int argc, char *argv[]) +{ + if (pNFC_Test_Shim) { + nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_get_max_ndef); + return CMDLINE_RETCODE_EXCUTING_CONTINUE; + } + return CMDLINE_RETCODE_FAIL; +} + + int HandleTestCommand::cmd_init_nfc(int argc, char *argv[]) { @@ -82,7 +93,7 @@ int HandleTestCommand::cmd_init_nfc(int argc, char *argv[]) int HandleTestCommand::cmd_read_message(int argc, char *argv[]) { - nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_read_nfceeprom); + nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_read_nfc_contents); return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } @@ -133,6 +144,12 @@ int HandleTestCommand::cmd_write_long_ndef_message(int argc, char *argv[]) cmd_printf("Cannot convert value to int\r\n"); return (CMDLINE_RETCODE_INVALID_PARAMETERS); } + // check that it would not overflow + if (length > MBED_CONF_APP_TEST_NDEF_MSG_MAX) { + cmd_printf("Buffer length may not exceed %d !\r\n", (int)MBED_CONF_APP_TEST_NDEF_MSG_MAX); + return (CMDLINE_RETCODE_FAIL); + } + data = (char *) malloc(length + 1); if (!data) { cmd_printf("WARN out of memory!\r\n"); diff --git a/TEST_APPS/device/nfcapp/nfccommands.h b/TEST_APPS/device/nfcapp/nfccommands.h index 700567f055..0bc1da8270 100644 --- a/TEST_APPS/device/nfcapp/nfccommands.h +++ b/TEST_APPS/device/nfcapp/nfccommands.h @@ -38,44 +38,49 @@ extern events::EventQueue nfcQueue; */ class HandleTestCommand { public: - // start thread and handle queue HandleTestCommand(); - /* set corresponding mask bit on, return false if the supplied string cannot parse */ + /** set corresponding mask bit on in referenced structure, return false if the supplied string cannot parse */ static bool set_protocol_target(mbed::nfc::nfc_rf_protocols_bitmask_t &bitmask, const char *protocolName); - /* return and clear the last result code. Type "help getlastnfcerror" for a list of error codes */ + /** return and clear the last result code. Type "help getlastnfcerror" for a list of error codes */ static int cmd_get_last_nfc_error(int argc, char *argv[]) { nfcQueue.call(NFCTestShim::cmd_get_last_nfc_error); return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } - /* internal function to test getlastnfcerror */ + /** internal function to test getlastnfcerror */ static int cmd_set_last_nfc_error(int argc, char *argv[]); - /* compile time flag */ + /** returns compile time flag if NFC EEPROM was compiled */ static int cmd_get_conf_nfceeprom(int argc, char *argv[]) { nfcQueue.call(NFCTestShim::cmd_get_conf_nfceeprom); return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } - /* must be called before invoking any other calls */ + /** For EEPROM, returns the driver max_ndef value, else returns the app config MBED_CONF_APP_TEST_NDEF_MSG_MAX */ + static int cmd_get_max_ndef(int argc, char *argv[]); + + /** Init must be called before invoking any other calls, obtains a driver reference and initializes driver */ static int cmd_init_nfc(int argc, char *argv[]); - /* write a smartposter url, 'Sp' NDEF to the target */ + /** write a smartposter url, 'Sp' NDEF to the target */ static int cmd_set_smartposter(int argc, char *argv[]); - /* erase EEPROM */ + /** erases the EEPROM if present */ static int cmd_erase(int argc, char *argv[]); - // controller driver methods: + /** Returns a CSV list of protocols supported */ static int cmd_get_supported_rf_protocols(int argc, char *argv[]); + /** Sets the protocols supported (unimplemented) */ static int cmd_configure_rf_protocols(int argc, char *argv[]); + /** starts the NFC discovery loop if controller, has no effect on EEPROM */ static int cmd_start_discovery(int argc, char *argv[]); + /** stops the NFC discovery loop if controller */ static int cmd_stop_discovery(int argc, char *argv[]); - /* read raw EEPROM contents */ + /** read raw EEPROM contents, reads a buffer if a controller is used */ static int cmd_read_message(int argc, char *argv[]); - /* write a text 'T' NDEF message to the target */ + /** write a text 'T' NDEF message to the target */ static int cmd_write_long_ndef_message(int argc, char *argv[]); }; diff --git a/TEST_APPS/device/nfcapp/nfcprocessCtrl.h b/TEST_APPS/device/nfcapp/nfcprocessCtrl.h index 24736d07c0..0a1ad629de 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessCtrl.h +++ b/TEST_APPS/device/nfcapp/nfcprocessCtrl.h @@ -48,6 +48,11 @@ class NFCProcessController: NFCTestShim, public: NFCProcessController(events::EventQueue &queue); + void cmd_get_max_ndef() + { + cmd_printf("{{maxndef=%d}}\r\n", (int)MBED_CONF_APP_TEST_NDEF_MSG_MAX); + cmd_ready(CMDLINE_RETCODE_SUCCESS); + } nfc_err_t init(); nfc_err_t start_discovery(); nfc_err_t stop_discovery(); diff --git a/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp b/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp index c9abf9cb45..371b58dd19 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp +++ b/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp @@ -47,7 +47,7 @@ using mbed::nfc::NFCEEPROMDriver; // implements : mbed::nfc::NFCEEPROM::Delegate NFCProcessEEPROM::NFCProcessEEPROM(events::EventQueue &queue, NFCEEPROMDriver &eeprom_driver) : - NFCTestShim(queue), _eeprom(&eeprom_driver, &queue, _ndef_buffer) + NFCTestShim(queue), _eeprom(&eeprom_driver, &queue, _ndef_buffer), _ptr_eeprom_driver(&eeprom_driver) {} nfc_err_t NFCProcessEEPROM::init() @@ -63,6 +63,13 @@ nfc_err_t NFCProcessEEPROM::init() return (err); } + +void NFCProcessEEPROM::cmd_get_max_ndef() +{ + cmd_printf("{{maxndef=%d}}\r\n", (int)_ptr_eeprom_driver->read_max_size()); + cmd_ready(CMDLINE_RETCODE_SUCCESS); +} + void NFCProcessEEPROM::queue_write_call() { cmd_printf("NFCProcessEEPROM::queue_write_call() entry\r\n"); @@ -83,7 +90,6 @@ void NFCProcessEEPROM::queue_erase_call() void NFCProcessEEPROM::on_ndef_message_written(nfc_err_t result) { - // todo: de-duplicate this code set_last_nfc_error(result); if (result == NFC_OK) { cmd_printf("message written successfully\r\n"); @@ -108,7 +114,6 @@ void NFCProcessEEPROM::on_ndef_message_read(nfc_err_t result) void NFCProcessEEPROM::on_ndef_message_erased(nfc_err_t result) { - // todo : de-duplicate/template this callback handler set_last_nfc_error(result); if (result == NFC_OK) { cmd_printf("message erased successfully\r\n"); diff --git a/TEST_APPS/device/nfcapp/nfcprocessEeprom.h b/TEST_APPS/device/nfcapp/nfcprocessEeprom.h index a4af6517a3..52b61cc3a9 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessEeprom.h +++ b/TEST_APPS/device/nfcapp/nfcprocessEeprom.h @@ -43,6 +43,8 @@ public: void queue_write_long_call(); void queue_read_call(); void queue_erase_call(); + void cmd_get_max_ndef(); + private: virtual void on_ndef_message_written(nfc_err_t result); @@ -52,6 +54,7 @@ private: virtual void on_ndef_message_erased(nfc_err_t result); private: mbed::nfc::NFCEEPROM _eeprom; + mbed::nfc::NFCEEPROMDriver *_ptr_eeprom_driver; }; #endif // eeprom diff --git a/TEST_APPS/device/nfcapp/nfctestshim.cpp b/TEST_APPS/device/nfcapp/nfctestshim.cpp index b0c5793272..15dd48f951 100644 --- a/TEST_APPS/device/nfcapp/nfctestshim.cpp +++ b/TEST_APPS/device/nfcapp/nfctestshim.cpp @@ -42,7 +42,6 @@ using mbed::nfc::nfc_rf_protocols_bitmask_t; // statics namespace { char long_string[MBED_CONF_APP_TEST_NDEF_MSG_MAX]; - char const *uri_prefix_string[] = { "", "http://www.", "https://www.", @@ -234,18 +233,19 @@ void NFCTestShim::cmd_configure_rf_protocols( * the framework * \return void An ICETEA error code and NFC error is set asyncronously */ -void NFCTestShim::cmd_read_nfceeprom() +void NFCTestShim::cmd_read_nfc_contents() { #if MBED_CONF_NFCEEPROM ((NFCProcessEEPROM *)this)->queue_read_call(); - cmd_printf("NFCTestShim::read_nfceeprom() exit\r\n"); + cmd_printf("NFCTestShim::cmd_read_nfc_contents() exit\r\n"); #else // returns last message "written", since we cannot read print_ndef_message(_ndef_write_buffer, _ndef_write_buffer_used); + cmd_printf("Controller buffer data size=%d\r\n", _ndef_write_buffer_used); set_last_nfc_error(NFC_OK); - cmd_printf("NFCTestShim::read_nfceeprom()\r\n"); + cmd_printf("NFCTestShim::cmd_read_nfc_contents()\r\n"); cmd_ready(CMDLINE_RETCODE_SUCCESS); #endif } @@ -271,11 +271,10 @@ void NFCTestShim::cmd_erase() * \param uri This method must free the passed in pointer * \return void An ICETEA error code and NFC error is set asyncronously */ -void NFCTestShim::cmd_write_long(char *data) +void NFCTestShim::cmd_write_long(char *text_string) { MessageBuilder builder(ndef_poster_message); - - strcpy(::long_string, data); //max_ndef - header - overheads + strcpy(::long_string, text_string); //max_ndef - header - overheads Text text(Text::UTF8, span_from_cstr("en-US"), span_from_cstr((const char *)(::long_string))); @@ -290,9 +289,8 @@ void NFCTestShim::cmd_write_long(char *data) set_last_nfc_error(NFC_OK); cmd_ready(CMDLINE_RETCODE_SUCCESS); #endif - cmd_printf("NFCTestShim::write_long() exit\r\n"); - free(data); + free(text_string); } /** \brief Write a URI Use case would be to prompt to install an app from the appstore using the tag diff --git a/TEST_APPS/device/nfcapp/nfctestshim.h b/TEST_APPS/device/nfcapp/nfctestshim.h index bdcb52352c..d7bd659106 100644 --- a/TEST_APPS/device/nfcapp/nfctestshim.h +++ b/TEST_APPS/device/nfcapp/nfctestshim.h @@ -28,7 +28,9 @@ #include "nfc/ndef/common/util.h" #include "nfc/NFCDefinitions.h" - +/** + * Test app driver wrapper. This is a base class containing shared EEPROM and Controller test data + logic + */ class NFCTestShim { public: NFCTestShim(events::EventQueue &queue); @@ -46,6 +48,7 @@ public: { get_conf_nfceeprom(); }; + virtual void cmd_get_max_ndef() = 0; static void get_last_nfc_error(); static void set_last_nfc_error(int err); static void get_conf_nfceeprom(); @@ -59,8 +62,8 @@ public: void cmd_set_smartposter(char *cmdUri); void cmd_erase(); - void cmd_write_long(char *data); - void cmd_read_nfceeprom(); + void cmd_write_long(char *text_string); + void cmd_read_nfc_contents(); void cmd_start_discovery(bool manual = false); void cmd_stop_discovery(); void cmd_configure_rf_protocols(mbed::nfc::nfc_rf_protocols_bitmask_t protocols); @@ -101,7 +104,6 @@ protected: uint8_t _ndef_buffer[MBED_CONF_APP_TEST_NDEF_MSG_MAX]; // driver I/O buffer bool _discovery_restart; // default true, restart discovery loop again on remote disconnect events::EventQueue &_queue; - }; // forward declare single instance diff --git a/TEST_APPS/icetea_plugins/nfc_test_parsers.py b/TEST_APPS/icetea_plugins/nfc_test_parsers.py index bfbfbaa393..5afec0842e 100644 --- a/TEST_APPS/icetea_plugins/nfc_test_parsers.py +++ b/TEST_APPS/icetea_plugins/nfc_test_parsers.py @@ -25,9 +25,10 @@ class NfcTestParsers(PluginBase): def get_parsers(self): return { + 'iseeprom': self.status_parser, + 'getmaxndef': self.status_parser, 'getlastnfcerror': self.trace_parser, 'setlastnfcerror': self.trace_parser, - 'iseeprom': self.trace_parser, 'initnfc': self.trace_parser, # all commands that return an NFC error code 'readmessage' : self.trace_parser, 'erase' : self.trace_parser, @@ -39,18 +40,17 @@ class NfcTestParsers(PluginBase): 'setprotocols': self.trace_parser } - def trace_parser(self, response): + def status_parser(self, response): + """ + commands that do NOT return an NFC result-code + """ results = {'iseeprom': None, # 'true' if EEPROM - 'lastnfcerror':None, # 0=OK >0 = error - 'nfcmessage':None, # NDEF array of bytes - 'protocols':None, # csv list - 'uri_id':None} # nfc URI type identifier + 'lastnfcerror':0, # ignored here + 'maxndef':None # actual driver buffer size + } respLines = response.lines for line in respLines: try: - value = PluginBase.find_one(line, "{{lastnfcerror=([0-9]+)}}") - if value is not False: - results['lastnfcerror'] = int(value) # iseeprom value = PluginBase.find_one(line, "{{iseeprom=([\w]+)}}") if value is not False: @@ -58,18 +58,78 @@ class NfcTestParsers(PluginBase): results['iseeprom'] = True else: results['iseeprom'] = False - # nfcmessage (hex data dumps) - data = PluginBase.find_one(line, "{{nfcmessage=([0-9a-f\s]*)}}") + # max ndef + value = PluginBase.find_one(line, "{{maxndef=([0-9]+)}}") + if value is not False: + results['maxndef'] = int(value) + except re.error as e: # the framework gobbles errors in the plugin + print("Regex error",e,"occured in",os.path.basename(__file__), "!!") + raise e + return results + + def convert_from_hex(self, data): + value = [] + for byte in data.split(' '): + if bool(byte): + if len(byte) > 2: # the line wrapping code for huge lines appears to munch the space character between 2 bytes + value.append( int(byte[:2], 16)) + value.append( int(byte[2:4], 16)) + else: + value.append( int(byte, 16)) + return value + + # regex findall with additional filtering + @staticmethod + def find_all_hex_data(line, lookup): + """ + regexp search with one value to return. + + :param line: Line + :param lookup: regexp + :return: List of match groups or False + """ + results = re.findall(lookup, line) + if results is not False: + results = [m for m in results if len(m) > 2] # remove empty matches and + # non hex-data result entries + return results + return False + + def trace_parser(self, response): + results = {'iseeprom': None, # 'true' if EEPROM + 'lastnfcerror':None, # 0=OK >0 = error + 'nfcmessage':None, # NDEF array of bytes + 'protocols':None, # csv list + 'uri_id':None} # nfc URI type identifier + respLines = response.lines + started_read_data = False + partial_data = "" + for line in respLines: + try: + value = PluginBase.find_one(line, "{{lastnfcerror=([0-9]+)}}") + if value is not False: + results['lastnfcerror'] = int(value) + # {{nfcmessage=([0-9a-f\s]*)}} + data = PluginBase.find_one(line, "{{nfcmessage=([0-9a-f\s]*).*") if data is not False: - value = [] - for byte in data.split(' '): - if bool(byte): - value.append( int(byte, 16)) - results['nfcmessage'] = value + started_read_data = True + if started_read_data: # read data until we see a }} pair + values = self.find_all_hex_data(line, "([0-9a-f\s]*)") + if values is not False: + if "{{nfcmessage" in line: + value = values[0] + else: + value = values[0] + #print ("==%s==" % value) + partial_data += value + if PluginBase.find_one(line, ".*(}})") is not False: # search for end marker + started_read_data = False + results['nfcmessage'] = self.convert_from_hex(partial_data) # t1t,t2t,t3t,isodep,nfcdef,t5t value = PluginBase.find_one(line, "{{protocols=(([\w]*,?)*)}}") if value is not False: results['protocols'] = value # a csv list + # smartposter uri value = PluginBase.find_one(line, "{{uri_id=([0-9]+)}}") if value is not False: results['uri_id'] = int(value) diff --git a/TEST_APPS/testcases/nfc/README.md b/TEST_APPS/testcases/nfc/README.md index 43e64a20f1..8e4cf59836 100644 --- a/TEST_APPS/testcases/nfc/README.md +++ b/TEST_APPS/testcases/nfc/README.md @@ -9,7 +9,7 @@ This project is called CreamScone, which is an ice tea framework based cli-drive - [NFC tests.](#nfc-tests) - [Overview](#overview) -- [System Test high level design](#system-test-high-level-design) +- [System Test high level requirement](#system-test-high-level-requirement) - [Low level design](#low-level-design) - [User Guide](#user-guide) - [Test cases](#test-cases) @@ -46,7 +46,7 @@ Because the comissioning workflow application quality is the end goal, the NFC s -# System Test high level design +# System Test high level requirement Mitigate risks identified, to the product from an internal view to supporting releases. Help customers develop a driver or a design, and reduce their production risks. In summary: - Architecture risks and Api breaks - Partner cannot NFC forum Certify @@ -67,16 +67,15 @@ In short, “Empower engineers to efficiently ship quality code with confidence. API standalone Self tests [test_self.py](TEST_APPS\testcases\nfc\test_self.py) - API E2E (wireless) tests [test_nfc.py](TEST_APPS\testcases\nfc\test_nfc.py) -Commandline (serial port) driven [target app](TEST_APPS\devices\nfcapp\main.cpp) aka _'CreamScone'_ which allows manual interactions with the driver. The app will send all API return data over serial link. +An [icetea](https://github.com/ARMmbed/icetea/blob/master/README.md) framework test program. Commandline (serial port) driven [target app](TEST_APPS\devices\nfcapp\main.cpp) aka _'CreamScone'_ which allows manual interactions with the driver. The app will send all API return data over serial link. An icetea plugin [nfc_test_parsers.py](TEST_APPS\icetea_plugins\nfc_test_parsers.py) which parses API responses over the serial port into python variables. MbedOS cli test app [main.cpp](TEST_APPS\device\nfcapp\main.cpp). The CLI commands return results asynchronously for most commands which get passed to and handled on a driver thread. -**Future: ** A complete inter-op ready design intended to include a switch-box to allow the reader to connect to NFC enabled targets nearby using flying cables and a sticky-back antenna. The switch allows selecting either alternative tags, or NFC peers. The switch can be controlled using GPIO either driven from spare IO pins on the target DUT itself (preferred option), or perhaps from a Raspberry pi. +**Future: ** A complete inter-op ready design intended to include a switch-box to allow the reader to connect to NFC enabled targets nearby using flying cables and a sticky-back antenna. The switch should allow selecting either alternative tags, or NFC peers, and provide inter-operability coverage. The switch-box may be controlled using GPIO either driven from spare IO pins on the target DUT itself (preferred option), or perhaps from a Raspberry pi. ![inter-op](img/inter-op-view.png) @@ -159,7 +158,7 @@ writelong fill entire FILE with pattern ... ``` Note: Most commands also return a NFC status value (type "getlastnfcerror help" in console) which allow us to build negative test cases. -Note: Some commands only apply to NFC controllers, these commands fail with the appropriate not-supported code NFC_ERR_UNSUPPORTED and additionally return -2 error code to ice-tea. +Note: Some commands only apply to NFC controllers, these commands fail with the appropriate not-supported code NFC_ERR_UNSUPPORTED and additionally return -2 error code to ice-tea. Commands like the erase command is a no-op on a NFC Controller target in the test app, for test-writting convenience. **unimplemented CLI commands** @@ -323,7 +322,8 @@ You can issue the command "getlastnfcerror help" to see a list of error codes th ``` # Known issues -1. The test app defines large buffer to store the maximum realistic message of 8K by default. For targets with limited memory (< ~32K) will need to modify the app config. Open mbed_app.config and modify the setting ` "TEST_NDEF_MSG_MAX" : 8192` to suit by overriding it on specific targets. The test cases (python code) which stress read/write will need updates if the buffer is reduced to 2K by editing test_nfc.py and modifying the line(s) to fall within the new macro value. +1. The test app defines large buffer to store the maximum realistic message of 8K by default. For targets with limited memory (< ~32K) will need to modify the app config. Open mbed_app.config and modify the setting +` "TEST_NDEF_MSG_MAX" : 8192` to suit by overriding it on specific targets. The test cases (python code) which stress read/write will need updates if the buffer is reduced to 2K by editing test_nfc.py and modifying the line(s) to fall within the new macro value. ```python # Values > 4 k incur large time costs STRESS_BUFFLEN = 2050 diff --git a/TEST_APPS/testcases/nfc/nfc_cli_helper.py b/TEST_APPS/testcases/nfc/nfc_cli_helper.py index a54e2464b5..c66e443b26 100644 --- a/TEST_APPS/testcases/nfc/nfc_cli_helper.py +++ b/TEST_APPS/testcases/nfc/nfc_cli_helper.py @@ -23,7 +23,7 @@ import logging import icetea_lib.tools.asserts as asserts # Values > 4 k incur large time costs -STRESS_BUFFLEN = 2050 # constant value for large buffers, this needs to be less than the target supported size. +STRESS_BUFFLEN = 4096 # Default value for large buffer tests, this value can be read from the target with a command class CliHelper(): """ @@ -61,20 +61,20 @@ class CliHelper(): text = ">> %s=%s" % (key, value) logging.Logger.info(text) - def assert_binary_equal(self, left, right, left_length, right_length): - asserts.assertEqual(left_length, right_length, "Buffers are not same length") + def assert_binary_equal(self, left, right): + asserts.assertEqual(len(left), len(right), "Buffers are not same length %d %d" % (len(left), len(right))) i = 0 - while i < left_length: + while i < len(left): asserts.assertEqual(left[i], ord(right[i]), ("Missmatch @offset %d 0x%x <> 0x%x" % (i, left[i], ord(right[i]))) ) i = i + 1 - def assert_text_equal(self, left, right, left_length, right_length): + def assert_text_equal(self, left, right): """ Asserts if the 2 buffers (Text) differ """ - asserts.assertEqual(left_length, right_length, "Buffers are not same length") + asserts.assertEqual(len(left), len(right), "Buffers are not same length %d %d" % (len(left), len(right))) i = 0 - while i < left_length: + while i < len(left): asserts.assertEqual(ord(left[i]), ord(right[i]), ("Missmatch @offset %d %d <> %d" % (i, ord(left[i]), ord(right[i]))) ) i = i + 1 diff --git a/TEST_APPS/testcases/nfc/nfc_messages.py b/TEST_APPS/testcases/nfc/nfc_messages.py index 38e4eace47..895dab2943 100644 --- a/TEST_APPS/testcases/nfc/nfc_messages.py +++ b/TEST_APPS/testcases/nfc/nfc_messages.py @@ -21,28 +21,28 @@ import logging class NfcErrors(Enum): - nfc_ok = 0 + nfc_ok = 0 nfc_err_unknown = 1 nfc_err_length = 2 nfc_err_not_found = 3 nfc_err_unsupported = 4 nfc_err_params = 5 - nfc_err_buffer_too_small= 6 + nfc_err_buffer_too_small = 6 nfc_err_timeout = 7 nfc_err_crc = 8 nfc_err_nopeer = 9 - nfc_err_parity = 10 - nfc_err_field = 11 - nfc_err_collision = 12 - nfc_err_wrong_comm = 13 - nfc_err_protocol = 14 - nfc_err_busy = 15 - nfc_err_controller = 16 - nfc_err_halted = 17 - nfc_err_mac = 18 - nfc_err_underflow = 19 - nfc_err_disconnected = 20 - nfc_err_aborted = 21 + nfc_err_parity = 10 + nfc_err_field = 11 + nfc_err_collision = 12 + nfc_err_wrong_comm = 13 + nfc_err_protocol = 14 + nfc_err_busy = 15 + nfc_err_controller = 16 + nfc_err_halted = 17 + nfc_err_mac = 18 + nfc_err_underflow = 19 + nfc_err_disconnected = 20 + nfc_err_aborted = 21 ''' diff --git a/TEST_APPS/testcases/nfc/test_nfc.py b/TEST_APPS/testcases/nfc/test_nfc.py index 7fabf8dae0..f2d7f6c1f4 100644 --- a/TEST_APPS/testcases/nfc/test_nfc.py +++ b/TEST_APPS/testcases/nfc/test_nfc.py @@ -169,27 +169,22 @@ def test_nfce2e_reprogrammed(self): asserts.assertEqual(tag.ndef.records[0].__class__.__name__, "SmartposterRecord", "expected SmartposterRecord") asserts.assertEqual(expectedURI, tag.ndef.records[0].uri_records[0].uri, "expected exact URI") - self.clf.parse("mute") # disable radio + self.clf.parse("mute") # disable radio, to allow a local session # verify in target response = self.nfc_command("dev1", "readmessage") # check contents expected_message = str(smartposter) - self.assert_binary_equal(response.parsed['nfcmessage'], expected_message, len(response.parsed['nfcmessage']), len(expected_message)) + self.assert_binary_equal(response.parsed['nfcmessage'], expected_message) @test_case(CreamSconeTests) def test_nfce2e_read_stress(self): """ - check - Large record can be read via contactless + check - Large record can be programmed in and read via contactless """ messageRep = 'thequickbrownfoxjumpedoverthelazydog' # repeating message written - textLength = STRESS_BUFFLEN # 2K < x < 4K - - # calculate actual message to compare to using the library - expected_text = nfc_messages.repeat_string_to_length(messageRep, textLength) - message = nfc_messages.make_textrecord( expected_text ) response = self.nfc_command("dev1", "iseeprom") eeprom = response.parsed['iseeprom'] @@ -198,16 +193,23 @@ def test_nfce2e_read_stress(self): self.nfc_command("dev1", "initnfc") if not eeprom: self.nfc_command("dev1", "start") + textLength = STRESS_BUFFLEN + else: + max_ndef = self.nfc_command("dev1", "getmaxndef").parsed['maxndef'] + textLength = max_ndef / 2 # large values slow down test runs and may time out self.nfc_command("dev1", "erase") - self.nfc_command("dev1", "writelong %d %s" % (textLength,messageRep)) + # calculate actual message to compare to using the library + expected_text = nfc_messages.repeat_string_to_length(messageRep, textLength) + # write a large message to the tag via API, then read it wirelessly + self.nfc_command("dev1", "writelong %d %s" % (textLength,messageRep)) self.clf.parse("connect") tag = self.clf.clf_response() asserts.assertNotNone(tag, "Could not connect to any tag") # assert that read the eeprom contents gives correct data and length asserts.assertEqual(tag.ndef.records[0].__class__.__name__, "TextRecord", "expected TextRecord") - self.assert_text_equal(tag.ndef.records[0].text, expected_text, len(tag.ndef.records[0].text), len(expected_text)) + self.assert_text_equal(tag.ndef.records[0].text, expected_text) @test_case(CreamSconeTests) @@ -216,11 +218,7 @@ def test_nfce2e_reprogrammed_stress(self): check - Large record can be programmed from a remote and read via contactless """ messageRep = 'thequickbrownfoxjumpedoverthelazydog' # repeating message written - textLength = STRESS_BUFFLEN # 2K < x < 4K - # calculate actual message to compare to using the library - message = nfc_messages.make_textrecord( nfc_messages.repeat_string_to_length(messageRep, textLength)) - expected_message = str(message) response = self.nfc_command("dev1", "iseeprom") eeprom = response.parsed['iseeprom'] @@ -229,31 +227,38 @@ def test_nfce2e_reprogrammed_stress(self): self.nfc_command("dev1", "initnfc") if not eeprom: self.nfc_command("dev1", "start") + textLength = STRESS_BUFFLEN + else: + max_ndef = self.nfc_command("dev1", "getmaxndef").parsed['maxndef'] + textLength = max_ndef / 2 # large values slow down test runs and may time out + # calculate actual message to compare to using the library + message = nfc_messages.make_textrecord( nfc_messages.repeat_string_to_length(messageRep, textLength)) + expected_message = str(message) self.nfc_command("dev1", "erase") - # program a large tag to target wirelessly + # program a large tag to target remotely self.clf.parse("connect") tag = self.clf.clf_response() asserts.assertNotNone(tag, "Could not connect to any tag") nfc_messages.program_remote_tag(message, tag) self.logger.info("%d bytes chunk of data written to tag remotely" % len(str(message))) - # read device + # read device locally self.clf.parse("connect") tag = self.clf.clf_response() asserts.assertNotNone(tag, "Could not re-connect to any tag") - self.clf.parse("mute") # disable the reader radio + self.clf.parse("mute") # disable the reader radio, to allow local access # verify in target response = self.nfc_command("dev1", "readmessage") - self.assert_binary_equal(response.parsed['nfcmessage'], expected_message, len(response.parsed['nfcmessage']), len(expected_message)) + self.assert_binary_equal(response.parsed['nfcmessage'], expected_message) @test_case(CreamSconeTests) def test_nfce2e_discovery_loop(self): """ check - Controller discovery loop stop/start - fails : blocked by an issue + fails : blocked by an issue on NFC controllers only """ expectedURI = "https://www.nasa.com" # ensure that these differ per test case @@ -261,7 +266,8 @@ def test_nfce2e_discovery_loop(self): eeprom = response.parsed['iseeprom'] self.logger.info("Target includes NFCEEPROM: %s" % eeprom) - self.nfc_command("dev1", "initnfc") # this NOT automatically start discovery at the same time, the test command "start" must be used on a controller. (Eeeproms always have the loop enabled.) + self.nfc_command("dev1", "initnfc") # this NOT automatically start discovery at the same time, the test command + # "start" must be used on a controller. (Eeeproms always have the loop enabled.) # By default, the test app automatically starts discovery loop again after a reader disconnects from the controller. # Automatic resume after disconnect can be turned off by using command "start man" , the default is "start auto" . @@ -294,3 +300,4 @@ def test_nfce2e_discovery_loop(self): # eeprom, so not supported self.nfc_command("dev1", "start", expected_retcode=-2, expected_nfc_error= NfcErrors.nfc_err_unsupported ) self.nfc_command("dev1", "stop", expected_retcode=-2, expected_nfc_error= NfcErrors.nfc_err_unsupported ) + diff --git a/TEST_APPS/testcases/nfc/test_self.py b/TEST_APPS/testcases/nfc/test_self.py index 17e2bbf5b0..c4714d8cc3 100644 --- a/TEST_APPS/testcases/nfc/test_self.py +++ b/TEST_APPS/testcases/nfc/test_self.py @@ -23,6 +23,7 @@ import nfc_messages from nfc_messages import NfcErrors from nfc_cli_helper import CliHelper from nfc_cli_helper import STRESS_BUFFLEN +import nfc """ Standalone (no NFC reader needed) tests, which cover API with no end-to-end checks. @@ -146,7 +147,7 @@ def test_nfc_write_long(self): self.nfc_command("dev1", "writelong %d %s" % (textLength,messageRep)) response = self.nfc_command("dev1", "readmessage") # assert that read the eeprom contents gives textlength bytes (including framing bytes which will vary) - self.assert_binary_equal(response.parsed['nfcmessage'], expected_message, len(response.parsed['nfcmessage']), len(expected_message)) + self.assert_binary_equal(response.parsed['nfcmessage'], expected_message) ''' check - Query supported protocols if we have a controller @@ -177,13 +178,13 @@ def test_nfc_set_controller_protocols(self): if eeprom: self.logger.info("Test ignore - target includes NFCEEPROM: %s" % eeprom) else: - response = self.nfc_command("dev1", "setprotocols t1t") - response = self.nfc_command("dev1", "setprotocols t2t") - response = self.nfc_command("dev1", "setprotocols t3t") - response = self.nfc_command("dev1", "setprotocols isodep") - response = self.nfc_command("dev1", "setprotocols nfcdep") - response = self.nfc_command("dev1", "setprotocols t5t") - response = self.nfc_command("dev1", "setprotocols t1t t2t t3t isodep nfcdep t5t") + self.nfc_command("dev1", "setprotocols t1t") + self.nfc_command("dev1", "setprotocols t2t") + self.nfc_command("dev1", "setprotocols t3t") + self.nfc_command("dev1", "setprotocols isodep") + self.nfc_command("dev1", "setprotocols nfcdep") + self.nfc_command("dev1", "setprotocols t5t") + self.nfc_command("dev1", "setprotocols t1t t2t t3t isodep nfcdep t5t") ''' check - SmartPoster URI forms are supported (in the test-app) @@ -241,3 +242,14 @@ def test_nfc_check_smartposter_uri_forms(self): asserts.assertEqual(result.parsed['uri_id'], IDS.TEL) result = self.nfc_command("dev1", "setsmartposter ftp://www.mbed.com/files/") asserts.assertEqual(result.parsed['uri_id'], IDS.FTP ) + +''' +smoke - driver buffer size can be retrieved +''' +@test_case(CreamSconeSelfTests) +def test_nfc_get_max_ndef(self): + self.nfc_command("dev1", "initnfc") + max = self.nfc_command("dev1", "getmaxndef").parsed['maxndef'] + self.logger.info("Target NDEF max buffer size %d" % max) + self.logger.info("Teststress size %d" % STRESS_BUFFLEN) + From d285b5f23e67d7978eb5a3b10965922ef8ebb28d Mon Sep 17 00:00:00 2001 From: Conrad Braam Date: Tue, 29 Jan 2019 09:06:28 +0000 Subject: [PATCH 06/14] cpp refactored, refactor the python next --- TEST_APPS/device/nfcapp/main.cpp | 67 ++++++----- TEST_APPS/device/nfcapp/nfccommands.cpp | 133 ++++++++++++--------- TEST_APPS/device/nfcapp/nfccommands.h | 47 +++++--- TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp | 32 +++-- TEST_APPS/device/nfcapp/nfcprocessCtrl.h | 6 +- TEST_APPS/device/nfcapp/nfctestshim.cpp | 52 ++++---- TEST_APPS/device/nfcapp/nfctestshim.h | 28 ++--- TEST_APPS/testcases/nfc/nfc_clf_wrapper.py | 3 +- TEST_APPS/testcases/nfc/test_nfc.py | 29 +++-- TEST_APPS/testcases/nfc/test_self.py | 3 +- 10 files changed, 231 insertions(+), 169 deletions(-) diff --git a/TEST_APPS/device/nfcapp/main.cpp b/TEST_APPS/device/nfcapp/main.cpp index 4d5a3fef8c..d083b3bb9f 100644 --- a/TEST_APPS/device/nfcapp/main.cpp +++ b/TEST_APPS/device/nfcapp/main.cpp @@ -35,35 +35,36 @@ using mbed::nfc::NFCEEPROMDriver; #endif // MBED_CONF_NFCEEPROM +const char *errorcodes = // descriptions from nfc/stack/nfc_errors.h + " 0 NFC_OK\r\n" + " 1 NFC_ERR_UNKNOWN\r\n" + " 2 NFC_ERR_LENGTH\r\n" + " 3 NFC_ERR_NOT_FOUND\r\n" + " 4 NFC_ERR_UNSUPPORTED\r\n" + " 5 NFC_ERR_PARAMS\r\n" + " 6 NFC_ERR_BUFFER_TOO_SMALL\r\n" + " 7 NFC_ERR_TIMEOUT\r\n" + " 8 NFC_ERR_CRC\r\n" + " 9 NFC_ERR_NOPEER\r\n" + "10 NFC_ERR_PARITY\r\n" + "11 NFC_ERR_FIELD\r\n" + "12 NFC_ERR_COLLISION\r\n" + "13 NFC_ERR_WRONG_COMM\r\n" + "14 NFC_ERR_PROTOCOL\r\n" + "15 NFC_ERR_BUSY\r\n" + "16 NFC_ERR_CONTROLLER\r\n" + "17 NFC_ERR_HALTED\r\n" + "18 NFC_ERR_MAC\r\n" + "19 NFC_ERR_UNDERFLOW\r\n" + "20 NFC_ERR_DISCONNECTED\r\n" + "21 NFC_ERR_ABORTED\r\n"; + void wrap_printf(const char *f, va_list a) { vprintf(f, a); } -const char *errorcodes = // descriptions from nfc/stack/nfc_errors.h - " 0 NFC_OK \n" - " 1 NFC_ERR_UNKNOWN\n" - " 2 NFC_ERR_LENGTH \n" - " 3 NFC_ERR_NOT_FOUND\n" - " 4 NFC_ERR_UNSUPPORTED\n" - " 5 NFC_ERR_PARAMS \n" - " 6 NFC_ERR_BUFFER_TOO_SMALL\n" - " 7 NFC_ERR_TIMEOUT\n" - " 8 NFC_ERR_CRC\n" - " 9 NFC_ERR_NOPEER \n" - "10 NFC_ERR_PARITY \n" - "11 NFC_ERR_FIELD\n" - "12 NFC_ERR_COLLISION\n" - "13 NFC_ERR_WRONG_COMM \n" - "14 NFC_ERR_PROTOCOL \n" - "15 NFC_ERR_BUSY \n" - "16 NFC_ERR_CONTROLLER \n" - "17 NFC_ERR_HALTED \n" - "18 NFC_ERR_MAC\n" - "19 NFC_ERR_UNDERFLOW\n" - "20 NFC_ERR_DISCONNECTED \n" - "21 NFC_ERR_ABORTED\n"; /** Disables VT100 etc. for easy manual UI interaction */ int seteasy(int argc, char *argv[]) @@ -75,6 +76,7 @@ int seteasy(int argc, char *argv[]) } return (CMDLINE_RETCODE_SUCCESS); } + /** * This test app can be used standalone interactively with at 115200 baud terminal. It is designed to work with the * IceTea test framework https://os.mbed.com/docs/latest/tools/icetea-testing-applications.html . This app does @@ -88,7 +90,7 @@ int seteasy(int argc, char *argv[]) * If using an NFC EEPROM, an extra library is needed. Please see the documentation in the README.MD for instructions * on how to modify this test for new drivers/targets, and the steps to run this test suite. */ -int main(int argc, char *argv[]) +int main() { cmd_init(&wrap_printf); cmd_add("getlastnfcerror", HandleTestCommand::cmd_get_last_nfc_error, @@ -127,6 +129,8 @@ int main(int argc, char *argv[]) "set rf protocols", "-p [t1t]/[t2t]/[t3t]/[isodep]/[nfcdep]/[t5t]"); cmd_add("easy", seteasy, "Use human readable terminal output", "echo off,vt100 off,return-codes visible"); + cmd_add("trace", HandleTestCommand::cmd_set_trace, "detailed tracing on/off, ", + "Defaults to enabled; values like 'on','true','1' all turn it on, anything else turns off detailed tracing."); #if MBED_CONF_NFCEEPROM cmd_printf("MBED NFC EEPROM defined\r\n"); @@ -135,16 +139,17 @@ int main(int argc, char *argv[]) #endif #ifdef TARGET_M24SR - cmd_printf("Using driver:M24SR\r\n"); + cmd_printf("Using driver: M24SR\r\n"); #endif #ifdef TARGET_PN512 - cmd_printf("Using driver:PN512\r\n"); + cmd_printf("Using driver: PN512\r\n"); #endif - - int c; - HandleTestCommand handleCommands; // starts handling nfc messages - while ((c = getc(stdin)) != EOF) { - cmd_char_input(c); + { + int c; + HandleTestCommand handleCommands; // For handling test commands and set nfc message queue + while ((c = getc(stdin)) != EOF) { + cmd_char_input(c); + } } return 0; } diff --git a/TEST_APPS/device/nfcapp/nfccommands.cpp b/TEST_APPS/device/nfcapp/nfccommands.cpp index 27941d1f53..3c708d5f78 100644 --- a/TEST_APPS/device/nfcapp/nfccommands.cpp +++ b/TEST_APPS/device/nfcapp/nfccommands.cpp @@ -15,6 +15,7 @@ */ #include #include +#include #include #include "mbed_events.h" #include "mbed-client-cli/ns_cmdline.h" @@ -29,33 +30,81 @@ using mbed::nfc::nfc_rf_protocols_bitmask_t; -events::EventQueue nfcQueue; -rtos::Thread nfcThread; -NFCTestShim *pNFC_Test_Shim = NULL; +events::EventQueue HandleTestCommand::_nfcQueue; -NFCTestShim *new_testshim() +rtos::Thread nfcThread; +bool human_trace_enabled = true; + +NFCTestShim *HandleTestCommand::new_testshim() { #if MBED_CONF_NFCEEPROM - mbed::nfc::NFCEEPROMDriver &eeprom_driver = get_eeprom_driver(nfcQueue); + mbed::nfc::NFCEEPROMDriver &eeprom_driver = get_eeprom_driver(_nfcQueue); - return ((NFCTestShim *)(new NFCProcessEEPROM(nfcQueue, eeprom_driver))); + return ((NFCTestShim *)(new NFCProcessEEPROM(_nfcQueue, eeprom_driver))); #else - return ((NFCTestShim *)(new NFCProcessController(nfcQueue))); + return ((NFCTestShim *)(new NFCProcessController(_nfcQueue))); #endif // EEPROM } -void nfcRoutine() +void HandleTestCommand::nfc_routine() { - nfcQueue.dispatch_forever(); + _nfcQueue.dispatch_forever(); +} + +void trace_printf(const char *fmt, ...) +{ + if (human_trace_enabled) { + va_list ap; + va_start(ap, fmt); + cmd_vprintf(fmt, ap); + va_end(ap); + } } HandleTestCommand::HandleTestCommand() { - osStatus status = nfcThread.start(mbed::callback(&nfcRoutine)); + osStatus status = nfcThread.start(mbed::callback(&HandleTestCommand::nfc_routine)); MBED_ASSERT(status == osOK); } +int HandleTestCommand::cmd_get_last_nfc_error(int argc, char *argv[]) +{ + _nfcQueue.call(NFCTestShim::cmd_get_last_nfc_error); + return (CMDLINE_RETCODE_EXCUTING_CONTINUE); +} + +/** returns compile time flag if NFC EEPROM was compiled */ +int HandleTestCommand::cmd_get_conf_nfceeprom(int argc, char *argv[]) +{ + _nfcQueue.call(NFCTestShim::cmd_get_conf_nfceeprom); + return (CMDLINE_RETCODE_EXCUTING_CONTINUE); +} + + + +int HandleTestCommand::cmd_set_trace(int argc, char *argv[]) +{ + human_trace_enabled = false; + if (argc > 1) { + static char buffer[7]; + char *p_buffer = buffer; + strncpy(buffer, argv[1], sizeof(buffer) - 1); + buffer[sizeof(buffer) - 1] = 0; + while (*p_buffer) { + *p_buffer = toupper(*p_buffer); + p_buffer++; + } + cmd_printf(buffer); + human_trace_enabled = (0 == strcmp(buffer, "TRUE")) || (0 == strcmp(buffer, "1")) || (0 == strcmp(buffer, "ON")); + } + cmd_printf("set trace '%s'", (human_trace_enabled ? "true" : "false")); + return (CMDLINE_RETCODE_SUCCESS); +} + + +//////////////////////////////////////////////////////////////////////////////////// + int HandleTestCommand::cmd_set_last_nfc_error(int argc, char *argv[]) { if (argc <= 1) { @@ -63,7 +112,7 @@ int HandleTestCommand::cmd_set_last_nfc_error(int argc, char *argv[]) return (CMDLINE_RETCODE_INVALID_PARAMETERS); } else { int value = strtol(argv[1], NULL, 10); - nfcQueue.call(NFCTestShim::cmd_set_last_nfc_error, value); + _nfcQueue.call(NFCTestShim::cmd_set_last_nfc_error, value); } return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } @@ -72,7 +121,7 @@ int HandleTestCommand::cmd_set_last_nfc_error(int argc, char *argv[]) int HandleTestCommand::cmd_get_max_ndef(int argc, char *argv[]) { if (pNFC_Test_Shim) { - nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_get_max_ndef); + _nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_get_max_ndef); return CMDLINE_RETCODE_EXCUTING_CONTINUE; } return CMDLINE_RETCODE_FAIL; @@ -81,19 +130,18 @@ int HandleTestCommand::cmd_get_max_ndef(int argc, char *argv[]) int HandleTestCommand::cmd_init_nfc(int argc, char *argv[]) { - if (pNFC_Test_Shim) { cmd_printf("WARN init called again!\r\n"); // only legal here, if eeprom driver stops talking } else { pNFC_Test_Shim = new_testshim(); } - nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_init); + _nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_init); return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } int HandleTestCommand::cmd_read_message(int argc, char *argv[]) { - nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_read_nfc_contents); + _nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_read_nfc_contents); return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } @@ -109,8 +157,8 @@ int HandleTestCommand::cmd_set_smartposter(int argc, char *argv[]) char *uri = (char *) malloc(strlen(argv[1]) + 1); if (uri) { strcpy(uri, argv[1]); - nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_set_smartposter, - uri); // called thread must free + _nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_set_smartposter, + uri); // called thread must free } else { cmd_printf("WARN out of memory!\r\n"); return (CMDLINE_RETCODE_FAIL); @@ -119,10 +167,9 @@ int HandleTestCommand::cmd_set_smartposter(int argc, char *argv[]) return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } -// todo: jira IOTPAN-295 int HandleTestCommand::cmd_erase(int argc, char *argv[]) { - nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_erase); + _nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_erase); return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } @@ -169,31 +216,31 @@ int HandleTestCommand::cmd_write_long_ndef_message(int argc, char *argv[]) data[length] = '\0'; // method must release buffer - nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_write_long, data); + _nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_write_long, data); return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } int HandleTestCommand::cmd_start_discovery(int argc, char *argv[]) { if ((argc > 1) && (0 == strcmp(argv[1], "man"))) { - cmd_printf("User must restart discovery manually()\r\n"); - nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_start_discovery, false); + trace_printf("User must restart discovery manually()\r\n"); + _nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_start_discovery, false); } else { - cmd_printf("App will restart discovery loop on auto()\r\n"); - nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_start_discovery, true); + trace_printf("App will restart discovery loop on auto()\r\n"); + _nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_start_discovery, true); } return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } int HandleTestCommand::cmd_stop_discovery(int argc, char *argv[]) { - nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_stop_discovery); + _nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_stop_discovery); return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } int HandleTestCommand::cmd_get_supported_rf_protocols(int argc, char *argv[]) { - nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_get_rf_protocols); + _nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_get_rf_protocols); return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } @@ -234,38 +281,8 @@ int HandleTestCommand::cmd_configure_rf_protocols(int argc, char *argv[]) } argindex--; } - nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_configure_rf_protocols, - protocols); + _nfcQueue.call(pNFC_Test_Shim, &NFCTestShim::cmd_configure_rf_protocols, + protocols); return (CMDLINE_RETCODE_EXCUTING_CONTINUE); } -// todo: implement -int cmd_start_stop_discovery_wait_tag(int argc, char *argv[]) -{ - - return (CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED); -} - -///////////////////////////////////////////////////////////////////// -// boilerplate only - -int cmd_is_iso7816_supported(int argc, char *argv[]) -{ - return (CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED); -} - -int cmd_add_iso7816_application(int argc, char *argv[]) -{ - return (CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED); -} - -int cmd_set_tagtype(int argc, char *argv[]) -{ - return (CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED); -} - -int cmd_get_tagtype(int argc, char *argv[]) -{ - return (CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED); -} - diff --git a/TEST_APPS/device/nfcapp/nfccommands.h b/TEST_APPS/device/nfcapp/nfccommands.h index 0bc1da8270..751ad0d156 100644 --- a/TEST_APPS/device/nfcapp/nfccommands.h +++ b/TEST_APPS/device/nfcapp/nfccommands.h @@ -23,41 +23,51 @@ #include "nfcProcessCtrl.h" #endif -extern events::EventQueue nfcQueue; - // see https://support.microsoft.com/en-my/help/208427/maximum-url-length-is-2-083-characters-in-internet-explorer #define MAX_URL_LENGTH 2000 +#ifndef MBED_CONF_NFCEEPROM +#define MBED_CONF_NFCEEPROM false +#endif + + +/** + * \brief adds human-readable traces not needed by the framework, filter is via a switch set_trace_enable() + * \param fmt standard printf formatter + * \param varargs + */ +void trace_printf(const char *fmt, ...); + +/** + * \brief turns on human readable traces + * \param enabled : set to false to disable the extra traces + */ +void set_trace_enable(bool enabled = true); + /** * HandleTestCommand turns all the typed-in/serial commands into function calls sent via a eventqueue to the driver - * shim/wrapper class. Most application level code that is agnostic of EEPROM/controller drivers lives in this class. + * shim/wrapper class. * Methods with cmd_ prefix map to the serial commands, and are building blocks for test cases and scenarios. The * first function a test must call is typically the initnfc command. Commands report back a test verdict, and a * NFC status code. The test verdict is always success unless the command is not allowed. Tests much check the * NFC error code for NFC_OK or zero; this pattern allows us to write negative tests which expect a specific NFC error. + * + * Handlers are statics because the test framework is not supporting C++ */ class HandleTestCommand { public: HandleTestCommand(); - /** set corresponding mask bit on in referenced structure, return false if the supplied string cannot parse */ - static bool set_protocol_target(mbed::nfc::nfc_rf_protocols_bitmask_t &bitmask, const char *protocolName); /** return and clear the last result code. Type "help getlastnfcerror" for a list of error codes */ - static int cmd_get_last_nfc_error(int argc, char *argv[]) - { - nfcQueue.call(NFCTestShim::cmd_get_last_nfc_error); - return (CMDLINE_RETCODE_EXCUTING_CONTINUE); - } + static int cmd_get_last_nfc_error(int argc, char *argv[]); /** internal function to test getlastnfcerror */ static int cmd_set_last_nfc_error(int argc, char *argv[]); /** returns compile time flag if NFC EEPROM was compiled */ - static int cmd_get_conf_nfceeprom(int argc, char *argv[]) - { - nfcQueue.call(NFCTestShim::cmd_get_conf_nfceeprom); - return (CMDLINE_RETCODE_EXCUTING_CONTINUE); - } + static int cmd_get_conf_nfceeprom(int argc, char *argv[]); + + static int cmd_set_trace(int argc, char *argv[]); /** For EEPROM, returns the driver max_ndef value, else returns the app config MBED_CONF_APP_TEST_NDEF_MSG_MAX */ static int cmd_get_max_ndef(int argc, char *argv[]); @@ -83,6 +93,13 @@ public: /** write a text 'T' NDEF message to the target */ static int cmd_write_long_ndef_message(int argc, char *argv[]); +private: + /** set corresponding mask bit on in referenced structure, return false if the supplied string cannot parse */ + static bool set_protocol_target(mbed::nfc::nfc_rf_protocols_bitmask_t &bitmask, const char *protocolName); + static NFCTestShim *new_testshim(); + static void nfc_routine(); + + static events::EventQueue _nfcQueue; }; diff --git a/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp b/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp index 949419017f..ec6ff54e9f 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp +++ b/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp @@ -29,6 +29,7 @@ #include "nfc/ndef/common/util.h" +#include "nfcCommands.h" #include "nfcProcessCtrl.h" #include "SmartPoster.h" @@ -65,13 +66,20 @@ NFCProcessController::NFCProcessController(events::EventQueue &queue) : */ nfc_err_t NFCProcessController::init() { - cmd_printf("init()\r\n"); + trace_printf("init()\r\n"); // register callbacks _nfc_controller.set_delegate(this); return _nfc_controller.initialize(); } + +void NFCProcessController::cmd_get_max_ndef() +{ + cmd_printf("{{maxndef=%d}}\r\n", (int)MBED_CONF_APP_TEST_NDEF_MSG_MAX); + cmd_ready(CMDLINE_RETCODE_SUCCESS); +} + /** * Start the discovery of peers. * @@ -80,7 +88,7 @@ nfc_err_t NFCProcessController::init() */ nfc_err_t NFCProcessController::start_discovery() { - cmd_printf("start_discovery()\r\n"); + trace_printf("start_discovery()\r\n"); return _nfc_controller.start_discovery(); } @@ -93,20 +101,20 @@ nfc_err_t NFCProcessController::start_discovery() */ nfc_err_t NFCProcessController::stop_discovery() { - cmd_printf("stop_discovery()\r\n"); + trace_printf("stop_discovery()\r\n"); return _nfc_controller.cancel_discovery(); } nfc_rf_protocols_bitmask_t NFCProcessController::get_rf_protocols() { - cmd_printf("get_supported_rf_protocols()\r\n"); + trace_printf("get_supported_rf_protocols()\r\n"); return _nfc_controller.get_supported_rf_protocols(); } nfc_err_t NFCProcessController::set_rf_protocols( nfc_rf_protocols_bitmask_t protocols) { - cmd_printf("configure_rf_protocols()\r\n"); + trace_printf("configure_rf_protocols()\r\n"); return _nfc_controller.configure_rf_protocols(protocols); } @@ -138,18 +146,18 @@ void NFCProcessController::parse_ndef_message( size_t len = buffer.size(); // copy remotely written message into our dummy buffer if (len <= sizeof(_ndef_write_buffer)) { - cmd_printf("Store remote ndef message of size %d\r\n", len); + trace_printf("Store remote ndef message of size %d\r\n", len); memcpy(_ndef_write_buffer, buffer.data(), len); _ndef_write_buffer_used = len; } else { - cmd_printf("Remote ndef message of size %d too large!\r\n", len); + trace_printf("Remote ndef message of size %d too large!\r\n", len); } } size_t NFCProcessController::build_ndef_message(const Span &buffer) { - cmd_printf("Copying message %d bytes to query buffer\r\n", - _ndef_write_buffer_used); + trace_printf("Copying message %d bytes to query buffer\r\n", + _ndef_write_buffer_used); memcpy(buffer.data(), _ndef_write_buffer, _ndef_write_buffer_used); for (size_t k = 0; k < _ndef_write_buffer_used; k++) { cmd_printf("%02x ", buffer[k]); @@ -174,8 +182,8 @@ const char *NFCProcessController::str_discovery_terminated_reason( void NFCProcessController::on_discovery_terminated( nfc_discovery_terminated_reason_t reason) { - cmd_printf("on_discovery_terminated(%s)\r\n", - str_discovery_terminated_reason(reason)); + trace_printf("on_discovery_terminated(%s)\r\n", + str_discovery_terminated_reason(reason)); if (reason != nfc_discovery_terminated_completed && this->_discovery_restart) { start_discovery(); @@ -185,7 +193,7 @@ void NFCProcessController::on_discovery_terminated( void NFCProcessController::on_nfc_initiator_discovered( const SharedPtr &nfc_initiator) { - cmd_printf("on_nfc_initiator_discovered()\r\n"); + trace_printf("on_nfc_initiator_discovered()\r\n"); // setup the local remote initiator _nfc_remote_initiator = nfc_initiator; diff --git a/TEST_APPS/device/nfcapp/nfcprocessCtrl.h b/TEST_APPS/device/nfcapp/nfcprocessCtrl.h index 0a1ad629de..77cd3ca483 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessCtrl.h +++ b/TEST_APPS/device/nfcapp/nfcprocessCtrl.h @@ -48,11 +48,7 @@ class NFCProcessController: NFCTestShim, public: NFCProcessController(events::EventQueue &queue); - void cmd_get_max_ndef() - { - cmd_printf("{{maxndef=%d}}\r\n", (int)MBED_CONF_APP_TEST_NDEF_MSG_MAX); - cmd_ready(CMDLINE_RETCODE_SUCCESS); - } + void cmd_get_max_ndef(); nfc_err_t init(); nfc_err_t start_discovery(); nfc_err_t stop_discovery(); diff --git a/TEST_APPS/device/nfcapp/nfctestshim.cpp b/TEST_APPS/device/nfcapp/nfctestshim.cpp index 15dd48f951..0652799862 100644 --- a/TEST_APPS/device/nfcapp/nfctestshim.cpp +++ b/TEST_APPS/device/nfcapp/nfctestshim.cpp @@ -81,20 +81,33 @@ char const *uri_prefix_string[] = { "", }; int last_nfc_error = 0; -#if MBED_CONF_NFCEEPROM -int using_eeprom = true; -#else -int using_eeprom = false; -#endif } +NFCTestShim *pNFC_Test_Shim = NULL; + NFCTestShim::NFCTestShim(events::EventQueue &queue) : - _ndef_write_buffer_used(0), ndef_poster_message(_ndef_write_buffer), + _ndef_write_buffer_used(0), _ndef_poster_message(_ndef_write_buffer), _discovery_restart(true), // on disconnect, will restart discovery _queue(queue) { } +void NFCTestShim::cmd_get_last_nfc_error() +{ + get_last_nfc_error(); +} + +void NFCTestShim::cmd_set_last_nfc_error(int err) +{ + set_last_nfc_error(err); + cmd_ready(CMDLINE_RETCODE_SUCCESS); +} + +void NFCTestShim::cmd_get_conf_nfceeprom() +{ + get_conf_nfceeprom(); +} + /** \brief The last failed NFC API call status, gets cleared upon reading it. * \return void The NFC error is set asyncronously by sending text back over serial */ @@ -118,7 +131,7 @@ void NFCTestShim::get_conf_nfceeprom() { set_last_nfc_error(NFC_OK); // return data as text to the plugin framework - cmd_printf("{{iseeprom=%s}}\r\n", (::using_eeprom ? "true" : "false")); + cmd_printf("{{iseeprom=%s}}\r\n", (MBED_CONF_NFCEEPROM ? "true" : "false")); cmd_ready(CMDLINE_RETCODE_SUCCESS); } @@ -237,31 +250,28 @@ void NFCTestShim::cmd_read_nfc_contents() { #if MBED_CONF_NFCEEPROM ((NFCProcessEEPROM *)this)->queue_read_call(); - cmd_printf("NFCTestShim::cmd_read_nfc_contents() exit\r\n"); + trace_printf("NFCTestShim::cmd_read_nfc_contents() exit\r\n"); #else // returns last message "written", since we cannot read print_ndef_message(_ndef_write_buffer, _ndef_write_buffer_used); - cmd_printf("Controller buffer data size=%d\r\n", _ndef_write_buffer_used); + trace_printf("Controller buffer data size=%d\r\n", _ndef_write_buffer_used); set_last_nfc_error(NFC_OK); - cmd_printf("NFCTestShim::cmd_read_nfc_contents()\r\n"); + trace_printf("NFCTestShim::cmd_read_nfc_contents() exit\r\n"); cmd_ready(CMDLINE_RETCODE_SUCCESS); #endif } void NFCTestShim::cmd_erase() { - #if MBED_CONF_NFCEEPROM ((NFCProcessEEPROM *)this)->queue_erase_call(); - #else - cmd_printf("erase %d bytes, last msg\r\n", - sizeof(_ndef_write_buffer)); + trace_printf("Erase (reset) controller msg buffer\r\n"); _ndef_write_buffer_used = 0; memset(_ndef_write_buffer, 0, sizeof(_ndef_write_buffer)); - set_last_nfc_error(NFC_OK); // effectively a no-op + set_last_nfc_error(NFC_OK); cmd_ready(CMDLINE_RETCODE_SUCCESS); #endif } @@ -273,14 +283,14 @@ void NFCTestShim::cmd_erase() */ void NFCTestShim::cmd_write_long(char *text_string) { - MessageBuilder builder(ndef_poster_message); + MessageBuilder builder(_ndef_poster_message); strcpy(::long_string, text_string); //max_ndef - header - overheads Text text(Text::UTF8, span_from_cstr("en-US"), span_from_cstr((const char *)(::long_string))); text.append_as_record(builder, true); _ndef_write_buffer_used = builder.get_message().size(); - cmd_printf("Composed NDEF message %d bytes\r\n", _ndef_write_buffer_used); + trace_printf("Composed NDEF message %d bytes\r\n", _ndef_write_buffer_used); #if MBED_CONF_NFCEEPROM ((NFCProcessEEPROM *)this)->queue_write_call(); @@ -289,7 +299,7 @@ void NFCTestShim::cmd_write_long(char *text_string) set_last_nfc_error(NFC_OK); cmd_ready(CMDLINE_RETCODE_SUCCESS); #endif - cmd_printf("NFCTestShim::write_long() exit\r\n"); + trace_printf("NFCTestShim::write_long() exit\r\n"); free(text_string); } @@ -299,7 +309,7 @@ void NFCTestShim::cmd_write_long(char *text_string) */ void NFCTestShim::cmd_set_smartposter(char *cmdUri) { - MessageBuilder builder(ndef_poster_message); + MessageBuilder builder(_ndef_poster_message); uint8_t smart_poster_buffer[1024]; MessageBuilder smart_poster_builder(smart_poster_buffer); @@ -316,7 +326,7 @@ void NFCTestShim::cmd_set_smartposter(char *cmdUri) smart_poster_builder.get_message(), true); _ndef_write_buffer_used = builder.get_message().size(); - cmd_printf("Composed NDEF message %d bytes\r\n", _ndef_write_buffer_used); + trace_printf("Composed NDEF message %d bytes\r\n", _ndef_write_buffer_used); #if MBED_CONF_NFCEEPROM ((NFCProcessEEPROM *)this)->queue_write_call(); @@ -325,7 +335,7 @@ void NFCTestShim::cmd_set_smartposter(char *cmdUri) set_last_nfc_error(NFC_OK); cmd_ready(CMDLINE_RETCODE_SUCCESS); #endif - cmd_printf("NFCTestShim::setsmartposter() exit\r\n"); + trace_printf("NFCTestShim::setsmartposter() exit\r\n"); free(cmdUri); } diff --git a/TEST_APPS/device/nfcapp/nfctestshim.h b/TEST_APPS/device/nfcapp/nfctestshim.h index d7bd659106..29967718f6 100644 --- a/TEST_APPS/device/nfcapp/nfctestshim.h +++ b/TEST_APPS/device/nfcapp/nfctestshim.h @@ -29,26 +29,22 @@ #include "nfc/NFCDefinitions.h" /** - * Test app driver wrapper. This is a base class containing shared EEPROM and Controller test data + logic + * Test app driver wrapper. This is a base class containing shared EEPROM and Controller test data + logic. + * Variations for the 2 different kinds of driver supported are delegated to derived classes. */ class NFCTestShim { public: NFCTestShim(events::EventQueue &queue); - static void cmd_get_last_nfc_error() - { - get_last_nfc_error(); - }; - static void cmd_set_last_nfc_error(int err) - { - set_last_nfc_error(err); - cmd_ready(CMDLINE_RETCODE_SUCCESS); - }; - static void cmd_get_conf_nfceeprom() - { - get_conf_nfceeprom(); - }; + static void cmd_get_last_nfc_error(); + static void cmd_set_last_nfc_error(int err); + static void cmd_get_conf_nfceeprom(); + /** + * For an EEPROM, this queries and responds with the flash size, + * For a Controller, responds with the config macro TEST_NDEF_MSG_MAX + */ virtual void cmd_get_max_ndef() = 0; + static void get_last_nfc_error(); static void set_last_nfc_error(int err); static void get_conf_nfceeprom(); @@ -58,7 +54,6 @@ public: static char const *get_ndef_record_type_prefix(mbed::nfc::ndef::common::URI::uri_identifier_code_t id); void cmd_init(); - virtual nfc_err_t init() = 0; void cmd_set_smartposter(char *cmdUri); void cmd_erase(); @@ -71,6 +66,7 @@ public: protected: // implement/declare EEPROM and Controller model underlying common BH and delegate specializations + virtual nfc_err_t init() = 0; virtual nfc_err_t set_rf_protocols(mbed::nfc::nfc_rf_protocols_bitmask_t protocols) { return NFC_ERR_UNSUPPORTED ; @@ -99,7 +95,7 @@ protected: protected: size_t _ndef_write_buffer_used; - mbed::Span ndef_poster_message; // message to build and send + mbed::Span _ndef_poster_message; // message to build and send uint8_t _ndef_write_buffer[MBED_CONF_APP_TEST_NDEF_MSG_MAX]; // if this buffer is smaller than the EEPROM, the driver may crash see IOTPAN-297 uint8_t _ndef_buffer[MBED_CONF_APP_TEST_NDEF_MSG_MAX]; // driver I/O buffer bool _discovery_restart; // default true, restart discovery loop again on remote disconnect diff --git a/TEST_APPS/testcases/nfc/nfc_clf_wrapper.py b/TEST_APPS/testcases/nfc/nfc_clf_wrapper.py index c90493095b..e8df914a26 100644 --- a/TEST_APPS/testcases/nfc/nfc_clf_wrapper.py +++ b/TEST_APPS/testcases/nfc/nfc_clf_wrapper.py @@ -39,8 +39,7 @@ def debug_nfc_data(key, value): logger.info(text) -logging.basicConfig(level=logging.DEBUG) # read commandline value? -logger = logging.getLogger() +logger = logging.getLogger() class NfcWrapper: diff --git a/TEST_APPS/testcases/nfc/test_nfc.py b/TEST_APPS/testcases/nfc/test_nfc.py index f2d7f6c1f4..58f167d620 100644 --- a/TEST_APPS/testcases/nfc/test_nfc.py +++ b/TEST_APPS/testcases/nfc/test_nfc.py @@ -155,6 +155,7 @@ def test_nfce2e_reprogrammed(self): self.nfc_command("dev1", "erase") # program a poster tag to target + print("Write Smartposter MESSAGE wirelessly") self.clf.parse("connect") tag = self.clf.clf_response() asserts.assertNotNone(tag, "Could not connect to any tag") @@ -162,7 +163,7 @@ def test_nfce2e_reprogrammed(self): nfc_messages.program_remote_tag(smartposter, tag) self.logger.info("Remote programmed %d bytes Smartposter" % len(str(smartposter))) - # read device + print("Write back Smartposter MESSAGE wirelessly") self.clf.parse("connect") tag = self.clf.clf_response() asserts.assertNotNone(tag, "Could not re-connect to any tag") @@ -189,25 +190,30 @@ def test_nfce2e_read_stress(self): response = self.nfc_command("dev1", "iseeprom") eeprom = response.parsed['iseeprom'] self.logger.info("Target includes NFCEEPROM: %s" % eeprom) + max_ndef = STRESS_BUFFLEN self.nfc_command("dev1", "initnfc") if not eeprom: self.nfc_command("dev1", "start") - textLength = STRESS_BUFFLEN else: max_ndef = self.nfc_command("dev1", "getmaxndef").parsed['maxndef'] - textLength = max_ndef / 2 # large values slow down test runs and may time out + if (max_ndef > 800 ): + textLength = 800 # large values slow down test runs and may time out + else: + textLength = max_ndef self.nfc_command("dev1", "erase") # calculate actual message to compare to using the library expected_text = nfc_messages.repeat_string_to_length(messageRep, textLength) # write a large message to the tag via API, then read it wirelessly + print("Write/set tag MESSAGE (%d) bytes" % textLength) self.nfc_command("dev1", "writelong %d %s" % (textLength,messageRep)) self.clf.parse("connect") tag = self.clf.clf_response() asserts.assertNotNone(tag, "Could not connect to any tag") # assert that read the eeprom contents gives correct data and length + print("Read tag MESSAGE wirelessly" ) asserts.assertEqual(tag.ndef.records[0].__class__.__name__, "TextRecord", "expected TextRecord") self.assert_text_equal(tag.ndef.records[0].text, expected_text) @@ -223,20 +229,26 @@ def test_nfce2e_reprogrammed_stress(self): response = self.nfc_command("dev1", "iseeprom") eeprom = response.parsed['iseeprom'] self.logger.info("Target includes NFCEEPROM: %s" % eeprom) + max_ndef = STRESS_BUFFLEN self.nfc_command("dev1", "initnfc") if not eeprom: self.nfc_command("dev1", "start") - textLength = STRESS_BUFFLEN else: max_ndef = self.nfc_command("dev1", "getmaxndef").parsed['maxndef'] - textLength = max_ndef / 2 # large values slow down test runs and may time out + + if (max_ndef > 800 ): + textLength = 800 # large values slow down test runs and may time out + else: + textLength = max_ndef + self.nfc_command("dev1", "erase") + # calculate actual message to compare to using the library message = nfc_messages.make_textrecord( nfc_messages.repeat_string_to_length(messageRep, textLength)) expected_message = str(message) - self.nfc_command("dev1", "erase") # program a large tag to target remotely + print("Write tag MESSAGE wirelessly (%d) bytes" % len(str(message))) self.clf.parse("connect") tag = self.clf.clf_response() asserts.assertNotNone(tag, "Could not connect to any tag") @@ -244,6 +256,7 @@ def test_nfce2e_reprogrammed_stress(self): self.logger.info("%d bytes chunk of data written to tag remotely" % len(str(message))) # read device locally + print("Read back tag MESSAGE wirelessly") self.clf.parse("connect") tag = self.clf.clf_response() asserts.assertNotNone(tag, "Could not re-connect to any tag") @@ -268,8 +281,8 @@ def test_nfce2e_discovery_loop(self): self.nfc_command("dev1", "initnfc") # this NOT automatically start discovery at the same time, the test command # "start" must be used on a controller. (Eeeproms always have the loop enabled.) - # By default, the test app automatically starts discovery loop again after a reader disconnects from the controller. - # Automatic resume after disconnect can be turned off by using command "start man" , the default is "start auto" . + # By default, the test app automatically starts discovery loop again after a reader disconnects from the controller. + # Automatic resume after disconnect can be turned off by using command "start man" , the default is "start auto" . if not eeprom: self.clf.parse("connect") diff --git a/TEST_APPS/testcases/nfc/test_self.py b/TEST_APPS/testcases/nfc/test_self.py index c4714d8cc3..4cc178a415 100644 --- a/TEST_APPS/testcases/nfc/test_self.py +++ b/TEST_APPS/testcases/nfc/test_self.py @@ -132,7 +132,7 @@ can be read back. @test_case(CreamSconeSelfTests) def test_nfc_write_long(self): messageRep = 'thequickbrownfoxjumpedoverthelazydog' # repeating message written - textLength = STRESS_BUFFLEN # 2K < x < 4K + textLength = STRESS_BUFFLEN / 2 # 2K < x < 4K # calculate actual message to compare to using the library message = nfc_messages.make_textrecord( nfc_messages.repeat_string_to_length(messageRep, textLength)) expected_message = str(message) @@ -176,6 +176,7 @@ def test_nfc_set_controller_protocols(self): response = self.nfc_command("dev1", "iseeprom") eeprom = response.parsed['iseeprom'] if eeprom: + # eeproms do not allow target control self.logger.info("Test ignore - target includes NFCEEPROM: %s" % eeprom) else: self.nfc_command("dev1", "setprotocols t1t") From c509af32ce84b718105b50c3656b0acb0842aca5 Mon Sep 17 00:00:00 2001 From: Conrad Braam Date: Tue, 29 Jan 2019 14:20:28 +0000 Subject: [PATCH 07/14] python removed extra plumbing --- TEST_APPS/device/nfcapp/main.cpp | 8 ++-- TEST_APPS/testcases/nfc/nfc_clf_wrapper.py | 45 +++------------------- TEST_APPS/testcases/nfc/test_nfc.py | 38 +++++++++--------- 3 files changed, 30 insertions(+), 61 deletions(-) diff --git a/TEST_APPS/device/nfcapp/main.cpp b/TEST_APPS/device/nfcapp/main.cpp index d083b3bb9f..ce2f3fdb49 100644 --- a/TEST_APPS/device/nfcapp/main.cpp +++ b/TEST_APPS/device/nfcapp/main.cpp @@ -67,7 +67,7 @@ void wrap_printf(const char *f, va_list a) /** Disables VT100 etc. for easy manual UI interaction */ -int seteasy(int argc, char *argv[]) +int set_easy_printer(int argc, char *argv[]) { const char msg[][20] = { "echo off", "set --retcode true", "set --vt100 off" }; @@ -77,6 +77,7 @@ int seteasy(int argc, char *argv[]) return (CMDLINE_RETCODE_SUCCESS); } + /** * This test app can be used standalone interactively with at 115200 baud terminal. It is designed to work with the * IceTea test framework https://os.mbed.com/docs/latest/tools/icetea-testing-applications.html . This app does @@ -93,6 +94,8 @@ int seteasy(int argc, char *argv[]) int main() { cmd_init(&wrap_printf); + HandleTestCommand handleCommands; // For handling test commands and set nfc message queue + cmd_add("getlastnfcerror", HandleTestCommand::cmd_get_last_nfc_error, "last NFC error code", errorcodes); cmd_add("setlastnfcerror", HandleTestCommand::cmd_set_last_nfc_error, @@ -127,7 +130,7 @@ int main() "get supported protocols", "returns CSV list, see setprotocols"); cmd_add("setprotocols", HandleTestCommand::cmd_configure_rf_protocols, "set rf protocols", "-p [t1t]/[t2t]/[t3t]/[isodep]/[nfcdep]/[t5t]"); - cmd_add("easy", seteasy, "Use human readable terminal output", + cmd_add("easy", set_easy_printer, "Use human readable terminal output", "echo off,vt100 off,return-codes visible"); cmd_add("trace", HandleTestCommand::cmd_set_trace, "detailed tracing on/off, ", "Defaults to enabled; values like 'on','true','1' all turn it on, anything else turns off detailed tracing."); @@ -146,7 +149,6 @@ int main() #endif { int c; - HandleTestCommand handleCommands; // For handling test commands and set nfc message queue while ((c = getc(stdin)) != EOF) { cmd_char_input(c); } diff --git a/TEST_APPS/testcases/nfc/nfc_clf_wrapper.py b/TEST_APPS/testcases/nfc/nfc_clf_wrapper.py index e8df914a26..380edda944 100644 --- a/TEST_APPS/testcases/nfc/nfc_clf_wrapper.py +++ b/TEST_APPS/testcases/nfc/nfc_clf_wrapper.py @@ -20,15 +20,13 @@ import nfc from nfc.clf import RemoteTarget import logging -logprefixnfc = "NFCPY: " - """ Wrap calls to nfcpi testing module, handle loading the driver """ -def command_is(string, command): - return string.split(' ')[0] == command +# def command_is(string, command): +# return string.split(' ')[0] == command def debug_nfc_data(key, value): @@ -60,52 +58,25 @@ class NfcWrapper: def clf_response(self): return self.clfResponse - def parse(self,line): - logging.debug(line) - parseok = False - # find command and call the needed nfcWrapper method - if command_is(line, "ping"): - self.pong() - parseok = True - if command_is(line, "connect"): - detectedTag = self.connect() - debug_nfc_data("Connectedtag", detectedTag) - parseok = True - if command_is(line, "mute"): - detectedTag = self.mute() - parseok = True - if command_is(line, "disconnect"): - self.disconnect() - parseok = True - return parseok - - """return the detected tag, else timeout after interval""" - def sense(self, target_options = ("106A","106B","212F")): - logging.info(logprefixnfc + "detecting tags with options " + target_options) - # todo filter using the target_options - targets = self.clf.sense(RemoteTarget('106A'), RemoteTarget('106B'), RemoteTarget('212F')) - self.clfResponse = targets - return targets - def connect(self, target_options = ("106A","106B","212F")): - # todo: decide on tag types to allow/filter + # note: only supporting type4 after5s = lambda: time.time() - started > 5 started = time.time() tag = self.clf.connect( rdwr={'on-connect': lambda tag: False}, llcp={}, terminate = after5s) self.clfResponse = tag if tag: # None if timeout expires - logging.info(logprefixnfc + str(tag)) + logging.info("NFCReader: connected " + str(tag)) return tag def mute(self): """turn off the reader radio""" if self.clf.device: - logging.info(logprefixnfc + "radio mute" + self.clf.device.product_name) + logging.info("NFCReader: radio mute" + self.clf.device.product_name) self.clf.device.mute() def disconnect(self): - logging.info(logprefixnfc + "close frontend.") + logging.info("NFCReader: close frontend.") self.clf.close() """ @@ -126,9 +97,5 @@ class ContactlessCommandRunner(): __nfc_wrapper = None - # plumbing, calls a static instance for the reader object. - def parse(self, line): - return self.nfc.parse(line) - def clf_response(self): return self.nfc.clf_response() diff --git a/TEST_APPS/testcases/nfc/test_nfc.py b/TEST_APPS/testcases/nfc/test_nfc.py index 58f167d620..1eb785d810 100644 --- a/TEST_APPS/testcases/nfc/test_nfc.py +++ b/TEST_APPS/testcases/nfc/test_nfc.py @@ -56,11 +56,11 @@ class CreamSconeTests(Bench, CliHelper): Bench.__init__(self, **testcase_args) def setup(self): - try: + #try: self.clf = ContactlessCommandRunner() - self.clf.parse("mute") - except: - raise asserts.TestStepFail("Could not find NFC reader") + self.clf.nfc.mute() + #except: + # raise asserts.TestStepFail("Could not find NFC reader") def teardown(self): self.logger.info("Test teardown: Reboot target...") @@ -83,7 +83,7 @@ def test_nfce2e_target_found(self): if not eeprom: self.nfc_command("dev1", "start") - self.clf.parse("connect") + self.clf.nfc.connect() tag = self.clf.clf_response() asserts.assertNotNone(tag, "Could not connect to any tag") @@ -103,7 +103,7 @@ def test_nfce2e_type4_found(self): if not eeprom: self.nfc_command("dev1", "start") - self.clf.parse("connect") + self.clf.nfc.connect() tag = self.clf.clf_response() asserts.assertNotNone(tag, "Could not connect to any tag") @@ -129,7 +129,7 @@ def test_nfce2e_smartposter(self): # write poster tag to target self.command("dev1", "setsmartposter %s" % expectedURI) - self.clf.parse("connect") + self.clf.nfc.connect() tag = self.clf.clf_response() asserts.assertNotNone(tag, "Could not connect to any tag") asserts.assertEqual(1, len(tag.ndef.records), "expected number NDEF records") @@ -156,7 +156,7 @@ def test_nfce2e_reprogrammed(self): # program a poster tag to target print("Write Smartposter MESSAGE wirelessly") - self.clf.parse("connect") + self.clf.nfc.connect() tag = self.clf.clf_response() asserts.assertNotNone(tag, "Could not connect to any tag") smartposter = nfc_messages.make_smartposter(expectedURI, ["en-US:Other search engines exist"]) @@ -164,13 +164,13 @@ def test_nfce2e_reprogrammed(self): self.logger.info("Remote programmed %d bytes Smartposter" % len(str(smartposter))) print("Write back Smartposter MESSAGE wirelessly") - self.clf.parse("connect") + self.clf.nfc.connect() tag = self.clf.clf_response() asserts.assertNotNone(tag, "Could not re-connect to any tag") asserts.assertEqual(tag.ndef.records[0].__class__.__name__, "SmartposterRecord", "expected SmartposterRecord") asserts.assertEqual(expectedURI, tag.ndef.records[0].uri_records[0].uri, "expected exact URI") - self.clf.parse("mute") # disable radio, to allow a local session + self.clf.nfc.mute() # disable radio, to allow a local session # verify in target response = self.nfc_command("dev1", "readmessage") @@ -208,7 +208,7 @@ def test_nfce2e_read_stress(self): # write a large message to the tag via API, then read it wirelessly print("Write/set tag MESSAGE (%d) bytes" % textLength) self.nfc_command("dev1", "writelong %d %s" % (textLength,messageRep)) - self.clf.parse("connect") + self.clf.nfc.connect() tag = self.clf.clf_response() asserts.assertNotNone(tag, "Could not connect to any tag") @@ -249,7 +249,7 @@ def test_nfce2e_reprogrammed_stress(self): # program a large tag to target remotely print("Write tag MESSAGE wirelessly (%d) bytes" % len(str(message))) - self.clf.parse("connect") + self.clf.nfc.connect() tag = self.clf.clf_response() asserts.assertNotNone(tag, "Could not connect to any tag") nfc_messages.program_remote_tag(message, tag) @@ -257,10 +257,10 @@ def test_nfce2e_reprogrammed_stress(self): # read device locally print("Read back tag MESSAGE wirelessly") - self.clf.parse("connect") + self.clf.nfc.connect() tag = self.clf.clf_response() asserts.assertNotNone(tag, "Could not re-connect to any tag") - self.clf.parse("mute") # disable the reader radio, to allow local access + self.clf.nfc.mute() # disable the reader radio, to allow local access # verify in target response = self.nfc_command("dev1", "readmessage") @@ -285,26 +285,26 @@ def test_nfce2e_discovery_loop(self): # Automatic resume after disconnect can be turned off by using command "start man" , the default is "start auto" . if not eeprom: - self.clf.parse("connect") + self.clf.nfc.connect() tag = self.clf.clf_response() asserts.assertNone(tag, "post-init: Tag discovery loop should be stopped!") self.nfc_command("dev1", "stop") time.sleep(1) - self.clf.parse("connect") + self.clf.nfc.connect() tag = self.clf.clf_response() asserts.assertNone(tag, "post-stop: Tag discovery loop should be stopped!") self.nfc_command("dev1", "start") time.sleep(1) - self.clf.parse("connect") + self.clf.nfc.connect() tag = self.clf.clf_response() asserts.assertNotNone(tag, "Could not connect to any tag") - self.clf.parse("mute") + self.clf.nfc.mute() self.nfc_command("dev1", "stop") time.sleep(10) - self.clf.parse("connect") + self.clf.nfc.connect() tag = self.clf.clf_response() # test blocked by issue raised IOTPAN313 NFC Controller discovery can stop but cannot restart - PN512 asserts.assertNone(tag, "post-restart: Tag discovery loop should be stopped!") From dea37cb97e50877c334f26cf4402c1238bd437e8 Mon Sep 17 00:00:00 2001 From: Conrad Braam Date: Mon, 4 Feb 2019 18:00:30 +0000 Subject: [PATCH 08/14] code review from paul,don addressed:compilers fixed --- TEST_APPS/device/nfcapp/mbed_app.json | 15 +- TEST_APPS/device/nfcapp/nfccommands.cpp | 1 + TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp | 2 + TEST_APPS/device/nfcapp/nfcprocessEeprom.h | 4 +- TEST_APPS/device/nfcapp/nfctestshim.cpp | 15 +- TEST_APPS/device/nfcapp/nfctestshim.h | 1 + TEST_APPS/icetea_plugins/nfc_test_parsers.py | 6 +- TEST_APPS/testcases/nfc/nfc_clf_wrapper.py | 21 ++- TEST_APPS/testcases/nfc/nfc_cli_helper.py | 4 +- TEST_APPS/testcases/nfc/test_nfc.py | 145 +++++++------------ TEST_APPS/testcases/nfc/test_self.py | 9 +- 11 files changed, 98 insertions(+), 125 deletions(-) diff --git a/TEST_APPS/device/nfcapp/mbed_app.json b/TEST_APPS/device/nfcapp/mbed_app.json index a0faf90cf9..291ce371f0 100644 --- a/TEST_APPS/device/nfcapp/mbed_app.json +++ b/TEST_APPS/device/nfcapp/mbed_app.json @@ -1,19 +1,28 @@ { "config": { - "TEST_NDEF_MSG_MAX" : 1024 + "TEST_NDEF_MSG_MAX": { + "help": "NFC-Driver buffer (EEPROM and Controller) maximum MAX Size of NFC message(s) driver buffer", + "value": 4096 + } }, "target_overrides": { "DISCO_L475VG_IOT01A": { "target.extra_labels_add": ["M24SR"], - "MBED_NFC_M24SR.nfceeprom": true + "MBED_NFC_M24SR.nfceeprom": true }, "NUCLEO_F401RE": { - "target.extra_labels_add": ["PN512"] + "target.extra_labels_add": ["PN512"], + "MBED_NFC_M24SR.nfceeprom": false }, "NUCLEO_F746ZG": { "target.extra_labels_add": ["M24SR"], "MBED_NFC_M24SR.X_NUCLEO_NFC01A1": true, "MBED_NFC_M24SR.nfceeprom": true + }, + "NUCLEO_F429ZI": { + "target.extra_labels_add": ["M24SR"], + "MBED_NFC_M24SR.X_NUCLEO_NFC01A1": true, + "MBED_NFC_M24SR.nfceeprom": true } } } diff --git a/TEST_APPS/device/nfcapp/nfccommands.cpp b/TEST_APPS/device/nfcapp/nfccommands.cpp index 3c708d5f78..c5a404e688 100644 --- a/TEST_APPS/device/nfcapp/nfccommands.cpp +++ b/TEST_APPS/device/nfcapp/nfccommands.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "mbed_events.h" #include "mbed-client-cli/ns_cmdline.h" #include "rtos\Thread.h" diff --git a/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp b/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp index 371b58dd19..e53d401daf 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp +++ b/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "mbed_events.h" #include "mbed-client-cli/ns_cmdline.h" @@ -98,6 +99,7 @@ void NFCProcessEEPROM::on_ndef_message_written(nfc_err_t result) } // complete the async test method here cmd_ready(CMDLINE_RETCODE_SUCCESS); + free(long_string); // free buffer allocated by the command class now } void NFCProcessEEPROM::on_ndef_message_read(nfc_err_t result) diff --git a/TEST_APPS/device/nfcapp/nfcprocessEeprom.h b/TEST_APPS/device/nfcapp/nfcprocessEeprom.h index 52b61cc3a9..98e55c60c7 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessEeprom.h +++ b/TEST_APPS/device/nfcapp/nfcprocessEeprom.h @@ -38,12 +38,12 @@ class NFCProcessEEPROM : NFCTestShim, mbed::nfc::NFCEEPROM::Delegate { public: NFCProcessEEPROM(events::EventQueue &queue, mbed::nfc::NFCEEPROMDriver &eeprom_driver); - nfc_err_t init(); + virtual nfc_err_t init(); void queue_write_call(); void queue_write_long_call(); void queue_read_call(); void queue_erase_call(); - void cmd_get_max_ndef(); + virtual void cmd_get_max_ndef(); private: diff --git a/TEST_APPS/device/nfcapp/nfctestshim.cpp b/TEST_APPS/device/nfcapp/nfctestshim.cpp index 0652799862..abe74a9934 100644 --- a/TEST_APPS/device/nfcapp/nfctestshim.cpp +++ b/TEST_APPS/device/nfcapp/nfctestshim.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "mbed-client-cli/ns_cmdline.h" #include "nfc/ndef/common/Text.h" #include "nfc/ndef/common/URI.h" @@ -41,7 +42,7 @@ using mbed::nfc::nfc_rf_protocols_bitmask_t; // statics namespace { -char long_string[MBED_CONF_APP_TEST_NDEF_MSG_MAX]; + char const *uri_prefix_string[] = { "", "http://www.", "https://www.", @@ -278,15 +279,15 @@ void NFCTestShim::cmd_erase() /** \brief Writes a Text T record buffer with really long message - length checks to be done by driver only. * If an NFC controller, no write to the chip happens, we copy the data into a Controller buffer - * \param uri This method must free the passed in pointer - * \return void An ICETEA error code and NFC error is set asyncronously + * \param text_string This method must free the passed in pointer + * \return void An ICETEA error code and NFC error is set asynchronously */ void NFCTestShim::cmd_write_long(char *text_string) { MessageBuilder builder(_ndef_poster_message); - strcpy(::long_string, text_string); //max_ndef - header - overheads + long_string = text_string; // copy the pointer and free it when the write completes Text text(Text::UTF8, span_from_cstr("en-US"), - span_from_cstr((const char *)(::long_string))); + span_from_cstr((const char *)(long_string))); text.append_as_record(builder, true); _ndef_write_buffer_used = builder.get_message().size(); @@ -295,12 +296,12 @@ void NFCTestShim::cmd_write_long(char *text_string) #if MBED_CONF_NFCEEPROM ((NFCProcessEEPROM *)this)->queue_write_call(); #else - // not on a wire, so the caller will store the message in a buffer + // not on a wire, and we just stored the message in _ndef_write_buffer above set_last_nfc_error(NFC_OK); cmd_ready(CMDLINE_RETCODE_SUCCESS); + free(long_string); // free buffer allocated by the command class now #endif trace_printf("NFCTestShim::write_long() exit\r\n"); - free(text_string); } /** \brief Write a URI Use case would be to prompt to install an app from the appstore using the tag diff --git a/TEST_APPS/device/nfcapp/nfctestshim.h b/TEST_APPS/device/nfcapp/nfctestshim.h index 29967718f6..4fe8ea162c 100644 --- a/TEST_APPS/device/nfcapp/nfctestshim.h +++ b/TEST_APPS/device/nfcapp/nfctestshim.h @@ -100,6 +100,7 @@ protected: uint8_t _ndef_buffer[MBED_CONF_APP_TEST_NDEF_MSG_MAX]; // driver I/O buffer bool _discovery_restart; // default true, restart discovery loop again on remote disconnect events::EventQueue &_queue; + char *long_string; }; // forward declare single instance diff --git a/TEST_APPS/icetea_plugins/nfc_test_parsers.py b/TEST_APPS/icetea_plugins/nfc_test_parsers.py index 5afec0842e..bd4439dd08 100644 --- a/TEST_APPS/icetea_plugins/nfc_test_parsers.py +++ b/TEST_APPS/icetea_plugins/nfc_test_parsers.py @@ -110,6 +110,7 @@ class NfcTestParsers(PluginBase): if value is not False: results['lastnfcerror'] = int(value) # {{nfcmessage=([0-9a-f\s]*)}} + # message may be split over multiple lines, so we will start a search and wait until we get a }} pair data = PluginBase.find_one(line, "{{nfcmessage=([0-9a-f\s]*).*") if data is not False: started_read_data = True @@ -117,10 +118,9 @@ class NfcTestParsers(PluginBase): values = self.find_all_hex_data(line, "([0-9a-f\s]*)") if values is not False: if "{{nfcmessage" in line: - value = values[0] + value = values[0] # first (and possibly only data line) else: - value = values[0] - #print ("==%s==" % value) + value += values[0] # concatenate (2nd and possibly last data line) partial_data += value if PluginBase.find_one(line, ".*(}})") is not False: # search for end marker started_read_data = False diff --git a/TEST_APPS/testcases/nfc/nfc_clf_wrapper.py b/TEST_APPS/testcases/nfc/nfc_clf_wrapper.py index 380edda944..8a47dbfca1 100644 --- a/TEST_APPS/testcases/nfc/nfc_clf_wrapper.py +++ b/TEST_APPS/testcases/nfc/nfc_clf_wrapper.py @@ -25,9 +25,6 @@ import logging """ -# def command_is(string, command): -# return string.split(' ')[0] == command - def debug_nfc_data(key, value): """ @@ -55,25 +52,27 @@ class NfcWrapper: logger.error("The NFC reader was not detected on any USB port!") self.clfResponse = None - def clf_response(self): - return self.clfResponse - - def connect(self, target_options = ("106A","106B","212F")): + def connect(self): # note: only supporting type4 + time.sleep(0.5) after5s = lambda: time.time() - started > 5 started = time.time() + tag = self.clf.connect( rdwr={'on-connect': lambda tag: False}, - llcp={}, terminate = after5s) - self.clfResponse = tag + terminate = after5s) if tag: # None if timeout expires logging.info("NFCReader: connected " + str(tag)) + else: + logging.info("NFCReader: warning, no tag detected ") return tag def mute(self): """turn off the reader radio""" - if self.clf.device: + if (self.clf.device is not None): logging.info("NFCReader: radio mute" + self.clf.device.product_name) self.clf.device.mute() + else: + logging.warning("NFCReader: reader not initialized!") def disconnect(self): logging.info("NFCReader: close frontend.") @@ -97,5 +96,3 @@ class ContactlessCommandRunner(): __nfc_wrapper = None - def clf_response(self): - return self.nfc.clf_response() diff --git a/TEST_APPS/testcases/nfc/nfc_cli_helper.py b/TEST_APPS/testcases/nfc/nfc_cli_helper.py index c66e443b26..8d49dc9044 100644 --- a/TEST_APPS/testcases/nfc/nfc_cli_helper.py +++ b/TEST_APPS/testcases/nfc/nfc_cli_helper.py @@ -22,8 +22,8 @@ from nfc_messages import NfcErrors import logging import icetea_lib.tools.asserts as asserts -# Values > 4 k incur large time costs -STRESS_BUFFLEN = 4096 # Default value for large buffer tests, this value can be read from the target with a command +# Values > 1 k incur large time costs +LARGE_BUFFLEN = 400 # Value for large buffer tests, a maximum value can be read from the target with a command class CliHelper(): """ diff --git a/TEST_APPS/testcases/nfc/test_nfc.py b/TEST_APPS/testcases/nfc/test_nfc.py index 1eb785d810..5e8f33c14a 100644 --- a/TEST_APPS/testcases/nfc/test_nfc.py +++ b/TEST_APPS/testcases/nfc/test_nfc.py @@ -24,7 +24,7 @@ from mbed_clitest.tools.tools import test_case import icetea_lib.tools.asserts as asserts from nfc_messages import NfcErrors from nfc_cli_helper import CliHelper -from nfc_cli_helper import STRESS_BUFFLEN +from nfc_cli_helper import LARGE_BUFFLEN class CreamSconeTests(Bench, CliHelper): @@ -56,16 +56,31 @@ class CreamSconeTests(Bench, CliHelper): Bench.__init__(self, **testcase_args) def setup(self): - #try: + self.logger.info("Test setup: Open Reader and mute...") + try: self.clf = ContactlessCommandRunner() - self.clf.nfc.mute() - #except: - # raise asserts.TestStepFail("Could not find NFC reader") + self.clf.nfc.mute() # mute if the last test case did not mute + except: + raise asserts.TestStepFail("Could not find NFC reader") def teardown(self): self.logger.info("Test teardown: Reboot target...") self.reset_dut() + self.clf.nfc.mute() # mute if the last test case did not mute + def prepare_target(self): + """ + simple set up a clean target device + :return: + """ + response = self.nfc_command("dev1", "iseeprom") + eeprom = response.parsed['iseeprom'] + self.logger.info("Target includes NFCEEPROM: %s" % eeprom) + self.nfc_command("dev1", "initnfc") + if not eeprom: + self.nfc_command("dev1", "start") + + self.nfc_command("dev1", "erase") @test_case(CreamSconeTests) @@ -83,8 +98,7 @@ def test_nfce2e_target_found(self): if not eeprom: self.nfc_command("dev1", "start") - self.clf.nfc.connect() - tag = self.clf.clf_response() + tag = self.clf.nfc.connect() asserts.assertNotNone(tag, "Could not connect to any tag") @@ -103,8 +117,7 @@ def test_nfce2e_type4_found(self): if not eeprom: self.nfc_command("dev1", "start") - self.clf.nfc.connect() - tag = self.clf.clf_response() + tag = self.clf.nfc.connect() asserts.assertNotNone(tag, "Could not connect to any tag") asserts.assertEqual(tag.type, 'Type4Tag', "Tag of type Type4Tag not found") @@ -117,20 +130,12 @@ def test_nfce2e_smartposter(self): """ expectedURI = "https://www.mbed.com" # ensure that these differ per test case - self.nfc_command("dev1", "initnfc") - self.nfc_command("dev1", "erase") - response = self.nfc_command("dev1", "iseeprom") - eeprom = response.parsed['iseeprom'] - - self.logger.info("Target includes NFCEEPROM: %s" % eeprom) - if not eeprom: - self.nfc_command("dev1", "start") + self.prepare_target() # write poster tag to target self.command("dev1", "setsmartposter %s" % expectedURI) - self.clf.nfc.connect() - tag = self.clf.clf_response() + tag = self.clf.nfc.connect() asserts.assertNotNone(tag, "Could not connect to any tag") asserts.assertEqual(1, len(tag.ndef.records), "expected number NDEF records") @@ -145,27 +150,18 @@ def test_nfce2e_reprogrammed(self): """ expectedURI = "https://www.google.com" - response = self.nfc_command("dev1", "iseeprom") - eeprom = response.parsed['iseeprom'] - self.logger.info("Target includes NFCEEPROM: %s" % eeprom) - - self.nfc_command("dev1", "initnfc") - if not eeprom: - self.nfc_command("dev1", "start") - self.nfc_command("dev1", "erase") + self.prepare_target() # program a poster tag to target - print("Write Smartposter MESSAGE wirelessly") - self.clf.nfc.connect() - tag = self.clf.clf_response() + self.logger.info("Write Smartposter MESSAGE wirelessly") + tag = self.clf.nfc.connect() asserts.assertNotNone(tag, "Could not connect to any tag") smartposter = nfc_messages.make_smartposter(expectedURI, ["en-US:Other search engines exist"]) nfc_messages.program_remote_tag(smartposter, tag) self.logger.info("Remote programmed %d bytes Smartposter" % len(str(smartposter))) - print("Write back Smartposter MESSAGE wirelessly") - self.clf.nfc.connect() - tag = self.clf.clf_response() + self.logger.info("Write back Smartposter MESSAGE wirelessly") + tag = self.clf.nfc.connect() asserts.assertNotNone(tag, "Could not re-connect to any tag") asserts.assertEqual(tag.ndef.records[0].__class__.__name__, "SmartposterRecord", "expected SmartposterRecord") @@ -185,35 +181,22 @@ def test_nfce2e_read_stress(self): """ check - Large record can be programmed in and read via contactless """ - messageRep = 'thequickbrownfoxjumpedoverthelazydog' # repeating message written - - response = self.nfc_command("dev1", "iseeprom") - eeprom = response.parsed['iseeprom'] - self.logger.info("Target includes NFCEEPROM: %s" % eeprom) - max_ndef = STRESS_BUFFLEN - - self.nfc_command("dev1", "initnfc") - if not eeprom: - self.nfc_command("dev1", "start") - else: - max_ndef = self.nfc_command("dev1", "getmaxndef").parsed['maxndef'] - if (max_ndef > 800 ): - textLength = 800 # large values slow down test runs and may time out - else: - textLength = max_ndef - self.nfc_command("dev1", "erase") + message_to_repeat = 'thequickbrownfoxjumpedoverthelazydog' # repeating message written + text_length = LARGE_BUFFLEN # calculate actual message to compare to using the library - expected_text = nfc_messages.repeat_string_to_length(messageRep, textLength) + expected_text = nfc_messages.repeat_string_to_length(message_to_repeat, text_length) + self.prepare_target() # write a large message to the tag via API, then read it wirelessly - print("Write/set tag MESSAGE (%d) bytes" % textLength) - self.nfc_command("dev1", "writelong %d %s" % (textLength,messageRep)) - self.clf.nfc.connect() - tag = self.clf.clf_response() - asserts.assertNotNone(tag, "Could not connect to any tag") + self.logger.info("Write/set tag MESSAGE (%d) bytes" % text_length) + self.nfc_command("dev1", "writelong %d %s" % (text_length,message_to_repeat)) + # assert that read the eeprom contents gives correct data and length - print("Read tag MESSAGE wirelessly" ) + self.logger.info("Read tag MESSAGE wirelessly" ) + tag = self.clf.nfc.connect() + asserts.assertNotNone(tag, "Could not connect to any tag") + asserts.assertEqual(tag.ndef.records[0].__class__.__name__, "TextRecord", "expected TextRecord") self.assert_text_equal(tag.ndef.records[0].text, expected_text) @@ -223,43 +206,27 @@ def test_nfce2e_reprogrammed_stress(self): """ check - Large record can be programmed from a remote and read via contactless """ - messageRep = 'thequickbrownfoxjumpedoverthelazydog' # repeating message written - - - response = self.nfc_command("dev1", "iseeprom") - eeprom = response.parsed['iseeprom'] - self.logger.info("Target includes NFCEEPROM: %s" % eeprom) - max_ndef = STRESS_BUFFLEN - - self.nfc_command("dev1", "initnfc") - if not eeprom: - self.nfc_command("dev1", "start") - else: - max_ndef = self.nfc_command("dev1", "getmaxndef").parsed['maxndef'] - - if (max_ndef > 800 ): - textLength = 800 # large values slow down test runs and may time out - else: - textLength = max_ndef - self.nfc_command("dev1", "erase") + message_to_repeat = 'thequickbrownfoxjumpedoverthelazydog' # repeating message written + text_length = LARGE_BUFFLEN # large values slow down test runs and may time out # calculate actual message to compare to using the library - message = nfc_messages.make_textrecord( nfc_messages.repeat_string_to_length(messageRep, textLength)) + message = nfc_messages.make_textrecord( nfc_messages.repeat_string_to_length(message_to_repeat, text_length)) expected_message = str(message) + self.prepare_target() # program a large tag to target remotely - print("Write tag MESSAGE wirelessly (%d) bytes" % len(str(message))) - self.clf.nfc.connect() - tag = self.clf.clf_response() + self.logger.info("Write tag MESSAGE wirelessly (%d) bytes" % len(str(message))) + tag = self.clf.nfc.connect() asserts.assertNotNone(tag, "Could not connect to any tag") nfc_messages.program_remote_tag(message, tag) self.logger.info("%d bytes chunk of data written to tag remotely" % len(str(message))) + self.clf.nfc.mute() # read device locally - print("Read back tag MESSAGE wirelessly") - self.clf.nfc.connect() - tag = self.clf.clf_response() + self.logger.info("Read back tag MESSAGE wirelessly") + tag = self.clf.nfc.connect() asserts.assertNotNone(tag, "Could not re-connect to any tag") + asserts.assertEqual(tag.ndef.records[0].__class__.__name__, "TextRecord", "expected TextRecord") self.clf.nfc.mute() # disable the reader radio, to allow local access # verify in target @@ -285,27 +252,23 @@ def test_nfce2e_discovery_loop(self): # Automatic resume after disconnect can be turned off by using command "start man" , the default is "start auto" . if not eeprom: - self.clf.nfc.connect() - tag = self.clf.clf_response() + tag = self.clf.nfc.connect() asserts.assertNone(tag, "post-init: Tag discovery loop should be stopped!") self.nfc_command("dev1", "stop") time.sleep(1) - self.clf.nfc.connect() - tag = self.clf.clf_response() + tag = self.clf.nfc.connect() asserts.assertNone(tag, "post-stop: Tag discovery loop should be stopped!") self.nfc_command("dev1", "start") time.sleep(1) - self.clf.nfc.connect() - tag = self.clf.clf_response() + tag = self.clf.nfc.connect() asserts.assertNotNone(tag, "Could not connect to any tag") self.clf.nfc.mute() self.nfc_command("dev1", "stop") time.sleep(10) - self.clf.nfc.connect() - tag = self.clf.clf_response() + tag = self.clf.nfc.connect() # test blocked by issue raised IOTPAN313 NFC Controller discovery can stop but cannot restart - PN512 asserts.assertNone(tag, "post-restart: Tag discovery loop should be stopped!") diff --git a/TEST_APPS/testcases/nfc/test_self.py b/TEST_APPS/testcases/nfc/test_self.py index 4cc178a415..1c36cd543d 100644 --- a/TEST_APPS/testcases/nfc/test_self.py +++ b/TEST_APPS/testcases/nfc/test_self.py @@ -22,7 +22,7 @@ import icetea_lib.tools.asserts as asserts import nfc_messages from nfc_messages import NfcErrors from nfc_cli_helper import CliHelper -from nfc_cli_helper import STRESS_BUFFLEN +from nfc_cli_helper import LARGE_BUFFLEN import nfc """ @@ -111,6 +111,7 @@ check - Create a SmartPoster but does not read it back def test_nfc_setsmartposter(self): self.nfc_command("dev1", "initnfc") + self.nfc_command("dev1", "setsmartposter https://www.mbed.com") @test_case(CreamSconeSelfTests) @@ -120,7 +121,6 @@ def test_nfc_erase(self): eeprom = response.parsed['iseeprom'] if eeprom: self.logger.info("Target includes NFCEEPROM: %s" % eeprom) - self.nfc_command("dev1", "erase", timeout=30) response = self.nfc_command("dev1", "readmessage") asserts.assertEqual(response.parsed['nfcmessage'] is None, True) @@ -132,7 +132,7 @@ can be read back. @test_case(CreamSconeSelfTests) def test_nfc_write_long(self): messageRep = 'thequickbrownfoxjumpedoverthelazydog' # repeating message written - textLength = STRESS_BUFFLEN / 2 # 2K < x < 4K + textLength = LARGE_BUFFLEN # large values take longer # calculate actual message to compare to using the library message = nfc_messages.make_textrecord( nfc_messages.repeat_string_to_length(messageRep, textLength)) expected_message = str(message) @@ -142,7 +142,6 @@ def test_nfc_write_long(self): eeprom = response.parsed['iseeprom'] if eeprom: self.logger.info("Target includes NFCEEPROM: %s" % eeprom) - self.nfc_command("dev1", "erase") self.nfc_command("dev1", "writelong %d %s" % (textLength,messageRep)) response = self.nfc_command("dev1", "readmessage") @@ -252,5 +251,5 @@ def test_nfc_get_max_ndef(self): self.nfc_command("dev1", "initnfc") max = self.nfc_command("dev1", "getmaxndef").parsed['maxndef'] self.logger.info("Target NDEF max buffer size %d" % max) - self.logger.info("Teststress size %d" % STRESS_BUFFLEN) + self.logger.info("Teststress size %d" % LARGE_BUFFLEN) From 4ff7ebbb3f59786ff1319d019f377a1b125459d8 Mon Sep 17 00:00:00 2001 From: Conrad Braam Date: Tue, 5 Feb 2019 13:34:23 +0000 Subject: [PATCH 09/14] resolved free and long datas --- TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp | 1 - TEST_APPS/device/nfcapp/nfctestshim.cpp | 5 ++--- TEST_APPS/device/nfcapp/nfctestshim.h | 1 - TEST_APPS/icetea_plugins/nfc_test_parsers.py | 11 ++++++----- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp b/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp index e53d401daf..717ca4643f 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp +++ b/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp @@ -99,7 +99,6 @@ void NFCProcessEEPROM::on_ndef_message_written(nfc_err_t result) } // complete the async test method here cmd_ready(CMDLINE_RETCODE_SUCCESS); - free(long_string); // free buffer allocated by the command class now } void NFCProcessEEPROM::on_ndef_message_read(nfc_err_t result) diff --git a/TEST_APPS/device/nfcapp/nfctestshim.cpp b/TEST_APPS/device/nfcapp/nfctestshim.cpp index abe74a9934..79ba38dc74 100644 --- a/TEST_APPS/device/nfcapp/nfctestshim.cpp +++ b/TEST_APPS/device/nfcapp/nfctestshim.cpp @@ -285,9 +285,8 @@ void NFCTestShim::cmd_erase() void NFCTestShim::cmd_write_long(char *text_string) { MessageBuilder builder(_ndef_poster_message); - long_string = text_string; // copy the pointer and free it when the write completes Text text(Text::UTF8, span_from_cstr("en-US"), - span_from_cstr((const char *)(long_string))); + span_from_cstr((const char *)(text_string))); text.append_as_record(builder, true); _ndef_write_buffer_used = builder.get_message().size(); @@ -299,8 +298,8 @@ void NFCTestShim::cmd_write_long(char *text_string) // not on a wire, and we just stored the message in _ndef_write_buffer above set_last_nfc_error(NFC_OK); cmd_ready(CMDLINE_RETCODE_SUCCESS); - free(long_string); // free buffer allocated by the command class now #endif + free(text_string); // free buffer allocated by the command class now trace_printf("NFCTestShim::write_long() exit\r\n"); } diff --git a/TEST_APPS/device/nfcapp/nfctestshim.h b/TEST_APPS/device/nfcapp/nfctestshim.h index 4fe8ea162c..29967718f6 100644 --- a/TEST_APPS/device/nfcapp/nfctestshim.h +++ b/TEST_APPS/device/nfcapp/nfctestshim.h @@ -100,7 +100,6 @@ protected: uint8_t _ndef_buffer[MBED_CONF_APP_TEST_NDEF_MSG_MAX]; // driver I/O buffer bool _discovery_restart; // default true, restart discovery loop again on remote disconnect events::EventQueue &_queue; - char *long_string; }; // forward declare single instance diff --git a/TEST_APPS/icetea_plugins/nfc_test_parsers.py b/TEST_APPS/icetea_plugins/nfc_test_parsers.py index bd4439dd08..ee2895b65d 100644 --- a/TEST_APPS/icetea_plugins/nfc_test_parsers.py +++ b/TEST_APPS/icetea_plugins/nfc_test_parsers.py @@ -115,12 +115,13 @@ class NfcTestParsers(PluginBase): if data is not False: started_read_data = True if started_read_data: # read data until we see a }} pair - values = self.find_all_hex_data(line, "([0-9a-f\s]*)") + line_values = "" + if "{{nfcmessage=" in line: + line_values = line[13:] + else: + line_values = line + values = self.find_all_hex_data(line_values, "([0-9a-f\s]*)") if values is not False: - if "{{nfcmessage" in line: - value = values[0] # first (and possibly only data line) - else: - value += values[0] # concatenate (2nd and possibly last data line) partial_data += value if PluginBase.find_one(line, ".*(}})") is not False: # search for end marker started_read_data = False From 37ec35f671965b5a6c9d778a408ebfbab04867a4 Mon Sep 17 00:00:00 2001 From: Conrad Braam Date: Tue, 5 Feb 2019 14:18:42 +0000 Subject: [PATCH 10/14] simplify gathering hex values --- TEST_APPS/icetea_plugins/nfc_test_parsers.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/TEST_APPS/icetea_plugins/nfc_test_parsers.py b/TEST_APPS/icetea_plugins/nfc_test_parsers.py index ee2895b65d..982f75f6f3 100644 --- a/TEST_APPS/icetea_plugins/nfc_test_parsers.py +++ b/TEST_APPS/icetea_plugins/nfc_test_parsers.py @@ -115,16 +115,13 @@ class NfcTestParsers(PluginBase): if data is not False: started_read_data = True if started_read_data: # read data until we see a }} pair - line_values = "" if "{{nfcmessage=" in line: - line_values = line[13:] - else: - line_values = line - values = self.find_all_hex_data(line_values, "([0-9a-f\s]*)") - if values is not False: - partial_data += value + line = line[13:] # trim header if PluginBase.find_one(line, ".*(}})") is not False: # search for end marker started_read_data = False + line = line[:-2] # trim closing }} + partial_data += line + if not started_read_data: results['nfcmessage'] = self.convert_from_hex(partial_data) # t1t,t2t,t3t,isodep,nfcdef,t5t value = PluginBase.find_one(line, "{{protocols=(([\w]*,?)*)}}") From ec806bdb3ea34cfcf0bf59f37863afc46e8d6057 Mon Sep 17 00:00:00 2001 From: Conrad Braam Date: Tue, 5 Feb 2019 17:09:37 +0000 Subject: [PATCH 11/14] removed t4t,removed unnecessary buffer on stack, nfcerror moved into class --- .gitignore | 49 ++++++++++++++++++++++ TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp | 4 +- TEST_APPS/device/nfcapp/nfctestshim.cpp | 39 +++++++++++------ TEST_APPS/device/nfcapp/nfctestshim.h | 3 ++ TEST_APPS/testcases/nfc/test_nfc.py | 22 +--------- TEST_APPS/testcases/nfc/test_self.py | 1 + 6 files changed, 83 insertions(+), 35 deletions(-) diff --git a/.gitignore b/.gitignore index e89472c010..75a3ee6fee 100644 --- a/.gitignore +++ b/.gitignore @@ -97,3 +97,52 @@ test_suite.json # default delivery dir DELIVERY/ +cmake-build-debug/.mbedignore +cmake-build-debug/cmake_install.cmake +cmake-build-debug/CMakeCache.txt +cmake-build-debug/CMakeFiles/3.13.2/CMakeASMCompiler.cmake +cmake-build-debug/CMakeFiles/3.13.2/CMakeCCompiler.cmake +cmake-build-debug/CMakeFiles/3.13.2/CMakeCXXCompiler.cmake +cmake-build-debug/CMakeFiles/3.13.2/CMakeSystem.cmake +cmake-build-debug/CMakeFiles/3.13.2/CompilerIdC/CMakeCCompilerId.c +cmake-build-debug/CMakeFiles/3.13.2/CompilerIdC/CMakeCCompilerId.o +cmake-build-debug/CMakeFiles/3.13.2/CompilerIdCXX/CMakeCXXCompilerId.cpp +cmake-build-debug/CMakeFiles/3.13.2/CompilerIdCXX/CMakeCXXCompilerId.o +cmake-build-debug/CMakeFiles/clion-environment.txt +cmake-build-debug/CMakeFiles/clion-log.txt +cmake-build-debug/CMakeFiles/cmake.check_cache +cmake-build-debug/CMakeFiles/CMakeDirectoryInformation.cmake +cmake-build-debug/CMakeFiles/CMakeError.log +cmake-build-debug/CMakeFiles/CMakeOutput.log +cmake-build-debug/CMakeFiles/CMakeRuleHashes.txt +cmake-build-debug/CMakeFiles/feature_tests.c +cmake-build-debug/CMakeFiles/feature_tests.cxx +cmake-build-debug/CMakeFiles/Makefile.cmake +cmake-build-debug/CMakeFiles/Makefile2 +cmake-build-debug/CMakeFiles/mbed-cli-build.dir/build.make +cmake-build-debug/CMakeFiles/mbed-cli-build.dir/cmake_clean.cmake +cmake-build-debug/CMakeFiles/mbed-cli-build.dir/DependInfo.cmake +cmake-build-debug/CMakeFiles/mbed-cli-build.dir/progress.make +cmake-build-debug/CMakeFiles/mbed-os.dir/build.make +cmake-build-debug/CMakeFiles/mbed-os.dir/cmake_clean.cmake +cmake-build-debug/CMakeFiles/mbed-os.dir/depend.make +cmake-build-debug/CMakeFiles/mbed-os.dir/DependInfo.cmake +cmake-build-debug/CMakeFiles/mbed-os.dir/flags.make +cmake-build-debug/CMakeFiles/mbed-os.dir/link.txt +cmake-build-debug/CMakeFiles/mbed-os.dir/objects1.rsp +cmake-build-debug/CMakeFiles/mbed-os.dir/progress.make +cmake-build-debug/CMakeFiles/progress.marks +cmake-build-debug/CMakeFiles/TargetDirectories.txt +cmake-build-debug/compiler-file3040531024441447850.d +cmake-build-debug/compiler-file4529788487702700900.d +cmake-build-debug/compiler-file4727057748131971377.d +cmake-build-debug/compiler-file8813331766360922248.d +cmake-build-debug/main.d +cmake-build-debug/Makefile +cmake-build-debug/mbed-os.cbp +cmake-build-debug/nfccommands.d +cmake-build-debug/NFCController.d +cmake-build-debug/nfcprocess.d +cmake-build-debug/nfcprocessCtrl.d +cmake-build-debug/nfctestshim.d +cmake-build-debug/uart.d diff --git a/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp b/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp index ec6ff54e9f..42f38f6f38 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp +++ b/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp @@ -47,7 +47,9 @@ using mbed::nfc::ndef::common::Text; using mbed::nfc::ndef::common::URI; using mbed::nfc::NFCController; -//class NFCProcessController : NFCRemoteInitiator::Delegate, NFCController::Delegate { +/** + * Implements NFCRemoteInitiator::Delegate, NFCController::Delegate + */ NFCProcessController::NFCProcessController(events::EventQueue &queue) : NFCTestShim(queue), diff --git a/TEST_APPS/device/nfcapp/nfctestshim.cpp b/TEST_APPS/device/nfcapp/nfctestshim.cpp index 79ba38dc74..6136e84b17 100644 --- a/TEST_APPS/device/nfcapp/nfctestshim.cpp +++ b/TEST_APPS/device/nfcapp/nfctestshim.cpp @@ -81,9 +81,11 @@ char const *uri_prefix_string[] = { "", "urn:nfc:" }; -int last_nfc_error = 0; + } + +int NFCTestShim::last_nfc_error = 0; NFCTestShim *pNFC_Test_Shim = NULL; NFCTestShim::NFCTestShim(events::EventQueue &queue) : @@ -114,8 +116,8 @@ void NFCTestShim::cmd_get_conf_nfceeprom() */ void NFCTestShim::get_last_nfc_error() { - int last = ::last_nfc_error; - ::last_nfc_error = 0; + int last = last_nfc_error; + last_nfc_error = 0; // return data as text to the plugin framework cmd_printf("{{lastnfcerror=%d}}\r\n", last); cmd_ready(CMDLINE_RETCODE_SUCCESS); @@ -123,8 +125,8 @@ void NFCTestShim::get_last_nfc_error() void NFCTestShim::set_last_nfc_error(int err) { - ::last_nfc_error = err; - cmd_printf("\r\n{{lastnfcerror=%d}}\r\n", ::last_nfc_error); + last_nfc_error = err; + cmd_printf("\r\n{{lastnfcerror=%d}}\r\n", last_nfc_error); } // if an NFC EEPROM driver is configured @@ -311,19 +313,30 @@ void NFCTestShim::cmd_set_smartposter(char *cmdUri) { MessageBuilder builder(_ndef_poster_message); - uint8_t smart_poster_buffer[1024]; - MessageBuilder smart_poster_builder(smart_poster_buffer); + struct SPBuilder: MessageBuilder::PayloadBuilder { + SPBuilder(char * cmd_uri) { + URI::uri_identifier_code_t uri_id = get_ndef_record_type(cmd_uri); + char *urlbegin = cmd_uri + + strlen(get_ndef_record_type_prefix(uri_id)); + uri = URI(uri_id, span_from_cstr(urlbegin)); + cmd_printf("{{uri_id=%d}}\r\n", (int) uri_id); + } - URI::uri_identifier_code_t uri_id = get_ndef_record_type(cmdUri); - char *urlbegin = cmdUri + strlen(get_ndef_record_type_prefix(uri_id)); - URI uri(uri_id, span_from_cstr(urlbegin)); - cmd_printf("{{uri_id=%d}}\r\n", (int)uri_id); + virtual size_t size() const { + return uri.get_record_size(); + } - uri.append_as_record(smart_poster_builder, true); + virtual void build(const Span &buffer) const { + MessageBuilder smart_poster_builder(buffer); + + uri.append_as_record(smart_poster_builder, true); + } + URI uri; + }; builder.append_record( RecordType(RecordType::well_known_type, span_from_cstr("Sp")), - smart_poster_builder.get_message(), true); + SPBuilder(cmdUri), true); _ndef_write_buffer_used = builder.get_message().size(); trace_printf("Composed NDEF message %d bytes\r\n", _ndef_write_buffer_used); diff --git a/TEST_APPS/device/nfcapp/nfctestshim.h b/TEST_APPS/device/nfcapp/nfctestshim.h index 29967718f6..b6d463acdb 100644 --- a/TEST_APPS/device/nfcapp/nfctestshim.h +++ b/TEST_APPS/device/nfcapp/nfctestshim.h @@ -74,6 +74,7 @@ protected: virtual mbed::nfc::nfc_rf_protocols_bitmask_t get_rf_protocols() { mbed::nfc::nfc_rf_protocols_bitmask_t none; + memset((void*)&none, 0, sizeof(none)); return none; }; virtual nfc_err_t start_discovery() @@ -100,6 +101,8 @@ protected: uint8_t _ndef_buffer[MBED_CONF_APP_TEST_NDEF_MSG_MAX]; // driver I/O buffer bool _discovery_restart; // default true, restart discovery loop again on remote disconnect events::EventQueue &_queue; +private: + static int last_nfc_error; }; // forward declare single instance diff --git a/TEST_APPS/testcases/nfc/test_nfc.py b/TEST_APPS/testcases/nfc/test_nfc.py index 5e8f33c14a..cdad33f9e9 100644 --- a/TEST_APPS/testcases/nfc/test_nfc.py +++ b/TEST_APPS/testcases/nfc/test_nfc.py @@ -102,27 +102,6 @@ def test_nfce2e_target_found(self): asserts.assertNotNone(tag, "Could not connect to any tag") -@test_case(CreamSconeTests) -def test_nfce2e_type4_found(self): - """ - check - Type 4 tag is detected wirelessly - NOTE: If the tage emulation does not default to type4, this test needs to be modified - +++ self.nfc_command("dev1", "setprotocols t4t") - or the assertion be adapated according to support desired level - """ - - response = self.nfc_command("dev1", "iseeprom") - eeprom = response.parsed['iseeprom'] - self.nfc_command("dev1", "initnfc") - if not eeprom: - self.nfc_command("dev1", "start") - - tag = self.clf.nfc.connect() - asserts.assertNotNone(tag, "Could not connect to any tag") - - asserts.assertEqual(tag.type, 'Type4Tag', "Tag of type Type4Tag not found") - - @test_case(CreamSconeTests) def test_nfce2e_smartposter(self): """ @@ -252,6 +231,7 @@ def test_nfce2e_discovery_loop(self): # Automatic resume after disconnect can be turned off by using command "start man" , the default is "start auto" . if not eeprom: + # we are muted at this point, and the target is not in discovery mode yet. tag = self.clf.nfc.connect() asserts.assertNone(tag, "post-init: Tag discovery loop should be stopped!") self.nfc_command("dev1", "stop") diff --git a/TEST_APPS/testcases/nfc/test_self.py b/TEST_APPS/testcases/nfc/test_self.py index 1c36cd543d..d59eeb3c46 100644 --- a/TEST_APPS/testcases/nfc/test_self.py +++ b/TEST_APPS/testcases/nfc/test_self.py @@ -162,6 +162,7 @@ def test_nfc_get_controller_protocols(self): else: response = self.nfc_command("dev1", "getprotocols") self.logger.info("Protocols = %s" % response.parsed['protocols']) + self.assertNotEqual(len(response.parsed['protocols']), 0, "Expected at least 1 protocol supported") ''' From d43ad08fd484cc2d4b94bdcfc6e28055f4e48145 Mon Sep 17 00:00:00 2001 From: Conrad Braam Date: Wed, 6 Feb 2019 11:09:28 +0000 Subject: [PATCH 12/14] files case sensitive --- .../device/nfcapp/{nfccommands.cpp => NFCCommands.cpp} | 6 +++--- TEST_APPS/device/nfcapp/{nfccommands.h => NFCCommands.h} | 4 ++-- .../nfcapp/{nfcprocessCtrl.cpp => NFCProcessCtrl.cpp} | 4 ++-- .../device/nfcapp/{nfcprocessCtrl.h => NFCProcessCtrl.h} | 2 +- .../nfcapp/{nfcprocessEeprom.cpp => NFCProcessEEPROM.cpp} | 2 +- .../nfcapp/{nfcprocessEeprom.h => NFCProcessEEPROM.h} | 2 +- .../device/nfcapp/{nfctestshim.cpp => NFCTestShim.cpp} | 4 ++-- TEST_APPS/device/nfcapp/{nfctestshim.h => NFCTestShim.h} | 0 TEST_APPS/device/nfcapp/main.cpp | 4 ++-- 9 files changed, 14 insertions(+), 14 deletions(-) rename TEST_APPS/device/nfcapp/{nfccommands.cpp => NFCCommands.cpp} (99%) rename TEST_APPS/device/nfcapp/{nfccommands.h => NFCCommands.h} (98%) rename TEST_APPS/device/nfcapp/{nfcprocessCtrl.cpp => NFCProcessCtrl.cpp} (99%) rename TEST_APPS/device/nfcapp/{nfcprocessCtrl.h => NFCProcessCtrl.h} (99%) rename TEST_APPS/device/nfcapp/{nfcprocessEeprom.cpp => NFCProcessEEPROM.cpp} (99%) rename TEST_APPS/device/nfcapp/{nfcprocessEeprom.h => NFCProcessEEPROM.h} (98%) rename TEST_APPS/device/nfcapp/{nfctestshim.cpp => NFCTestShim.cpp} (99%) rename TEST_APPS/device/nfcapp/{nfctestshim.h => NFCTestShim.h} (100%) diff --git a/TEST_APPS/device/nfcapp/nfccommands.cpp b/TEST_APPS/device/nfcapp/NFCCommands.cpp similarity index 99% rename from TEST_APPS/device/nfcapp/nfccommands.cpp rename to TEST_APPS/device/nfcapp/NFCCommands.cpp index c5a404e688..3a77671f7f 100644 --- a/TEST_APPS/device/nfcapp/nfccommands.cpp +++ b/TEST_APPS/device/nfcapp/NFCCommands.cpp @@ -21,12 +21,12 @@ #include "mbed_events.h" #include "mbed-client-cli/ns_cmdline.h" #include "rtos\Thread.h" -#include "nfcTestShim.h" -#include "nfcCommands.h" +#include "NFCTestShim.h" +#include "NFCCommands.h" #if MBED_CONF_NFCEEPROM #include "NFCEEPROMDriver.h" -#include "nfcProcessEeprom.h" +#include "NFCProcessEEPROM.h" #endif using mbed::nfc::nfc_rf_protocols_bitmask_t; diff --git a/TEST_APPS/device/nfcapp/nfccommands.h b/TEST_APPS/device/nfcapp/NFCCommands.h similarity index 98% rename from TEST_APPS/device/nfcapp/nfccommands.h rename to TEST_APPS/device/nfcapp/NFCCommands.h index 751ad0d156..ffa9746f3e 100644 --- a/TEST_APPS/device/nfcapp/nfccommands.h +++ b/TEST_APPS/device/nfcapp/NFCCommands.h @@ -18,9 +18,9 @@ #define _NFCCOMMANDS_H_INCLUDED #if MBED_CONF_NFCEEPROM -#include "nfcProcessEeprom.h" +#include "NFCProcessEEPROM.h" #else -#include "nfcProcessCtrl.h" +#include "NFCProcessCtrl.h" #endif // see https://support.microsoft.com/en-my/help/208427/maximum-url-length-is-2-083-characters-in-internet-explorer diff --git a/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp b/TEST_APPS/device/nfcapp/NFCProcessCtrl.cpp similarity index 99% rename from TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp rename to TEST_APPS/device/nfcapp/NFCProcessCtrl.cpp index 42f38f6f38..30fabb1036 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessCtrl.cpp +++ b/TEST_APPS/device/nfcapp/NFCProcessCtrl.cpp @@ -29,8 +29,8 @@ #include "nfc/ndef/common/util.h" -#include "nfcCommands.h" -#include "nfcProcessCtrl.h" +#include "NFCCommands.h" +#include "NFCProcessCtrl.h" #include "SmartPoster.h" using mbed::Span; diff --git a/TEST_APPS/device/nfcapp/nfcprocessCtrl.h b/TEST_APPS/device/nfcapp/NFCProcessCtrl.h similarity index 99% rename from TEST_APPS/device/nfcapp/nfcprocessCtrl.h rename to TEST_APPS/device/nfcapp/NFCProcessCtrl.h index 77cd3ca483..2f610bc08c 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessCtrl.h +++ b/TEST_APPS/device/nfcapp/NFCProcessCtrl.h @@ -25,7 +25,7 @@ #include "nfc/ndef/MessageBuilder.h" #include "nfc/ndef/common/URI.h" #include "nfc/ndef/common/util.h" -#include "nfcTestShim.h" +#include "NFCTestShim.h" #if !MBED_CONF_NFCEEPROM diff --git a/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp b/TEST_APPS/device/nfcapp/NFCProcessEEPROM.cpp similarity index 99% rename from TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp rename to TEST_APPS/device/nfcapp/NFCProcessEEPROM.cpp index 717ca4643f..f475ccc65c 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessEeprom.cpp +++ b/TEST_APPS/device/nfcapp/NFCProcessEEPROM.cpp @@ -30,7 +30,7 @@ #if MBED_CONF_NFCEEPROM #include "NFCEEPROMDriver.h" -#include "nfcProcessEeprom.h" +#include "NFCProcessEEPROM.h" #include "SmartPoster.h" using mbed::Span; diff --git a/TEST_APPS/device/nfcapp/nfcprocessEeprom.h b/TEST_APPS/device/nfcapp/NFCProcessEEPROM.h similarity index 98% rename from TEST_APPS/device/nfcapp/nfcprocessEeprom.h rename to TEST_APPS/device/nfcapp/NFCProcessEEPROM.h index 98e55c60c7..c56fec7248 100644 --- a/TEST_APPS/device/nfcapp/nfcprocessEeprom.h +++ b/TEST_APPS/device/nfcapp/NFCProcessEEPROM.h @@ -25,7 +25,7 @@ #include "nfc/ndef/MessageBuilder.h" #include "nfc/ndef/common/URI.h" #include "nfc/ndef/common/util.h" -#include "nfcTestShim.h" +#include "NFCTestShim.h" #if MBED_CONF_NFCEEPROM #include "NFCEEPROM.h" diff --git a/TEST_APPS/device/nfcapp/nfctestshim.cpp b/TEST_APPS/device/nfcapp/NFCTestShim.cpp similarity index 99% rename from TEST_APPS/device/nfcapp/nfctestshim.cpp rename to TEST_APPS/device/nfcapp/NFCTestShim.cpp index 6136e84b17..beb8e32c3f 100644 --- a/TEST_APPS/device/nfcapp/nfctestshim.cpp +++ b/TEST_APPS/device/nfcapp/NFCTestShim.cpp @@ -27,8 +27,8 @@ #include "nfc/nfcdefinitions.h" #include "NFCEEPROMDriver.h" -#include "nfcCommands.h" -#include "nfcTestShim.h" +#include "NFCCommands.h" +#include "NFCTestShim.h" #include "SmartPoster.h" using mbed::Span; diff --git a/TEST_APPS/device/nfcapp/nfctestshim.h b/TEST_APPS/device/nfcapp/NFCTestShim.h similarity index 100% rename from TEST_APPS/device/nfcapp/nfctestshim.h rename to TEST_APPS/device/nfcapp/NFCTestShim.h diff --git a/TEST_APPS/device/nfcapp/main.cpp b/TEST_APPS/device/nfcapp/main.cpp index ce2f3fdb49..72f6ead2d5 100644 --- a/TEST_APPS/device/nfcapp/main.cpp +++ b/TEST_APPS/device/nfcapp/main.cpp @@ -22,8 +22,8 @@ #include "platform/Span.h" #include "mbed_events.h" #include "mbed-client-cli/ns_cmdline.h" -#include "nfcTestShim.h" -#include "nfcCommands.h" +#include "NFCTestShim.h" +#include "NFCCommands.h" #if MBED_CONF_NFCEEPROM using mbed::nfc::NFCEEPROM; From 4965d3c8a7f4cae2c06b1c9f29666ac5bacdd238 Mon Sep 17 00:00:00 2001 From: Conrad Braam Date: Wed, 6 Feb 2019 11:40:02 +0000 Subject: [PATCH 13/14] address astyle issues in the last buffer refactoring change --- TEST_APPS/device/nfcapp/NFCTestShim.cpp | 11 +++++++---- TEST_APPS/device/nfcapp/NFCTestShim.h | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/TEST_APPS/device/nfcapp/NFCTestShim.cpp b/TEST_APPS/device/nfcapp/NFCTestShim.cpp index beb8e32c3f..c62482f210 100644 --- a/TEST_APPS/device/nfcapp/NFCTestShim.cpp +++ b/TEST_APPS/device/nfcapp/NFCTestShim.cpp @@ -314,19 +314,22 @@ void NFCTestShim::cmd_set_smartposter(char *cmdUri) MessageBuilder builder(_ndef_poster_message); struct SPBuilder: MessageBuilder::PayloadBuilder { - SPBuilder(char * cmd_uri) { + SPBuilder(char *cmd_uri) + { URI::uri_identifier_code_t uri_id = get_ndef_record_type(cmd_uri); char *urlbegin = cmd_uri - + strlen(get_ndef_record_type_prefix(uri_id)); + + strlen(get_ndef_record_type_prefix(uri_id)); uri = URI(uri_id, span_from_cstr(urlbegin)); cmd_printf("{{uri_id=%d}}\r\n", (int) uri_id); } - virtual size_t size() const { + virtual size_t size() const + { return uri.get_record_size(); } - virtual void build(const Span &buffer) const { + virtual void build(const Span &buffer) const + { MessageBuilder smart_poster_builder(buffer); uri.append_as_record(smart_poster_builder, true); diff --git a/TEST_APPS/device/nfcapp/NFCTestShim.h b/TEST_APPS/device/nfcapp/NFCTestShim.h index b6d463acdb..6aba9b820b 100644 --- a/TEST_APPS/device/nfcapp/NFCTestShim.h +++ b/TEST_APPS/device/nfcapp/NFCTestShim.h @@ -74,7 +74,7 @@ protected: virtual mbed::nfc::nfc_rf_protocols_bitmask_t get_rf_protocols() { mbed::nfc::nfc_rf_protocols_bitmask_t none; - memset((void*)&none, 0, sizeof(none)); + memset((void *)&none, 0, sizeof(none)); return none; }; virtual nfc_err_t start_discovery() From 0ca6ee8bce974b18ef87a4e1a019ef8312515e2c Mon Sep 17 00:00:00 2001 From: Conrad Braam Date: Thu, 7 Feb 2019 15:42:11 +0000 Subject: [PATCH 14/14] undo gitignore, fix \slash --- .gitignore | 49 ------------------------- TEST_APPS/device/nfcapp/NFCCommands.cpp | 2 +- 2 files changed, 1 insertion(+), 50 deletions(-) diff --git a/.gitignore b/.gitignore index 75a3ee6fee..e89472c010 100644 --- a/.gitignore +++ b/.gitignore @@ -97,52 +97,3 @@ test_suite.json # default delivery dir DELIVERY/ -cmake-build-debug/.mbedignore -cmake-build-debug/cmake_install.cmake -cmake-build-debug/CMakeCache.txt -cmake-build-debug/CMakeFiles/3.13.2/CMakeASMCompiler.cmake -cmake-build-debug/CMakeFiles/3.13.2/CMakeCCompiler.cmake -cmake-build-debug/CMakeFiles/3.13.2/CMakeCXXCompiler.cmake -cmake-build-debug/CMakeFiles/3.13.2/CMakeSystem.cmake -cmake-build-debug/CMakeFiles/3.13.2/CompilerIdC/CMakeCCompilerId.c -cmake-build-debug/CMakeFiles/3.13.2/CompilerIdC/CMakeCCompilerId.o -cmake-build-debug/CMakeFiles/3.13.2/CompilerIdCXX/CMakeCXXCompilerId.cpp -cmake-build-debug/CMakeFiles/3.13.2/CompilerIdCXX/CMakeCXXCompilerId.o -cmake-build-debug/CMakeFiles/clion-environment.txt -cmake-build-debug/CMakeFiles/clion-log.txt -cmake-build-debug/CMakeFiles/cmake.check_cache -cmake-build-debug/CMakeFiles/CMakeDirectoryInformation.cmake -cmake-build-debug/CMakeFiles/CMakeError.log -cmake-build-debug/CMakeFiles/CMakeOutput.log -cmake-build-debug/CMakeFiles/CMakeRuleHashes.txt -cmake-build-debug/CMakeFiles/feature_tests.c -cmake-build-debug/CMakeFiles/feature_tests.cxx -cmake-build-debug/CMakeFiles/Makefile.cmake -cmake-build-debug/CMakeFiles/Makefile2 -cmake-build-debug/CMakeFiles/mbed-cli-build.dir/build.make -cmake-build-debug/CMakeFiles/mbed-cli-build.dir/cmake_clean.cmake -cmake-build-debug/CMakeFiles/mbed-cli-build.dir/DependInfo.cmake -cmake-build-debug/CMakeFiles/mbed-cli-build.dir/progress.make -cmake-build-debug/CMakeFiles/mbed-os.dir/build.make -cmake-build-debug/CMakeFiles/mbed-os.dir/cmake_clean.cmake -cmake-build-debug/CMakeFiles/mbed-os.dir/depend.make -cmake-build-debug/CMakeFiles/mbed-os.dir/DependInfo.cmake -cmake-build-debug/CMakeFiles/mbed-os.dir/flags.make -cmake-build-debug/CMakeFiles/mbed-os.dir/link.txt -cmake-build-debug/CMakeFiles/mbed-os.dir/objects1.rsp -cmake-build-debug/CMakeFiles/mbed-os.dir/progress.make -cmake-build-debug/CMakeFiles/progress.marks -cmake-build-debug/CMakeFiles/TargetDirectories.txt -cmake-build-debug/compiler-file3040531024441447850.d -cmake-build-debug/compiler-file4529788487702700900.d -cmake-build-debug/compiler-file4727057748131971377.d -cmake-build-debug/compiler-file8813331766360922248.d -cmake-build-debug/main.d -cmake-build-debug/Makefile -cmake-build-debug/mbed-os.cbp -cmake-build-debug/nfccommands.d -cmake-build-debug/NFCController.d -cmake-build-debug/nfcprocess.d -cmake-build-debug/nfcprocessCtrl.d -cmake-build-debug/nfctestshim.d -cmake-build-debug/uart.d diff --git a/TEST_APPS/device/nfcapp/NFCCommands.cpp b/TEST_APPS/device/nfcapp/NFCCommands.cpp index 3a77671f7f..d9c6a609f1 100644 --- a/TEST_APPS/device/nfcapp/NFCCommands.cpp +++ b/TEST_APPS/device/nfcapp/NFCCommands.cpp @@ -20,7 +20,7 @@ #include #include "mbed_events.h" #include "mbed-client-cli/ns_cmdline.h" -#include "rtos\Thread.h" +#include "rtos/Thread.h" #include "NFCTestShim.h" #include "NFCCommands.h"