From d4c29207cc1af194e27c1b0960ef80bd665058ce Mon Sep 17 00:00:00 2001 From: Donatien Garnier Date: Wed, 15 Aug 2018 15:35:55 +0100 Subject: [PATCH] Type 4 Target and dependencies implementation --- features/nfc/nfc/ISO7816App.h | 21 +- features/nfc/nfc/NFCController.h | 3 + features/nfc/nfc/NFCNDEFCapable.h | 18 +- features/nfc/nfc/NFCRemoteEndpoint.h | 49 ++- features/nfc/nfc/NFCRemoteInitiator.h | 22 +- features/nfc/nfc/Type4RemoteInitiator.h | 35 +- features/nfc/source/nfc/NFCController.cpp | 5 + features/nfc/source/nfc/NFCNDEFCapable.cpp | 26 +- features/nfc/source/nfc/NFCRemoteEndpoint.cpp | 55 +++ .../nfc/source/nfc/NFCRemoteInitiator.cpp | 57 ++++ .../nfc/source/nfc/Type4RemoteInitiator.cpp | 109 ++++++ features/nfc/stack/ndef/ndef.c | 64 ++++ features/nfc/stack/ndef/ndef.h | 99 ++++++ features/nfc/stack/tech/type4/type4_target.c | 312 ++++++++++++++++++ features/nfc/stack/tech/type4/type4_target.h | 63 ++++ 15 files changed, 906 insertions(+), 32 deletions(-) create mode 100644 features/nfc/source/nfc/NFCRemoteEndpoint.cpp create mode 100644 features/nfc/source/nfc/NFCRemoteInitiator.cpp create mode 100644 features/nfc/source/nfc/Type4RemoteInitiator.cpp create mode 100644 features/nfc/stack/ndef/ndef.c create mode 100644 features/nfc/stack/ndef/ndef.h create mode 100644 features/nfc/stack/tech/type4/type4_target.c create mode 100644 features/nfc/stack/tech/type4/type4_target.h diff --git a/features/nfc/nfc/ISO7816App.h b/features/nfc/nfc/ISO7816App.h index 3f5c9695cc..949106d408 100644 --- a/features/nfc/nfc/ISO7816App.h +++ b/features/nfc/nfc/ISO7816App.h @@ -29,6 +29,8 @@ namespace nfc { * @addtogroup nfc * @{ */ + + class Type4RemoteInitiator; /** * This base class represents an ISO7816-4 application. @@ -85,27 +87,34 @@ namespace nfc { private: CAPDU _command; RAPDU _response; - ISO7816App* _app; + ISO7816App* _iso7816_app; }; + /** + * Construct ISO7816 app instance + */ + ISO7816App(); + private: + friend class Type4RemoteInitiator; + /** * Retrieve the application's identifier (AID). * AIDs are composed of a RID (Registered Application Provider Identifier) that needs to be registered and a custom suffix. * * @return a pointer to a const buffer containing the application's identifier (AID). */ - virtual const ac_buffer_t* get_aid() const; + virtual const ac_buffer_t* get_aid() const = 0; /** * Called when the application is selected and before any exchange is performed. */ - virtual void on_selected(); + virtual void on_selected() = 0; /** * Called when the application is deselected (or link is lost). */ - virtual void on_deselected(); + virtual void on_deselected() = 0; /** * Called when an exchange is performed. @@ -113,7 +122,9 @@ namespace nfc { * * @param[in] exchange an instance of the Exchange class populated with the C-APDU which was received */ - virtual void on_exchange(Exchange* exchange); + virtual void on_exchange(Exchange* exchange) = 0; + + nfc_tech_iso7816_app_t iso7816_app; }; /** diff --git a/features/nfc/nfc/NFCController.h b/features/nfc/nfc/NFCController.h index 529fbd94f2..ba712a3149 100644 --- a/features/nfc/nfc/NFCController.h +++ b/features/nfc/nfc/NFCController.h @@ -138,6 +138,9 @@ namespace nfc { nfc_err_t cancel_discovery(); private: + friend class NFCRemoteEndpoint; + friend class NFCRemoteInitiator; + nfc_transceiver_t* transceiver() const; void polling_callback(nfc_err_t ret); // Callbacks from NFC stack diff --git a/features/nfc/nfc/NFCNDEFCapable.h b/features/nfc/nfc/NFCNDEFCapable.h index e3804b5309..02bf6642de 100644 --- a/features/nfc/nfc/NFCNDEFCapable.h +++ b/features/nfc/nfc/NFCNDEFCapable.h @@ -36,8 +36,10 @@ namespace nfc { public: /** * Construct a NFCNDEFCapable instance. + * @param[in] buffer a bytes array used to store NDEF messages + * @param[in] buffer_size the array size in bytes */ - NFCNDEFCapable(); + NFCNDEFCapable(uint8_t* buffer, size_t buffer_size); /** * Check if this instance actually supports NDEF content. @@ -88,8 +90,22 @@ namespace nfc { */ void build_ndef_message(ac_buffer_builder_t& buffer_builder); + /** + * Retrieve underlying NDEF message instance + * @return pointer to NDEF message instance + */ + ndef_msg_t* ndef_message(); + private: + // Callbacks from NDEF stack + static nfc_err_t s_ndef_encode(ndef_msg_t* pTag, buffer_builder_t* pBufferBldr, void* pUserData); + static nfc_err_t s_ndef_decode(ndef_msg_t* pTag, buffer_t* pBuffer, void* pUserData); + nfc_err_t ndef_encode(buffer_builder_t* pBufferBldr); + nfc_err_t ndef_decode(buffer_t* pBuffer); + + Delegate* _delegate; + ndef_msg_t _ndef_message; }; /** diff --git a/features/nfc/nfc/NFCRemoteEndpoint.h b/features/nfc/nfc/NFCRemoteEndpoint.h index 5b900a250b..3888405132 100644 --- a/features/nfc/nfc/NFCRemoteEndpoint.h +++ b/features/nfc/nfc/NFCRemoteEndpoint.h @@ -28,6 +28,8 @@ namespace nfc { * @addtogroup nfc * @{ */ + + class NFCController; /** * This is the base class for all remote endpoints (initiators and targets) @@ -35,28 +37,65 @@ namespace nfc { */ class NFCRemoteEndpoint { public: + NFCRemoteEndpoint(); /** * The NFCRemoteEndpoint base delegate. */ struct Delegate { + /** + * This method is called when the endpoint is connected + */ + virtual void on_connected() {}; + /** * This method is called when the endpoint is lost (air interface link disconnnected) */ - virtual void on_lost() {}; + virtual void on_disconnected() {}; }; /** - * Check if the endpoint is lost. - * @return whether the endpoint is lost + * Connect the remote endpoint + * + * @return NFC_OK or an error code + */ + virtual nfc_err_t connect() = 0; + + /** + * Disconnect the remote endpoint + * + * @return NFC_OK or an error code + */ + virtual nfc_err_t disconnect() = 0; + + /** + * Check if the endpoint is connected. + * @return whether the endpoint is connected */ - bool is_lost() const; + virtual bool is_connected() const = 0; + + /** + * Check if the endpoint is disconnected/lost. + * @return whether the endpoint has been disconnected + */ + virtual bool is_disconnected() const = 0; /** * Get the list of RF protocols supported and activated over the air interface. * @return a bitmask of activated protocols */ - nfc_rf_protocols_bitmask_t rf_protocols() const; + virtual nfc_rf_protocols_bitmask_t rf_protocols() = 0; + + protected: + /** + * Mark endpoint as connected + */ + void connected(); + + /** + * Mark endpoint as disconnected + */ + void disconnected(); }; /** diff --git a/features/nfc/nfc/NFCRemoteInitiator.h b/features/nfc/nfc/NFCRemoteInitiator.h index 64dec674c9..67c4208dd3 100644 --- a/features/nfc/nfc/NFCRemoteInitiator.h +++ b/features/nfc/nfc/NFCRemoteInitiator.h @@ -30,6 +30,8 @@ namespace nfc { * @addtogroup nfc * @{ */ + + class NFCController; /** * This class represents a remote NFC initiator (the local controller being in target mode). @@ -40,6 +42,7 @@ namespace nfc { public: /** * Create a NFCRemoteInitiator. + * */ NFCRemoteInitiator(); virtual ~NFCRemoteInitiator(); @@ -48,15 +51,7 @@ namespace nfc { * The NFCRemoteInitiator delegate. Users of the NFCRemoteInitiator class need to implement this delegate's methods to receive events. */ struct Delegate : NFCEndpoint::Delegate, NFCNDEFCapable::Delegate { - /** - * The controller was selected by the initiator. - */ - virtual void on_selected() {} - /** - * The controller was deselected by the initiator. - */ - virtual void on_deselected() {} }; /** @@ -64,28 +59,31 @@ namespace nfc { * * @oaram[in] delegate the delegate instance to use */ - void set_delegate(Delegate* delegate); + void set_remote_initiator_delegate(Delegate* delegate); /** * Retrieve the NFC tag type exposed by the controller to communicate with the initiator. * * @return the relevant NFC tag type */ - nfc_tag_type_t nfc_tag_type() const; + virtual nfc_tag_type_t nfc_tag_type() const = 0; /** * Retrieve whether ISO7816 applications are supported by the underlying technology. * * @return whether ISO7816 applications are supported */ - bool is_iso7816_supported() const; + virtual bool is_iso7816_supported() const = 0; /** * Register an ISO7816 application to be used by the initiator. * * @param[in] application a pointer to an ISO7816App instance */ - void add_iso7816_application(ISO7816App* application); + virtual void add_iso7816_application(ISO7816App* application) = 0; + + private: + Delegate* _delegate; }; /** diff --git a/features/nfc/nfc/Type4RemoteInitiator.h b/features/nfc/nfc/Type4RemoteInitiator.h index ea2664a19b..7c54adeb0f 100644 --- a/features/nfc/nfc/Type4RemoteInitiator.h +++ b/features/nfc/nfc/Type4RemoteInitiator.h @@ -37,18 +37,39 @@ namespace nfc { /** * This class is an implementation of the Type 4 tag application. */ - class Type4RemoteInitiator : public NFCRemoteInitiator, public ISO7816App, public NFCNDEFCapable { + class Type4RemoteInitiator : public NFCRemoteInitiator, public NFCNDEFCapable { private: - Type4RemoteInitiator(nfc_transceiver_t* transceiver); + /** + * Create a Type4RemoteInitiator. + * + * @param[in] controller pointer to the NFCController instance that created this object + */ + Type4RemoteInitiator(NFCController* controller); + + // NFCRemoteEndpoint implementation + virtual nfc_err_t connect(); + virtual nfc_err_t disconnect(); + virtual bool is_connected(); + virtual bool is_disconnected(); + virtual nfc_rf_protocols_bitmask_t rf_protocols(); + + // NFCRemoteInitiator implementation + virtual nfc_tag_type_t nfc_tag_type() const; + virtual bool is_iso7816_supported() const; + virtual void add_iso7816_application(ISO7816App* application); // NFCNDEFCapable implementation virtual bool is_ndef_supported() const; - // ISO7816App implementation - virtual const ac_buffer_t* get_aid() const; - virtual void on_selected(); - virtual void on_deselected(); - virtual void on_exchange(Exchange* exchange); + // Callbacks from NFC stack + void disconnected_callback(bool deselected); + static void s_disconnected_callback(nfc_tech_iso7816_t* pIso7816, bool deselected, void* pUserData); + + NFCController* _controller; + bool _is_connected; + bool _is_disconnected; + nfc_tech_iso7816_t _iso7816; + nfc_tech_type4_target_t _type4; }; /** diff --git a/features/nfc/source/nfc/NFCController.cpp b/features/nfc/source/nfc/NFCController.cpp index d3754684f2..2c03e0b0aa 100644 --- a/features/nfc/source/nfc/NFCController.cpp +++ b/features/nfc/source/nfc/NFCController.cpp @@ -109,6 +109,11 @@ nfc_err_t NFCController::cancel_discovery() transceiver_abort(_transceiver); } +nfc_transceiver_t* NFCController::transceiver() const +{ + return _transceiver; +} + void NFCController::polling_callback(nfc_err_t ret) { // Polling has completed diff --git a/features/nfc/source/nfc/NFCNDEFCapable.cpp b/features/nfc/source/nfc/NFCNDEFCapable.cpp index c396bc24c3..793cae6e54 100644 --- a/features/nfc/source/nfc/NFCNDEFCapable.cpp +++ b/features/nfc/source/nfc/NFCNDEFCapable.cpp @@ -20,12 +20,14 @@ #include "acore/buffer_reader.h" #include "acore/buffer_builder.h" +#include "ndef/ndef.h" + using namespace mbed; using namespace mbed::nfc; -NFCNDEFCapable::NFCNDEFCapable() : _delegate(NULL) +NFCNDEFCapable::NFCNDEFCapable(uint8_t* buffer, size_t buffer_size) : _delegate(NULL) { - + ndef_msg_init(&_ndef_message, s_encode_callback, s_decode_callback); } void NFCNDEFCapable::set_ndef_delegate(NFCNDEFCapable::Delegate* delegate) @@ -49,3 +51,23 @@ void NFCNDEFCapable::build_ndef_message(ac_buffer_builder_t& buffer_builder) ac_buffer_builder_write_n_skip(&buffer_builder, count); } } + +nfc_err_t NFCNDEFCapable::s_ndef_encode(ndef_msg_t* pTag, buffer_builder_t* pBufferBldr, void* pUserData) { + NFCNDEFCapable* self = (NFCNDEFCapable*)pUserData; + self->ndef_encode(pBufferBldr); +} + +nfc_err_t NFCNDEFCapable::s_ndef_decode(ndef_msg_t* pTag, buffer_t* pBuffer, void* pUserData) { + NFCNDEFCapable* self = (NFCNDEFCapable*)pUserData; + self->ndef_decode(pBuffer); +} + +nfc_err_t NFCNDEFCapable::ndef_encode(buffer_builder_t* pBufferBldr) { + build_ndef_message(buffer_builder); + return NFC_OK; +} + +nfc_err_t NFCNDEFCapable::ndef_decode(buffer_t* pBuffer) { + parse_ndef_message(pBuffer); + return NFC_OK; +} diff --git a/features/nfc/source/nfc/NFCRemoteEndpoint.cpp b/features/nfc/source/nfc/NFCRemoteEndpoint.cpp new file mode 100644 index 0000000000..77c2a14727 --- /dev/null +++ b/features/nfc/source/nfc/NFCRemoteEndpoint.cpp @@ -0,0 +1,55 @@ +/* mbed Microcontroller Library + * Copyright (c) 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 "NFCRemoteEndpoint.h" + +#include "acore/buffer.h" +#include "acore/buffer_reader.h" +#include "acore/buffer_builder.h" + +#include "stack/transceiver/transceiver.h" + +using namespace mbed; +using namespace mbed::nfc; + +NFCRemoteEndpoint::NFCRemoteEndpoint(NFCController* controller) : _controller(controller), _is_lost(false) { + +} + +bool NFCRemoteEndpoint::is_lost() const { + return _is_lost; +} + +nfc_rf_protocols_bitmask_t NFCRemoteEndpoint::rf_protocols() { + nfc_rf_protocols_bitmask_t rf_protocols = {0}; + nfc_tech_t active_tech = transceiver_get_active_techs(_transceiver); + if(!transceiver_is_initiator_mode(_transceiver)) + { + // Note: We only support ISO-DEP for now + rf_protocols.target_iso_dep = active_tech.nfc_iso_dep_a || active_tech.nfc_iso_dep_b; + } + + return rf_protocols; +} + +void NFCRemoteEndpoint::set_lost() { + _is_lost = true; +} + +NFCController* NFCRemoteEndpoint::nfc_controller() const { + return _controller; +} + diff --git a/features/nfc/source/nfc/NFCRemoteInitiator.cpp b/features/nfc/source/nfc/NFCRemoteInitiator.cpp new file mode 100644 index 0000000000..a1373b5a04 --- /dev/null +++ b/features/nfc/source/nfc/NFCRemoteInitiator.cpp @@ -0,0 +1,57 @@ +/* mbed Microcontroller Library + * Copyright (c) 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 "NFCRemoteInitiator.h" + +#include "acore/buffer.h" +#include "acore/buffer_reader.h" +#include "acore/buffer_builder.h" + +#include "stack/transceiver/transceiver.h" +#include "stack/tech/iso7816/iso7816.h" +#include "stack/tech/iso7816/iso7816_app.h" + +using namespace mbed; +using namespace mbed::nfc; + +NFCRemoteInitiator::NFCRemoteInitiator(NFCController* controller) : NFCRemoteEndpoint(controller) { + +} + +NFCRemoteInitiator::~NFCRemoteInitiator() { + +} + + +void NFCRemoteInitiator::set_remote_initiator_delegate(Delegate* delegate) +{ + +} + +nfc_tag_type_t NFCRemoteInitiator::nfc_tag_type() const +{ + +} + +bool NFCRemoteInitiator::is_iso7816_supported() const +{ + +} + +void NFCRemoteInitiator::add_iso7816_application(ISO7816App* application) +{ + +} diff --git a/features/nfc/source/nfc/Type4RemoteInitiator.cpp b/features/nfc/source/nfc/Type4RemoteInitiator.cpp new file mode 100644 index 0000000000..560feebfcd --- /dev/null +++ b/features/nfc/source/nfc/Type4RemoteInitiator.cpp @@ -0,0 +1,109 @@ +/* mbed Microcontroller Library + * Copyright (c) 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 "Type4RemoteInitiator.h" + +#include "acore/buffer.h" +#include "acore/buffer_reader.h" +#include "acore/buffer_builder.h" + +#include "stack/transceiver/transceiver.h" +#include "stack/tech/iso7816/iso7816.h" +#include "stack/tech/iso7816/iso7816_app.h" +#include "stack/tech/type4/type4_target.h" + +using namespace mbed; +using namespace mbed::nfc; + +Type4RemoteInitiator::Type4RemoteInitiator(NFCController* controller) : + _controller(controller), _is_connected(false) , _is_disconnected(false), _apps(NULL) { + // Init ISO7816 + nfc_tech_iso7816_init(&_iso7816, _controller->transceiver(), &Type4RemoteInitiator::s_disconnected_callback, this); + + // Init Type 4 app + nfc_tech_type4_target_init(&_type4, &_iso7816, ndef_message()); +} + +nfc_err_t Type4RemoteInitiator::connect() { + if(_is_connected) { + return NFC_BUSY; + } + + if(_is_disconnected) { + return NFC_ERR_DISCONNECTED; + } + + // Connect ISO7816 stack + nfc_tech_iso7816_connect(&_iso7816); + + // Call callback as it's a synchronous API + connected(); +} + +nfc_err_t Type4RemoteInitiator::disconnect() { + if(!_is_connected) { + return NFC_OK; + } + + if(_is_disconnected) { + return NFC_OK; + } + + // Disconnect ISO7816 stack + nfc_tech_iso7816_disconnect(&_iso7816); +} + +bool Type4RemoteInitiator::is_connected() const { + return _is_connected; +} + +bool Type4RemoteInitiator::is_disconnected() const { + return _is_disconnected; +} + +nfc_rf_protocols_bitmask_t Type4RemoteInitiator::rf_protocols() { + nfc_rf_protocols_bitmask_t rf_protocols = {0}; + nfc_tech_t active_tech = transceiver_get_active_techs(_transceiver); + if(!transceiver_is_initiator_mode(_transceiver)) + { + // We only support ISO-DEP + rf_protocols.target_iso_dep = active_tech.nfc_iso_dep_a || active_tech.nfc_iso_dep_b; + } + + return rf_protocols; +} + +virtual nfc_tag_type_t Type4RemoteInitiator::nfc_tag_type() const { + nfc_tech_t active_tech = transceiver_get_active_techs(_transceiver); + if(active_tech.nfc_iso_dep_a) { + return nfc_tag_type_4a; + } + else { // if(active_tech.nfc_iso_dep_b) + return nfc_tag_type_4b; + } +} + +virtual bool Type4RemoteInitiator::is_iso7816_supported() const { + return true; +} + +virtual void Type4RemoteInitiator::add_iso7816_application(ISO7816App* application) { + nfc_tech_iso7816_add_app(&_iso7816, application->_iso7816_app); +} + +virtual bool Type4RemoteInitiator::is_ndef_supported() const { + return true; +} diff --git a/features/nfc/stack/ndef/ndef.c b/features/nfc/stack/ndef/ndef.c new file mode 100644 index 0000000000..4871a42e0a --- /dev/null +++ b/features/nfc/stack/ndef/ndef.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013-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. + */ +/** + * \file ndef.c + * \copyright Copyright (c) ARM Ltd 2013 + * \author Donatien Garnier + * \details NDEF tag abstraction + */ + + +#include "inc/nfc.h" +#include "ndef.h" + +/** \addtogroup NDEF + * @{ + * \name Generic NDEF Tag + * @{ + */ + +/** Initialize NDEF tag abstraction + * \param pNdefTag pointer to ndef_tag_t structure to initialize + * \param encode function that will be called to generate the NDEF message before sending it to the other party + * \param decode function that will be called to parse the NDEF message after receiving it from the other party + * \param buffer underlying buffer to use (it should be big enough so that any NDEF message you might need could be stored inside) + * \param buffer_size size of the underlying buffer + * \param pImpl pointer to actual implementation + */ +void ndef_msg_init( ndef_msg_t* pNdef, ndef_encode_fn_t encode, ndef_decode_fn_t decode, uint8_t* data, size_t size, void* pUserData ) +{ + pNdef->encode = encode; + pNdef->decode = decode; + buffer_builder_init(&pNdef->bufferBldr, data, size); + pNdef->pUserData = pUserData; +} + +/** Get NDEF tag implementation + * \param pNdefTag pointer to ndef_tag_t structure + * \return implementation + */ +/* +void* ndef_tag_impl(ndef_tag_t* pNdefTag) +{ + return pNdefTag->pImpl; +} +*/ + +/** + * @} + * @} + * */ diff --git a/features/nfc/stack/ndef/ndef.h b/features/nfc/stack/ndef/ndef.h new file mode 100644 index 0000000000..f95378ed53 --- /dev/null +++ b/features/nfc/stack/ndef/ndef.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2013-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. + */ +/** + * \file ndef.h + * \copyright Copyright (c) ARM Ltd 2013 + * \author Donatien Garnier + */ + +/** \addtogroup NDEF + * @{ + * \name Generic NDEF Tag + * @{ + */ + +#ifndef NDEF_H_ +#define NDEF_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "inc/nfc.h" + +//Generic interface for NDEF messages + +typedef struct __ndef_msg ndef_msg_t; + +/** Function called to generate the tag's content on read (target mode) + * \param pTag pointer to ndef_tag_t instance + * \param type pMem buffer in which to store the generated content + */ +typedef nfc_err_t (*ndef_encode_fn_t)(ndef_msg_t* pTag, buffer_builder_t* pBufferBldr, void* pUserData); + +/** Function called to decode the tag's content on write (target mode) or read (reader mode) + * \param pTag pointer to ndef_tag_t instance + * \param type pMem buffer containing the tag's content + */ +typedef nfc_err_t (*ndef_decode_fn_t)(ndef_msg_t* pTag, buffer_t* pBuffer, void* pUserData); + +typedef struct __ndef_msg +{ + ndef_encode_fn_t encode; + ndef_decode_fn_t decode; + buffer_builder_t bufferBldr; + void* pUserData; +} ndef_msg_t; + +void ndef_msg_init( ndef_msg_t* pNdef, ndef_encode_fn_t encode, ndef_decode_fn_t decode, uint8_t* data, size_t size, void* pUserData ); + +static inline nfc_err_t ndef_msg_encode(ndef_msg_t* pNdef) +{ + if(pNdef->encode == NULL) + { + return NFC_OK; + } + return pNdef->encode(pNdef, &pNdef->bufferBldr, pNdef->pUserData); +} + +static inline nfc_err_t ndef_msg_decode(ndef_msg_t* pNdef) +{ + if(pNdef->decode == NULL) + { + return NFC_OK; + } + return pNdef->decode(pNdef, buffer_builder_buffer(&pNdef->bufferBldr), pNdef->pUserData); +} + +static inline buffer_builder_t* ndef_msg_buffer_builder(ndef_msg_t* pNdef) +{ + return &pNdef->bufferBldr; +} + +//void* ndef_tag_impl(ndef_tag_t* pNdefTag); + +#ifdef __cplusplus +} +#endif + +#endif /* NDEF_H_ */ + +/** + * @} + * @} + * */ + diff --git a/features/nfc/stack/tech/type4/type4_target.c b/features/nfc/stack/tech/type4/type4_target.c new file mode 100644 index 0000000000..fc04971e89 --- /dev/null +++ b/features/nfc/stack/tech/type4/type4_target.c @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2015-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. + */ +/** + * \file type4_target.c + * \copyright Copyright (c) ARM Ltd 2015 + * \author Donatien Garnier + */ + +#define __DEBUG__ 0 +#ifndef __MODULE__ +#define __MODULE__ "type4_target.c" +#endif + +#include "inc/nfc.h" + +#include "type4_target.h" +#include "tech/iso7816/iso7816_defs.h" + +#define TYPE4_NDEF_VERSION 2 + +#if TYPE4_NDEF_VERSION == 2 +static const uint8_t aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01 }; +#else +static const uint8_t aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x00 }; +#endif +#define CC_FILE 0xE103 //Must not be changed +#define NDEF_FILE 0xA443 +#define DEFAULT_FILE 0x0000 + +static void app_selected( nfc_tech_iso7816_app_t* pIso7816App, void* pUserData ); +static void app_deselected( nfc_tech_iso7816_app_t* pIso7816App, void* pUserData ); +static void app_apdu( nfc_tech_iso7816_app_t* pIso7816App, void* pUserData ); + +static nfc_err_t data_read(nfc_tech_type4_target_t* pType4Target, buffer_t* pBuf, uint16_t file, size_t off, size_t len); +static nfc_err_t data_write(nfc_tech_type4_target_t* pType4Target, buffer_t* pBuf, uint16_t file, size_t off); + +void nfc_tech_type4_target_init(nfc_tech_type4_target_t* pType4Target, nfc_tech_iso7816_t* pIso7816, ndef_msg_t* pNdef) +{ + buffer_builder_init(&pType4Target->ccFileBldr, pType4Target->ccFileBuf, /*sizeof(pType4Target->ccFileBuf)*/15); + + buffer_builder_init(&pType4Target->ndefFileBldr, pType4Target->ndefFileBuf, /*sizeof(pType4Target->ndefFileBuf)*/2); + + pType4Target->selFile = DEFAULT_FILE; + pType4Target->pNdef = pNdef; + pType4Target->written = false; + + nfc_tech_iso7816_app_init(&pType4Target->app, pIso7816, aid, sizeof(aid), app_selected, app_deselected, app_apdu, pType4Target); + + nfc_tech_iso7816_add_app(pIso7816, &pType4Target->app); +} + +void app_selected( nfc_tech_iso7816_app_t* pIso7816App, void* pUserData ) +{ + nfc_tech_type4_target_t* pType4Target = (nfc_tech_type4_target_t*) pUserData; + DBG("Selected"); + + (void) pIso7816App; + + buffer_builder_reset(ndef_msg_buffer_builder(pType4Target->pNdef)); + + //Populate CC file + buffer_builder_reset(&pType4Target->ccFileBldr); + buffer_builder_write_nu16( &pType4Target->ccFileBldr, 15 ); //CC file is 15 bytes long +#if TYPE4_NDEF_VERSION == 2 + buffer_builder_write_nu8( &pType4Target->ccFileBldr, 0x20 ); //NFC Forum Tag Type 4 V2.0 compliant +#else + buffer_builder_write_nu8( &pType4Target->ccFileBldr, 0x10 ); //NFC Forum Tag Type 4 V1.0 compliant +#endif + buffer_builder_write_nu16( &pType4Target->ccFileBldr, 256 /* Max frame size */ - 2 /* SW */ - 3 /* ISO-DEP PFB + DID + NAD */ ); //Max data size that can be read from the tag + buffer_builder_write_nu16( &pType4Target->ccFileBldr, 256 /* Max frame size */ - 6 /* CLA INS P1 P2 LC LE */ - 3 /* ISO-DEP PFB + DID + NAD */ ); //Max data size that can be written to the tag + buffer_builder_write_nu8( &pType4Target->ccFileBldr, 0x04 ); //NDEF File Control TLV - Type + buffer_builder_write_nu8( &pType4Target->ccFileBldr, 6 ); //NDEF File Control TLV - Length + buffer_builder_write_nu16( &pType4Target->ccFileBldr, NDEF_FILE ); //NDEF file id + buffer_builder_write_nu16( &pType4Target->ccFileBldr, 2 /* length header */ + buffer_builder_writeable( ndef_msg_buffer_builder(pType4Target->pNdef) ) ); //Max size of NDEF data + buffer_builder_write_nu8( &pType4Target->ccFileBldr, 0x00 ); //Open read access + buffer_builder_write_nu8( &pType4Target->ccFileBldr, 0x00 ); //Open write access + + //Encode NDEF file + ndef_msg_encode(pType4Target->pNdef); + + //Populate NDEF file + buffer_builder_init(&pType4Target->ndefFileBldr, pType4Target->ndefFileBuf, /*sizeof(pType4Target->ndefFileBuf)*/2); + + buffer_builder_write_nu16( &pType4Target->ndefFileBldr, buffer_reader_readable( buffer_builder_buffer(ndef_msg_buffer_builder(pType4Target->pNdef)) ) ); + + //Pad NDEF file with 0s + while( buffer_builder_writeable( ndef_msg_buffer_builder(pType4Target->pNdef) ) > 0 ) + { + buffer_builder_write_nu8(ndef_msg_buffer_builder(pType4Target->pNdef), 0); + } + + //No file selected + pType4Target->selFile = DEFAULT_FILE; + + pType4Target->written = false; +} + +void app_deselected( nfc_tech_iso7816_app_t* pIso7816App, void* pUserData ) +{ + nfc_tech_type4_target_t* pType4Target = (nfc_tech_type4_target_t*) pUserData; + + (void) pIso7816App; + + //Reset buffers + buffer_builder_reset(&pType4Target->ccFileBldr); + buffer_builder_set_full(&pType4Target->ndefFileBldr); //To read length + buffer_builder_reset(ndef_msg_buffer_builder(pType4Target->pNdef)); + + DBG("Deselected"); + + if(pType4Target->written) + { + DBG("New content has been written"); + //Try to parse NDEF + //Set buffer length based on file header + size_t length = buffer_read_nu16(buffer_builder_buffer(&pType4Target->ndefFileBldr)); + DBG("Length is %lu", length); + if( length < buffer_builder_writeable( ndef_msg_buffer_builder(pType4Target->pNdef) )) + { + buffer_builder_set_write_offset( ndef_msg_buffer_builder(pType4Target->pNdef), length ); + ndef_msg_decode(pType4Target->pNdef); + } + else + { + ERR("Invalid length"); + } + } +} + +void app_apdu( nfc_tech_iso7816_app_t* pIso7816App, void* pUserData ) +{ + nfc_tech_type4_target_t* pType4Target = (nfc_tech_type4_target_t*) pUserData; + + //Reset buffers + buffer_builder_set_full(&pType4Target->ccFileBldr); + buffer_builder_set_full(&pType4Target->ndefFileBldr); + buffer_builder_set_full( ndef_msg_buffer_builder(pType4Target->pNdef) ); //Set offset to 0, size to max + + buffer_set_next(buffer_builder_buffer(&pType4Target->ndefFileBldr), buffer_builder_buffer(ndef_msg_buffer_builder(pType4Target->pNdef))); + + //Recover PDU + nfc_tech_iso7816_c_apdu_t* pCApdu = nfc_tech_iso7816_app_c_apdu(pIso7816App); + nfc_tech_iso7816_r_apdu_t* pRApdu = nfc_tech_iso7816_app_r_apdu(pIso7816App); + + nfc_err_t ret; + switch(pCApdu->ins) + { + case ISO7816_INS_SELECT: + switch (pCApdu->p1) + { + case 0x00: //Selection by ID + case 0x02: //Selection by child ID + if ( buffer_reader_readable(&pCApdu->dataIn) != 2) + { + pRApdu->sw = ISO7816_SW_NOT_FOUND; + break; + } + + uint16_t file = buffer_read_nu16(&pCApdu->dataIn); + if ( file == NDEF_FILE ) + { + pType4Target->selFile = NDEF_FILE; + DBG("NDEF File selected"); + pRApdu->sw = ISO7816_SW_OK; + } + else if ( file == CC_FILE ) + { + pType4Target->selFile = CC_FILE; + DBG("CC File selected"); + pRApdu->sw = ISO7816_SW_OK; + } + else + { + //file = DEFAULT_FILE; + DBG("Could not select file %04X", file); + pRApdu->sw = ISO7816_SW_NOT_FOUND; + } + break; + default: + pRApdu->sw = ISO7816_SW_NOT_FOUND; + break; + } + break; + case 0xB0: //Read binary + DBG("Trying to read %d bytes at offset %d from file %04x", pCApdu->maxRespLength, (pCApdu->p1 << 8) | pCApdu->p2, pType4Target->selFile); + ret = data_read(pType4Target, &pRApdu->dataOut, pType4Target->selFile, (pCApdu->p1 << 8) | pCApdu->p2, pCApdu->maxRespLength); + if (ret == NFC_OK) + { + DBG("Read %d bytes", buffer_reader_readable(&pRApdu->dataOut)); + DBG_BLOCK( buffer_dump(&pRApdu->dataOut); ) + pRApdu->sw = ISO7816_SW_OK; + } + else + { + DBG("Failed with ret code %d", ret); + pRApdu->sw = ISO7816_SW_WRONG_LENGTH; + } + break; + case 0xD6: //Update binary + DBG("Trying to write %d bytes at offset %d to file %04x", buffer_reader_readable(&pCApdu->dataIn), (pCApdu->p1 << 8) | pCApdu->p2, pType4Target->selFile); + ret = data_write(pType4Target, &pCApdu->dataIn, pType4Target->selFile, (pCApdu->p1 << 8) | pCApdu->p2); + if (ret == NFC_OK) + { + DBG("OK"); + pRApdu->sw = ISO7816_SW_OK; + pType4Target->written = true; + } + else + { + DBG("Failed with ret code %d", ret); + pRApdu->sw = ISO7816_SW_WRONG_LENGTH; + } + break; + default: + pRApdu->sw = ISO7816_SW_INVALID_INS; + break; + } + + //Send reply + nfc_tech_iso7816_app_reply(pIso7816App); +} + +nfc_err_t data_read(nfc_tech_type4_target_t* pType4Target, buffer_t* pBuf, uint16_t file, size_t off, size_t len) +{ + buffer_t* pFile; + switch(file) + { + case CC_FILE: + pFile = buffer_builder_buffer(&pType4Target->ccFileBldr); + break; + case NDEF_FILE: + pFile = buffer_builder_buffer(&pType4Target->ndefFileBldr); + break; + default: + return NFC_ERR_NOT_FOUND; + } + + if( off > buffer_reader_readable(pFile) ) + { + return NFC_ERR_LENGTH; + } + + buffer_read_n_skip(pFile, off); + + if( len > buffer_reader_readable(pFile)) + { + len = buffer_reader_readable(pFile); + } + + buffer_split(pBuf, pFile, pFile, len); + + return NFC_OK; +} + +nfc_err_t data_write(nfc_tech_type4_target_t* pType4Target, buffer_t* pBuf, uint16_t file, size_t off) +{ + buffer_t* pFile; + switch(file) + { + case NDEF_FILE: + pFile = buffer_builder_buffer(&pType4Target->ndefFileBldr); + break; + case CC_FILE: //Cannot write to CC file! + default: + return NFC_ERR_NOT_FOUND; + } + + size_t len = buffer_reader_readable(pBuf); + + if( off > buffer_reader_readable(pFile) ) + { + return NFC_ERR_LENGTH; + } + + buffer_read_n_skip(pFile, off); + + if( len > buffer_reader_readable(pFile) ) + { + len = buffer_reader_readable(pFile); + } + + while( len > 0 ) + { + size_t cpy; + buffer_builder_t builder; + buffer_dup(buffer_builder_buffer(&builder), pFile); + buffer_builder_from_buffer(&builder); + cpy = buffer_builder_writeable(&builder); + cpy = MIN(cpy, len); + buffer_builder_copy_n_bytes(&builder, pBuf, cpy); + pFile = buffer_next(pFile); + len -= cpy; + } + + return NFC_OK; +} + + diff --git a/features/nfc/stack/tech/type4/type4_target.h b/features/nfc/stack/tech/type4/type4_target.h new file mode 100644 index 0000000000..ecbe259a9e --- /dev/null +++ b/features/nfc/stack/tech/type4/type4_target.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2015-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. + */ +/** + * \file type4_target.h + * \copyright Copyright (c) ARM Ltd 2015 + * \author Donatien Garnier + */ + +#ifndef TECH_TYPE4_TYPE4_TARGET_H_ +#define TECH_TYPE4_TYPE4_TARGET_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "inc/nfc.h" + +#include "tech/iso7816/iso7816.h" +#include "tech/iso7816/iso7816_app.h" +#include "ndef/ndef.h" + +typedef struct nfc_tech_type4_target nfc_tech_type4_target_t; + +typedef void (*nfc_tech_type4_cb)(nfc_tech_type4_target_t* pType4Target, nfc_err_t ret, void* pUserData); + +struct nfc_tech_type4_target +{ + nfc_tech_iso7816_app_t app; + + ndef_msg_t* pNdef; + + uint8_t ccFileBuf[15]; + buffer_builder_t ccFileBldr; + + uint8_t ndefFileBuf[2]; + buffer_builder_t ndefFileBldr; + + uint16_t selFile; + + bool written; +}; + +void nfc_tech_type4_target_init(nfc_tech_type4_target_t* pType4Target, nfc_tech_iso7816_t* pIso7816, ndef_msg_t* pNdef); + +#ifdef __cplusplus +} +#endif + +#endif /* TECH_TYPE4_TYPE4_TARGET_H_ */