From cd7f518596f238dded71e3ddc3b0546a1febdee4 Mon Sep 17 00:00:00 2001 From: Donatien Garnier Date: Mon, 13 Aug 2018 18:14:54 +0100 Subject: [PATCH] Add NFC Stack --- features/nfc/acore/acore/buffer.h | 122 ++ features/nfc/acore/acore/buffer_builder.h | 380 +++++ features/nfc/acore/acore/buffer_reader.h | 169 ++ features/nfc/acore/acore/debug.h | 41 + features/nfc/acore/acore/macros.h | 28 + features/nfc/acore/acore/stream.h | 73 + features/nfc/acore/source/buffer.c | 105 ++ features/nfc/acore/source/buffer_builder.c | 119 ++ features/nfc/acore/source/buffer_reader.c | 183 +++ features/nfc/acore/source/stream.c | 50 + features/nfc/stack/nfc_errors.h | 66 + features/nfc/stack/platform/nfc_debug.h | 62 + features/nfc/stack/platform/nfc_scheduler.c | 208 +++ features/nfc/stack/platform/nfc_scheduler.h | 124 ++ features/nfc/stack/platform/nfc_transport.c | 53 + features/nfc/stack/platform/nfc_transport.h | 84 + features/nfc/stack/tech/iso7816/iso7816.c | 366 +++++ features/nfc/stack/tech/iso7816/iso7816.h | 114 ++ features/nfc/stack/tech/iso7816/iso7816_app.c | 42 + features/nfc/stack/tech/iso7816/iso7816_app.h | 81 + .../nfc/stack/tech/iso7816/iso7816_defs.h | 42 + features/nfc/stack/tech/isodep/isodep.h | 48 + .../nfc/stack/tech/isodep/isodep_target.c | 750 +++++++++ .../nfc/stack/tech/isodep/isodep_target.h | 119 ++ features/nfc/stack/transceiver/pn512/pn512.c | 377 +++++ features/nfc/stack/transceiver/pn512/pn512.h | 167 ++ .../stack/transceiver/pn512/pn512_callback.h | 37 + .../nfc/stack/transceiver/pn512/pn512_cmd.c | 149 ++ .../nfc/stack/transceiver/pn512/pn512_cmd.h | 78 + .../nfc/stack/transceiver/pn512/pn512_hw.c | 54 + .../nfc/stack/transceiver/pn512/pn512_hw.h | 97 ++ .../stack/transceiver/pn512/pn512_internal.h | 82 + .../nfc/stack/transceiver/pn512/pn512_irq.c | 50 + .../nfc/stack/transceiver/pn512/pn512_irq.h | 109 ++ .../nfc/stack/transceiver/pn512/pn512_poll.c | 1409 +++++++++++++++++ .../nfc/stack/transceiver/pn512/pn512_poll.h | 41 + .../stack/transceiver/pn512/pn512_registers.c | 157 ++ .../stack/transceiver/pn512/pn512_registers.h | 114 ++ .../nfc/stack/transceiver/pn512/pn512_rf.c | 343 ++++ .../nfc/stack/transceiver/pn512/pn512_rf.h | 56 + .../nfc/stack/transceiver/pn512/pn512_timer.c | 77 + .../nfc/stack/transceiver/pn512/pn512_timer.h | 43 + .../transceiver/pn512/pn512_transceive.c | 495 ++++++ .../transceiver/pn512/pn512_transceive.h | 45 + .../nfc/stack/transceiver/pn512/pn512_types.h | 39 + features/nfc/stack/transceiver/protocols.h | 89 ++ features/nfc/stack/transceiver/transceiver.c | 47 + features/nfc/stack/transceiver/transceiver.h | 280 ++++ .../stack/transceiver/transceiver_internal.h | 41 + 49 files changed, 7905 insertions(+) create mode 100644 features/nfc/acore/acore/buffer.h create mode 100644 features/nfc/acore/acore/buffer_builder.h create mode 100644 features/nfc/acore/acore/buffer_reader.h create mode 100644 features/nfc/acore/acore/debug.h create mode 100644 features/nfc/acore/acore/macros.h create mode 100644 features/nfc/acore/acore/stream.h create mode 100644 features/nfc/acore/source/buffer.c create mode 100644 features/nfc/acore/source/buffer_builder.c create mode 100644 features/nfc/acore/source/buffer_reader.c create mode 100644 features/nfc/acore/source/stream.c create mode 100644 features/nfc/stack/nfc_errors.h create mode 100644 features/nfc/stack/platform/nfc_debug.h create mode 100644 features/nfc/stack/platform/nfc_scheduler.c create mode 100644 features/nfc/stack/platform/nfc_scheduler.h create mode 100644 features/nfc/stack/platform/nfc_transport.c create mode 100644 features/nfc/stack/platform/nfc_transport.h create mode 100644 features/nfc/stack/tech/iso7816/iso7816.c create mode 100644 features/nfc/stack/tech/iso7816/iso7816.h create mode 100644 features/nfc/stack/tech/iso7816/iso7816_app.c create mode 100644 features/nfc/stack/tech/iso7816/iso7816_app.h create mode 100644 features/nfc/stack/tech/iso7816/iso7816_defs.h create mode 100644 features/nfc/stack/tech/isodep/isodep.h create mode 100644 features/nfc/stack/tech/isodep/isodep_target.c create mode 100644 features/nfc/stack/tech/isodep/isodep_target.h create mode 100644 features/nfc/stack/transceiver/pn512/pn512.c create mode 100644 features/nfc/stack/transceiver/pn512/pn512.h create mode 100644 features/nfc/stack/transceiver/pn512/pn512_callback.h create mode 100644 features/nfc/stack/transceiver/pn512/pn512_cmd.c create mode 100644 features/nfc/stack/transceiver/pn512/pn512_cmd.h create mode 100644 features/nfc/stack/transceiver/pn512/pn512_hw.c create mode 100644 features/nfc/stack/transceiver/pn512/pn512_hw.h create mode 100644 features/nfc/stack/transceiver/pn512/pn512_internal.h create mode 100644 features/nfc/stack/transceiver/pn512/pn512_irq.c create mode 100644 features/nfc/stack/transceiver/pn512/pn512_irq.h create mode 100644 features/nfc/stack/transceiver/pn512/pn512_poll.c create mode 100644 features/nfc/stack/transceiver/pn512/pn512_poll.h create mode 100644 features/nfc/stack/transceiver/pn512/pn512_registers.c create mode 100644 features/nfc/stack/transceiver/pn512/pn512_registers.h create mode 100644 features/nfc/stack/transceiver/pn512/pn512_rf.c create mode 100644 features/nfc/stack/transceiver/pn512/pn512_rf.h create mode 100644 features/nfc/stack/transceiver/pn512/pn512_timer.c create mode 100644 features/nfc/stack/transceiver/pn512/pn512_timer.h create mode 100644 features/nfc/stack/transceiver/pn512/pn512_transceive.c create mode 100644 features/nfc/stack/transceiver/pn512/pn512_transceive.h create mode 100644 features/nfc/stack/transceiver/pn512/pn512_types.h create mode 100644 features/nfc/stack/transceiver/protocols.h create mode 100644 features/nfc/stack/transceiver/transceiver.c create mode 100644 features/nfc/stack/transceiver/transceiver.h create mode 100644 features/nfc/stack/transceiver/transceiver_internal.h diff --git a/features/nfc/acore/acore/buffer.h b/features/nfc/acore/acore/buffer.h new file mode 100644 index 0000000000..4e746eeba9 --- /dev/null +++ b/features/nfc/acore/acore/buffer.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2017, 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 buffer.h + * \copyright Copyright (c) ARM Ltd 2013 + * \author Donatien Garnier + */ + +/** \addtogroup ACore + * @{ + * \name Buffer + * @{ + */ + +#ifndef ACORE_BUFFER_H_ +#define ACORE_BUFFER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "stdint.h" +#include "stddef.h" +#include "stdbool.h" + +typedef struct __ac_buffer +{ + const uint8_t* data; + size_t size; + struct __ac_buffer* pNext; +} ac_buffer_t; + +/** Initialize ac_buffer using underlying byte array, set ac_buffer's length to 0 (empty) + * \param pBuf pointer to ac_buffer_t structure to initialize + * \param data byte array to use + * \param size size of byte array + */ +void ac_buffer_init(ac_buffer_t* pBuf, const uint8_t* data, size_t size); + +/** Copy pBufIn to pBuf + * \param pBuf pointer to ac_buffer_t structure to initialize + * \param pBufIn the source buffer + */ +void ac_buffer_dup(ac_buffer_t* pBuf, const ac_buffer_t* pBufIn); + +/** Get buffer's underlying byte array + * \param pBuf pointer to ac_buffer_t structure + * \return underlying array + */ +static inline const uint8_t* ac_buffer_data(const ac_buffer_t* pBuf) +{ + return pBuf->data; +} + +/** Get buffer's size + * \param pBuf pointer to ac_buffer_t structure + * \return buffer's size + */ +static inline size_t ac_buffer_size(const ac_buffer_t* pBuf) +{ + return pBuf->size; +} + +/** Get next buffer in chain + * \param pBuf pointer to ac_buffer_t structure + * \return pointer to next buffer + */ +static inline ac_buffer_t* ac_buffer_next(const ac_buffer_t* pBuf) +{ + return pBuf->pNext; +} + +/** Set next buffer in chain + * \param pBuf pointer to ac_buffer_t structure + * \param pNextBuf pointer to next buffer (or NULL to break chain) + */ +static inline void ac_buffer_set_next(ac_buffer_t* pBuf, ac_buffer_t* pNextBuf) +{ + pBuf->pNext = (ac_buffer_t*) pNextBuf; +} + +/** Append buffer to end of chain + * \param pBuf pointer to ac_buffer_t structure + * \param pAppBuf pointer to buffer to append to chain + */ +void ac_buffer_append(ac_buffer_t* pBuf, ac_buffer_t* pAppBuf); + +/** Truncate pBuf to length bytes and save the remaining bytes in pEndBuf + * \param pBuf The buffer to split (will be set to invalid state) + * \param pStartBuf A new buffer at the head of the split + * \param pEndBuf A new buffer at the tail of the split + * \param length How long pStartBuf should be (if longer than pBuf, then pStartBuf will be pBuf) + */ +void ac_buffer_split(ac_buffer_t* pStartBuf, ac_buffer_t* pEndBuf, ac_buffer_t* pBuf, size_t length); + +//Debug +void ac_buffer_dump(ac_buffer_t* pBuf); + +#ifdef __cplusplus +} +#endif + +#endif /* ACORE_BUFFER_H_ */ + +/** + * @} + * @} + * */ diff --git a/features/nfc/acore/acore/buffer_builder.h b/features/nfc/acore/acore/buffer_builder.h new file mode 100644 index 0000000000..3549bccc90 --- /dev/null +++ b/features/nfc/acore/acore/buffer_builder.h @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2017, 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 buffer_builder.h + * \copyright Copyright (c) ARM Ltd 2017 + * \author Donatien Garnier + */ + +/** \addtogroup ACore + * @{ + * \name Buffer Builder + * @{ + */ + +#ifndef ACORE_BUFFER_BUILDER_H_ +#define ACORE_BUFFER_BUILDER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "stdint.h" +#include "stddef.h" +#include "stdbool.h" + +#include "acore/buffer.h" + +typedef struct __ac_buffer_builder +{ + ac_buffer_t ac_buffer; + uint8_t* data; + size_t size; +} ac_buffer_builder_t; + +/** Write data to big endian ac_buffer (on a LE architecture, byte order will be swapped) + * \param pBuilder ac_buffer builder to use + * \param buf pointer to data + * \param size the data size + */ +void ac_buffer_builder_write_be(ac_buffer_builder_t* pBuilder, const uint8_t* buf, size_t size); + +/** Write data to little endian ac_buffer (on a LE architecture, byte order will be preserved) + * \param pBuilder ac_buffer builder to use + * \param buf pointer to data + * \param size the data size + */ +void ac_buffer_builder_write_le(ac_buffer_builder_t* pBuilder, const uint8_t* buf, size_t size); + +/** Write data to big endian ac_buffer at specific position (on a LE architecture, byte order will be swapped) + * \param pBuilder ac_buffer builder to use + * \param pos position in ac_buffer to write from + * \param buf pointer to data + * \param size the data size + */ +void ac_buffer_builder_write_be_at(ac_buffer_builder_t* pBuilder, size_t pos, const uint8_t* buf, size_t size); + +/** Write data to little endian ac_buffer at specific position (on a LE architecture, byte order will be preserved) + * \param pBuilder ac_buffer builder to use + * \param pos position in ac_buffer to write from + * \param buf pointer to data + * \param size the data size + */ +void ac_buffer_builder_write_le_at(ac_buffer_builder_t* pBuilder, size_t pos, const uint8_t* buf, size_t size); + +/** Initialize ac_buffer builder + * \param pBuilder ac_buffer builder to init + * \param data pointer to byte array to use + * \param size of byte array + */ +void ac_buffer_builder_init(ac_buffer_builder_t* pBuilder, uint8_t* data, size_t size); + +/** Initialize ac_buffer builder from underlying ac_buffer + * \param pBuilder ac_buffer builder to init + */ +void ac_buffer_builder_from_ac_buffer(ac_buffer_builder_t* pBuilder); + +/** Reset ac_buffer builder + * \param pBuilder ac_buffer builder to reset + */ +void ac_buffer_builder_reset(ac_buffer_builder_t* pBuilder); + +/** Set ac_buffer builder's ac_buffer to full size + * \param pBuilder ac_buffer builder to set to full size + */ +void ac_buffer_builder_set_full(ac_buffer_builder_t* pBuilder); + +/** Get ac_buffer builder's length + * \param pBuilder ac_buffer builder to get length of + * \return number of valid bytes in ac_buffer + */ +static inline size_t ac_buffer_builder_length(ac_buffer_builder_t* pBuilder) +{ + return ac_buffer_size(&pBuilder->ac_buffer); +} + +/** Set ac_buffer builder's length + * \param pBuilder ac_buffer builder to set length of + * \param length number of valid bytes in ac_buffer + */ +static inline void ac_buffer_builder_set_length(ac_buffer_builder_t* pBuilder, size_t length) +{ + if( ac_buffer_data(&pBuilder->ac_buffer) + length > pBuilder->data + pBuilder->size ) + { + return; + } + pBuilder->ac_buffer.size = length; +} + +/** Get ac_buffer builder's pointer to write position + * \param pBuilder ac_buffer builder + * \return pointer to write position + */ +static inline uint8_t* ac_buffer_builder_write_position(ac_buffer_builder_t* pBuilder) +{ + return (uint8_t*)ac_buffer_data(&pBuilder->ac_buffer) + ac_buffer_size(&pBuilder->ac_buffer); +} + +/** Get ac_buffer builder's write offset + * \param pBuilder ac_buffer builder + * \return write offset + */ +static inline size_t ac_buffer_builder_write_offset(ac_buffer_builder_t* pBuilder) +{ + return ac_buffer_data(&pBuilder->ac_buffer) + ac_buffer_size(&pBuilder->ac_buffer) - pBuilder->data; +} + +/** Set ac_buffer builder's write offset + * \param pBuilder ac_buffer builder + * \param off new write offset + */ +static inline void ac_buffer_builder_set_write_offset(ac_buffer_builder_t* pBuilder, size_t off) +{ + if( off > pBuilder->size ) + { + return; + } + if( pBuilder->data + off > pBuilder->ac_buffer.data ) + { + pBuilder->ac_buffer.size = off - (pBuilder->ac_buffer.data - pBuilder->data); + } + else + { + pBuilder->ac_buffer.size = 0; + } +} + +/** Get ac_buffer builder's read offset + * \param pBuilder ac_buffer builder + * \return read offset + */ +static inline size_t ac_buffer_builder_read_offset(ac_buffer_builder_t* pBuilder) +{ + return ac_buffer_data(&pBuilder->ac_buffer) - pBuilder->data; +} + +/** Set ac_buffer builder's read offset + * \param pBuilder ac_buffer builder + * \param off new read offset + */ +static inline void ac_buffer_builder_set_read_offset(ac_buffer_builder_t* pBuilder, size_t off) +{ + if( off > pBuilder->size ) + { + return; + } + if( pBuilder->data + off < pBuilder->ac_buffer.data + pBuilder->ac_buffer.size ) + { + pBuilder->ac_buffer.size = pBuilder->ac_buffer.data - (pBuilder->data + off); + } + else + { + pBuilder->ac_buffer.size = 0; + } + pBuilder->ac_buffer.data = pBuilder->data + off; +} + +/** Get ac_buffer builder's underlying ac_buffer + * \param pBuilder ac_buffer builder + * \return ac_buffer + */ +static inline ac_buffer_t* ac_buffer_builder_buffer(ac_buffer_builder_t* pBuilder) +{ + return &pBuilder->ac_buffer; +} + +/** Get space in ac_buffer builder + * \param pBuilder ac_buffer builder + * \return number of free bytes in ac_buffer builder + */ +static inline size_t ac_buffer_builder_space(ac_buffer_builder_t* pBuilder) +{ + return pBuilder->size - (ac_buffer_data(&pBuilder->ac_buffer) - pBuilder->data + ac_buffer_size(&pBuilder->ac_buffer)); +} + +/** Is ac_buffer builder empty + * \param pBuilder ac_buffer builder + * \return true if ac_buffer builder is empty + */ +static inline bool ac_buffer_builder_empty(ac_buffer_builder_t* pBuilder) +{ + return (ac_buffer_builder_length(pBuilder) == 0); +} + +/** Is ac_buffer builder full + * \param pBuilder ac_buffer builder + * \return true if ac_buffer builder is full + */ +static inline bool ac_buffer_full(ac_buffer_builder_t* pBuilder) +{ + return (ac_buffer_builder_space(pBuilder) == 0); +} + +/** Write 8-bit value in ac_buffer builder + * \param pBuilder ac_buffer builder + * \param hu8 8-bit value to write + */ +static inline void ac_buffer_builder_write_nu8(ac_buffer_builder_t* pBuilder, uint8_t hu8) +{ + ac_buffer_builder_write_be(pBuilder, &hu8, 1); +} + +/** Write 16-bit value in ac_buffer builder + * \param pBuilder ac_buffer builder + * \param hu16 16-bit value to write in big-endian format + */ +static inline void ac_buffer_builder_write_nu16(ac_buffer_builder_t* pBuilder, uint16_t hu16) +{ + ac_buffer_builder_write_be(pBuilder, (uint8_t*)&hu16, 2); +} + +/** Write 24-bit value in ac_buffer builder + * \param pBuilder ac_buffer builder + * \param hu24 24-bit value to write in big-endian format + */ +static inline void ac_buffer_builder_write_nu24(ac_buffer_builder_t* pBuilder, uint32_t hu24) +{ + ac_buffer_builder_write_be(pBuilder, (uint8_t*)&hu24, 3); +} + +/** Write 32-bit value in ac_buffer builder + * \param pBuilder ac_buffer builder + * \param hu32 32-bit value to write in big-endian format + */ +static inline void ac_buffer_builder_write_nu32(ac_buffer_builder_t* pBuilder, uint32_t hu32) +{ + ac_buffer_builder_write_be(pBuilder, (uint8_t*)&hu32, 4); +} + +/** Write 64-bit value in ac_buffer builder + * \param pBuilder ac_buffer builder + * \param hu64 64-bit value to write in big-endian format + */ +static inline void ac_buffer_builder_write_nu64(ac_buffer_builder_t* pBuilder, uint64_t hu64) +{ + ac_buffer_builder_write_be(pBuilder, (uint8_t*)&hu64, 8); +} + +/** Write n-bytes value in ac_buffer builder + * \param pBuilder ac_buffer builder + * \param data data to write + * \param size data length + */ +static inline void ac_buffer_builder_write_n_bytes(ac_buffer_builder_t* pBuilder, const uint8_t* data, size_t size) +{ + ac_buffer_builder_write_le(pBuilder, data, size); +} + +/** Write 8-bit value in ac_buffer builder at specified position + * \param pBuilder ac_buffer builder + * \param off offset at which to write + * \param hu8 8-bit value to write + */ +static inline void ac_buffer_builder_write_nu8_at(ac_buffer_builder_t* pBuilder, size_t off, uint8_t hu8) +{ + ac_buffer_builder_write_be_at(pBuilder, off, &hu8, 1); +} + +/** Write 16-bit value in ac_buffer builder at specified position + * \param pBuilder ac_buffer builder + * \param off offset at which to write + * \param hu16 16-bit value to write + */ +static inline void ac_buffer_builder_write_nu16_at(ac_buffer_builder_t* pBuilder, size_t off, uint16_t hu16) +{ + ac_buffer_builder_write_be_at(pBuilder, off, (uint8_t*)&hu16, 2); +} + +/** Write 24-bit value in ac_buffer builder at specified position + * \param pBuilder ac_buffer builder + * \param off offset at which to write + * \param hu24 24-bit value to write + */ +static inline void ac_buffer_builder_write_nu24_at(ac_buffer_builder_t* pBuilder, size_t off, uint32_t hu24) +{ + ac_buffer_builder_write_be_at(pBuilder, off, (uint8_t*)&hu24, 3); +} + +/** Write 32-bit value in ac_buffer builder at specified position + * \param pBuilder ac_buffer builder + * \param off offset at which to write + * \param hu32 32-bit value to write + */ +static inline void ac_buffer_builder_write_nu32_at(ac_buffer_builder_t* pBuilder, size_t off, uint32_t hu32) +{ + ac_buffer_builder_write_be_at(pBuilder, off, (uint8_t*)&hu32, 4); +} + +/** Write 64-bit value in ac_buffer builder at specified position + * \param pBuilder ac_buffer builder + * \param off offset at which to write + * \param hu64 64-bit value to write + */ +static inline void ac_buffer_builder_write_nu64_at(ac_buffer_builder_t* pBuilder, size_t off, uint64_t hu64) +{ + ac_buffer_builder_write_be_at(pBuilder, off, (uint8_t*)&hu64, 8); +} + +/** Write n-bytes value in ac_buffer builder at specified position + * \param pBuilder ac_buffer builder + * \param off offset at which to write + * \param data data to write + * \param size data length + */ +static inline void ac_buffer_builder_write_n_bytes_at(ac_buffer_builder_t* pBuilder, size_t off, const uint8_t* data, size_t size) +{ + ac_buffer_builder_write_be_at(pBuilder, off, data, size); +} + +/** Skip n-bytes in ac_buffer builder + * \param pBuilder ac_buffer builder + * \param size number of bytes to skip + */ +void ac_buffer_builder_write_n_skip(ac_buffer_builder_t* pBuilder, size_t size); + +/** Copy n bytes from buffer to builder + * \param pBuilderOut ac_buffer builder + * \param pBufIn the input buffer + * \param size number of bytes to copy + */ +void ac_buffer_builder_copy_n_bytes(ac_buffer_builder_t* pBuilderOut, ac_buffer_t* pBufIn, size_t size); + +/** Compact builder + * Will move underlying buffer's byte to start of allocated buffer + * \param pBuilder ac_buffer builder + */ +void ac_buffer_builder_compact(ac_buffer_builder_t* pBuilder); + +/** Get number of writable bytes in ac_buffer builder + * \param pBuilder ac_buffer builder + * \return number of free bytes in ac_buffer builder + */ +static inline size_t ac_buffer_builder_writable(ac_buffer_builder_t* pBuilder) +{ + return ac_buffer_builder_space(pBuilder); +} + +#ifdef __cplusplus +} +#endif + +#endif /* ACORE_BUFFER_BUILDER_H_ */ + +/** + * @} + * @} + * */ diff --git a/features/nfc/acore/acore/buffer_reader.h b/features/nfc/acore/acore/buffer_reader.h new file mode 100644 index 0000000000..a7f053e5d2 --- /dev/null +++ b/features/nfc/acore/acore/buffer_reader.h @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2017, 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 buffer_reader.h + * \copyright Copyright (c) ARM Ltd 2015 + * \author Donatien Garnier + */ + +/** \addtogroup ACore + * @{ + * \name Buffer Reader + * @{ + */ + +#ifndef ACORE_BUFFER_READER_H_ +#define ACORE_BUFFER_READER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "stdint.h" +#include "stddef.h" +#include "stdbool.h" + +#include "acore/buffer.h" + +/** Read n-bytes in big-endian format from buffer reader and advance read posiion + * \param pBuf the buffer to read from + * \param buf the array to write to + * \param size the number of bytes to read + */ +void ac_buffer_read_be(ac_buffer_t* pBuf, uint8_t* buf, size_t size); + +/** Read n-bytes in little-endian format from buffer reader and advance read posiion + * \param pBuf the buffer to read from + * \param buf the array to write to + * \param size the number of bytes to read + */ +void ac_buffer_read_le(ac_buffer_t* pBuf, uint8_t* buf, size_t size); + +/** Read 8-bit value from buffer reader and advance read posiion + * \param pBuf the buffer to read from + * \return 8-bit value read + */ +static inline uint8_t ac_buffer_read_nu8(ac_buffer_t* pBuf) +{ + uint8_t hu8; + ac_buffer_read_be(pBuf, &hu8, 1); + return hu8; +} + +/** Read BE 16-bit value from buffer reader and advance read posiion + * \param pBuf the buffer to read from + * \return 16-bit value read + */ +static inline uint16_t ac_buffer_read_nu16(ac_buffer_t* pBuf) +{ + uint16_t hu16; + ac_buffer_read_be(pBuf, (uint8_t*)&hu16, 2); + return hu16; +} + +/** Read BE 24-bit value from buffer reader and advance read posiion + * \param pBuf the buffer to read from + * \return 24-bit value read + */ +static inline uint32_t ac_buffer_read_nu24(ac_buffer_t* pBuf) +{ + uint32_t hu24; + ac_buffer_read_be(pBuf, (uint8_t*)&hu24, 3); + return hu24; +} + +/** Read BE 32-bit value from buffer reader and advance read posiion + * \param pBuf the buffer to read from + * \return 32-bit value read + */ +static inline uint32_t ac_buffer_read_nu32(ac_buffer_t* pBuf) +{ + uint32_t hu32; + ac_buffer_read_be(pBuf, (uint8_t*)&hu32, 4); + return hu32; +} + +/** Read BE 64-bit value from buffer reader and advance read posiion + * \param pBuf the buffer to read from + * \return 64-bit value read + */ +static inline uint64_t ac_buffer_read_nu64(ac_buffer_t* pBuf) +{ + uint64_t hu64; + ac_buffer_read_be(pBuf, (uint8_t*)&hu64, 8); + return hu64; +} + +/** Read n bytes from buffer reader and advance read posiion + * \param pBuf the buffer to read from + * \param data the array to write bytes to + * \param size the number of bytes to read + */ +static inline void ac_buffer_read_n_bytes(ac_buffer_t* pBuf, uint8_t* data, size_t size) +{ + ac_buffer_read_le(pBuf, data, size); +} + +/** Skip n bytes from buffer reader and advance read posiion + * \param pBuf the buffer to read from + * \param size the number of bytes to skip + */ +void ac_buffer_read_n_skip(ac_buffer_t* pBuf, size_t size); + +/** Get number of bytes readable from buffer + * \param pBuf the buffer to read from + * \return The number of bytes which can be read + */ +size_t ac_buffer_reader_readable(const ac_buffer_t* pBuf); + +/** Get a pointer to the current position within this buffer's current backing array + * \param pBuf the buffer to read from + * \return A pointer to the current position within the current backing array + */ +const uint8_t* ac_buffer_reader_current_buffer_pointer(ac_buffer_t* pBuf); + +/** Get the number of bytes readable within the current backing array + * \param pBuf the buffer to read from + * \return The number of bytes readable within the current backing array + */ +size_t ac_buffer_reader_current_buffer_length(ac_buffer_t* pBuf); + +/** Compare buffer with array (does not advance read position) + * \param pBuf the buffer to compare from + * \param bytes the array to compare with + * \param length the array length + * \return Whether the buffer is AT LEAST as long as the array AND the buffer and array have the same content + */ +bool ac_buffer_reader_cmp_bytes(const ac_buffer_t* pBuf, const uint8_t* bytes, size_t length); + +/** Compare buffer with array (does not advance read position) + * \param pBuf1 the buffer to compare from + * \param pBuf2 the buffer to compare with + * \return Whether the buffers have the same length and content + */ +bool ac_buffer_reader_cmp(const ac_buffer_t* pBuf1, const ac_buffer_t* pBuf2); + +#ifdef __cplusplus +} +#endif + +#endif /* CORE_ac_buffer_READER_H_ */ + +/** + * @} + * @} + * */ diff --git a/features/nfc/acore/acore/debug.h b/features/nfc/acore/acore/debug.h new file mode 100644 index 0000000000..be52d108d5 --- /dev/null +++ b/features/nfc/acore/acore/debug.h @@ -0,0 +1,41 @@ +/** + * \file debug.h + * \copyright Copyright (c) ARM Ltd 2018 + * \author Donatien Garnier + */ +/* + * 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 ACORE_DEBUG_H_ +#define ACORE_DEBUG_H_ + +// Macro that can be defined to lock stdio in multithreaded environments +#if !defined(ACORE_STDIO_LOCK) +#define ACORE_STDIO_LOCK() +#endif + +#if !defined(ACORE_STDIO_UNLOCK) +#define ACORE_STDIO_UNLOCK() +#endif + +// Macro that can be defined to define an alternative printf impl for debugging +#if !defined(ACORE_STDIO_PRINT) +#include "stdio.h" +#define ACORE_STDIO_PRINT(...) printf(__VA_ARGS__) +#endif + +#endif \ No newline at end of file diff --git a/features/nfc/acore/acore/macros.h b/features/nfc/acore/acore/macros.h new file mode 100644 index 0000000000..cf56295ca8 --- /dev/null +++ b/features/nfc/acore/acore/macros.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017, 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 ACORE_MACROS_H_ +#define ACORE_MACROS_H_ + +//Macros +#ifndef MAX +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +#endif + diff --git a/features/nfc/acore/acore/stream.h b/features/nfc/acore/acore/stream.h new file mode 100644 index 0000000000..1d3a54f98c --- /dev/null +++ b/features/nfc/acore/acore/stream.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2017, 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 stream.h + * \copyright Copyright (c) ARM Ltd 2015 + * \author Donatien Garnier + */ + +#ifndef ACORE_STREAM_H_ +#define ACORE_STREAM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct __ac_istream; +struct __ac_ostream; + +typedef struct __ac_istream ac_istream_t; +typedef struct __ac_ostream ac_ostream_t; + +#include "stddef.h" +#include "stdbool.h" +#include "stdint.h" + +#include "acore/buffer.h" + +typedef void (*ac_istream_fn)(ac_buffer_t* pDataIn, bool* pClose, size_t maxLength, void* pUserParam); +typedef void (*ac_ostream_fn)(ac_buffer_t* pDataOut, bool closed, void* pUserParam); + +//Input stream -- pulled by consumer +struct __ac_istream +{ + ac_istream_fn fn; + void* pUserParam; +}; + +//Output stream -- pushed by supplier +struct __ac_ostream +{ + ac_ostream_fn fn; + void* pUserParam; +}; + +//Called by supplier +void ac_istream_init(ac_istream_t* pac_istream, ac_istream_fn fn, void* pUserParam); +//Called by consumer +void ac_istream_pull(ac_istream_t* pac_istream, ac_buffer_t* pDataIn, bool* pClose, size_t maxLength); + +//Called by consumer +void ac_ostream_init(ac_ostream_t* pac_ostream, ac_ostream_fn fn, void* pUserParam); +//Called by supplier +void ac_ostream_push(ac_ostream_t* pac_ostream, ac_buffer_t* pDataOut, bool closed); + +#ifdef __cplusplus +} +#endif + +#endif /* ACORE_STREAM_H_ */ diff --git a/features/nfc/acore/source/buffer.c b/features/nfc/acore/source/buffer.c new file mode 100644 index 0000000000..524c24f46e --- /dev/null +++ b/features/nfc/acore/source/buffer.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017, 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 buffer.c + * \copyright Copyright (c) ARM Ltd 2013 + * \author Donatien Garnier + * \desc Module to ease ac_buffers' management + */ + +#include "string.h" + +#include "acore/buffer.h" +#include "acore/buffer_reader.h" +#include "acore/macros.h" + +#include "acore/debug.h" + +void ac_buffer_init(ac_buffer_t* pBuf, const uint8_t* data, size_t size) +{ + pBuf->data = data; + pBuf->size = size; + + pBuf->pNext = NULL; +} + +void ac_buffer_dup(ac_buffer_t* pBuf, const ac_buffer_t* pBufIn) +{ + if(pBuf != pBufIn) + { + memcpy(pBuf, pBufIn, sizeof(ac_buffer_t)); + } +} + +void ac_buffer_append(ac_buffer_t* pBuf, ac_buffer_t* pAppBuf) +{ + while(pBuf->pNext != NULL) + { + pBuf = pBuf->pNext; + } + pBuf->pNext = pAppBuf; +} + +void ac_buffer_split(ac_buffer_t* pStartBuf, ac_buffer_t* pEndBuf, ac_buffer_t* pBuf, size_t length) +{ + ac_buffer_dup(pStartBuf, pBuf); + ac_buffer_dup(pEndBuf, pBuf); + + ac_buffer_read_n_skip(pEndBuf, length); + + while( length > ac_buffer_size(pStartBuf) ) + { + length -= pStartBuf->size; + pStartBuf = pStartBuf->pNext; + } + + pStartBuf->size = length; + pStartBuf->pNext = NULL; +} + +/** Dump a ac_buffer's content to stdout (useful for debugging) + * \param pBuf pointer to ac_buffer_t structure + */ +void ac_buffer_dump(ac_buffer_t* pBuf) +{ +#if !defined(NDEBUG) + ACORE_STDIO_LOCK(); + while(pBuf != NULL) + { + size_t r = ac_buffer_size(pBuf); + size_t i = 0; + size_t j = 0; + while(i < r) + { + for(j = i; j < MIN(i + 16, r); j++) + { + ACORE_STDIO_PRINT("%02x ", ac_buffer_data(pBuf)[j]); + } + ACORE_STDIO_PRINT("\r\n"); + i = j; + } + pBuf = ac_buffer_next(pBuf); + if(pBuf != NULL) + { + ACORE_STDIO_PRINT("->\r\n"); + } + } + ACORE_STDIO_UNLOCK(); +#else + (void)pBuf; +#endif +} diff --git a/features/nfc/acore/source/buffer_builder.c b/features/nfc/acore/source/buffer_builder.c new file mode 100644 index 0000000000..0d6e58de80 --- /dev/null +++ b/features/nfc/acore/source/buffer_builder.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2017, 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 buffer_builder.c + * \copyright Copyright (c) ARM Ltd 2015 + * \author Donatien Garnier + */ + +#include "acore/buffer_builder.h" +#include "acore/buffer_reader.h" +#include "acore/macros.h" + +#include "string.h" + +#define VOID +#define ENSURE_WRITE_LENGTH(pBuilder, n) do{ if( ac_buffer_builder_space(pBuilder) < n ) { return; } }while(0); + +void ac_buffer_builder_init(ac_buffer_builder_t* pBuilder, uint8_t* data, size_t size) +{ + pBuilder->data = data; + pBuilder->size = size; + ac_buffer_init(&pBuilder->ac_buffer, data, 0); +} + +void ac_buffer_builder_from_ac_buffer(ac_buffer_builder_t* pBuilder) +{ + pBuilder->data = (uint8_t*)pBuilder->ac_buffer.data; + pBuilder->size = pBuilder->ac_buffer.size; + pBuilder->ac_buffer.size = 0; +} + +void ac_buffer_builder_reset(ac_buffer_builder_t* pBuilder) +{ + ac_buffer_init(&pBuilder->ac_buffer, pBuilder->data, 0); +} + +void ac_buffer_builder_set_full(ac_buffer_builder_t* pBuilder) +{ + ac_buffer_init(&pBuilder->ac_buffer, pBuilder->data, pBuilder->size); +} + +void ac_buffer_builder_write_be(ac_buffer_builder_t* pBuilder, const uint8_t* buf, size_t size) +{ + ENSURE_WRITE_LENGTH(pBuilder, size); + buf += size; + while(size > 0) + { + buf--; + *ac_buffer_builder_write_position(pBuilder) = *buf; + pBuilder->ac_buffer.size++; + size--; + } +} + +void ac_buffer_builder_write_le(ac_buffer_builder_t* pBuilder, const uint8_t* buf, size_t size) +{ + ENSURE_WRITE_LENGTH(pBuilder, size); + memcpy(ac_buffer_builder_write_position(pBuilder), buf, size); + pBuilder->ac_buffer.size+=size; +} + +void ac_buffer_builder_write_be_at(ac_buffer_builder_t* pBuilder, size_t pos, const uint8_t* buf, size_t size) +{ + size_t currentPos = pBuilder->ac_buffer.size; + pBuilder->ac_buffer.size = pos; + ac_buffer_builder_write_be(pBuilder, buf, size); + pBuilder->ac_buffer.size = currentPos; +} + +void ac_buffer_builder_write_le_at(ac_buffer_builder_t* pBuilder, size_t pos, const uint8_t* buf, size_t size) +{ + size_t currentPos = pBuilder->ac_buffer.size; + pBuilder->ac_buffer.size = pos; + ac_buffer_builder_write_le(pBuilder, buf, size); + pBuilder->ac_buffer.size = currentPos; +} + +void ac_buffer_builder_write_n_skip(ac_buffer_builder_t* pBuilder, size_t size) +{ + ENSURE_WRITE_LENGTH(pBuilder, size); + pBuilder->ac_buffer.size += size; +} + +void ac_buffer_builder_copy_n_bytes(ac_buffer_builder_t* pBuilderOut, ac_buffer_t* pBufIn, size_t size) +{ + ENSURE_WRITE_LENGTH(pBuilderOut, size); + if( ac_buffer_reader_readable(pBufIn) < size ) + { + return; + } + while(size > 0) + { + size_t cpy = ac_buffer_reader_current_buffer_length(pBufIn); + cpy = MIN(cpy, size); + ac_buffer_builder_write_n_bytes(pBuilderOut, ac_buffer_reader_current_buffer_pointer(pBufIn), cpy); + ac_buffer_read_n_skip(pBufIn, cpy); + size -= cpy; + } +} + +void ac_buffer_builder_compact(ac_buffer_builder_t* pBuilder) +{ + memmove(pBuilder->data, ac_buffer_data(&pBuilder->ac_buffer), ac_buffer_size(&pBuilder->ac_buffer)); + pBuilder->ac_buffer.data = pBuilder->data; +} diff --git a/features/nfc/acore/source/buffer_reader.c b/features/nfc/acore/source/buffer_reader.c new file mode 100644 index 0000000000..268322514f --- /dev/null +++ b/features/nfc/acore/source/buffer_reader.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2017, 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 buffer_reader.c + * \copyright Copyright (c) ARM Ltd 2015 + * \author Donatien Garnier + */ + +#include "acore/buffer_reader.h" +#include "acore/macros.h" + +#include "string.h" + +#define VOID +#define ENSURE_READ_LENGTH(pBuf, n) do{ if( ac_buffer_reader_readable(pBuf) < n ) { return; } }while(0); + +static inline void update_buf(ac_buffer_t* pBuf) +{ + while( ac_buffer_size(pBuf) == 0 ) + { + if( ac_buffer_next(pBuf) != NULL ) + { + ac_buffer_t* pNext = ac_buffer_next(pBuf); + ac_buffer_init(pBuf, ac_buffer_data(pNext), ac_buffer_size(pNext)); + pBuf->pNext = ac_buffer_next(pNext); + } + else if( pBuf->data != NULL ) + { + ac_buffer_init(pBuf, NULL, 0); + } + else + { + return; + } + } +} + +void ac_buffer_read_be(ac_buffer_t* pBuf, uint8_t* buf, size_t size) +{ + ENSURE_READ_LENGTH(pBuf, size); + buf += size; + while(size > 0) + { + buf--; + *buf = *ac_buffer_data(pBuf); + pBuf->data++; + pBuf->size--; + update_buf(pBuf); + size--; + } +} + +void ac_buffer_read_le(ac_buffer_t* pBuf, uint8_t* buf, size_t size) +{ + ENSURE_READ_LENGTH(pBuf, size); + while(size > 0) + { + size_t cpy = ac_buffer_size(pBuf); + cpy = MIN(cpy, size); + memcpy(buf, ac_buffer_data(pBuf), cpy); + pBuf->data+=cpy; + pBuf->size-=cpy; + update_buf(pBuf); + size-=cpy; + buf+=cpy; + } +} + +void ac_buffer_read_n_skip(ac_buffer_t* pBuf, size_t size) +{ + ENSURE_READ_LENGTH(pBuf, size); + while(size > 0) + { + size_t cpy = ac_buffer_size(pBuf); + cpy = MIN(cpy, size); + pBuf->data+=cpy; + pBuf->size-=cpy; + update_buf(pBuf); + size-=cpy; + } +} + +size_t ac_buffer_reader_readable(const ac_buffer_t* pBuf) +{ + size_t r = 0; + while( pBuf != NULL ) + { + r += ac_buffer_size(pBuf); + pBuf = ac_buffer_next(pBuf); + } + return r; +} + +const uint8_t* ac_buffer_reader_current_buffer_pointer(ac_buffer_t* pBuf) +{ + update_buf(pBuf); + return ac_buffer_data(pBuf); +} + +size_t ac_buffer_reader_current_buffer_length(ac_buffer_t* pBuf) +{ + update_buf(pBuf); + return ac_buffer_size(pBuf); +} + +bool ac_buffer_reader_cmp_bytes(const ac_buffer_t* pBuf, const uint8_t* bytes, size_t length) +{ + ac_buffer_t reader; + + if( length > ac_buffer_reader_readable(pBuf) ) + { + return false; + } + + ac_buffer_dup(&reader, pBuf); + + while(length > 0) + { + size_t sz = ac_buffer_reader_current_buffer_length(&reader); + if( sz > length ) + { + sz = length; + } + int c = memcmp(ac_buffer_reader_current_buffer_pointer(&reader), bytes, sz); + if(c) + { + return false; + } + length -= sz; + bytes += sz; + ac_buffer_read_n_skip(&reader, sz); + } + + return true; +} + +bool ac_buffer_reader_cmp(const ac_buffer_t* pBuf1, const ac_buffer_t* pBuf2) +{ + ac_buffer_t reader1; + ac_buffer_t reader2; + + if( ac_buffer_reader_readable(pBuf1) != ac_buffer_reader_readable(pBuf2) ) + { + return false; + } + + ac_buffer_dup(&reader1, pBuf1); + ac_buffer_dup(&reader2, pBuf2); + + size_t length = ac_buffer_reader_readable(pBuf1); + while(length > 0) + { + size_t sz1 = ac_buffer_reader_current_buffer_length(&reader1); + size_t sz2 = ac_buffer_reader_current_buffer_length(&reader2); + + size_t sz = MIN(sz1, sz2); + + int c = memcmp(ac_buffer_reader_current_buffer_pointer(&reader1), ac_buffer_reader_current_buffer_pointer(&reader2), sz); + if(c) + { + return false; + } + length -= sz; + ac_buffer_read_n_skip(&reader1, sz); + ac_buffer_read_n_skip(&reader2, sz); + } + + return true; +} diff --git a/features/nfc/acore/source/stream.c b/features/nfc/acore/source/stream.c new file mode 100644 index 0000000000..af05e2992f --- /dev/null +++ b/features/nfc/acore/source/stream.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017, 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 stream.c + * \copyright Copyright (c) ARM Ltd 2015 + * \author Donatien Garnier + */ + +#include "acore/stream.h" +#include "acore/macros.h" + +//Called by supplier +void ac_istream_init(ac_istream_t* pac_istream, ac_istream_fn fn, void* pUserParam) +{ + pac_istream->fn = fn; + pac_istream->pUserParam = pUserParam; +} + +//Called by consumer +void ac_istream_pull(ac_istream_t* pac_istream, ac_buffer_t* pDataIn, bool* pClose, size_t maxLength) +{ + pac_istream->fn(pDataIn, pClose, maxLength, pac_istream->pUserParam); +} + +//Called by consumer +void ac_ostream_init(ac_ostream_t* pac_ostream, ac_ostream_fn fn, void* pUserParam) +{ + pac_ostream->fn = fn; + pac_ostream->pUserParam = pUserParam; +} + +//Called by supplier +void ac_ostream_push(ac_ostream_t* pac_ostream, ac_buffer_t* pDataOut, bool closed) +{ + pac_ostream->fn(pDataOut, closed, pac_ostream->pUserParam); +} diff --git a/features/nfc/stack/nfc_errors.h b/features/nfc/stack/nfc_errors.h new file mode 100644 index 0000000000..f8870cbe1e --- /dev/null +++ b/features/nfc/stack/nfc_errors.h @@ -0,0 +1,66 @@ +/* + * 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 nfc_errors.h + * \copyright Copyright (c) ARM Ltd 2013 + * \author Donatien Garnier + * \details NFC Error codes + */ + +/** \addtogroup Core + * @{ + * \name Error codes + * @{ + */ + +#ifndef NFC_ERRORS_H_ +#define NFC_ERRORS_H_ + +#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 + +/** Type for NFC errors + */ +typedef int nfc_err_t; + +#endif /* NFC_ERRORS_H_ */ + +/** + * @} + * @} + * */ diff --git a/features/nfc/stack/platform/nfc_debug.h b/features/nfc/stack/platform/nfc_debug.h new file mode 100644 index 0000000000..3a64a9a585 --- /dev/null +++ b/features/nfc/stack/platform/nfc_debug.h @@ -0,0 +1,62 @@ +/* + * 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 MBED_NFC_DEBUG_H +#define MBED_NFC_DEBUG_H + +#if NFC_DEBUG && !defined(NDEBUG) +#include "stdio.h" +#include "stdarg.h" +static inline void nfc_dbg_print(const char* type, const char* module, unsigned int line, const char* fmt, ...) { +#if !defined(NDEBUG) + printf("NFC [%s] %s:%u ", type, module, line); + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + printf("\r\n"); +#endif +} + +#if !defined(NFC_DBG) +#define NFC_DBG(...) nfc_dbg_print("DBG", __MODULE__, __LINE__, __VA_ARGS__) +#endif + +#if !defined(NFC_WARN) +#define NFC_WARN(...) nfc_dbg_print("WARN", __MODULE__, __LINE__, __VA_ARGS__) +#endif + +#if !defined(NFC_ERR) +#define NFC_ERR(...) nfc_dbg_print("ERR", __MODULE__, __LINE__, __VA_ARGS__) +#endif + +#else + +#if !defined(NFC_DBG) +#define NFC_DBG(...) +#endif + +#if !defined(NFC_WARN) +#define NFC_WARN(...) +#endif + +#if !defined(NFC_ERR) +#define NFC_ERR(...) +#endif +#endif + +#endif diff --git a/features/nfc/stack/platform/nfc_scheduler.c b/features/nfc/stack/platform/nfc_scheduler.c new file mode 100644 index 0000000000..7687526c4c --- /dev/null +++ b/features/nfc/stack/platform/nfc_scheduler.c @@ -0,0 +1,208 @@ +/* + * 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 nfc_scheduler.c + * \copyright Copyright (c) ARM Ltd 2015 + * \author Donatien Garnier + */ + +#define __DEBUG__ 0 +#ifndef __MODULE__ +#define __MODULE__ "nfc_scheduler.c" +#endif + +#include "platform/nfc_scheduler.h" + +void nfc_scheduler_init(nfc_scheduler_t* pScheduler, nfc_scheduler_timer_t* pTimer) +{ + pScheduler->pNext = NULL; + pScheduler->pTimer = pTimer; + + //Start timer + nfc_scheduler_timer_start(pTimer); +} + +#define MAX_TIMEOUT UINT32_MAX + +uint32_t nfc_scheduler_iteration(nfc_scheduler_t* pScheduler, uint32_t events) +{ + bool triggered = false; + while(true) + { + nfc_task_t* pPrioTask = NULL; + nfc_task_t* pPrioTaskPrevious = NULL; + uint32_t prioTaskEvent = 0; + int64_t timeout; + nfc_task_t* pPreviousTask = NULL; + nfc_task_t* pTask = pScheduler->pNext; + + if( pTask == NULL ) + { + NFC_DBG("Empty queue, %lu ms elapsed", nfc_scheduler_timer_get(pScheduler->pTimer)); + //Empty queue, return + return MAX_TIMEOUT; + } + + //Get timer value + uint32_t timeElapsed = nfc_scheduler_timer_get(pScheduler->pTimer); + NFC_DBG("%lu ms elapsed", timeElapsed); + nfc_scheduler_timer_reset(pScheduler->pTimer); + + do + { + //Apply timeouts + if( pTask->events & EVENT_TIMEOUT ) + { + pTask->timeout -= timeElapsed; + } + pPreviousTask = pTask; + pTask = pTask->pNext; + } while( pTask != NULL ); + + pTask = pScheduler->pNext; + pPreviousTask = NULL; + timeout = MAX_TIMEOUT; + do + { + //Check which task should be woken up first + if( (events & EVENT_HW_INTERRUPT) && (pTask->events & EVENT_HW_INTERRUPT) ) + { + //Hardware interrupts have prio + pPrioTask = pTask; + pPrioTaskPrevious = pPreviousTask; + timeout = 0; + events &= ~EVENT_HW_INTERRUPT; //Only one task gets triggered per event + prioTaskEvent = EVENT_HW_INTERRUPT; + break; + } + else if( (pTask->events & EVENT_TIMEOUT) && (pTask->timeout < timeout) ) + { + pPrioTask = pTask; + pPrioTaskPrevious = pPreviousTask; + timeout = pTask->timeout; + prioTaskEvent = EVENT_TIMEOUT; + } + pPreviousTask = pTask; + pTask = pTask->pNext; + } while( pTask != NULL ); + + if( pPrioTask == NULL ) + { + //No task to wake up, exit + NFC_DBG("No task to wake up"); + return MAX_TIMEOUT; + } + + if( timeout > 0 ) + { + //No task to wake up yet + if( timeout > MAX_TIMEOUT ) + { + NFC_DBG("No task to wake up"); + return MAX_TIMEOUT; + } + else + { + NFC_DBG("No task to wake up, wait %lu ms", timeout); + return timeout; + } + } + + //Dequeue task + if( pPrioTaskPrevious == NULL ) + { + pScheduler->pNext = pPrioTask->pNext; + } + else + { + pPrioTaskPrevious->pNext = pPrioTask->pNext; + } + pPrioTask->pNext = NULL; + + //Execute task + NFC_DBG("Calling task %p - events %02X", pPrioTask, prioTaskEvent); + pPrioTask->fn(prioTaskEvent, pPrioTask->pUserData); + events &= ~EVENT_HW_INTERRUPT; //Only one task gets triggered per event + triggered = true; + } + return MAX_TIMEOUT; +} + +void nfc_scheduler_queue_task(nfc_scheduler_t* pScheduler, nfc_task_t* pTask) +{ + pTask->timeout = pTask->timeoutInitial + nfc_scheduler_timer_get(pScheduler->pTimer); + NFC_DBG("Queuing task %p: events %1X, timeout %lu ms", pTask, pTask->events, pTask->timeout); + //Find last task + nfc_task_t* pPrevTask = pScheduler->pNext; + pTask->pNext = NULL; + if( pPrevTask == NULL ) + { + pScheduler->pNext = pTask; + return; + } + while( pPrevTask->pNext != NULL ) + { + pPrevTask = pPrevTask->pNext; + } + pPrevTask->pNext = pTask; +} + +void nfc_scheduler_dequeue_task(nfc_scheduler_t* pScheduler, bool abort, nfc_task_t* pTask) +{ + NFC_DBG("Dequeuing task %p", pTask); + //Find task + nfc_task_t* pPrevTask = pScheduler->pNext; + if( pPrevTask == NULL ) + { + pTask->pNext = NULL; + return; + } + if( pPrevTask == pTask ) + { + if(abort) + { + pTask->fn(EVENT_ABORTED, pTask->pUserData); + } + pScheduler->pNext = pTask->pNext; + pTask->pNext = NULL; + return; + } + while( pPrevTask->pNext != NULL ) + { + if(pPrevTask->pNext == pTask) + { + if(abort) + { + pTask->fn(EVENT_ABORTED, pTask->pUserData); + } + pPrevTask->pNext = pTask->pNext; + pTask->pNext = NULL; + return; + } + pPrevTask = pPrevTask->pNext; + } + pTask->pNext = NULL; +} + +void task_init(nfc_task_t* pTask, uint32_t events, uint32_t timeout, nfc_task_fn fn, void* pUserData) +{ + pTask->events = events; + pTask->timeoutInitial = timeout; + pTask->fn = fn; + pTask->pUserData = pUserData; + pTask->pNext = NULL; +} diff --git a/features/nfc/stack/platform/nfc_scheduler.h b/features/nfc/stack/platform/nfc_scheduler.h new file mode 100644 index 0000000000..ba7f62423a --- /dev/null +++ b/features/nfc/stack/platform/nfc_scheduler.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2014-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 nfc_scheduler.h + * \copyright Copyright (c) ARM Ltd 2014 + * \author Donatien Garnier + */ +/** \addtogroup Core + * @{ + * \name Scheduler + * @{ + */ + +#ifndef NFC_SCHEDULER_H_ +#define NFC_SCHEDULER_H_ + +#include "core/fwk.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define EVENT_NONE 0 +#define EVENT_TIMEOUT 1 +#define EVENT_ABORTED 2 +#define EVENT_HW_INTERRUPT 4 + +struct __nfc_timer; +typedef struct __nfc_timer nfc_scheduler_timer_t; + +struct __nfc_task; +typedef struct __nfc_task nfc_task_t; + +typedef struct __scheduler +{ + nfc_task_t* pNext; + nfc_scheduler_timer_t* pTimer; +} nfc_scheduler_t; + +typedef void (*nfc_task_fn)(uint32_t events, void* pUserData); + +struct __nfc_task +{ + uint32_t events; + int64_t timeout; //millisecs + int64_t timeoutInitial; + + nfc_task_fn fn; + void* pUserData; + + nfc_task_t* pNext; +}; + +void nfc_scheduler_timer_init(nfc_scheduler_timer_t* timer); + +void nfc_scheduler_timer_start(nfc_scheduler_timer_t* timer); + +uint32_t nfc_scheduler_timer_get(nfc_scheduler_timer_t* timer); + +void nfc_scheduler_timer_stop(nfc_scheduler_timer_t* timer); + +void nfc_scheduler_timer_reset(nfc_scheduler_timer_t* timer); + +/** Init scheduler + * \param pScheduler scheduler instance to init + * \param pTimer timer instance + */ +void nfc_scheduler_init(nfc_scheduler_t* pScheduler, nfc_scheduler_timer_t* pTimer); + +/** Iterate through all tasks + * \param pScheduler scheduler instance + * \param events mask of events (except EVENT_TIMEOUT) that have been raised since this function last returned (0 on first call) + * \return time after which this function must be called again if no other event arises + */ +uint32_t nfc_scheduler_iteration(nfc_scheduler_t* pScheduler, uint32_t events); + +/** Queue a task to execute + * \param pScheduler scheduler instance + * \param pTask task to queue + * + */ +void nfc_scheduler_queue_task(nfc_scheduler_t* pScheduler, nfc_task_t* pTask); + +/** Remove a task to execute + * \param pScheduler scheduler instance + * \param pTask task to remove + * \param abort abort task if queued + */ +void nfc_scheduler_dequeue_task(nfc_scheduler_t* pScheduler, bool abort, nfc_task_t* pTask); + +/** Initialize task with the following parameters + * \param pTask task to initialize + * \param events events on which to call task + * \param timeout if relevant + * \param fn function to be called + * \param pUserData data that will be passed to function + */ +void task_init(nfc_task_t* pTask, uint32_t events, uint32_t timeout, nfc_task_fn fn, void* pUserData); + +#ifdef __cplusplus +} +#endif + +#endif /* NFC_SCHEDULER_H_ */ + +/** + * @} + * @} + * */ + diff --git a/features/nfc/stack/platform/nfc_transport.c b/features/nfc/stack/platform/nfc_transport.c new file mode 100644 index 0000000000..f030f33bf2 --- /dev/null +++ b/features/nfc/stack/platform/nfc_transport.c @@ -0,0 +1,53 @@ +/* + * 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 nfc_transport.c + * \copyright Copyright (c) ARM Ltd 2013-2018 + * \author Donatien Garnier + * \details Transport layer + */ + +/** \addtogroup Implementation + * @{ + * \name Transport + * @{ + */ + +#include "inc/nfc.h" +#include "nfc_transport.h" + +/** Initialize transport with a specific implementation + * \param pTransport pointer to a nfc_transport_t structure to initialize + * \param write transport write function + * \param read transport read function + * \param pUser parameter that will be passed to any of the above functions + */ +void nfc_transport_init( nfc_transport_t* pTransport, nfc_transport_write_fn_t write, nfc_transport_read_fn_t read, void* pUser ) +{ + pTransport->write = write; + pTransport->read = read; + pTransport->pUser = pUser; +} + + + + +/** + * @} + * @} + * */ + diff --git a/features/nfc/stack/platform/nfc_transport.h b/features/nfc/stack/platform/nfc_transport.h new file mode 100644 index 0000000000..9d963f09ab --- /dev/null +++ b/features/nfc/stack/platform/nfc_transport.h @@ -0,0 +1,84 @@ +/* + * 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 nfc_transport.h + * \copyright Copyright (c) ARM Ltd 2013-2018 + * \author Donatien Garnier + */ + +/** \addtogroup Implementation + * @{ + * \name Transport + * @{ + */ + +#ifndef NFC_TRANSPORT_H_ +#define NFC_TRANSPORT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "inc/nfc.h" + +/** Function called to write a register's value + * \param address address of the register to write to + * \param outBuf buffer to write + * \param outLen buffer's length + * \param pUser parameter passed to the nfc_transport_init function + */ +typedef void (*nfc_transport_write_fn_t)( uint8_t address, const uint8_t* outBuf, size_t outLen, void* pUser ); + +/** Function called to read a register's value + * \param address address to read packet from + * \param outBuf buffer to read + * \param outLen buffer's length + * \param pUser parameter passed to the nfc_transport_init function + */ +typedef void (*nfc_transport_read_fn_t)( uint8_t address, uint8_t* inBuf, size_t inLen, void* pUser ); + +typedef struct __transport +{ + nfc_transport_write_fn_t write; + nfc_transport_read_fn_t read; + void* pUser; +} nfc_transport_t; + +void nfc_transport_init( nfc_transport_t* pTransport, nfc_transport_write_fn_t write, nfc_transport_read_fn_t read, void* pUser ); + +static inline void nfc_transport_write( nfc_transport_t* pTransport, uint8_t address, const uint8_t* outBuf, size_t outLen ) +{ + pTransport->write( address, outBuf, outLen, pTransport->pUser ); +} + +static inline void nfc_transport_read( nfc_transport_t* pTransport, uint8_t address, uint8_t* inBuf, size_t inLen ) +{ + pTransport->read( address, inBuf, inLen, pTransport->pUser ); +} + +#ifdef __cplusplus +} +#endif + +#endif /* NFC_TRANSPORT_H_ */ + + +/** + * @} + * @} + * */ + diff --git a/features/nfc/stack/tech/iso7816/iso7816.c b/features/nfc/stack/tech/iso7816/iso7816.c new file mode 100644 index 0000000000..df661d4799 --- /dev/null +++ b/features/nfc/stack/tech/iso7816/iso7816.c @@ -0,0 +1,366 @@ +/* + * 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 iso7816.c + * \copyright Copyright (c) ARM Ltd 2015 + * \author Donatien Garnier + */ + +#define __DEBUG__ 0 +#ifndef __MODULE__ +#define __MODULE__ "iso7816.c" +#endif + +#include "inc/nfc.h" + +#include "iso7816.h" +#include "iso7816_app.h" +#include "iso7816_defs.h" +#include "tech/isodep/isodep_target.h" + +static void iso7816_disconnected(nfc_tech_iso7816_t* pIso7816, bool deselected); + +static nfc_err_t iso7816_parse(nfc_tech_iso7816_t* pIso7816); + +static void iso7816_receive(nfc_tech_iso7816_t* pIso7816); +static nfc_err_t iso7816_transmit(nfc_tech_iso7816_t* pIso7816); + +static bool iso7816_mf_command(nfc_tech_iso7816_t* pIso7816); + +static void iso_dep_received_cb(nfc_tech_isodep_t* pIsodep, nfc_err_t ret, void* pUserData); +static void iso_dep_transmitted_cb(nfc_tech_isodep_t* pIsodep, nfc_err_t ret, void* pUserData); +static void iso_dep_disconnected_cb(nfc_tech_isodep_t* pIsodep, bool deselected, void* pUserData); + +static void iso_dep_stream_transmit_cb(buffer_t* ppDataIn, bool* pClose, size_t maxLength, void* pUserParam); +static void iso_dep_stream_receive_cb(buffer_t* pDataOut, bool closed, void* pUserParam); + +void nfc_tech_iso7816_init(nfc_tech_iso7816_t* pIso7816, nfc_transceiver_t* pTransceiver, nfc_tech_iso7816_disconnected_cb disconnectedCb, void* pUserData) +{ + buffer_init(&pIso7816->hist, NULL, 0); + nfc_tech_isodep_target_init(&pIso7816->isoDepTarget, pTransceiver, &pIso7816->hist, iso_dep_disconnected_cb, pIso7816); + pIso7816->pAppList = NULL; + pIso7816->pSelectedApp = NULL; + pIso7816->disconnectedCb = disconnectedCb; + + istream_init(&pIso7816->inputStream, iso_dep_stream_transmit_cb, pIso7816); + ostream_init(&pIso7816->outputStream, iso_dep_stream_receive_cb, pIso7816); + + buffer_builder_init(&pIso7816->txBldr, pIso7816->txBuf, 2); //Just enough to fit sw + + buffer_builder_init(&pIso7816->rxBldr, pIso7816->rxBuf, ISO7816_RX_BUFFER_SIZE); + + pIso7816->pUserData = pUserData; +} + +void nfc_tech_iso7816_connect(nfc_tech_iso7816_t* pIso7816) +{ + pIso7816->disconnected = false; + pIso7816->responseReady = true; + + iso7816_receive(pIso7816); + nfc_tech_isodep_target_connect(&pIso7816->isoDepTarget); +} + +void nfc_tech_iso7816_disconnect(nfc_tech_iso7816_t* pIso7816) +{ + nfc_tech_isodep_target_disconnect(&pIso7816->isoDepTarget); +} + +void nfc_tech_iso7816_add_app(nfc_tech_iso7816_t* pIso7816, nfc_tech_iso7816_app_t* pIso7816App) +{ + nfc_tech_iso7816_app_t** ppPrevApp = &pIso7816->pAppList; + while( *ppPrevApp != NULL ) + { + ppPrevApp = &((*ppPrevApp)->pNext); + } + *ppPrevApp = pIso7816App; + pIso7816App->pNext = NULL; +} + +nfc_err_t nfc_tech_iso7816_reply(nfc_tech_iso7816_t* pIso7816) +{ + nfc_err_t ret; + + //Serialize APDU and send + buffer_builder_reset(&pIso7816->txBldr); + buffer_builder_write_nu16(&pIso7816->txBldr, pIso7816->rApdu.sw); + + buffer_append(&pIso7816->rApdu.dataOut, buffer_builder_buffer(&pIso7816->txBldr)); + + NFC_DBG("R-ADPU: (LE):%02X SW:%04X", buffer_reader_readable(&pIso7816->rApdu.dataOut), pIso7816->rApdu.sw); + DBG_BLOCK( buffer_dump(&pIso7816->rApdu.dataOut); ) + + ret = iso7816_transmit(pIso7816); + if(ret) + { + return ret; + } + + return NFC_OK; +} + +void iso7816_disconnected(nfc_tech_iso7816_t* pIso7816, bool deselected) +{ + pIso7816->disconnected = true; + if(pIso7816->pSelectedApp != NULL) + { + //Deselect previous app + pIso7816->pSelectedApp->deselected(pIso7816->pSelectedApp, pIso7816->pSelectedApp->pUserData); + pIso7816->pSelectedApp = NULL; + } + pIso7816->disconnectedCb(pIso7816, deselected, pIso7816->pUserData); +} + +nfc_err_t iso7816_parse(nfc_tech_iso7816_t* pIso7816) +{ + //Reset R-APDU + buffer_init(&pIso7816->rApdu.dataOut, NULL, 0); + pIso7816->rApdu.sw = ISO7816_SW_OK; + + DBG_BLOCK( buffer_dump(buffer_builder_buffer(&pIso7816->rxBldr)); ) + + if( buffer_reader_readable( buffer_builder_buffer(&pIso7816->rxBldr) ) < 4 ) + { + NFC_ERR("C-APDU is too small"); + pIso7816->rApdu.sw = ISO7816_SW_INVALID_CLASS; + nfc_tech_iso7816_reply(pIso7816); + return NFC_ERR_PROTOCOL; + } + + pIso7816->cApdu.cla = buffer_read_nu8(buffer_builder_buffer(&pIso7816->rxBldr)); + pIso7816->cApdu.ins = buffer_read_nu8(buffer_builder_buffer(&pIso7816->rxBldr)); + pIso7816->cApdu.p1 = buffer_read_nu8(buffer_builder_buffer(&pIso7816->rxBldr)); + pIso7816->cApdu.p2 = buffer_read_nu8(buffer_builder_buffer(&pIso7816->rxBldr)); + buffer_init(&pIso7816->cApdu.dataIn, NULL, 0); + pIso7816->cApdu.maxRespLength = 0; + + if( buffer_reader_readable(buffer_builder_buffer(&pIso7816->rxBldr)) > 1 ) + { + size_t lc = buffer_read_nu8(buffer_builder_buffer(&pIso7816->rxBldr)); + if( buffer_reader_readable(buffer_builder_buffer(&pIso7816->rxBldr)) >= lc ) + { + buffer_split(&pIso7816->cApdu.dataIn, buffer_builder_buffer(&pIso7816->rxBldr), buffer_builder_buffer(&pIso7816->rxBldr), lc); + } + else + { + pIso7816->rApdu.sw = ISO7816_SW_WRONG_LENGTH; + nfc_tech_iso7816_reply(pIso7816); + return NFC_ERR_LENGTH; //Not a valid frame + } + } + + if( buffer_reader_readable(buffer_builder_buffer(&pIso7816->rxBldr)) >= 1 ) + { + pIso7816->cApdu.maxRespLength = buffer_read_nu8(buffer_builder_buffer(&pIso7816->rxBldr)); + } + + NFC_DBG("C-APDU: CLA:%02X INS:%02X P1:%02X P2:%02X LC:%02X LE:%02X", pIso7816->cApdu.cla, pIso7816->cApdu.ins, pIso7816->cApdu.p1, pIso7816->cApdu.p2, + buffer_reader_readable(&pIso7816->cApdu.dataIn), pIso7816->cApdu.maxRespLength); + + if( buffer_reader_readable(buffer_builder_buffer(&pIso7816->rxBldr)) > 0 ) + { + pIso7816->rApdu.sw = ISO7816_SW_WRONG_LENGTH; + nfc_tech_iso7816_reply(pIso7816); + return NFC_ERR_LENGTH; //Not a valid frame + } + + //See if can select an app + if( iso7816_mf_command(pIso7816) ) + { + nfc_tech_iso7816_reply(pIso7816); + return NFC_OK; + } + + //Pass command to selected app + if( pIso7816->pSelectedApp == NULL ) + { + pIso7816->rApdu.sw = ISO7816_SW_NOT_FOUND; + nfc_tech_iso7816_reply(pIso7816); + return NFC_ERR_NOT_FOUND; //Not a valid frame + } + + pIso7816->pSelectedApp->apdu(pIso7816->pSelectedApp, pIso7816->pSelectedApp->pUserData); + + return NFC_OK; +} + +void iso7816_receive(nfc_tech_iso7816_t* pIso7816) +{ + buffer_builder_reset(&pIso7816->rxBldr); + nfc_tech_isodep_target_receive(&pIso7816->isoDepTarget, &pIso7816->outputStream, iso_dep_received_cb, pIso7816); +} + +nfc_err_t iso7816_transmit(nfc_tech_iso7816_t* pIso7816) +{ + return nfc_tech_isodep_target_transmit(&pIso7816->isoDepTarget, &pIso7816->inputStream, iso_dep_transmitted_cb, pIso7816); +} + +/** Handle ISO7816-4 command + * \param pTarget pointer to target instance + * \param CLA ISO7816-4 class byte + * \param INS ISO7816-4 instruction byte + * \param P1 ISO7816-4 P1 byte + * \param P2 ISO7816-4 P2 byte + * \param pDataIn ISO7816-4 command payload + * \param pDataOut ISO7816-4 response payload + * \param SW status word + * \return true if command was handled, false if it should be passed to the selected application + */ +bool iso7816_mf_command(nfc_tech_iso7816_t* pIso7816) +{ + nfc_tech_iso7816_app_t* pApp; + if(pIso7816->cApdu.cla != 0x00) + { + return false; + } + switch(pIso7816->cApdu.ins) + { + case ISO7816_INS_SELECT: + switch(pIso7816->cApdu.p1) + { + case 0x04: //Selection by DF name + pApp = pIso7816->pAppList; + while(pApp != NULL) + { + if( buffer_reader_readable(&pIso7816->cApdu.dataIn) <= pApp->aidSize ) + { + if( buffer_reader_cmp_bytes(&pIso7816->cApdu.dataIn, pApp->aid, buffer_reader_readable(&pIso7816->cApdu.dataIn)) ) + { + if(pIso7816->pSelectedApp != NULL) + { + //Deselect previous app + pIso7816->pSelectedApp->deselected(pIso7816->pSelectedApp, pIso7816->pSelectedApp->pUserData); + } + pIso7816->pSelectedApp = pApp; + pIso7816->pSelectedApp->selected(pIso7816->pSelectedApp, pIso7816->pSelectedApp->pUserData); + pIso7816->rApdu.sw = ISO7816_SW_OK; + return true; + } + } + pApp = pApp->pNext; + } + pIso7816->rApdu.sw = ISO7816_SW_NOT_FOUND; + return true; + default: + if(pIso7816->pSelectedApp == NULL) + { + pIso7816->rApdu.sw = ISO7816_SW_NOT_FOUND; + return true; + } + else + { + return false; + } + } + break; + default: + if(pIso7816->pSelectedApp == NULL) + { + pIso7816->rApdu.sw = ISO7816_SW_INVALID_INS; + return true; + } + else + { + return false; + } + break; + } +} + +void iso_dep_received_cb(nfc_tech_isodep_t* pIsodep, nfc_err_t ret, void* pUserData) +{ + nfc_tech_iso7816_t* pIso7816 = (nfc_tech_iso7816_t*) pUserData; + + (void) pIsodep; + + if( ret ) + { + NFC_WARN("Got error %d", ret); + return; + } + + //Parse received APDU + ret = iso7816_parse(pIso7816); + if( ret ) + { + NFC_WARN("Got error %d", ret); + return; + } +} + +void iso_dep_transmitted_cb(nfc_tech_isodep_t* pIsodep, nfc_err_t ret, void* pUserData) +{ + nfc_tech_iso7816_t* pIso7816 = (nfc_tech_iso7816_t*) pUserData; + + (void) pIsodep; + + if( ret ) + { + NFC_WARN("Got error %d", ret); + return; + } + + //Advertise that we have space in our buffer? + + //Switch to receive mode! + iso7816_receive(pIso7816); +} + +void iso_dep_disconnected_cb(nfc_tech_isodep_t* pIsodep, bool deselected, void* pUserData) +{ + nfc_tech_iso7816_t* pIso7816 = (nfc_tech_iso7816_t*) pUserData; + + (void) pIsodep; + + NFC_DBG("ISO DEP disconnected"); + iso7816_disconnected(pIso7816, deselected); +} + +void iso_dep_stream_transmit_cb(buffer_t* pDataIn, bool* pClose, size_t maxLength, void* pUserParam) +{ + nfc_tech_iso7816_t* pIso7816 = (nfc_tech_iso7816_t*) pUserParam; + + //Only close if buffer fits in this frame + if( maxLength >= buffer_reader_readable(&pIso7816->rApdu.dataOut) ) + //if( buffer_total_length(&pLlcp->tx) <= maxLength ) + { + maxLength = buffer_reader_readable(&pIso7816->rApdu.dataOut); + *pClose = true; + } + else + { + *pClose = false; + } + + buffer_split(pDataIn, &pIso7816->rApdu.dataOut, &pIso7816->rApdu.dataOut, maxLength); +} + +void iso_dep_stream_receive_cb(buffer_t* pDataOut, bool closed, void* pUserParam) +{ + nfc_tech_iso7816_t* pIso7816 = (nfc_tech_iso7816_t*) pUserParam; + + (void) closed; + + if( buffer_reader_readable(pDataOut) > buffer_builder_writeable(&pIso7816->rxBldr) ) + { + NFC_ERR("Frame will not fit (%d > %d)", buffer_reader_readable(pDataOut), buffer_builder_writeable(&pIso7816->rxBldr) ); + } + + //Feed rx buffer + buffer_builder_copy_n_bytes(&pIso7816->rxBldr, pDataOut, buffer_reader_readable(pDataOut)); +} + diff --git a/features/nfc/stack/tech/iso7816/iso7816.h b/features/nfc/stack/tech/iso7816/iso7816.h new file mode 100644 index 0000000000..1a0b837c08 --- /dev/null +++ b/features/nfc/stack/tech/iso7816/iso7816.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2014-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 iso7816.h + * \copyright Copyright (c) ARM Ltd 2014 + * \author Donatien Garnier + */ + +#ifndef ISO7816_H_ +#define ISO7816_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "acore/fwk.h" +#include "transceiver/protocols.h" + +#include "tech/isodep/isodep_target.h" + +struct nfc_tech_iso7816_c_apdu +{ + uint8_t cla; + uint8_t ins; + uint8_t p1; + uint8_t p2; + buffer_t dataIn; + size_t maxRespLength; +}; + +struct nfc_tech_iso7816_r_apdu +{ + buffer_t dataOut; + uint16_t sw; +}; + +#define ISO7816_RX_BUFFER_SIZE 256 + +typedef struct nfc_tech_iso7816_c_apdu nfc_tech_iso7816_c_apdu_t; +typedef struct nfc_tech_iso7816_r_apdu nfc_tech_iso7816_r_apdu_t; + +typedef struct nfc_tech_iso7816 nfc_tech_iso7816_t; + +typedef void (*nfc_tech_iso7816_disconnected_cb)(nfc_tech_iso7816_t* pIso7816, bool deselected, void* pUserData); + +struct nfc_tech_iso7816_app; +typedef struct nfc_tech_iso7816_app nfc_tech_iso7816_app_t; + +struct nfc_tech_iso7816 +{ + nfc_tech_isodep_target_t isoDepTarget; + + nfc_tech_iso7816_app_t* pAppList; + nfc_tech_iso7816_app_t* pSelectedApp; + + bool disconnected; + + nfc_tech_iso7816_c_apdu_t cApdu; + nfc_tech_iso7816_r_apdu_t rApdu; + + bool responseReady; + + nfc_tech_iso7816_disconnected_cb disconnectedCb; + void* pUserData; + + buffer_t hist; //Historical bytes + + istream_t inputStream; + ostream_t outputStream; + + //PDU buffer (tx) + uint8_t txBuf[2]; + buffer_builder_t txBldr; + + //Receive buffer + uint8_t rxBuf[ISO7816_RX_BUFFER_SIZE]; + buffer_builder_t rxBldr; +}; + +void nfc_tech_iso7816_init(nfc_tech_iso7816_t* pIso7816, nfc_transceiver_t* pTransceiver, nfc_tech_iso7816_disconnected_cb disconnectedCb, void* pUserData); +void nfc_tech_iso7816_add_app(nfc_tech_iso7816_t* pIso7816, nfc_tech_iso7816_app_t* pIso7816App); +void nfc_tech_iso7816_connect(nfc_tech_iso7816_t* pIso7816); +void nfc_tech_iso7816_disconnect(nfc_tech_iso7816_t* pIso7816); +nfc_err_t nfc_tech_iso7816_reply(nfc_tech_iso7816_t* pIso7816); + +inline static nfc_tech_iso7816_c_apdu_t* nfc_tech_iso7816_c_apdu(nfc_tech_iso7816_t* pIso7816) +{ + return &pIso7816->cApdu; +} + +inline static nfc_tech_iso7816_r_apdu_t* nfc_tech_iso7816_r_apdu(nfc_tech_iso7816_t* pIso7816) +{ + return &pIso7816->rApdu; +} + +#ifdef __cplusplus +} +#endif + +#endif /* ISO7816_H_ */ diff --git a/features/nfc/stack/tech/iso7816/iso7816_app.c b/features/nfc/stack/tech/iso7816/iso7816_app.c new file mode 100644 index 0000000000..dd5b62669b --- /dev/null +++ b/features/nfc/stack/tech/iso7816/iso7816_app.c @@ -0,0 +1,42 @@ +/* + * 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 iso7816_app.c + * \copyright Copyright (c) ARM Ltd 2015-2018 + * \author Donatien Garnier + */ + +#include "iso7816_app.h" + +void nfc_tech_iso7816_app_init(nfc_tech_iso7816_app_t* pIso7816App, + nfc_tech_iso7816_t* pIso7816, + const uint8_t* aid, size_t aidSize, + nfc_tech_iso7816_app_cb selected, + nfc_tech_iso7816_app_cb deselected, + nfc_tech_iso7816_app_cb apdu, + void* pUserData +) +{ + pIso7816App->pIso7816 = pIso7816; + pIso7816App->aid = aid; + pIso7816App->aidSize = aidSize; + pIso7816App->selected = selected; + pIso7816App->deselected = deselected; + pIso7816App->apdu = apdu; + pIso7816App->pUserData = pUserData; + pIso7816App->pNext = NULL; +} diff --git a/features/nfc/stack/tech/iso7816/iso7816_app.h b/features/nfc/stack/tech/iso7816/iso7816_app.h new file mode 100644 index 0000000000..a52494941b --- /dev/null +++ b/features/nfc/stack/tech/iso7816/iso7816_app.h @@ -0,0 +1,81 @@ +/* + * 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 iso7816_app.h + * \copyright Copyright (c) ARM Ltd 2015 + * \author Donatien Garnier + */ +#ifndef TECH_ISO7816_ISO7816_APP_H_ +#define TECH_ISO7816_ISO7816_APP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "inc/nfc.h" +#include "iso7816.h" + +struct nfc_tech_iso7816; +struct nfc_tech_iso7816_app; +typedef struct nfc_tech_iso7816 nfc_tech_iso7816_t; +typedef struct nfc_tech_iso7816_app nfc_tech_iso7816_app_t; + +typedef void (*nfc_tech_iso7816_app_cb)( nfc_tech_iso7816_app_t* pIso7816App, void* pUserData ); + +struct nfc_tech_iso7816_app +{ + nfc_tech_iso7816_t* pIso7816; + + const uint8_t* aid; + size_t aidSize; + + nfc_tech_iso7816_app_cb selected; + nfc_tech_iso7816_app_cb deselected; + nfc_tech_iso7816_app_cb apdu; + + void* pUserData; + + nfc_tech_iso7816_app_t* pNext; +}; + +void nfc_tech_iso7816_app_init(nfc_tech_iso7816_app_t* pIso7816App, nfc_tech_iso7816_t* pIso7816, const uint8_t* aid, size_t aidSize, + nfc_tech_iso7816_app_cb selected, + nfc_tech_iso7816_app_cb deselected, + nfc_tech_iso7816_app_cb apdu, + void* pUserData +); + +inline static nfc_err_t nfc_tech_iso7816_app_reply(nfc_tech_iso7816_app_t* pIso7816App) +{ + return nfc_tech_iso7816_reply(pIso7816App->pIso7816); +} + +inline static nfc_tech_iso7816_c_apdu_t* nfc_tech_iso7816_app_c_apdu(nfc_tech_iso7816_app_t* pIso7816App) +{ + return nfc_tech_iso7816_c_apdu(pIso7816App->pIso7816); +} + +inline static nfc_tech_iso7816_r_apdu_t* nfc_tech_iso7816_app_r_apdu(nfc_tech_iso7816_app_t* pIso7816App) +{ + return nfc_tech_iso7816_r_apdu(pIso7816App->pIso7816); +} + +#ifdef __cplusplus +} +#endif + +#endif /* TECH_ISO7816_ISO7816_APP_H_ */ diff --git a/features/nfc/stack/tech/iso7816/iso7816_defs.h b/features/nfc/stack/tech/iso7816/iso7816_defs.h new file mode 100644 index 0000000000..7f7e42c130 --- /dev/null +++ b/features/nfc/stack/tech/iso7816/iso7816_defs.h @@ -0,0 +1,42 @@ +/* + * 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 iso7816_defs.h + * \copyright Copyright (c) ARM Ltd 2013 + * \author Donatien Garnier + */ + +#ifndef ISO7816_DEFS_H_ +#define ISO7816_DEFS_H_ + +#define ISO7816_INS_SELECT 0xA4 +#define ISO7816_INS_READ_BINARY 0xB0 +#define ISO7816_INS_UPDATE_BINARY 0xD6 +#define ISO7816_INS_ENVELOPE 0xC2 + + +#define ISO7816_SW_OK 0x9000 +#define ISO7816_SW_INVALID_CLASS 0x6E00 +#define ISO7816_SW_INVALID_INS 0x6D00 +#define ISO7816_SW_NOT_FOUND 0x6A82 +#define ISO7816_SW_WRONG_LENGTH 0x6700 + +#define ISO7816_PUT_SW(buf, sw) do{ *(buf)=(sw>>8) & 0xFF; *(buf+1)=(sw>>0) & 0xFF; } while(0); + + + +#endif /* ISO7816_DEFS_H_ */ diff --git a/features/nfc/stack/tech/isodep/isodep.h b/features/nfc/stack/tech/isodep/isodep.h new file mode 100644 index 0000000000..adda627b86 --- /dev/null +++ b/features/nfc/stack/tech/isodep/isodep.h @@ -0,0 +1,48 @@ +/* + * 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 isodep.h + * \copyright Copyright (c) ARM Ltd 2014 + * \author Donatien Garnier + */ + +#ifndef ISODEP_H_ +#define ISODEP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "transceiver/transceiver.h" + +struct nfc_tech_isodep; +typedef struct nfc_tech_isodep nfc_tech_isodep_t; + +typedef void (*nfc_tech_isodep_cb_t)(nfc_tech_isodep_t* pIsodep, nfc_err_t ret, void* pUserData); +typedef void (*nfc_tech_isodep_disconnected_cb)(nfc_tech_isodep_t* pIsodep, bool deselected, void* pUserData); + + +struct nfc_tech_isodep +{ + +}; + +#ifdef __cplusplus +} +#endif + +#endif /* ISODEP_H_ */ diff --git a/features/nfc/stack/tech/isodep/isodep_target.c b/features/nfc/stack/tech/isodep/isodep_target.c new file mode 100644 index 0000000000..5f7918c267 --- /dev/null +++ b/features/nfc/stack/tech/isodep/isodep_target.c @@ -0,0 +1,750 @@ +/* + * 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 isodep_target.c + * \copyright Copyright (c) ARM Ltd 2015 + * \author Donatien Garnier + */ + +#define __DEBUG__ 0 +#ifndef __MODULE__ +#define __MODULE__ "isodep_target.c" +#endif + +#include "inc/nfc.h" +#include "isodep_target.h" + +#include "transceiver/transceiver.h" + +//Private defines +#define RATS 0xE0 +#define FSDI_TO_FSD(x) (((x)<5)?(((x)<<3) + 16):((x)<8)?(((x)<<5)-96):256) +#define FSC_TO_FSCI(x) (((x)<=48)?(((x)-16)>>3):((x)<=128)?(((x)+96)>>5):8) //returns the closest lower or equal value +#define DID(x) ((x) & 0x0F) +#define FSDI(x) (((x) >> 4) & 0x0F) + +#define FSCI_TO_FSC(x) FSDI_TO_FSD(x) +#define FSD_TO_FSDI(x) FSC_TO_FSCI(x) + +//#define DI_TO_D(x) (1 << (x)) +#define DI_TO_BITRATE(x) ((RF_BITRATE)((x) + RF_BITRATE_106K)) + + +#define GET_FRAME_TYPE(pcb) ((((pcb) & 0xF0) == 0xD0)?PPS_FRAME:(((pcb) & 0xC0) == (0x00 << 6))?I_FRAME:((((pcb) & 0xC0) == (0x2 << 6))?R_FRAME:S_FRAME)) + +#define I_BLOCK_PCB 0 +#define R_BLOCK_PCB 2 +#define S_BLOCK_PCB 3 + +#define PCB_TYPE(pcb) (((pcb)>>6)&0x03) + +#define BUILD_I_BLOCK_PCB(chaining, cid, nad, block_toggle) ( (0x0 << 6) | (((chaining)?1:0) << 4) \ + | (((cid)?1:0) << 3) | (((nad)?1:0) << 2) | (1 << 1) | (((block_toggle)?1:0)) ) +#define BUILD_S_BLOCK_PCB(cid, wtx_n_deselect) ( (0x3 << 6) | (((wtx_n_deselect)?0x3:0) << 4) \ + | (((cid)?1:0) << 3) | (1 << 1) ) +#define BUILD_R_BLOCK_PCB(cid, block_toggle, nak) ( (0x2 << 6) | (1 <<5) | (((nak)?1:0) << 4) \ + | (((cid)?1:0) << 3) | (1 << 1) | (((block_toggle)?1:0)) ) + +#define PCB_IS_CID(pcb) (((pcb) & (1 << 3))?true:false) +#define PCB_BLOCK_TOGGLE(pcb) (((pcb) & 1)?true:false) +#define PCB_CHAINING(pcb) (((pcb) & 0x10)?true:false) +#define PCB_NACK(pcb) (((pcb) & 0x10)?true:false) +#define PCB_WTX(pcb) (((pcb)&0x30)==0x30) + +#define WTXM_DEFAULT 10 + +//Parameters +#define FSC 256 //Maximum frame size the PICC (us) can receive -- TODO should be a parameter at some point -- linked to PN512 buffer +#define SFGI 2 //Guard time ~ 1.2ms +//#define FWI 6 //Max time before answer is ~ 19.3ms +#define FWI 14 //Max time before answer is ~ 19.3ms + +typedef enum __dep_type dep_type_t; +enum __dep_type +{ + dep_type_information, + dep_type_response, + dep_type_supervisory, +}; + +//Local functions +static void dep_init(nfc_tech_isodep_target_t* pIsodepTarget); +static bool dep_ready(nfc_tech_isodep_target_t* pIsodepTarget); + +static void dep_req_information(nfc_tech_isodep_target_t* pIsodepTarget, buffer_t* pReq, bool moreInformation, uint8_t blockNumber); +static void dep_req_response(nfc_tech_isodep_target_t* pIsodepTarget, bool ack, uint8_t blockNumber); +static void dep_req_supervisory(nfc_tech_isodep_target_t* pIsodepTarget, bool wtxNDeselect, uint8_t wtxm); + +static dep_type_t dep_res_type(nfc_tech_isodep_target_t* pIsodepTarget); +static void dep_res_information(nfc_tech_isodep_target_t* pIsodepTarget, size_t maxLength, buffer_t** ppRes, bool* pMoreInformation, uint8_t* pBlockNumber); +static void dep_res_response(nfc_tech_isodep_target_t* pIsodepTarget, bool* pAck, uint8_t* pBlockNumber); +static void dep_res_supervisory(nfc_tech_isodep_target_t* pIsodepTarget, bool *pWtxNDeselect, uint8_t* pWtxm); + +static void dep_disconnected(nfc_tech_isodep_target_t* pIsodepTarget, bool deselected); + +static void command_init(nfc_tech_isodep_target_t* pIsodepTarget); + +static nfc_err_t command_ats_req(nfc_tech_isodep_target_t* pIsodepTarget); +static nfc_err_t command_dep_req(nfc_tech_isodep_target_t* pIsodepTarget); + +static nfc_err_t command_ats_res(nfc_tech_isodep_target_t* pIsodepTarget); +static nfc_err_t command_dep_res(nfc_tech_isodep_target_t* pIsodepTarget); + +static void command_reply(nfc_tech_isodep_target_t* pIsodepTarget, bool depWait); +static void command_transceiver_cb(nfc_transceiver_t* pTransceiver, nfc_err_t ret, void* pUserData); + +//High-level Target functions +void nfc_tech_isodep_target_init(nfc_tech_isodep_target_t* pIsodepTarget, nfc_transceiver_t* pTransceiver, + buffer_t* pHist, nfc_tech_isodep_disconnected_cb disconnectedCb, void* pUserData) +{ + pIsodepTarget->pTransceiver = pTransceiver; + + pIsodepTarget->pHist = pHist; + + pIsodepTarget->disconnectedCb = disconnectedCb; + pIsodepTarget->pUserData = pUserData; + + dep_init(pIsodepTarget); + command_init(pIsodepTarget); +} + +nfc_err_t nfc_tech_isodep_target_connect(nfc_tech_isodep_target_t* pIsodepTarget) +{ + NFC_DBG("Connecting"); + pIsodepTarget->commands.state = ISO_DEP_TARGET_COMMANDS_CONNECTING; + + transceiver_set_crc(pIsodepTarget->pTransceiver, true, true); + command_transceiver_cb(pIsodepTarget->pTransceiver, NFC_OK, pIsodepTarget); + + return NFC_OK; +} + +void nfc_tech_isodep_target_disconnect(nfc_tech_isodep_target_t* pIsodepTarget) +{ + // This should not be called within a callback + + transceiver_abort(pIsodepTarget->pTransceiver); + + dep_disconnected(pIsodepTarget, false); +} + +nfc_err_t nfc_tech_isodep_target_transmit(nfc_tech_isodep_target_t* pIsodepTarget, istream_t* pStream, nfc_tech_isodep_cb_t cb, void* pUserData) +{ + if( pIsodepTarget->dep.pReqStream != NULL ) + { + return NFC_ERR_BUSY; + } + + pIsodepTarget->dep.pResStream = pStream; + pIsodepTarget->dep.resCb = cb; + pIsodepTarget->dep.pResUserData = pUserData; + + //Do we need to start transceiving? + if( pIsodepTarget->commands.state == ISO_DEP_TARGET_COMMANDS_DEP_REQ_RECVD ) + { + command_reply(pIsodepTarget, false); //Force reply + } + + return NFC_OK; +} + +nfc_err_t nfc_tech_isodep_target_receive(nfc_tech_isodep_target_t* pIsodepTarget, ostream_t* pStream, nfc_tech_isodep_cb_t cb, void* pUserData) +{ + if( pIsodepTarget->dep.pResStream != NULL ) + { + return NFC_ERR_BUSY; + } + + pIsodepTarget->dep.pReqStream = pStream; + pIsodepTarget->dep.reqCb = cb; + pIsodepTarget->dep.pReqUserData = pUserData; + + return NFC_OK; +} + +//DEP Layer +void dep_init(nfc_tech_isodep_target_t* pIsodepTarget) +{ + //buffer_init(&pIsodepTarget->dep.res, NULL, 0); + pIsodepTarget->dep.pReqStream = NULL; + pIsodepTarget->dep.pResStream = NULL; + + pIsodepTarget->dep.reqCb = NULL; + pIsodepTarget->dep.pReqUserData = NULL; + + pIsodepTarget->dep.resCb = NULL; + pIsodepTarget->dep.pResUserData = NULL; + + pIsodepTarget->dep.blockNumber = 1; //Rule C + + //pIsodepTarget->dep.pduState = ISO_DEP_TARGET_DEP_PDU_IDLE; + pIsodepTarget->dep.chaining = false; + pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_IDLE; +} + +bool dep_ready(nfc_tech_isodep_target_t* pIsodepTarget) +{ + //Anything to send back? + if( pIsodepTarget->dep.frameState != ISO_DEP_TARGET_DEP_FRAME_INFORMATION_RECEIVED ) + { + return true; + } + + if( (pIsodepTarget->dep.pResStream != NULL) ) + { + return true; + } + else + { + return false; + } +} + +void dep_req_information(nfc_tech_isodep_target_t* pIsodepTarget, buffer_t* pReq, bool moreInformation, uint8_t blockNumber) +{ + (void) blockNumber; + + pIsodepTarget->dep.blockNumber++; + pIsodepTarget->dep.blockNumber%=2; + + // Note: callbacks can call nfc_tech_isodep_target_transmit() - however we must make sure that we wait AFTER this routine has been processed to actually transmit + // To do so, reset state to ATS_RES_SENT state + pIsodepTarget->commands.state = ISO_DEP_TARGET_COMMANDS_ATS_RES_SENT; + + if( !pIsodepTarget->dep.chaining + && (pIsodepTarget->dep.pResStream != NULL) ) + { + //Sent the full frame + pIsodepTarget->dep.pResStream = NULL; + pIsodepTarget->dep.resCb((nfc_tech_isodep_t*)pIsodepTarget, NFC_OK, pIsodepTarget->dep.pResUserData); + } + if( pIsodepTarget->dep.pReqStream != NULL ) + { + // Pull more + ostream_push(pIsodepTarget->dep.pReqStream, pReq, !moreInformation); + if(!moreInformation) + { + //Got the full frame + pIsodepTarget->dep.pReqStream = NULL; + pIsodepTarget->dep.reqCb((nfc_tech_isodep_t*)pIsodepTarget, NFC_OK, pIsodepTarget->dep.pReqUserData); + } + } + + // Update state + pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_INFORMATION_RECEIVED; + pIsodepTarget->dep.chaining = moreInformation; +} + +void dep_req_response(nfc_tech_isodep_target_t* pIsodepTarget, bool ack, uint8_t blockNumber) +{ + if( blockNumber != pIsodepTarget->dep.blockNumber ) + { + //Should be NACK + pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_NACK_DIFF_BLOCK_NUMBER_RECEIVED; + } + else + { + if(ack) + { + pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_ACK_RECEIVED; + } + else + { + pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_NACK_RECEIVED; + } + } +} + +void dep_req_supervisory(nfc_tech_isodep_target_t* pIsodepTarget, bool wtxNDeselect, uint8_t wtxm) +{ + (void) wtxm; + + if(wtxNDeselect) + { + if((pIsodepTarget->dep.frameState != ISO_DEP_TARGET_DEP_FRAME_WTX_SENT)) + { + NFC_WARN("Unexpected WTX frame"); + } + pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_WTX_RECEIVED; + } + else + { + pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_DESELECT_RECEIVED; + } +} + +dep_type_t dep_res_type(nfc_tech_isodep_target_t* pIsodepTarget) +{ + dep_type_t depType; + switch( pIsodepTarget->dep.frameState ) + { + case ISO_DEP_TARGET_DEP_FRAME_DESELECT_RECEIVED: + depType = dep_type_supervisory; //Deselect + break; + case ISO_DEP_TARGET_DEP_FRAME_INFORMATION_RECEIVED: + case ISO_DEP_TARGET_DEP_FRAME_WTX_RECEIVED: + if( pIsodepTarget->dep.chaining ) //Need to ack? + { + depType = dep_type_response; + } + else if( pIsodepTarget->dep.pResStream != NULL ) //Anything to send back? + { + depType = dep_type_information; + } + else + { + depType = dep_type_supervisory; //WTX + } + break; + case ISO_DEP_TARGET_DEP_FRAME_ACK_RECEIVED: + if(( pIsodepTarget->dep.pResStream != NULL ) && ( pIsodepTarget->dep.chaining )) + { + depType = dep_type_information; + } + else + { + depType = dep_type_supervisory; //WTX + } + break; + case ISO_DEP_TARGET_DEP_FRAME_NACK_DIFF_BLOCK_NUMBER_RECEIVED: + depType = dep_type_response; //Should send ACK + break; + case ISO_DEP_TARGET_DEP_FRAME_NACK_RECEIVED: + depType = dep_type_information; + break; + default: + depType = dep_type_supervisory; //ATN + break; + } + return depType; +} + +void dep_res_information(nfc_tech_isodep_target_t* pIsodepTarget, size_t maxLength, buffer_t** ppRes, bool* pMoreInformation, uint8_t* pBlockNumber) +{ + *pBlockNumber = pIsodepTarget->dep.blockNumber; + if( pIsodepTarget->dep.frameState != ISO_DEP_TARGET_DEP_FRAME_NACK_RECEIVED ) + { + if( pIsodepTarget->dep.pResStream != NULL ) + { + bool lastFrame = true; + istream_pull(pIsodepTarget->dep.pResStream, &pIsodepTarget->dep.res, &lastFrame, maxLength); + pIsodepTarget->dep.chaining = !lastFrame; + } + } + else + { + //Retransmit previous frame (leave it as it is) + } + *ppRes = &pIsodepTarget->dep.res; + *pMoreInformation = pIsodepTarget->dep.chaining; + pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_INFORMATION_SENT; +} + +void dep_res_response(nfc_tech_isodep_target_t* pIsodepTarget, bool* pAck, uint8_t* pBlockNumber) +{ + //Continue chaining or send ACK + *pAck = true; + *pBlockNumber = pIsodepTarget->dep.blockNumber; + pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_ACK_SENT; +} + +void dep_res_supervisory(nfc_tech_isodep_target_t* pIsodepTarget, bool *pWtxNDeselect, uint8_t* pWtxm) +{ + if( pIsodepTarget->dep.frameState == ISO_DEP_TARGET_DEP_FRAME_DESELECT_RECEIVED ) + { + *pWtxNDeselect = false; + pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_DESELECT_SENT; + } + else + { + *pWtxNDeselect = true; + *pWtxm = WTXM_DEFAULT; + pIsodepTarget->dep.frameState = ISO_DEP_TARGET_DEP_FRAME_WTX_SENT; + } +} + +void dep_disconnected(nfc_tech_isodep_target_t* pIsodepTarget, bool deselected) +{ + //Call callbacks if needed + if( pIsodepTarget->dep.pReqStream != NULL ) + { + pIsodepTarget->dep.reqCb((nfc_tech_isodep_t*)pIsodepTarget, NFC_ERR_DISCONNECTED, pIsodepTarget->dep.pReqUserData); + pIsodepTarget->dep.pReqStream = NULL; + } + if( pIsodepTarget->dep.pReqStream != NULL ) + { + pIsodepTarget->dep.resCb((nfc_tech_isodep_t*)pIsodepTarget, NFC_ERR_DISCONNECTED, pIsodepTarget->dep.pResUserData); + pIsodepTarget->dep.pResStream = NULL; + } + if( pIsodepTarget->commands.state != ISO_DEP_TARGET_COMMANDS_DISCONNECTED ) + { + pIsodepTarget->commands.state = ISO_DEP_TARGET_COMMANDS_DISCONNECTED; + pIsodepTarget->disconnectedCb((nfc_tech_isodep_t*)pIsodepTarget, deselected, pIsodepTarget->pUserData); + } +} + +//Commands Layer +void command_init(nfc_tech_isodep_target_t* pIsodepTarget) +{ + buffer_builder_init(&pIsodepTarget->commands.respBldr, pIsodepTarget->commands.respBuf, sizeof(pIsodepTarget->commands.respBuf)); + pIsodepTarget->commands.pReq = NULL; + // Update if/when we support DIDs + //pIsodepTarget->commands.did = 0; + //pIsodepTarget->commands.didUsed = false; + pIsodepTarget->commands.state = ISO_DEP_TARGET_COMMANDS_DISCONNECTED; + pIsodepTarget->commands.inPayloadSize = 0; +} + +nfc_err_t command_ats_req(nfc_tech_isodep_target_t* pIsodepTarget) +{ + //Check we are in a correct state -- this should be the first command received + if( pIsodepTarget->commands.state != ISO_DEP_TARGET_COMMANDS_CONNECTING ) + { + return NFC_ERR_PROTOCOL; + } + + if( buffer_reader_readable(pIsodepTarget->commands.pReq) < 2 ) + { + NFC_ERR("Payload too short"); + return NFC_ERR_PROTOCOL; + } + + buffer_read_n_skip(pIsodepTarget->commands.pReq, 1); + + uint8_t b = buffer_read_nu8(pIsodepTarget->commands.pReq); + + //Save DID -- not supported for now + //pIsodepTarget->commands.did = DID(b); + + uint8_t fsdi = FSDI(b); + pIsodepTarget->commands.inPayloadSize = FSDI_TO_FSD(fsdi); + + pIsodepTarget->commands.state = ISO_DEP_TARGET_COMMANDS_ATS_REQ_RECVD; + + return NFC_OK; +} + +nfc_err_t command_dep_req(nfc_tech_isodep_target_t* pIsodepTarget) +{ + if( pIsodepTarget->commands.state < ISO_DEP_TARGET_COMMANDS_ATS_RES_SENT ) + { + return NFC_ERR_PROTOCOL; + } + + if( buffer_reader_readable(pIsodepTarget->commands.pReq) < 1 ) + { + NFC_ERR("Payload too short"); + return NFC_ERR_PROTOCOL; + } + + uint8_t pcb = buffer_read_nu8(pIsodepTarget->commands.pReq); + + // Udpate if/when we support DIDs + /* + if( pfb & PFB_DID ) + { + uint8_t did = buffer_read_nu8(pIsodepTarget->commands.pReq); + if( pIsodepTarget->commands.did != did ) + { + //Not for us + return NFC_ERR_PROTOCOL; + } + pIsodepTarget->commands.didUsed = true; + } + else + { + pIsodepTarget->commands.didUsed = false; + } + + if( pfb & PFB_NAD ) + { + buffer_read_nu8(pIsodepTarget->commands.pReq); //Skip NAD + } + */ + + uint8_t wtxm = 0; + switch( PCB_TYPE(pcb) ) + { + case I_BLOCK_PCB: + dep_req_information(pIsodepTarget, pIsodepTarget->commands.pReq, PCB_CHAINING(pcb), PCB_BLOCK_TOGGLE(pcb)); + break; + case R_BLOCK_PCB: + dep_req_response(pIsodepTarget, !PCB_NACK(pcb), PCB_BLOCK_TOGGLE(pcb)); + break; + case S_BLOCK_PCB: + if( PCB_WTX(pcb) ) + { + if( buffer_reader_readable(pIsodepTarget->commands.pReq) < 1 ) + { + NFC_ERR("Payload too short"); + return NFC_ERR_PROTOCOL; + } + wtxm = buffer_read_nu8(pIsodepTarget->commands.pReq); + } + dep_req_supervisory(pIsodepTarget, PCB_WTX(pcb), wtxm); + break; + default: + NFC_ERR("PCB is invalid"); + return NFC_ERR_PROTOCOL; + } + + pIsodepTarget->commands.state = ISO_DEP_TARGET_COMMANDS_DEP_REQ_RECVD; + + return NFC_OK; +} + +nfc_err_t command_ats_res(nfc_tech_isodep_target_t* pIsodepTarget) +{ + //Send ATS back + if (buffer_builder_writeable(&pIsodepTarget->commands.respBldr) < 5 ) + { + return NFC_ERR_BUFFER_TOO_SMALL; + } + + buffer_builder_write_nu8(&pIsodepTarget->commands.respBldr, (5 + buffer_size(pIsodepTarget->pHist)) & 0xFF); + + //T0 + buffer_builder_write_nu8(&pIsodepTarget->commands.respBldr, (0x7 << 4) | FSC_TO_FSCI( FSC )); //TA(1), TB(1) and TC(1) are transmitted + + //TA(1) + //For now only 106kbps supported + buffer_builder_write_nu8(&pIsodepTarget->commands.respBldr, (1 << 7) | (0x0 << 4) | 0x0); + + //TODO when supporting other bitrates + //buffer_builder_write_nu8(&pIsodepTarget->commands.respBldr, (0 << 7) | (0x3 << 4) | 0x3); //106, 212, 414 kbps bitrates + + //TB(1) + buffer_builder_write_nu8(&pIsodepTarget->commands.respBldr, (FWI << 4) | SFGI); //Specify guard-time and time between frames + + //TC(1) + buffer_builder_write_nu8(&pIsodepTarget->commands.respBldr, (0 << 1) | 0); //DID not supported, NAD not supported + + buffer_set_next(buffer_builder_buffer(&pIsodepTarget->commands.respBldr), pIsodepTarget->pHist); //Queue general bytes + + pIsodepTarget->commands.state = ISO_DEP_TARGET_COMMANDS_ATS_RES_SENT; + + //TODO PPS + + return NFC_OK; +} + +nfc_err_t command_dep_res(nfc_tech_isodep_target_t* pIsodepTarget) +{ + uint8_t pcb = 0; + + // If/when supporting DIDs + /* + if( pIsodepTarget->commands.didUsed ) + { + pcb |= PFB_DID; + }*/ + + buffer_t* pDepBuf = NULL; + bool moreInformation = false; + bool ack = false; + bool wtxNDeselect = false; + uint8_t wtxm = 0; + uint8_t blockNumber = 0; + + size_t maxLength = pIsodepTarget->commands.inPayloadSize - 1; + + switch( dep_res_type(pIsodepTarget) ) + { + case dep_type_information: + dep_res_information(pIsodepTarget, maxLength, &pDepBuf, &moreInformation, &blockNumber); + pcb = BUILD_I_BLOCK_PCB(moreInformation, false, false, blockNumber); + break; + case dep_type_response: + dep_res_response(pIsodepTarget, &ack, &blockNumber); + pcb = BUILD_R_BLOCK_PCB(0, blockNumber, !ack); + break; + case dep_type_supervisory: + dep_res_supervisory(pIsodepTarget, &wtxNDeselect, &wtxm); + pcb = BUILD_S_BLOCK_PCB(0, wtxNDeselect); + break; + } + + buffer_builder_write_nu8(&pIsodepTarget->commands.respBldr, pcb); +/* + if( pIsodepTarget->commands.didUsed ) + { + buffer_builder_write_nu8(&pIsodepTarget->commands.respBldr, pIsodepTarget->commands.did); + } +*/ + if( pDepBuf != NULL ) + { + buffer_set_next(buffer_builder_buffer(&pIsodepTarget->commands.respBldr), pDepBuf); + } + else if(wtxNDeselect) + { + buffer_builder_write_nu8(&pIsodepTarget->commands.respBldr, wtxm); + } + + pIsodepTarget->commands.state = ISO_DEP_TARGET_COMMANDS_DEP_RES_SENT; + + return NFC_OK; +} + +void command_reply(nfc_tech_isodep_target_t* pIsodepTarget, bool depWait) +{ + nfc_err_t ret; + + //Check whether we want to reply or wait for the higher layer to send us something + if((pIsodepTarget->commands.state == ISO_DEP_TARGET_COMMANDS_DEP_REQ_RECVD) && depWait && !dep_ready(pIsodepTarget)) + { + return; + } + + //Reply + buffer_builder_reset(&pIsodepTarget->commands.respBldr); + + switch(pIsodepTarget->commands.state) + { + case ISO_DEP_TARGET_COMMANDS_ATS_REQ_RECVD: + ret = command_ats_res(pIsodepTarget); + break; + case ISO_DEP_TARGET_COMMANDS_DEP_REQ_RECVD: + ret = command_dep_res(pIsodepTarget); + break; + default: + NFC_ERR("Unknown state %d", pIsodepTarget->commands.state); + //Go back to receive mode + nfc_transceiver_transceive(pIsodepTarget->pTransceiver, command_transceiver_cb, pIsodepTarget); + return; + } + + if(ret) + { + NFC_ERR("Error %d", ret); + //Go back to receive mode + nfc_transceiver_transceive(pIsodepTarget->pTransceiver, command_transceiver_cb, pIsodepTarget); + return; + } + + NFC_DBG("Transceive"); + + if( pIsodepTarget->dep.frameState == ISO_DEP_TARGET_DEP_FRAME_DESELECT_SENT ) + { + transceiver_set_transceive_options(pIsodepTarget->pTransceiver, true, false, true); + } + else + { + transceiver_set_transceive_options(pIsodepTarget->pTransceiver, true, true, false); + } + + DBG_BLOCK( buffer_dump(buffer_builder_buffer(&pIsodepTarget->commands.respBldr)); ) + + //Send next frame + transceiver_set_write(pIsodepTarget->pTransceiver, buffer_builder_buffer(&pIsodepTarget->commands.respBldr)); + nfc_transceiver_transceive(pIsodepTarget->pTransceiver, command_transceiver_cb, pIsodepTarget); + + NFC_DBG("Processed"); +} + +void command_transceiver_cb(nfc_transceiver_t* pTransceiver, nfc_err_t ret, void* pUserData) +{ + nfc_tech_isodep_target_t* pIsodepTarget = (nfc_tech_isodep_target_t*) pUserData; + + if( ret == NFC_ERR_ABORTED ) + { + // Just return + return; + } + + if( pIsodepTarget->dep.frameState == ISO_DEP_TARGET_DEP_FRAME_DESELECT_SENT ) + { + //We are now disconnected (deselected) + //Reset status + dep_init(pIsodepTarget); + command_init(pIsodepTarget); + + transceiver_set_crc(pIsodepTarget->pTransceiver, true, true); + pIsodepTarget->commands.state = ISO_DEP_TARGET_COMMANDS_CONNECTING; + + //Call so that we can reinit higher layer + dep_disconnected(pIsodepTarget, true); //This will call us again + return; + } + + //Prepare default empty reply + transceiver_set_write(pTransceiver, NULL); + transceiver_set_transceive_options(pTransceiver, false, true, false); + + if( ret == NFC_ERR_FIELD ) + { + NFC_WARN("Lost initiator"); + dep_disconnected(pIsodepTarget, false); + return; + } + else if(ret) + { + //We should ignore this error and wait for another frame + NFC_WARN("Got invalid frame (error %d)", ret); + + nfc_transceiver_transceive(pTransceiver, command_transceiver_cb, pIsodepTarget); + return; + } + + NFC_DBG("Reading data from initiator"); + buffer_t* pDataInitiator = transceiver_get_read(pTransceiver); //In buffer + + DBG_BLOCK( buffer_dump(pDataInitiator); ) + + //Framing is handled by transceiver + if( (buffer_reader_readable(pDataInitiator) < 1) ) + { + NFC_ERR("Empty initiator message"); + + //Go back to receive mode + nfc_transceiver_transceive(pTransceiver, command_transceiver_cb, pIsodepTarget); + return; + } + + pIsodepTarget->commands.pReq = pDataInitiator; + + //Duplicate to peek on req + buffer_t dataInitiatorDup; + buffer_dup(&dataInitiatorDup, pDataInitiator); + uint8_t req = buffer_read_nu8(&dataInitiatorDup); + + switch(req) + { + case RATS: + ret = command_ats_req(pIsodepTarget); + break; + default: + ret = command_dep_req(pIsodepTarget); + break; + } + + if(ret) + { + NFC_ERR("Error %d", ret); + + //Go back to receive mode + nfc_transceiver_transceive(pTransceiver, command_transceiver_cb, pIsodepTarget); + return; + } + + NFC_DBG("Reply"); + + //Reply + command_reply(pIsodepTarget, true); //Make sure we send a WTX frame if we cannot respond straight away +} + + + diff --git a/features/nfc/stack/tech/isodep/isodep_target.h b/features/nfc/stack/tech/isodep/isodep_target.h new file mode 100644 index 0000000000..84d18cf76d --- /dev/null +++ b/features/nfc/stack/tech/isodep/isodep_target.h @@ -0,0 +1,119 @@ +/* + * 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 isodep_target.h + * \copyright Copyright (c) ARM Ltd 2015 + * \author Donatien Garnier + */ + +#ifndef ISODEP_TARGET_H_ +#define ISODEP_TARGET_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "inc/nfc.h" +#include "transceiver/transceiver.h" +#include "isodep.h" + +struct nfc_tech_isodep_target; +typedef struct nfc_tech_isodep_target nfc_tech_isodep_target_t; + +typedef struct nfc_tech_isodep_target nfc_tech_isodep_target_t; +struct nfc_tech_isodep_target +{ + nfc_tech_isodep_t isodep; + nfc_transceiver_t* pTransceiver; + + struct + { + ostream_t* pReqStream; + istream_t* pResStream; + + nfc_tech_isodep_cb_t reqCb; + void* pReqUserData; + + nfc_tech_isodep_cb_t resCb; + void* pResUserData; + + buffer_t res; + bool chaining; + + uint8_t blockNumber; + + enum + { + ISO_DEP_TARGET_DEP_FRAME_IDLE, + ISO_DEP_TARGET_DEP_FRAME_WTX_RECEIVED, + ISO_DEP_TARGET_DEP_FRAME_WTX_SENT, + ISO_DEP_TARGET_DEP_FRAME_INFORMATION_RECEIVED, + ISO_DEP_TARGET_DEP_FRAME_INFORMATION_SENT, + ISO_DEP_TARGET_DEP_FRAME_NACK_RECEIVED, + ISO_DEP_TARGET_DEP_FRAME_NACK_DIFF_BLOCK_NUMBER_RECEIVED, + ISO_DEP_TARGET_DEP_FRAME_NACK_SENT, + ISO_DEP_TARGET_DEP_FRAME_ACK_RECEIVED, + ISO_DEP_TARGET_DEP_FRAME_ACK_SENT, + ISO_DEP_TARGET_DEP_FRAME_DESELECT_RECEIVED, + ISO_DEP_TARGET_DEP_FRAME_DESELECT_SENT, + } frameState; + } dep; + struct + { + enum + { + ISO_DEP_TARGET_COMMANDS_DISCONNECTED, + + ISO_DEP_TARGET_COMMANDS_CONNECTING, + + ISO_DEP_TARGET_COMMANDS_ATS_REQ_RECVD, + ISO_DEP_TARGET_COMMANDS_ATS_RES_SENT, + + ISO_DEP_TARGET_COMMANDS_DEP_REQ_RECVD, + ISO_DEP_TARGET_COMMANDS_DEP_RES_SENT, + } state; + + size_t inPayloadSize; + + buffer_builder_t respBldr; + uint8_t respBuf[32]; + + buffer_t* pReq; + } commands; + + buffer_t* pHist; + + nfc_tech_isodep_disconnected_cb disconnectedCb; + void* pUserData; +}; + +//High-level Target functions +void nfc_tech_isodep_target_init(nfc_tech_isodep_target_t* pIsodepTarget, nfc_transceiver_t* pTransceiver, + buffer_t* pHist, nfc_tech_isodep_disconnected_cb disconnectedCb, void* pUserData); + +nfc_err_t nfc_tech_isodep_target_connect(nfc_tech_isodep_target_t* pIsodepTarget); +void nfc_tech_isodep_target_disconnect(nfc_tech_isodep_target_t* pIsodepTarget); + +nfc_err_t nfc_tech_isodep_target_transmit(nfc_tech_isodep_target_t* pIsodepTarget, istream_t* pStream, nfc_tech_isodep_cb_t cb, void* pUserData); +nfc_err_t nfc_tech_isodep_target_receive(nfc_tech_isodep_target_t* pIsodepTarget, ostream_t* pStream, nfc_tech_isodep_cb_t cb, void* pUserData); + + +#ifdef __cplusplus +} +#endif + +#endif /* ISODEP_TARGET_H_ */ diff --git a/features/nfc/stack/transceiver/pn512/pn512.c b/features/nfc/stack/transceiver/pn512/pn512.c new file mode 100644 index 0000000000..00b55c3491 --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512.c @@ -0,0 +1,377 @@ +/* + * 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 pn512.c + * \copyright Copyright (c) ARM Ltd 2013 + * \author Donatien Garnier + * \details PN512 implementation of the transceiver interface + */ + +#define __DEBUG__ 4 +#ifndef __MODULE__ +#define __MODULE__ "pn512.c" +#endif +#include "inc/nfc.h" + +#include "stdlib.h" + +#include "acore/buffer.h" + +#include "transceiver/transceiver.h" +#include "transceiver/protocols.h" +#include "pn512_rf.h" +#include "pn512_registers.h" +#include "pn512_cmd.h" +#include "pn512_hw.h" +#include "pn512_irq.h" +#include "pn512_poll.h" +#include "pn512_transceive.h" +#include "pn512_internal.h" + +#include "pn512.h" + +#define DEFAULT_READER_TRANSCEIVE_TIMEOUT 100 +#define DEFAULT_TARGET_TRANSCEIVE_TIMEOUT -1 + + +//Prototypes +#include "pn512_internal.h" + +/** \addtogroup PN512 + * @{ + * \name Transceiver + * \details Implementation of the transceiver interface + * @{ + */ + +//PN 512 VTABLE + +static const transceiver_impl_t pn512_impl = +{ + .set_protocols = pn512_set_protocols, + .poll = pn512_poll, + .transceive = pn512_transceive, + .abort = pn512_abort, + .set_crc = pn512_set_crc, + .set_timeout = pn512_set_timeout, + .set_transceive_options = pn512_set_transceive_options, + .set_transceive_framing = pn512_set_transceive_framing, + .set_write = pn512_set_write, + .get_read = pn512_get_read, + .set_last_byte_length = pn512_set_last_byte_length, + .get_last_byte_length = pn512_get_last_byte_length, + .set_first_byte_align = pn512_set_first_byte_align, + .close = pn512_close, + .sleep = pn512_sleep +}; + +/** Initialize PN512 transceiver + * \param pPN512 pointer to pn512_t structure to initialize + * \param pTransport pointer to already initialized nfc_transport_t structure + * \return NFC_OK (0) on success or NFC_ERR_* error on failure + */ +nfc_err_t pn512_init(pn512_t* pPN512, nfc_transport_t* pTransport, nfc_scheduler_timer_t* pTimer) +{ + //// + //For Self-test + //// + #if NFC_PN512_SELFTEST + const uint8_t null_array[25] = {0}; + #endif + //// + uint8_t r; + + //Init transceiver + transceiver_init((nfc_transceiver_t*)pPN512, pTransport, pTimer); + + //Init buffer + buffer_builder_init(&pPN512->readBufBldr, pPN512->payload, 256); + + pPN512->readFirstByteAlign = 0; + pPN512->readLastByteLength = 8; + pPN512->writeLastByteLength = 8; + + //Populate functions + pPN512->transceiver.fn = &pn512_impl; + + //Init variables + memset(&pPN512->config.initiators, 0, sizeof(nfc_tech_t)); + memset(&pPN512->config.targets, 0, sizeof(nfc_tech_t)); + pPN512->timeout = -1; + pPN512->nextFrameMode = pn512_transceive_mode_transceive; + + pn512_hw_init(pPN512); + pn512_registers_init(pPN512); //Cannot switch page now + pn512_cmd_init(pPN512); + + pn512_cmd_exec(pPN512, PN512_CMD_SOFTRST); + pn512_cmd_wait_idle(pPN512, -1); + + //pn512_registers_init(pPN512); + //Put into known state + pn512_registers_reset(pPN512); + + pPN512->transceive.mode = pn512_transceive_mode_idle; + + pn512_irq_clear(pPN512, PN512_IRQ_ALL); + pn512_fifo_clear(pPN512); + pn512_cmd_exec(pPN512, PN512_CMD_IDLE); + pn512_cmd_wait_idle(pPN512, -1); + + pn512_rf_field_switch_off(pPN512); + + //Required for polling loop + srand(4242); + +#if NFC_PN512_SELFTEST // Self test + pn512_cmd_exec(pPN512, PN512_CMD_SOFTRST); + pn512_cmd_wait_idle(pPN512, -1); + + const uint8_t null_array_buf[25]={0}; //FIXME + buffer_t null_array; + buffer_init(&null_array, null_array_buf, 25); + + //Perform self test + pn512_fifo_write(pPN512, &null_array); + pn512_cmd_exec(pPN512, PN512_CMD_CONFIG); + while(pn512_cmd_get(pPN512) != PN512_CMD_IDLE); + pn512_register_write(pPN512, PN512_REG_AUTOTEST, 0x09); + + buffer_init(&null_array, null_array_buf, 1); + + pn512_fifo_write(pPN512, &null_array); + pn512_cmd_exec(pPN512, PN512_CMD_CRC); + while(pn512_cmd_get(pPN512) != PN512_CMD_IDLE); + + DBGX_ENTER(); + NFC_DBG("Test result:"); + while(pn512_fifo_length(pPN512)) + { + buffer_builder_t read_byte; + buffer_builder_init(&read_byte, null_array_buf, 1); + + pn512_fifo_read(pPN512, &read_byte); + DBGX("%02x ", null_array_buf[0]); + } + DBGX("\n"); + DBGX_LEAVE(); +#endif + + r = pn512_register_read(pPN512, PN512_REG_VERSION); + + DBG_BLOCK( + NFC_DBG("PN512 version %02x", r); + ) + + if((r != 0x82) && (r != 0xB1) && (r != 0xB2)) + { + return NFC_ERR_UNSUPPORTED; //PN512 not found + } + + return NFC_OK; +} + +/** Get pointer to nfc_transceiver_t structure + * \param pPN512 pointer to pn512_t instance + * \return pointer to initialized nfc_transceiver_t instance + */ +nfc_transceiver_t* pn512_get_transceiver(pn512_t* pPN512) +{ + return &pPN512->transceiver; +} + +void pn512_set_protocols(nfc_transceiver_t* pTransceiver, nfc_tech_t initiators, nfc_tech_t targets, polling_options_t options) +{ + pn512_t* pPN512 = (pn512_t*) pTransceiver; + //If different, reconfigure + if( memcmp(&initiators, &pPN512->config.initiators, sizeof(nfc_tech_t)) || memcmp(&targets, &pPN512->config.targets, sizeof(nfc_tech_t)) ) + { + pPN512->config.initiators = initiators; + if( memcmp(&targets, &pPN512->config.targets, sizeof(nfc_tech_t)) ) + { + pPN512->config.targets = targets; + pn512_poll_setup(pPN512); + } + pTransceiver->initiator_ntarget = false; + memset(&pTransceiver->active_tech, 0, sizeof(nfc_tech_t)); + } + pPN512->config.options = options; +} + +void pn512_poll(nfc_transceiver_t* pTransceiver) +{ + pn512_t* pPN512 = (pn512_t*) pTransceiver; + pPN512->nextFrameMode = pn512_transceive_mode_transceive; + return pn512_poll_hw(pPN512, pn512_transceiver_callback); +} + +void pn512_set_crc(nfc_transceiver_t* pTransceiver, bool crc_out, bool crc_in) +{ + pn512_t* pPN512 = (pn512_t*) pTransceiver; + pn512_framing_crc_set(pPN512, crc_out, crc_in); +} + +void pn512_set_timeout(nfc_transceiver_t* pTransceiver, int timeout) +{ + pn512_t* pPN512 = (pn512_t*) pTransceiver; + pPN512->timeout = timeout; +} + +void pn512_set_transceive_options(nfc_transceiver_t* pTransceiver, bool transmit, bool receive, bool repoll) +{ + pn512_t* pPN512 = (pn512_t*) pTransceiver; + if( transmit && receive ) + { + pPN512->nextFrameMode = pn512_transceive_mode_transceive; + } + else if(transmit && repoll) + { + pPN512->nextFrameMode = pn512_transceive_mode_transmit_and_target_autocoll; + } + else if(transmit) + { + pPN512->nextFrameMode = pn512_transceive_mode_transmit; + } + else if(receive) + { + pPN512->nextFrameMode = pn512_transceive_mode_receive; + } + else + { + pPN512->nextFrameMode = pn512_transceive_mode_target_autocoll; + } +} + +void pn512_set_transceive_framing(nfc_transceiver_t* pTransceiver, nfc_framing_t framing) +{ + pn512_t* pPN512 = (pn512_t*) pTransceiver; + pn512_framing_set(pPN512, framing); + + //Switch NFC tech if NFC DEP + if( pTransceiver->active_tech.nfc_nfc_dep_a + || pTransceiver->active_tech.nfc_nfc_dep_f_212 + || pTransceiver->active_tech.nfc_nfc_dep_f_424 ) + { + //FIXME + pTransceiver->active_tech.nfc_nfc_dep_a = 0; + pTransceiver->active_tech.nfc_nfc_dep_f_212 = 0; + pTransceiver->active_tech.nfc_nfc_dep_f_424 = 0; + switch(framing) + { + case nfc_framing_target_a_106: + case nfc_framing_initiator_a_106: + pTransceiver->active_tech.nfc_nfc_dep_a = 1; + break; + case nfc_framing_target_f_212: + case nfc_framing_initiator_f_212: + pTransceiver->active_tech.nfc_nfc_dep_f_212 = 1; + break; + case nfc_framing_target_f_424: + case nfc_framing_initiator_f_424: + pTransceiver->active_tech.nfc_nfc_dep_f_424 = 1; + break; + default: + break; + } + } +} + +void pn512_set_write(nfc_transceiver_t* pTransceiver, buffer_t* pWriteBuf) +{ + pn512_t* pPN512 = (pn512_t*) pTransceiver; + if( pWriteBuf == NULL ) + { + buffer_init(&pPN512->writeBuf, NULL, 0); + return; + } + buffer_dup(&pPN512->writeBuf, pWriteBuf); +} + +buffer_t* pn512_get_read(nfc_transceiver_t* pTransceiver) +{ + pn512_t* pPN512 = (pn512_t*) pTransceiver; + return buffer_builder_buffer(&pPN512->readBufBldr); +} + +void pn512_set_last_byte_length(nfc_transceiver_t* pTransceiver, size_t lastByteLength) +{ + pn512_t* pPN512 = (pn512_t*) pTransceiver; + if( (lastByteLength > 8) || (lastByteLength == 0) ) + { + lastByteLength = 8; + } + pPN512->writeLastByteLength = lastByteLength; +} + +void pn512_set_first_byte_align(nfc_transceiver_t* pTransceiver, size_t firstByteAlign) +{ + pn512_t* pPN512 = (pn512_t*) pTransceiver; + firstByteAlign &= 0x7; + pPN512->readFirstByteAlign = firstByteAlign; +} + +size_t pn512_get_last_byte_length(nfc_transceiver_t* pTransceiver) +{ + pn512_t* pPN512 = (pn512_t*) pTransceiver; + return pPN512->readLastByteLength; +} + +void pn512_transceive(nfc_transceiver_t* pTransceiver) +{ + pn512_t* pPN512 = (pn512_t*) pTransceiver; + pn512_transceive_hw(pPN512, pPN512->nextFrameMode, pn512_transceiver_callback); + pPN512->nextFrameMode = pn512_transceive_mode_transceive; +} + +void pn512_abort(nfc_transceiver_t* pTransceiver) +{ + pn512_t* pPN512 = (pn512_t*) pTransceiver; + scheduler_dequeue_task(&pTransceiver->scheduler, true, &pPN512->transceiver.task); +} + +void pn512_close(nfc_transceiver_t* pTransceiver) +{ + //pn512_t* pPN512 = (pn512_t*) pTransceiver; + (void) pTransceiver; + //TODO + return; +} + +void pn512_sleep(nfc_transceiver_t* pTransceiver, bool sleep) +{ + pn512_t* pPN512 = (pn512_t*) pTransceiver; + + if(sleep) + { + pn512_register_write(pPN512, PN512_REG_COMMAND, 0x30); //Receiver off + soft power down + } + else + { + pn512_register_write(pPN512, PN512_REG_COMMAND, 0x00); + while( pn512_register_read(pPN512, PN512_REG_COMMAND) & 0x10 ); + } +} + +void pn512_transceiver_callback(pn512_t* pPN512, nfc_err_t ret) +{ + transceiver_callback(&pPN512->transceiver, ret); +} + +/** + * @} + * @} + * */ diff --git a/features/nfc/stack/transceiver/pn512/pn512.h b/features/nfc/stack/transceiver/pn512/pn512.h new file mode 100644 index 0000000000..ab83826273 --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512.h @@ -0,0 +1,167 @@ +/* + * 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 pn512.h + * \copyright Copyright (c) ARM Ltd 2013 + * \author Donatien Garnier + */ + +#ifndef PN512_H_ +#define PN512_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "transceiver/transceiver.h" + +typedef struct __pn512 pn512_t; + +#include "pn512_callback.h" +#include "pn512_types.h" + +typedef enum __pn512_state +{ + pn512_state_ready, + pn512_state_target_autocoll, + pn512_state_initiator_transceive_first_frame, + pn512_state_transceive, + pn512_state_transceive_last_frame, +} pn512_state_t; + +typedef enum __pn512_transceive_mode +{ + pn512_transceive_mode_idle, + pn512_transceive_mode_target_autocoll, + pn512_transceive_mode_transmit, + pn512_transceive_mode_transmit_and_target_autocoll, + pn512_transceive_mode_transceive, + pn512_transceive_mode_receive, +} pn512_transceive_mode_t; + +typedef struct __pn512 +{ + nfc_transceiver_t transceiver; + //Impl specific + pn512_registers_t registers; + bool rf_on; + struct + { + bool out; + bool in; + } crc; + int timeout; + + struct + { + nfc_tech_t initiators; + nfc_tech_t targets; + polling_options_t options; + } config; + + //Transceive options + pn512_transceive_mode_t nextFrameMode; + + nfc_framing_t framing; + uint16_t irqsEn; + uint8_t payload[256]; //Incoming buffer + + buffer_builder_t readBufBldr; + buffer_t writeBuf; + + uint8_t readFirstByteAlign; + uint8_t readLastByteLength; + uint8_t writeLastByteLength; + + //Task parameters + struct + { + //Polling + struct + { + enum { + pn512_polling_state_start_listening, + + pn512_polling_state_listen_wait_for_remote_field, + pn512_polling_state_listen_anticollision, + + pn512_polling_state_listen_no_target_found, + + pn512_polling_state_start_polling, + + pn512_polling_state_rf_collision_avoidance, // TID + n × TRFW, n is random, TID>4096/(13.56E6) ~ 302.06us, TRFW=51/(13.56E6) ~ 37.76us + pn512_polling_state_polling_nfc_a_start, + pn512_polling_state_polling_nfc_a_gt, // guard time nfc a >= 5.0 ms + pn512_polling_state_polling_nfc_a_anticollision, // polling for nfc a + pn512_polling_state_polling_nfc_b_start, + pn512_polling_state_polling_nfc_b_gt, // guard time nfc b >= 5.0 ms + pn512_polling_state_polling_nfc_b_anticollision, // polling for nfc b + pn512_polling_state_polling_nfc_f_start, + pn512_polling_state_polling_nfc_f_gt, // guard time nfc f >= 20 ms + pn512_polling_state_polling_nfc_f_anticollision, // polling for nfc f + + pn512_polling_state_finish_polling, + + } state; + + pn512_cb_t cb; + } poll; + struct + { + pn512_cb_t cb; + pn512_transceive_mode_t mode; + } transceive; + struct + { + pn512_cb_t cb; + } rf; + struct + { + union + { + // ISO A + struct + { + bool more_targets; // Collision detected + uint8_t cascade_level; + uint8_t cln[5]; + uint8_t valid_bits; // valid bits within cascade level + } iso_a; + // ISO B + struct + { + bool more_targets; // Collision detected + uint8_t slots_num_exponent; + uint8_t slot_number; + bool found_one; + } iso_b; + }; + pn512_cb_t cb; + } anticollision; + }; + +} pn512_t; + +nfc_err_t pn512_init(pn512_t* pPN512, nfc_transport_t* pTransport, nfc_scheduler_timer_t* pTimer); + +nfc_transceiver_t* pn512_get_transceiver(pn512_t* pPN512); + +#ifdef __cplusplus +} +#endif + +#endif /* PN512_H_ */ diff --git a/features/nfc/stack/transceiver/pn512/pn512_callback.h b/features/nfc/stack/transceiver/pn512/pn512_callback.h new file mode 100644 index 0000000000..820f37c465 --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512_callback.h @@ -0,0 +1,37 @@ +/* + * 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 pn512_callback.h + * \copyright Copyright (c) ARM Ltd 2015 + * \author Donatien Garnier + */ + +#ifndef PN512_CALLBACK_H_ +#define PN512_CALLBACK_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct __pn512 pn512_t; +typedef void (*pn512_cb_t)(pn512_t* pPN512, nfc_err_t ret); + +#ifdef __cplusplus +} +#endif + +#endif /* PN512_CALLBACK_H_ */ diff --git a/features/nfc/stack/transceiver/pn512/pn512_cmd.c b/features/nfc/stack/transceiver/pn512/pn512_cmd.c new file mode 100644 index 0000000000..2f995ce129 --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512_cmd.c @@ -0,0 +1,149 @@ +/* + * 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 pn512_cmd.c + * \copyright Copyright (c) ARM Ltd 2013 + * \author Donatien Garnier + * \details Format and execute PN512 commands + * \internal + */ + +#define __DEBUG__ 0 +#ifndef __MODULE__ +#define __MODULE__ "pn512_cmd.c" +#endif +#include "inc/nfc.h" + +#include "pn512_cmd.h" + +#define PN512_FIFO_SIZE 64 + +#include "pn512.h" +#include "pn512_registers.h" +#include "pn512_irq.h" +#include "pn512_hw.h" + +/** \addtogroup PN512 + * \internal + * @{ + * \name Commands + * @{ + */ + +/** \internal Initialize underlying pn512_cmd_t structure + * \param pPN512 pointer to pn512_t structure + */ +void pn512_cmd_init(pn512_t* pPN512) +{ + (void) pPN512; +} + +//Fifo read / write +/** \internal Write bytes to FIFO + * \param pPN512 pointer to pn512_t structure + * \param pData buffer to write + */ +void pn512_fifo_write(pn512_t* pPN512, buffer_t* pData) +{ + uint8_t fifo_space = pn512_fifo_space(pPN512); //Do not call this fn twice + size_t len = buffer_reader_readable(pData); + len = MIN(fifo_space, len); + + pn512_register_switch_page(pPN512, PN512_REG_FIFODATA); + pn512_hw_write_buffer(pPN512, PN512_REG_FIFODATA, pData, len); +} + +/** \internal Read bytes from FIFO + * \param pPN512 pointer to pn512_t structure + * \param pData buffer in which to read + */ +void pn512_fifo_read(pn512_t* pPN512, buffer_builder_t* pData) +{ + uint8_t fifo_len = pn512_fifo_length(pPN512); //Do not call this fn twice + size_t len = buffer_builder_writeable(pData); + len = MIN(fifo_len, len); + + pn512_register_switch_page(pPN512, PN512_REG_FIFODATA); + pn512_hw_read_buffer(pPN512, PN512_REG_FIFODATA, pData, len); +} + +/** \internal Clear FIFO + * Removes any bytes left in FIFO + * \param pPN512 pointer to pn512_t structure + */ +void pn512_fifo_clear(pn512_t* pPN512) +{ + pn512_register_write(pPN512, PN512_REG_FIFOLEVEL, 0x80); //Flush FIFO +} + +/** \internal Get space in FIFO + * \param pPN512 pointer to pn512_t structure + * \return number of bytes that can be written to FIFO + */ +size_t pn512_fifo_space(pn512_t* pPN512) +{ + return PN512_FIFO_SIZE - pn512_register_read(pPN512, PN512_REG_FIFOLEVEL); +} + +/** \internal Get FIFO length + * \param pPN512 pointer to pn512_t structure + * \return number of bytes that can be read from FIFO + */ +size_t pn512_fifo_length(pn512_t* pPN512) +{ + return pn512_register_read(pPN512, PN512_REG_FIFOLEVEL); +} + +/** \internal Execute command + * \param pPN512 pointer to pn512_t structure + * \param cmd PN512 command to execute + */ +void pn512_cmd_exec(pn512_t* pPN512, uint8_t cmd) +{ + pn512_register_write(pPN512, PN512_REG_COMMAND, cmd); +} + +/** \internal Wait for command completion + * \param pPN512 pointer to pn512_t structure + * \param timeout timeout in milliseconds or -1 for blocking mode + * \return NFC_OK on success or NFC_ERR_TIMEOUT on timeout + */ +nfc_err_t pn512_cmd_wait_idle(pn512_t* pPN512, int timeout) +{ + (void) timeout; + while( pn512_cmd_get(pPN512) != PN512_CMD_IDLE ) + { + + } + return NFC_OK; +} + + +/** \internal Read executed command + * \param pPN512 pointer to pn512_t structure + * \return PN512 command being executed + */ +uint8_t pn512_cmd_get(pn512_t* pPN512) +{ + return pn512_register_read(pPN512, PN512_REG_COMMAND) & PN512_CMD_REG_MASK; +} + +/** + * @} + * @} + * */ + diff --git a/features/nfc/stack/transceiver/pn512/pn512_cmd.h b/features/nfc/stack/transceiver/pn512/pn512_cmd.h new file mode 100644 index 0000000000..5101acc0d5 --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512_cmd.h @@ -0,0 +1,78 @@ +/* + * 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 pn512_cmd.h + * \copyright Copyright (c) ARM Ltd 2013 + * \author Donatien Garnier + */ + +#ifndef PN512_CMD_H_ +#define PN512_CMD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "inc/nfc.h" + + +#include "pn512.h" + +#define PN512_CMD_IDLE 0x00 //No action, cancels current command execution +#define PN512_CMD_MEM 0x01 //Stores 25 bytes into the internal buffer +#define PN512_CMD_CONFIG 0x01 //Configures the PN512 for FeliCa, MIFARE and NFCIP-1 communication +#define PN512_CMD_RNDIDG 0x02 //Generates a 10-byte random ID number +#define PN512_CMD_CRC 0x03 //Activates the CRC coprocessor or performs a self test +#define PN512_CMD_TRANSMIT 0x04 //Transmits data from the FIFO buffer +#define PN512_CMD_NOCHANGE 0x07 //No command change +#define PN512_CMD_RECEIVE 0x08 //Activates the receiver circuits +#define PN512_CMD_TRANSCEIVE 0x0C //Transmits data from FIFO buffer to antenna and automatically activates the receiver after transmission +#define PN512_CMD_AUTOCOLL 0x0D //Handles FeliCa polling (Card Operation mode only) and MIFARE anticollision (Card Operation mode only) +#define PN512_CMD_MFAUTH 0x0E //Performs the MIFARE standard authentication as a reader +#define PN512_CMD_SOFTRST 0x0F //Resets the PN512 + +#define PN512_CMD_REG_MASK 0x0F + +void pn512_cmd_init(pn512_t* pPN512); + +//Fifo read / write + +void pn512_fifo_write(pn512_t* pPN512, buffer_t* pData); +void pn512_fifo_read(pn512_t* pPN512, buffer_builder_t* pData); + +//Fifo clear +void pn512_fifo_clear(pn512_t* pPN512); + +//Fifo bytes read +size_t pn512_fifo_space(pn512_t* pPN512); +size_t pn512_fifo_length(pn512_t* pPN512); + +//Execute command +void pn512_cmd_exec(pn512_t* pPN512, uint8_t cmd); + +//Wait for command completion +nfc_err_t pn512_cmd_wait_idle(pn512_t* pPN512, int timeout); + +//Read executed command +uint8_t pn512_cmd_get(pn512_t* pPN512); + + +#ifdef __cplusplus +} +#endif + +#endif /* PN512_CMD_H_ */ diff --git a/features/nfc/stack/transceiver/pn512/pn512_hw.c b/features/nfc/stack/transceiver/pn512/pn512_hw.c new file mode 100644 index 0000000000..886f541cbc --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512_hw.c @@ -0,0 +1,54 @@ +/* + * 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 pn512_hw.c + * \copyright Copyright (c) ARM Ltd 2013 + * \author Donatien Garnier + * \details Format and execute PN512 frames + */ + +#include "inc/nfc.h" + +#include "pn512_hw.h" + +//Platform specific +#include "platform/nfc_transport.h" + + + +/** \addtogroup PN512 + * \internal + * @{ + * \name Hardware + * @{ + */ + +/** \internal Initialize underlying pn512_hw_t structure + * \param pPN512 pointer to pn512_t structure + */ +void pn512_hw_init(pn512_t* pPN512) +{ + //Nothing to init in this implementation + (void) pPN512; +} + + +/** + * @} + * @} + * */ + diff --git a/features/nfc/stack/transceiver/pn512/pn512_hw.h b/features/nfc/stack/transceiver/pn512/pn512_hw.h new file mode 100644 index 0000000000..55f1b09a8f --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512_hw.h @@ -0,0 +1,97 @@ +/* + * 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 pn512_hw.h + * \copyright Copyright (c) ARM Ltd 2013 + * \author Donatien Garnier + */ + +#ifndef PN512_HW_H_ +#define PN512_HW_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "inc/nfc.h" + +#include "pn512.h" + +//Utility for transport: SPI address read/write +#define PN512_SPI_ADDR_R(x) ((1<<7) | ((x) << 1)) +#define PN512_SPI_ADDR_W(x) ((0<<7) | ((x) << 1)) + +void pn512_hw_init(pn512_t* pPN512); + +/** \internal Write bytes at the specified address on the underlying transport link + * \param pPN512 pointer to pn512_t structure + * \param addr address at which to write + * \param buf buffer to write + * \param len length of buffer + */ +static inline void pn512_hw_write(pn512_t* pPN512, uint8_t addr, uint8_t* buf, size_t len) +{ + nfc_transport_write(((nfc_transceiver_t*)pPN512)->pTransport, addr, buf, len); +} + +/** \internal Read bytes from the specified address on the underlying transport link + * \param pPN512 pointer to pn512_t structure + * \param addr address from which to read + * \param buf buffer to read + * \param len length of buffer + */ +static inline void pn512_hw_read(pn512_t* pPN512, uint8_t addr, uint8_t* buf, size_t len) +{ + nfc_transport_read(((nfc_transceiver_t*)pPN512)->pTransport, addr, buf, len); +} + +static inline void pn512_hw_write_buffer(pn512_t* pPN512, uint8_t addr, buffer_t* pData, size_t len) +{ + while( len > 0 ) + { + if( buffer_reader_readable(pData) == 0 ) + { + return; + } + size_t cpyLen = MIN(len, buffer_reader_current_buffer_length(pData)); + nfc_transport_write(((nfc_transceiver_t*)pPN512)->pTransport, addr, buffer_reader_current_buffer_pointer(pData), cpyLen); + buffer_read_n_skip(pData, cpyLen); + len -= cpyLen; + } +} + +static inline void pn512_hw_read_buffer(pn512_t* pPN512, uint8_t addr, buffer_builder_t* pData, size_t len) +{ + while( len > 0 ) + { + if( buffer_builder_writeable(pData) == 0 ) + { + return; + } + //Read payload + size_t cpyLen = MIN(len, buffer_builder_space(pData)); + nfc_transport_read(((nfc_transceiver_t*)pPN512)->pTransport, addr, buffer_builder_write_position(pData), cpyLen); + buffer_builder_write_n_skip(pData, cpyLen); + len -= cpyLen; + } +} + +#ifdef __cplusplus +} +#endif + +#endif /* PN512_HW_H_ */ diff --git a/features/nfc/stack/transceiver/pn512/pn512_internal.h b/features/nfc/stack/transceiver/pn512/pn512_internal.h new file mode 100644 index 0000000000..72c3ed365b --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512_internal.h @@ -0,0 +1,82 @@ +/* + * 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 pn512_internal.h + * \copyright Copyright (c) ARM Ltd 2013 + * \author Donatien Garnier + */ + +#ifndef PN512_INTERNAL_H_ +#define PN512_INTERNAL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "inc/nfc.h" + +#include "transceiver/transceiver_internal.h" + +#include "pn512.h" +#include "pn512_callback.h" + +//Public +void pn512_set_protocols(nfc_transceiver_t* pTransceiver, nfc_tech_t initiators, nfc_tech_t targets, polling_options_t options); +void pn512_poll(nfc_transceiver_t* pTransceiver); +void pn512_set_crc(nfc_transceiver_t* pTransceiver, bool crc_out, bool crc_in); +void pn512_set_timeout(nfc_transceiver_t* pTransceiver, int timeout); +void pn512_set_transceive_options(nfc_transceiver_t* pTransceiver, bool transmit, bool receive, bool repoll); +void pn512_set_transceive_framing(nfc_transceiver_t* pTransceiver, nfc_framing_t framing); +void pn512_set_write(nfc_transceiver_t* pTransceiver, buffer_t* pWriteBuf); +buffer_t* pn512_get_read(nfc_transceiver_t* pTransceiver); +size_t pn512_get_last_byte_length(nfc_transceiver_t* pTransceiver); +void pn512_set_last_byte_length(nfc_transceiver_t* pTransceiver, size_t lastByteLength); +void pn512_set_first_byte_align(nfc_transceiver_t* pTransceiver, size_t firstByteAlign); +void pn512_abort(nfc_transceiver_t* pTransceiver); +void pn512_transceive(nfc_transceiver_t* pTransceiver); +void pn512_close(nfc_transceiver_t* pTransceiver); +void pn512_sleep(nfc_transceiver_t* pTransceiver, bool sleep); + +void pn512_transceiver_callback(pn512_t* pPN512, nfc_err_t ret); + +static inline void pn512_rf_callback(pn512_t* pPN512, nfc_err_t ret) +{ + pPN512->rf.cb(pPN512, ret); +} + +static inline void pn512_poll_callback(pn512_t* pPN512, nfc_err_t ret) +{ + pPN512->poll.cb(pPN512, ret); +} + +static inline void pn512_anticollision_callback(pn512_t* pPN512, nfc_err_t ret) +{ + pPN512->anticollision.cb(pPN512, ret); +} + +static inline void pn512_transceive_callback(pn512_t* pPN512, nfc_err_t ret) +{ + pPN512->transceive.cb(pPN512, ret); +} + + + +#ifdef __cplusplus +} +#endif + +#endif /* PN512_INTERNAL_H_ */ diff --git a/features/nfc/stack/transceiver/pn512/pn512_irq.c b/features/nfc/stack/transceiver/pn512/pn512_irq.c new file mode 100644 index 0000000000..52206afda7 --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512_irq.c @@ -0,0 +1,50 @@ +/* + * 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 pn512_irq.c + * \copyright Copyright (c) ARM Ltd 2013 + * \author Donatien Garnier + * \details Manage PN512 interrupt requests + */ + +#define __DEBUG__ 0 +#ifndef __MODULE__ +#define __MODULE__ "pn512_irq.c" +#endif +#include "inc/nfc.h" + +#include "pn512_irq.h" +#include "pn512_registers.h" +#include "pn512_hw.h" +#include "pn512.h" + +/** \addtogroup PN512 + * \internal + * @{ + * \name Interrupts + * @{ + */ + + + + + + +/** + * @} + * @} + * */ diff --git a/features/nfc/stack/transceiver/pn512/pn512_irq.h b/features/nfc/stack/transceiver/pn512/pn512_irq.h new file mode 100644 index 0000000000..6446220cbe --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512_irq.h @@ -0,0 +1,109 @@ +/* + * 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 pn512_irq.h + * \copyright Copyright (c) ARM Ltd 2013 + * \author Donatien Garnier + */ + +#ifndef PN512_IRQ_H_ +#define PN512_IRQ_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "inc/nfc.h" + +#include "pn512.h" +#include "pn512_registers.h" + +#define PN512_IRQ_TX (1<<6) +#define PN512_IRQ_RX (1<<5) +#define PN512_IRQ_IDLE (1<<4) +#define PN512_IRQ_HIGH_ALERT (1<<3) +#define PN512_IRQ_LOW_ALERT (1<<2) +#define PN512_IRQ_ERR (1<<1) +#define PN512_IRQ_TIMER (1<<0) + +#define PN512_IRQ_SIGIN (1<<(4+8)) +#define PN512_IRQ_MODE (1<<(3+8)) +#define PN512_IRQ_CRC (1<<(2+8)) +#define PN512_IRQ_RF_ON (1<<(1+8)) +#define PN512_IRQ_RF_OFF (1<<(0+8)) + +#define PN512_IRQ_NONE 0x00 +#define PN512_IRQ_ALL 0x1F7F + +#define PN512_REG_COMIEN_MASK 0x7F +#define PN512_REG_COMIEN_VAL 0x00 + +#define PN512_REG_DIVIEN_MASK 0x1F +#define PN512_REG_DIVIEN_VAL 0x80 + +#define PN512_REG_COMIRQ_MASK 0x7F +#define PN512_REG_COMIRQ_CLEAR 0x00 + +#define PN512_REG_DIVIRQ_MASK 0x1F +#define PN512_REG_DIVIRQ_CLEAR 0x00 + +/** \internal Set IRQ enable registers + * \param pPN512 pointer to pn512_t structure + * \param irqs MSB is DIVIEN value, LSB is COMIEN value + */ +static inline void pn512_irq_set(pn512_t* pPN512, uint16_t irqs) //ORed +{ + pn512_register_write(pPN512, PN512_REG_COMIEN, PN512_REG_COMIEN_VAL | (PN512_REG_COMIEN_MASK & (irqs & 0xFF))); + pn512_register_write(pPN512, PN512_REG_DIVIEN, PN512_REG_DIVIEN_VAL | (PN512_REG_DIVIEN_MASK & (irqs >> 8))); + pPN512->irqsEn = irqs; +} + +/** \internal Get IRQ enable registers + * \param pPN512 pointer to pn512_t structure + * \return MSB is DIVIEN value, LSB is COMIEN value + */ +static inline uint16_t pn512_irq_enabled(pn512_t* pPN512) //ORed +{ + return pPN512->irqsEn /*(pn512_register_read(pPN512, PN512_REG_COMIEN_VAL) & PN512_REG_COMIEN_MASK) + | ((pn512_register_read(pPN512, PN512_REG_DIVIEN_VAL) & PN512_REG_DIVIEN_MASK) << 8)*/; +} + +/** \internal Get IRQ status registers (masked with enabled IRQ register) + * \param pPN512 pointer to pn512_t structure + * \return MSB is DIVIRQ value, LSB is COMIRQ value + */ +static inline uint16_t pn512_irq_get(pn512_t* pPN512) //ORed +{ + return ((pn512_register_read(pPN512, PN512_REG_COMIRQ) & PN512_REG_COMIEN_MASK) + | ((pn512_register_read(pPN512, PN512_REG_DIVIRQ) & PN512_REG_DIVIEN_MASK) << 8)) & pPN512->irqsEn; +} + +/** \internal Clear some interrupts + * \param pPN512 pointer to pn512_t structure + * \param irqs MSB is DIVIEN value, LSB is COMIEN value + */ +static inline void pn512_irq_clear(pn512_t* pPN512, uint16_t irqs) +{ + pn512_register_write(pPN512, PN512_REG_COMIRQ, PN512_REG_COMIRQ_CLEAR | (PN512_REG_COMIRQ_MASK & (irqs & 0xFF))); + pn512_register_write(pPN512, PN512_REG_DIVIRQ, PN512_REG_DIVIRQ_CLEAR | (PN512_REG_DIVIRQ_MASK & (irqs >> 8))); +} + +#ifdef __cplusplus +} +#endif + +#endif /* PN512_IRQ_H_ */ diff --git a/features/nfc/stack/transceiver/pn512/pn512_poll.c b/features/nfc/stack/transceiver/pn512/pn512_poll.c new file mode 100644 index 0000000000..9b41dd65c0 --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512_poll.c @@ -0,0 +1,1409 @@ +/* + * Copyright (c) 2014-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 pn512_poll.c + * \copyright Copyright (c) ARM Ltd 2014 + * \author Donatien Garnier + */ + +#define __DEBUG__ 0 +#ifndef __MODULE__ +#define __MODULE__ "pn512_poll.c" +#endif + +#include "inc/nfc.h" + +#include "pn512.h" +#include "pn512_poll.h" +#include "pn512_transceive.h" +#include "pn512_registers.h" +#include "pn512_rf.h" +#include "pn512_cmd.h" +#include "pn512_internal.h" + +#define TIMEOUT 1000 + +static void pn512_target_anticollision(pn512_t* pPN512, pn512_cb_t cb); +static void pn512_initiator_isoa_anticollision(pn512_t* pPN512, pn512_cb_t cb); + +static inline bool pn512_config_initiator(pn512_t* pPN512) +{ + return (pPN512->config.initiators.nfc_iso_dep_a | pPN512->config.initiators.nfc_iso_dep_b | + pPN512->config.initiators.nfc_nfc_dep_a | pPN512->config.initiators.nfc_nfc_dep_f_212 | pPN512->config.initiators.nfc_nfc_dep_f_424 | + pPN512->config.initiators.nfc_type1 | pPN512->config.initiators.nfc_type2 | pPN512->config.initiators.nfc_type3) != 0; +} + +static inline bool pn512_config_target(pn512_t* pPN512) +{ + return (pPN512->config.targets.nfc_iso_dep_a | pPN512->config.targets.nfc_iso_dep_b | + pPN512->config.targets.nfc_nfc_dep_a | pPN512->config.targets.nfc_nfc_dep_f_212 | pPN512->config.initiators.nfc_nfc_dep_f_424 | + pPN512->config.targets.nfc_type1 | pPN512->config.targets.nfc_type2 | pPN512->config.targets.nfc_type3) != 0; +} + +void pn512_target_anticollision_complete(pn512_t* pPN512, nfc_err_t ret) +{ + + bool iso14443a = pPN512->config.targets.nfc_type2 || pPN512->config.targets.nfc_iso_dep_a || pPN512->config.targets.nfc_nfc_dep_a; //We do not support type 1 card emulation so irrelevant + bool felica = pPN512->config.targets.nfc_type3 || pPN512->config.targets.nfc_nfc_dep_f_212 || pPN512->config.targets.nfc_nfc_dep_f_424; + + nfc_transceiver_t* pTransceiver = &pPN512->transceiver; + if( ret ) + { + NFC_WARN("Returned %d", ret); + pn512_anticollision_callback(pPN512, ret); + return; + } + + //Data available in FIFO + if (pPN512->readLastByteLength != 8) //We should receive a full byte + { + NFC_WARN("Not enough data in FIFO"); + pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL); + return; + } + + //If successful, update state machine + if (iso14443a && felica) + { + //Update current protocol accordingly + uint8_t txmode = pn512_register_read(pPN512, PN512_REG_TXMODE); + if ((txmode & 0x03) == 0x00) + { + pn512_framing_set(pPN512, nfc_framing_target_a_106); + + NFC_DBG("A 106"); + felica = false; + } + else if ((txmode & 0x03) == 0x02) + { + if ((txmode & 0x70) == 0x20) + { + //424kbps + NFC_DBG("F 424"); + pn512_framing_set(pPN512, nfc_framing_target_f_424); + } + else + { + //212kbps + NFC_DBG("F 212"); + pn512_framing_set(pPN512, nfc_framing_target_f_212); + } + iso14443a = false; + } + else + { + //Unsupported mode, exit + pn512_anticollision_callback(pPN512, NFC_ERR_UNSUPPORTED); + return; + } + } + + if (iso14443a) + { + if(buffer_reader_readable(buffer_builder_buffer(&pPN512->readBufBldr)) == 0) + { + pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL); + return; + } + + //Halt device, so that if anticollision is restarted it's in the correct state (cleared automatically by RF reset) + pn512_register_write(pPN512, PN512_REG_MIFNFC, 0x62 | 0x04); + + //Copy buffer to peek + buffer_t readBufDup; + buffer_dup(&readBufDup, buffer_builder_buffer(&pPN512->readBufBldr)); + + uint8_t b0 = buffer_read_nu8(&readBufDup); + + //Get first byte + //Read FIFO to see if the target was selected as NFC-DEP, ISO-DEP or proprietary (NFC Type 2) + //F0 --> NFC-DEP + //E0 --> ISO-DEP + //Anything else --> NFC Type 2 + + //First check if this could be NFC-DEP + if ( pPN512->config.targets.nfc_nfc_dep_a && (b0 == 0xF0) ) + { + pTransceiver->active_tech.nfc_nfc_dep_a = true; + } + else if ( pPN512->config.targets.nfc_iso_dep_a && (b0 == 0xE0) ) + { + pTransceiver->active_tech.nfc_iso_dep_a = true; + } + else if ( pPN512->config.targets.nfc_type2 ) + { + pTransceiver->active_tech.nfc_type2 = true; + } + else + { + //Unknown tech, return error + pn512_anticollision_callback(pPN512, NFC_ERR_UNSUPPORTED); + return; + } + + //Give control to higher layer + pn512_anticollision_callback(pPN512, NFC_OK); + return; + } + else if(felica) + { + //First check if this could be NFC-DEP + if ( (pPN512->config.targets.nfc_nfc_dep_f_212 || pPN512->config.targets.nfc_nfc_dep_f_424) ) + { + if(pPN512->framing == nfc_framing_target_f_424) + { + pTransceiver->active_tech.nfc_nfc_dep_f_424 = true; + } + else + { + pTransceiver->active_tech.nfc_nfc_dep_f_212 = true; + } + } + else + { + pn512_anticollision_callback(pPN512, NFC_ERR_UNSUPPORTED); + return; + } + } + + //NFC-IP 1 active mode is not supported by other devices so ignore it for now + pn512_anticollision_callback(pPN512, NFC_OK); +} + + +void pn512_target_anticollision(pn512_t* pPN512, pn512_cb_t cb) +{ + pPN512->anticollision.cb = cb; + + //Reset active states + pPN512->transceiver.initiator_ntarget = false; + pPN512->transceiver.active_tech.nfc_type1 = pPN512->transceiver.active_tech.nfc_type2 = pPN512->transceiver.active_tech.nfc_type3 + = pPN512->transceiver.active_tech.nfc_iso_dep_a = pPN512->transceiver.active_tech.nfc_nfc_dep_a = + pPN512->transceiver.active_tech.nfc_nfc_dep_f_212 = pPN512->transceiver.active_tech.nfc_nfc_dep_f_424 = 0; + + pn512_set_timeout((nfc_transceiver_t*)pPN512, -1); //Should only fail on RF drop + + pn512_transceive_hw(pPN512, pn512_transceive_mode_target_autocoll, pn512_target_anticollision_complete); +} + +// ISO A + +#define ISO14443A_BUF_SIZE 8 + +#define REQA 0x26 +#define SEL(n) (0x93 + (n)*2) +#define NVB(bits) ( (((2*8 + (bits))>>3)<<4) | ((bits) & 0x7) ) +#define HALTA1 0x50 +#define HALTA2 0x00 +#define CT 0x88 + +static void pn512_initiator_isoa_anticollision_reqa(pn512_t* pPN512); +static void pn512_initiator_isoa_anticollision_atqa(pn512_t* pPN512, nfc_err_t ret); +static void pn512_initiator_isoa_anticollision_cascade_1(pn512_t* pPN512); +static void pn512_initiator_isoa_anticollision_cascade_2(pn512_t* pPN512, nfc_err_t ret); +static void pn512_initiator_isoa_anticollision_cascade_3(pn512_t* pPN512, nfc_err_t ret); +static void pn512_initiator_isoa_anticollision_cascade_4(pn512_t* pPN512, nfc_err_t ret); +static void pn512_initiator_isoa_anticollision_complete(pn512_t* pPN512); + +void pn512_initiator_isoa_anticollision(pn512_t* pPN512, pn512_cb_t cb) +{ + pPN512->anticollision.cb = cb; + + // Reset transceive mode + pPN512->transceive.mode = pn512_transceive_mode_idle; + + pn512_initiator_isoa_anticollision_reqa(pPN512); +} + +void pn512_initiator_isoa_anticollision_reqa(pn512_t* pPN512) +{ + buffer_builder_t* pDataOutBldr = &pPN512->readBufBldr; + buffer_builder_reset(pDataOutBldr); + + pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.uidLength = 0; + + buffer_builder_write_nu8(pDataOutBldr, REQA); + pn512_set_last_byte_length((nfc_transceiver_t*)pPN512, 7); //Only 7 bits in this first command + // FIXME PN512 Anomaly: pn512_register_write(pPN512, PN512_REG_COLL, 0x00); // Set MSB to 0, to accept collisions + pn512_set_crc((nfc_transceiver_t*)pPN512, false, false); + pn512_set_timeout((nfc_transceiver_t*)pPN512, 4); + pn512_set_write((nfc_transceiver_t*)pPN512, buffer_builder_buffer(pDataOutBldr)); + + pn512_transceive_hw(pPN512, pn512_transceive_mode_transceive, pn512_initiator_isoa_anticollision_atqa); +} + +void pn512_initiator_isoa_anticollision_atqa(pn512_t* pPN512, nfc_err_t ret) +{ + // Clear collisions register + // FIXME PN512 Anomaly: pn512_register_write(pPN512, PN512_REG_COLL, 0x80); // Set MSB to 1, to treat collisions as errors + + if (ret && (ret != NFC_ERR_COLLISION)) // There might be collisions here + { + NFC_WARN("Did not receive ATQA: error %d", ret); + pn512_anticollision_callback(pPN512, NFC_ERR_NOPEER); + return; + } + + buffer_t* pResp = pn512_get_read((nfc_transceiver_t*)pPN512); + + if (buffer_reader_readable(pResp) != 2) + { + NFC_WARN("Wrong length (%u bytes)", buffer_reader_readable(pResp)); + pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL); + return; + } + + NFC_DBG("Got ATQA:"); + DBG_BLOCK( buffer_dump(pResp); ) + + // Ignore ATQA as there can be collisions + buffer_read_n_bytes(pResp, pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.atqa, 2); + + //Start cascading + pPN512->anticollision.iso_a.cascade_level = 1; + pPN512->anticollision.iso_a.valid_bits = 0; + pPN512->anticollision.iso_a.more_targets = false; + memset(pPN512->anticollision.iso_a.cln, 0, 5); + + pn512_initiator_isoa_anticollision_cascade_1(pPN512); +} + +void pn512_initiator_isoa_anticollision_cascade_1(pn512_t* pPN512) +{ + buffer_builder_t* pDataOutBldr = &pPN512->readBufBldr; + buffer_builder_reset(pDataOutBldr); + + //SEL + buffer_builder_write_nu8(pDataOutBldr, SEL(pPN512->anticollision.iso_a.cascade_level - 1)); //SEL: Cascade level + buffer_builder_write_nu8(pDataOutBldr, NVB(pPN512->anticollision.iso_a.valid_bits)); //First NVB: Bytecount = 2, Bitcount = 0, then adapt if collision detected + + NFC_DBG("SEL - cascade level %u, %u valid bits - NVB %u", pPN512->anticollision.iso_a.cascade_level, pPN512->anticollision.iso_a.valid_bits, NVB(pPN512->anticollision.iso_a.valid_bits)); + + if( pPN512->anticollision.iso_a.valid_bits > 0 ) + { + // Transmit first part of uid + buffer_builder_write_n_bytes(pDataOutBldr, pPN512->anticollision.iso_a.cln, (pPN512->anticollision.iso_a.valid_bits >> 3) + ((pPN512->anticollision.iso_a.valid_bits & 0x7)?1:0)); + pn512_set_last_byte_length((nfc_transceiver_t*)pPN512, pPN512->anticollision.iso_a.valid_bits & 0x7); + pn512_set_first_byte_align((nfc_transceiver_t*)pPN512, pPN512->anticollision.iso_a.valid_bits & 0x7); + } + + // FIXME PN512 Anomaly: pn512_register_write(pPN512, PN512_REG_COLL, 0x00); // Set MSB to 0, to accept collisions + pn512_set_crc((nfc_transceiver_t*)pPN512, false, false); + pn512_set_timeout((nfc_transceiver_t*)pPN512, 30); + pn512_set_write((nfc_transceiver_t*)pPN512, buffer_builder_buffer(pDataOutBldr)); + + + pn512_register_write(pPN512, PN512_REG_COMIRQ, 2); + NFC_DBG("IRQ status %04X", ((pn512_register_read(pPN512, PN512_REG_COMIRQ) ) + | ((pn512_register_read(pPN512, PN512_REG_DIVIRQ) ) << 8))); + + pn512_transceive_hw(pPN512, pn512_transceive_mode_transceive, pn512_initiator_isoa_anticollision_cascade_2); +} + +void pn512_initiator_isoa_anticollision_cascade_2(pn512_t* pPN512, nfc_err_t ret) +{ + buffer_t* pResp = pn512_get_read((nfc_transceiver_t*)pPN512); + + // Load & clear collisions register + // FIXME PN512 Anomaly: pn512_register_write(pPN512, PN512_REG_COLL, 0x80); // Set MSB to 1, to treat collisions as errors + + if (ret && (ret != NFC_ERR_COLLISION)) // There might be collisions here + { + NFC_WARN("Did not receive response: error %d", ret); + pn512_anticollision_callback(pPN512, ret); + return; + } + + // We should receive 5 bytes back minus all the CLn bits that we have transmitted; we ignore all bits from the first collision + size_t expected_resp_bits = (5 << 3) - pPN512->anticollision.iso_a.valid_bits; + + // Check for collision + uint8_t valid_bits = expected_resp_bits; + if( ret == NFC_ERR_COLLISION ) + { + uint8_t coll_reg = pn512_register_read(pPN512, PN512_REG_COLL); + + // FIXME - PN512 error + //if( !(coll_reg & 0x20) ) // bit 5 is CollPosNotValidSet + { + valid_bits = (coll_reg & 0x1f); + + if(valid_bits == 0) + { + valid_bits = 32; + } + + valid_bits--; + + NFC_DBG("Collision detected, %u valid bits", valid_bits); + if( valid_bits < expected_resp_bits ) + { + // Collision detected + pPN512->anticollision.iso_a.more_targets = true; + } + else + { + valid_bits = expected_resp_bits; + } + } + } + + size_t resp_sz = (valid_bits >> 3) +((valid_bits&0x7)?1:0); + if( buffer_reader_readable(pResp) < resp_sz ) + { + (void) pn512_register_read(pPN512, PN512_REG_COLL); + + NFC_WARN("Wrong length (%u instead of %u - valid bits %u)", buffer_reader_readable(pResp), resp_sz, valid_bits); + pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL); + return; + } + + // Read bytes + uint8_t bufIn[5] = {0}; + buffer_read_n_bytes(pResp, bufIn + (pPN512->anticollision.iso_a.valid_bits >> 3), resp_sz); + + // Mask out valid bits that are already known + bufIn[pPN512->anticollision.iso_a.valid_bits >> 3] &= 0xff << (pPN512->anticollision.iso_a.valid_bits & 0x7); + + // Update number of valid bits + pPN512->anticollision.iso_a.valid_bits += valid_bits; + + // Mask out bits past valid bits in last byte + bufIn[pPN512->anticollision.iso_a.valid_bits >> 3] &= 0xff >> ((8 - pPN512->anticollision.iso_a.valid_bits) & 0x7); + + // Now remember bits before collision + for(size_t p = 0; p < 5; p++) + { + pPN512->anticollision.iso_a.cln[p] |= bufIn[p]; + } + + // If we have all bits, then check BCC, go to next step + if( pPN512->anticollision.iso_a.valid_bits < 5*8 ) // Collision, add a 1 at the end of known bits to resolve collision + { + pPN512->anticollision.iso_a.cln[pPN512->anticollision.iso_a.valid_bits >> 3] |= (1 << (pPN512->anticollision.iso_a.valid_bits & 0x7)); + pPN512->anticollision.iso_a.valid_bits++; + + // Restart first step with more valid bits + pn512_initiator_isoa_anticollision_cascade_1(pPN512); + return; + } + + //Check BCC if all bits are valid + if( pPN512->anticollision.iso_a.cln[4] != (pPN512->anticollision.iso_a.cln[0] ^ pPN512->anticollision.iso_a.cln[1] ^ pPN512->anticollision.iso_a.cln[2] ^ pPN512->anticollision.iso_a.cln[3]) ) + { + NFC_WARN("Wrong BCC %02X != %02X", bufIn[4], bufIn[0] ^ bufIn[1] ^ bufIn[2] ^ bufIn[3]); + pn512_anticollision_callback(pPN512, NFC_ERR_COLLISION); + return; //TODO handle this properly + } + + if( pPN512->anticollision.iso_a.cln[0] == CT ) + { + //Not the last cascade level + if( pPN512->anticollision.iso_a.cascade_level == 3 ) // not allowed + { + NFC_WARN("Cascade tag present in cascade level 3"); + pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL); + return; + } + memcpy( &pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.uid[(pPN512->anticollision.iso_a.cascade_level - 1)*3], &pPN512->anticollision.iso_a.cln[1], 3 ); + pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.uidLength += 3; + } + else + { + //Last cascade level + memcpy( &pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.uid[(pPN512->anticollision.iso_a.cascade_level - 1)*3], &pPN512->anticollision.iso_a.cln[0], 4 ); + pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.uidLength += 4; + } + + buffer_builder_t* pDataOutBldr = &pPN512->readBufBldr; + buffer_builder_reset(pDataOutBldr); + + //Select and get SAK + buffer_builder_write_nu8(pDataOutBldr, SEL(pPN512->anticollision.iso_a.cascade_level - 1) ); //SEL: Cascade level + buffer_builder_write_nu8(pDataOutBldr, NVB(40) ); //NVB: 40 valid bits = 5 bytes + + //Transmit last 4 transmitted UID bytes + BCC (including cascade byte if relevant) + buffer_builder_write_n_bytes(pDataOutBldr, pPN512->anticollision.iso_a.cln, 5); + + NFC_DBG("Selecting target"); + + //This time compute & check CRC + pn512_set_crc((nfc_transceiver_t*)pPN512, true, true); + pn512_set_write((nfc_transceiver_t*)pPN512, buffer_builder_buffer(pDataOutBldr)); + pn512_transceive_hw(pPN512, pn512_transceive_mode_transceive, pn512_initiator_isoa_anticollision_cascade_3); +} + +static void pn512_initiator_isoa_anticollision_cascade_3(pn512_t* pPN512, nfc_err_t ret) +{ + buffer_t* pResp = pn512_get_read((nfc_transceiver_t*)pPN512); + + if(ret) + { + NFC_WARN("Did not receive response: error %d", ret); + pn512_anticollision_callback(pPN512, ret); + return; + } + + if( buffer_reader_readable(pResp) != 1 ) + { + NFC_WARN("Wrong length"); + pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL); + return; + } + + pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.sak = buffer_read_nu8(pResp); + NFC_DBG("Got SAK %02X", pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.sak); + + //Check SAK + if( pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.sak & 0x04 ) + { + //Continue anticollision + pPN512->anticollision.iso_a.cascade_level++; + pPN512->anticollision.iso_a.valid_bits = 0; + memset(pPN512->anticollision.iso_a.cln, 0, 5); + pn512_initiator_isoa_anticollision_cascade_1(pPN512); + } + else + { + //Anticollision complete + + NFC_DBG("Found one target- SAK = %02X", pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.sak); + + //Analyze SAK + memset(&pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type, 0, sizeof(nfc_tech_t)); + + if( pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.sak & 0x40 ) //NFC-IP1 compliant + { + pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type.nfc_nfc_dep_a = true; + } + if( (pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.sak & 0x20) + && pPN512->config.initiators.nfc_iso_dep_a ) //ISO-14443A-4 compliant + { + pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type.nfc_iso_dep_a = true; + } + if( !(pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcA.sak & 0x60) + && pPN512->config.initiators.nfc_type2 ) //Potentially NFC Type 2 (or Mifare, etc) + { + pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type.nfc_type2 = true; + } + + // Unknown target + if( !pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type.nfc_iso_dep_a + && !pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type.nfc_nfc_dep_a + && !pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type.nfc_type2 + ) + { + pn512_anticollision_callback(pPN512, NFC_ERR_NOPEER); + return; + } + + // Valid target detected + pPN512->transceiver.active_tech = pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type; + pPN512->transceiver.remote_targets_count++; + + if( !pPN512->config.options.bail_at_first_target + && pPN512->anticollision.iso_a.more_targets + && (pPN512->transceiver.remote_targets_count < MUNFC_MAX_REMOTE_TARGETS) ) + { + // Halt target and continue with others + buffer_builder_t* pDataOutBldr = &pPN512->readBufBldr; + buffer_builder_reset(pDataOutBldr); + + //HALTA + buffer_builder_write_nu8(pDataOutBldr, HALTA1); + buffer_builder_write_nu8(pDataOutBldr, HALTA2); + + pn512_set_crc((nfc_transceiver_t*)pPN512, true, false); + pn512_set_timeout((nfc_transceiver_t*)pPN512, 30); + pn512_set_write((nfc_transceiver_t*)pPN512, buffer_builder_buffer(pDataOutBldr)); + pn512_transceive_hw(pPN512, pn512_transceive_mode_transmit, pn512_initiator_isoa_anticollision_cascade_4); + return; + } + + // Leave it activated and finish! + pn512_initiator_isoa_anticollision_complete(pPN512); + } +} + +static void pn512_initiator_isoa_anticollision_cascade_4(pn512_t* pPN512, nfc_err_t ret) +{ + if(ret) + { + NFC_WARN("Could not halt device: error %d", ret); + pn512_anticollision_callback(pPN512, ret); + return; + } + + // Start again + pn512_initiator_isoa_anticollision_reqa(pPN512); +} + +void pn512_initiator_isoa_anticollision_complete(pn512_t* pPN512) +{ + pn512_anticollision_callback(pPN512, NFC_OK); +} + +// ISO B +static void pn512_initiator_isob_anticollision(pn512_t* pPN512, pn512_cb_t cb); +static void pn512_initiator_isob_anticollision_reqb(pn512_t* pPN512); +static void pn512_initiator_isob_anticollision_atqb(pn512_t* pPN512, nfc_err_t ret); +static void pn512_initiator_isob_anticollision_next_slot(pn512_t* pPN512); +static void pn512_initiator_isob_anticollision_haltb_resp(pn512_t* pPN512, nfc_err_t ret); +static void pn512_initiator_isob_anticollision_complete(pn512_t* pPN512); + +#define REQB 0x05 +#define HALTB 0x50 + +void pn512_initiator_isob_anticollision(pn512_t* pPN512, pn512_cb_t cb) +{ + pPN512->anticollision.cb = cb; + pPN512->anticollision.iso_b.slots_num_exponent = 0; // Start with one slot + pPN512->anticollision.iso_b.slot_number = 0; + pPN512->anticollision.iso_b.found_one = false; + + // Reset transceive mode + pPN512->transceive.mode = pn512_transceive_mode_idle; + + pn512_initiator_isob_anticollision_reqb(pPN512); +} + +void pn512_initiator_isob_anticollision_reqb(pn512_t* pPN512) +{ + buffer_builder_t* pDataOutBldr = &pPN512->readBufBldr; + buffer_builder_reset(pDataOutBldr); + + if (pPN512->anticollision.iso_b.slot_number == 0) + { + // Send REQB/WUPB + pPN512->anticollision.iso_b.more_targets = false; + + buffer_builder_write_nu8(pDataOutBldr, REQB); + buffer_builder_write_nu8(pDataOutBldr, 0x00); // AFI: All card types should respond + uint8_t wup = 0; + if( (pPN512->anticollision.iso_b.slots_num_exponent == 0) ) //&& (pPN512->anticollision.iso_b.slot_number == 0)) + { + wup |= 0x8; // Send Wake-Up command on first iteration + } + buffer_builder_write_nu8(pDataOutBldr, wup | (pPN512->anticollision.iso_b.slots_num_exponent & 0x7)); // Param: number of slots + } + else + { + // Just send slot marker + buffer_builder_write_nu8(pDataOutBldr, REQB | ((pPN512->anticollision.iso_b.slot_number & 0xf) << 4)); + } + + pn512_set_crc((nfc_transceiver_t*)pPN512, true, true); + pn512_set_timeout((nfc_transceiver_t*)pPN512, 18); + pn512_set_write((nfc_transceiver_t*)pPN512, buffer_builder_buffer(pDataOutBldr)); + pn512_transceive_hw(pPN512, pn512_transceive_mode_transceive, pn512_initiator_isob_anticollision_atqb); +} + +void pn512_initiator_isob_anticollision_atqb(pn512_t* pPN512, nfc_err_t ret) +{ + // Three cases: + // - 1 response --> store response, halt PICC and check next slot number + // - No response --> check next slot number + // - Collision --> check next slot number but we will have to increment number of slots + buffer_t* pResp = pn512_get_read((nfc_transceiver_t*)pPN512); + + if (ret && ( ret != NFC_ERR_COLLISION ) && ( ret != NFC_ERR_WRONG_COMM ) && ( ret != NFC_ERR_TIMEOUT )) + { + NFC_WARN("Did not receive ATQB: error %u", ret); + pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL); + return; + } + + // Increment slot number + pPN512->anticollision.iso_b.slot_number++; + + if( (ret == NFC_ERR_COLLISION) || (ret == NFC_ERR_WRONG_COMM) ) + { + pPN512->anticollision.iso_b.more_targets = true; + } + else if( !ret ) + { + pPN512->anticollision.iso_b.found_one = true; + + // Decode ATQB + if (buffer_reader_readable(pResp) != 12) + { + NFC_WARN("Wrong length (%u bytes)", buffer_reader_readable(pResp)); + pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL); + return; + } + + NFC_DBG("Got ATQB:"); + DBG_BLOCK( buffer_dump(pResp); ) + + // Check first byte + uint8_t atqb0 = buffer_read_nu8(pResp); + if (atqb0 != 0x50) + { + NFC_WARN("Wrong first byte for ATQB: %02X", atqb0); + pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL); + return; + } + + memset(&pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type, 0, sizeof(nfc_tech_t)); + pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type.nfc_iso_dep_b = true; + + // Save PUPI, application data & protocol info + buffer_read_n_bytes(pResp, pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcB.pupi, 4); + buffer_read_n_bytes(pResp, pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcB.application_data, 4); + buffer_read_n_bytes(pResp, pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcB.protocol_info, 3); + + // Valid target detected + pPN512->transceiver.active_tech = pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type; + pPN512->transceiver.remote_targets_count++; + + if( !pPN512->config.options.bail_at_first_target + && (pPN512->anticollision.iso_b.more_targets || (pPN512->anticollision.iso_b.slot_number < (1 << pPN512->anticollision.iso_b.slots_num_exponent))) + && (pPN512->transceiver.remote_targets_count < MUNFC_MAX_REMOTE_TARGETS) ) + { + // Halt target and continue with others + buffer_builder_t* pDataOutBldr = &pPN512->readBufBldr; + buffer_builder_reset(pDataOutBldr); + + // Halt PICC and move on to the next slot + + //HALTB + buffer_builder_write_nu8(pDataOutBldr, HALTB); + buffer_builder_write_n_bytes(pDataOutBldr, pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count - 1].nfcB.pupi, 4); + + pn512_set_crc((nfc_transceiver_t*)pPN512, true, true); + pn512_set_timeout((nfc_transceiver_t*)pPN512, 30); + pn512_set_write((nfc_transceiver_t*)pPN512, buffer_builder_buffer(pDataOutBldr)); + pn512_transceive_hw(pPN512, pn512_transceive_mode_transceive, pn512_initiator_isob_anticollision_haltb_resp); + return; + } + else + { + pn512_initiator_isob_anticollision_complete(pPN512); + return; + } + } + + // Move on to the next slot + pn512_initiator_isob_anticollision_next_slot(pPN512); +} + +void pn512_initiator_isob_anticollision_next_slot(pn512_t* pPN512) +{ + if( pPN512->anticollision.iso_b.slot_number >= (1 << pPN512->anticollision.iso_b.slots_num_exponent)) + { + if(!pPN512->anticollision.iso_b.more_targets) + { + // No further collisions to resolve + pn512_initiator_isob_anticollision_complete(pPN512); + return; + } + if(pPN512->anticollision.iso_b.slots_num_exponent >= 4) + { + // Cannot handle more than 16 slots + pn512_initiator_isob_anticollision_complete(pPN512); + return; + } + pPN512->anticollision.iso_b.slots_num_exponent++; + pPN512->anticollision.iso_b.slot_number = 0; + } + pn512_initiator_isob_anticollision_reqb(pPN512); +} + +void pn512_initiator_isob_anticollision_haltb_resp(pn512_t* pPN512, nfc_err_t ret) +{ + // Check for response + if (ret) + { + NFC_WARN("Did not receive HALTB response: error %u", ret); + pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL); + return; + } + + buffer_t* pResp = pn512_get_read((nfc_transceiver_t*)pPN512); + + if (buffer_reader_readable(pResp) != 1) + { + NFC_WARN("Wrong length (%u bytes)", buffer_reader_readable(pResp)); + pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL); + return; + } + + NFC_DBG("Got HALTB response:"); + DBG_BLOCK( buffer_dump(pResp); ) + + // Check byte + uint8_t haltbr = buffer_read_nu8(pResp); + if (haltbr != 0x00) + { + NFC_WARN("Wrong byte for HALTB response: %02X", haltbr); + pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL); + return; + } + + // PICC halted, move to next slot + pn512_initiator_isob_anticollision_next_slot(pPN512); +} + +void pn512_initiator_isob_anticollision_complete(pn512_t* pPN512) +{ + if(pPN512->anticollision.iso_b.found_one) + { + pn512_anticollision_callback(pPN512, NFC_OK); + } + else + { + pn512_anticollision_callback(pPN512, NFC_ERR_NOPEER); + } +} + +// Felica +static void pn512_initiator_felica_anticollision(pn512_t* pPN512, pn512_cb_t cb); +static void pn512_initiator_felica_anticollision_reqc(pn512_t* pPN512); +static void pn512_initiator_felica_anticollision_atqc(pn512_t* pPN512, nfc_err_t ret); +static void pn512_initiator_felica_anticollision_complete(pn512_t* pPN512); + +#define REQC 0x00 + +void pn512_initiator_felica_anticollision(pn512_t* pPN512, pn512_cb_t cb) +{ + pPN512->anticollision.cb = cb; + + // Reset transceive mode + pPN512->transceive.mode = pn512_transceive_mode_idle; + + pn512_initiator_felica_anticollision_reqc(pPN512); +} + +void pn512_initiator_felica_anticollision_reqc(pn512_t* pPN512) +{ + buffer_builder_t* pDataOutBldr = &pPN512->readBufBldr; + buffer_builder_reset(pDataOutBldr); + + buffer_builder_write_nu8(pDataOutBldr, 6); //Length + buffer_builder_write_nu8(pDataOutBldr, REQC); + buffer_builder_write_nu16(pDataOutBldr, 0xFFFF); //Any system code + buffer_builder_write_nu8(pDataOutBldr, 0x00); // Padding + buffer_builder_write_nu8(pDataOutBldr, 0x07); // 8 time slots, more would overflow the rx buffer if many cards responded + + pn512_set_crc((nfc_transceiver_t*)pPN512, true, true); + pn512_set_timeout((nfc_transceiver_t*)pPN512, 13); //8 timeslots at 212kbps + pn512_set_write((nfc_transceiver_t*)pPN512, buffer_builder_buffer(pDataOutBldr)); + + pn512_framing_rx_multiple_enable(pPN512); // Set RxMultiple bit + + pn512_transceive_hw(pPN512, pn512_transceive_mode_transceive, pn512_initiator_felica_anticollision_atqc); +} + +void pn512_initiator_felica_anticollision_atqc(pn512_t* pPN512, nfc_err_t ret) +{ + buffer_t* pResp = pn512_get_read((nfc_transceiver_t*)pPN512); + + if (ret || (buffer_reader_readable(pResp) == 0)) + { + NFC_WARN("Did not receive ATQC: error %d", ret); + pn512_anticollision_callback(pPN512, NFC_ERR_NOPEER); + return; + } + + // We might have multiple responses + NFC_DBG("Got ATQC:"); + DBG_BLOCK( buffer_dump(pResp); ) + + while (buffer_reader_readable(pResp) > 0) + { + if (buffer_reader_readable(pResp) != 18 + 1) // ATQC is 18 bytes, 1 byte for errors added by PN512 + { + NFC_WARN("Wrong length (%d bytes)", buffer_reader_readable(pResp)); + pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL); + return; + } + + // First byte is length, check that it's correct + uint8_t frame_length = buffer_read_nu8(pResp); + uint8_t atqc0 = buffer_read_nu8(pResp); + if( (frame_length != 18) || (atqc0 != 0x01) ) + { + NFC_WARN("Wrong ATQC frame"); + pn512_anticollision_callback(pPN512, NFC_ERR_PROTOCOL); + return; + } + + // Read NFCID2 + buffer_read_n_bytes(pResp, pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcF.nfcid2, 8); + + // Discard padding bytes + buffer_read_n_skip(pResp, 8); + + // Read error register + uint8_t err_reg = buffer_read_nu8(pResp); + if(err_reg & 0x1f) + { + // Error within this time slot, skip + continue; + } + + //Populate tech accordingly + memset(&pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type, 0, sizeof(nfc_tech_t)); + if( + ( pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcF.nfcid2[0] == 0x01 ) + && ( pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].nfcF.nfcid2[1] == 0xFE ) + ) + { + // NFC-DEP supported + pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type.nfc_nfc_dep_f_212 = 1; + } + else + { + // Type 3 supported + pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type.nfc_type3 = 1; + } + pPN512->transceiver.active_tech = pPN512->transceiver.remote_targets[pPN512->transceiver.remote_targets_count].type; + pPN512->transceiver.remote_targets_count++; + + // Only continue if we can have more targets + if( !pPN512->config.options.bail_at_first_target + && (pPN512->transceiver.remote_targets_count < MUNFC_MAX_REMOTE_TARGETS) ) + { + continue; + } + break; + } + + pn512_initiator_felica_anticollision_complete(pPN512); +} + +void pn512_initiator_felica_anticollision_complete(pn512_t* pPN512) +{ + pn512_anticollision_callback(pPN512, NFC_OK); +} + +void pn512_poll_setup(pn512_t* pPN512) +{ + bool target = pPN512->config.targets.nfc_type2 + || pPN512->config.targets.nfc_type3 + || pPN512->config.targets.nfc_iso_dep_a + || pPN512->config.targets.nfc_nfc_dep_a + || pPN512->config.targets.nfc_nfc_dep_f_212 + || pPN512->config.targets.nfc_nfc_dep_f_424; + +// No need for initiator-specific configuration at this stage, but keep this just in case +// bool initiator = pPN512->config.initiators.nfc_type1 +// || pPN512->config.initiators.nfc_type2 +// || pPN512->config.initiators.nfc_type3 +// || pPN512->config.initiators.nfc_iso_dep_a +// || pPN512->config.initiators.nfc_nfc_dep_a +// || pPN512->config.initiators.nfc_nfc_dep_f_212 +// || pPN512->config.initiators.nfc_nfc_dep_f_424; + + if (target) + { + NFC_DBG("Configure anticoll/polling response"); + //Setup ATQA, SAK and Felica polling response + pn512_fifo_clear(pPN512); + + buffer_builder_t* pDataCfgBldr = &pPN512->readBufBldr; + buffer_builder_reset(pDataCfgBldr); + + //Write ATQA + buffer_builder_write_nu8(pDataCfgBldr, 0x04); + buffer_builder_write_nu8(pDataCfgBldr, 0x00); + + //Write NFCID1 (0s as it will be randomly generated) - first byte will be set to 0x08 by HW according to NFC-IP1 + for (int i = 0; i < 3; i++) + { + buffer_builder_write_nu8(pDataCfgBldr, 0x00); + } + + //Write SAK + uint8_t sak = 0x00; //Default SAK (UID complete) + if (pPN512->config.targets.nfc_iso_dep_a) + { + sak |= 0x20; //This target is ISO-14443A-4 compliant + } + if (pPN512->config.targets.nfc_nfc_dep_a) + { + sak |= 0x40; //This target is NFC-IP1 compliant + } + buffer_builder_write_nu8(pDataCfgBldr, sak); + + //Write NFCID2 (xx 0xfe according to NFC-IP1 and 0s as 6 bytes will be randomly generated) + if (pPN512->config.targets.nfc_nfc_dep_f_212 || pPN512->config.targets.nfc_nfc_dep_f_424) + { + //Byte 1 is 0x01 if supporting NFC-IP1 + buffer_builder_write_nu8(pDataCfgBldr, 0x01); + } + else if (pPN512->config.targets.nfc_type3) //NFC-IP will have priority over type 3 tag emulation + { + //Byte 1 is 0x02 if supporting Type 3 tag platform + buffer_builder_write_nu8(pDataCfgBldr, 0x02); + } + else + { + buffer_builder_write_nu8(pDataCfgBldr, 0x00); + } + buffer_builder_write_nu8(pDataCfgBldr, 0xFE); + + for (int i = 0; i < 6; i++) + { + buffer_builder_write_nu8(pDataCfgBldr, 0x00); + } + + //2 PAD0 bytes (Felica spec) - this would code the manufacturer + IC code (0xFFFF for NFC-IP1 tags) + buffer_builder_write_nu8(pDataCfgBldr, 0xFF); + buffer_builder_write_nu8(pDataCfgBldr, 0xFF); + + //3 PAD1 bytes + buffer_builder_write_nu8(pDataCfgBldr, 0x00); + buffer_builder_write_nu8(pDataCfgBldr, 0x00); + buffer_builder_write_nu8(pDataCfgBldr, 0x00); + + if (!(pPN512->config.targets.nfc_nfc_dep_f_212 || pPN512->config.targets.nfc_nfc_dep_f_424) + && pPN512->config.targets.nfc_type3) + { + buffer_builder_write_nu8(pDataCfgBldr, 0x01); //MRTI Check + buffer_builder_write_nu8(pDataCfgBldr, 0x01); //MRTI Update + } + else + { + buffer_builder_write_nu8(pDataCfgBldr, 0x00); + buffer_builder_write_nu8(pDataCfgBldr, 0x00); + } + + //1 PAD2 byte + buffer_builder_write_nu8(pDataCfgBldr, 0x00); + + //2 system code bytes + if (!(pPN512->config.targets.nfc_nfc_dep_f_212 || pPN512->config.targets.nfc_nfc_dep_f_424) + && pPN512->config.targets.nfc_type3) + { + buffer_builder_write_nu8(pDataCfgBldr, 0x12); + buffer_builder_write_nu8(pDataCfgBldr, 0xFC); + } + else + { + buffer_builder_write_nu8(pDataCfgBldr, 0xFF); //Wildcard system code + buffer_builder_write_nu8(pDataCfgBldr, 0xFF); + } + + //Write NFCID3 + buffer_builder_write_nu8(pDataCfgBldr, 0x00); + + pn512_fifo_write(pPN512, buffer_builder_buffer(pDataCfgBldr)); + + pn512_cmd_exec(pPN512, PN512_CMD_CONFIG); + pn512_cmd_wait_idle(pPN512, -1); + + NFC_DBG("Overwrite NFCIDs with random numbers"); + + //Overwrite NFCIDs with random numbers + pn512_cmd_exec(pPN512, PN512_CMD_RNDIDG); + pn512_cmd_wait_idle(pPN512, -1); + } +} + +static void pn512_poll_iteration(pn512_t* pPN512, nfc_err_t ret); +static void pn512_poll_delay_complete(uint32_t events, void* pUserData) +{ + pn512_t* pPN512 = (pn512_t*) pUserData; + + if(events & EVENT_ABORTED) + { + pn512_poll_callback(pPN512, NFC_ERR_ABORTED); + return; + } + + pn512_poll_iteration(pPN512, NFC_OK); +} + +static void pn512_poll_delay(pn512_t* pPN512, uint32_t timeout) +{ + task_init(&pPN512->transceiver.task, EVENT_TIMEOUT, timeout, pn512_poll_delay_complete, pPN512); + scheduler_queue_task(&pPN512->transceiver.scheduler, &pPN512->transceiver.task); +} + +void pn512_poll_iteration(pn512_t* pPN512, nfc_err_t ret) +{ + do { + if( pPN512->poll.state == pn512_polling_state_start_listening ) + { + NFC_DBG("pn512_polling_state_start_listening"); + // Start with listening + if( !pn512_config_target(pPN512) ) + { + // Otherwise switch to next step + pPN512->poll.state = pn512_polling_state_start_polling; + continue; + } + + pPN512->poll.state = pn512_polling_state_listen_wait_for_remote_field; + + // Fix for PN512 bug that sometimes detects its own RF field + // if(pPN512->rf_on) + { + //Switch RF field off + pn512_rf_field_switch_off(pPN512); + NFC_DBG("RF field switched off"); + } + + NFC_DBG("Target anticollision"); + + bool iso14443a = pPN512->config.targets.nfc_type2 || pPN512->config.targets.nfc_iso_dep_a || pPN512->config.targets.nfc_nfc_dep_a; + bool felica = pPN512->config.targets.nfc_type3 || pPN512->config.targets.nfc_nfc_dep_f_212 || pPN512->config.targets.nfc_nfc_dep_f_424; + + NFC_DBG("Switch in target mode and set framing: ISO A: %s; Felica: %s", iso14443a?"Yes":"No", felica?"Yes":"No"); + + //Switch in target mode + if(iso14443a && felica) + { + pn512_framing_set(pPN512, nfc_framing_target_mode_detector); + } + else if(iso14443a) + { + pn512_framing_set(pPN512, nfc_framing_target_a_106); + } + else if(felica) + { + pn512_framing_set(pPN512, nfc_framing_target_f_212); + } + + pn512_rf_field_wait_for_external(pPN512, pPN512->config.options.listen_for, pn512_poll_iteration); + return; + } + + if( pPN512->poll.state == pn512_polling_state_listen_wait_for_remote_field ) + { + NFC_DBG("pn512_polling_state_listen_wait_for_remote_field"); + + if( !pn512_config_target(pPN512) ) + { + // Otherwise switch to next step + pPN512->poll.state = pn512_polling_state_start_listening; + continue; + } + + if( ret == NFC_ERR_TIMEOUT ) + { + // Continue polling + pPN512->poll.state = pn512_polling_state_start_polling; + continue; + } + + if(ret) + { + pn512_poll_callback(pPN512, ret); + return; + } + + pPN512->poll.state = pn512_polling_state_listen_anticollision; + + pn512_target_anticollision(pPN512, pn512_poll_iteration); + return; + } + + if( pPN512->poll.state == pn512_polling_state_listen_anticollision ) + { + NFC_DBG("pn512_polling_state_listen_anticollision"); + + if(ret == NFC_ERR_FIELD) + { + // This means that a remote field was dropped - give it another chance + pPN512->poll.state = pn512_polling_state_listen_wait_for_remote_field; + pn512_rf_field_switch_off(pPN512); + pn512_rf_field_wait_for_external(pPN512, pPN512->config.options.listen_for, pn512_poll_iteration); + return; + } + + if(ret) + { + pn512_poll_callback(pPN512, ret); + return; + } + + // Be safe, not sure what the framing is + pPN512->framing = nfc_framing_unknown; + + pn512_poll_callback(pPN512, NFC_OK); + return; + } + + if( pPN512->poll.state == pn512_polling_state_start_polling ) + { + NFC_DBG("pn512_polling_state_start_polling"); + + if( !pn512_config_initiator(pPN512) ) + { + // Otherwise switch to next step + pPN512->poll.state = pn512_polling_state_start_listening; + continue; + } + + // Try to activate RF field + pPN512->poll.state = pn512_polling_state_rf_collision_avoidance; + + pn512_rf_field_nfcip1_rf_collision_avoidance(pPN512, pn512_poll_iteration); + return; + } + + if( pPN512->poll.state == pn512_polling_state_rf_collision_avoidance) + { + NFC_DBG("pn512_polling_state_rf_collision_avoidance"); + + if(ret) + { + pn512_poll_callback(pPN512, ret); + return; + } + + NFC_DBG("Own RF field is %s", pPN512->rf_on?"on":"off"); + + if(!pPN512->rf_on) + { + // Go back to listening, target framing is still valid so no need to reset it + pPN512->poll.state = pn512_polling_state_listen_wait_for_remote_field; + continue; + } + + pPN512->poll.state = pn512_polling_state_polling_nfc_a_start; + } + + if( pPN512->poll.state == pn512_polling_state_polling_nfc_a_start ) + { + NFC_DBG("pn512_polling_state_polling_nfc_a_start"); + + //Check if ISO A is needed + bool nfc_a = pPN512->config.initiators.nfc_type2 || pPN512->config.initiators.nfc_iso_dep_a || pPN512->config.initiators.nfc_nfc_dep_a; //We do not support type 1 card emulation so irrelevant + if(!nfc_a) + { + // Continue with NFC B + pPN512->poll.state = pn512_polling_state_polling_nfc_b_start; + continue; + } + + pPN512->transceiver.initiator_ntarget = true; + pn512_framing_set(pPN512, nfc_framing_initiator_a_106); + + pPN512->poll.state = pn512_polling_state_polling_nfc_a_gt; + pn512_poll_delay(pPN512, 10 + 5); // Guard time for ISO A is 5 ms + return; + } + + if( pPN512->poll.state == pn512_polling_state_polling_nfc_a_gt ) + { + NFC_DBG("pn512_polling_state_polling_nfc_a_gt"); + + // Start anticollision + pPN512->poll.state = pn512_polling_state_polling_nfc_a_anticollision; + pn512_initiator_isoa_anticollision(pPN512, pn512_poll_iteration); + return; + } + + if( pPN512->poll.state == pn512_polling_state_polling_nfc_a_anticollision ) + { + NFC_DBG("pn512_polling_state_polling_nfc_a_anticollision"); + + if( ret == NFC_ERR_NOPEER ) + { + NFC_DBG("Not found"); + // Continue with NFC B + pPN512->poll.state = pn512_polling_state_polling_nfc_b_start; + continue; + } + + if( (ret == NFC_OK) && (pPN512->config.options.bail_at_first_tech || (pPN512->transceiver.remote_targets_count == MUNFC_MAX_REMOTE_TARGETS)) ) + { + // At least one target found, exit polling loop + pn512_poll_callback(pPN512, NFC_OK); + return; + } + + if( ret == NFC_ERR_ABORTED ) + { + pn512_poll_callback(pPN512, ret); + return; + } + + // Continue with NFC B + pPN512->poll.state = pn512_polling_state_polling_nfc_b_start; + continue; + } + + if( pPN512->poll.state == pn512_polling_state_polling_nfc_b_start ) + { + NFC_DBG("pn512_polling_state_polling_nfc_b_start"); + + //Check if ISO B is needed + bool nfc_b = pPN512->config.initiators.nfc_iso_dep_b; + if (!nfc_b) + { + // Continue with NFC F + pPN512->poll.state = pn512_polling_state_polling_nfc_f_start; + continue; + } + + pPN512->transceiver.initiator_ntarget = true; + pn512_framing_set(pPN512, nfc_framing_initiator_b_106); + + pPN512->poll.state = pn512_polling_state_polling_nfc_b_gt; + pn512_poll_delay(pPN512, 10 + 5); // Guard time for ISO B is 5 ms + return; + } + + if (pPN512->poll.state == pn512_polling_state_polling_nfc_b_gt) + { + NFC_DBG("pn512_polling_state_polling_nfc_b_gt"); + + // Start anticollision + pPN512->poll.state = pn512_polling_state_polling_nfc_b_anticollision; + pn512_initiator_isob_anticollision(pPN512, pn512_poll_iteration); + return; + } + + if (pPN512->poll.state == pn512_polling_state_polling_nfc_b_anticollision) + { + NFC_DBG("pn512_polling_state_polling_nfc_b_anticollision"); + + if (ret == NFC_ERR_NOPEER) + { + NFC_DBG("Not found"); + // Continue with NFC F + pPN512->poll.state = pn512_polling_state_polling_nfc_f_start; + continue; + } + + if ((ret == NFC_OK) + && (pPN512->config.options.bail_at_first_tech + || (pPN512->transceiver.remote_targets_count + == MUNFC_MAX_REMOTE_TARGETS))) + { + // At least one target found, exit polling loop + pn512_poll_callback(pPN512, NFC_OK); + return; + } + + if( ret == NFC_ERR_ABORTED ) + { + pn512_poll_callback(pPN512, ret); + return; + } + + // Continue with NFC F + pPN512->poll.state = pn512_polling_state_polling_nfc_f_start; + continue; + } + + if( pPN512->poll.state == pn512_polling_state_polling_nfc_f_start ) + { + NFC_DBG("pn512_polling_state_polling_nfc_f_start"); + + //Check if Felica is needed + bool nfc_f = pPN512->config.initiators.nfc_type3 || pPN512->config.initiators.nfc_nfc_dep_f_212; + if(!nfc_f) + { + // Wrap up + pPN512->poll.state = pn512_polling_state_finish_polling; + continue; + } + + pPN512->transceiver.initiator_ntarget = true; + pn512_framing_set(pPN512, nfc_framing_initiator_f_212); + + pPN512->poll.state = pn512_polling_state_polling_nfc_f_gt; + pn512_poll_delay(pPN512, 20); // Guard time for Felica is 20 ms + return; + } + + if( pPN512->poll.state == pn512_polling_state_polling_nfc_f_gt ) + { + NFC_DBG("pn512_polling_state_polling_nfc_f_gt"); + + // Start anticollision + pPN512->poll.state = pn512_polling_state_polling_nfc_f_anticollision; + pn512_initiator_felica_anticollision(pPN512, pn512_poll_iteration); + return; + } + + if( pPN512->poll.state == pn512_polling_state_polling_nfc_f_anticollision ) + { + NFC_DBG("pn512_polling_state_polling_nfc_f_anticollision"); + + if( ret == NFC_ERR_NOPEER ) + { + NFC_DBG("Not found"); + // Resolve polling + pPN512->poll.state = pn512_polling_state_finish_polling; + continue; + } + + if( (ret == NFC_OK) && (pPN512->config.options.bail_at_first_tech || (pPN512->transceiver.remote_targets_count == MUNFC_MAX_REMOTE_TARGETS)) ) + { + // At least one target found, exit polling loop + pn512_poll_callback(pPN512, NFC_OK); + return; + } + + if( ret == NFC_ERR_ABORTED ) + { + pn512_poll_callback(pPN512, ret); + return; + } + + // Resolve polling + pPN512->poll.state = pn512_polling_state_finish_polling; + continue; + } + + if( pPN512->poll.state == pn512_polling_state_finish_polling ) + { + if(pPN512->transceiver.remote_targets_count > 0) + { + pn512_poll_callback(pPN512, NFC_OK); + } + else + { + pn512_poll_callback(pPN512, NFC_ERR_NOPEER); + } + return; + } + + } while(true); +} + +// Main polling loop function +void pn512_poll_hw(pn512_t* pPN512, pn512_cb_t cb) +{ + nfc_transceiver_t* pTransceiver = (nfc_transceiver_t*)pPN512; + pPN512->poll.cb = cb; + + //Reset active state + pTransceiver->initiator_ntarget = false; + memset(&pTransceiver->active_tech, 0, sizeof(nfc_tech_t)); + + //Reset discovered targets + pTransceiver->remote_targets_count = 0; + + //Initialize state machine + pPN512->poll.state = pn512_polling_state_start_listening; + + if( !pn512_config_target(pPN512) && !pn512_config_initiator(pPN512) ) + { + pn512_poll_callback(pPN512, NFC_ERR_PARAMS); + return; + } + + //First iteration + pn512_poll_iteration(pPN512, NFC_OK); +} diff --git a/features/nfc/stack/transceiver/pn512/pn512_poll.h b/features/nfc/stack/transceiver/pn512/pn512_poll.h new file mode 100644 index 0000000000..b7576f831c --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512_poll.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014-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 pn512_poll.h + * \copyright Copyright (c) ARM Ltd 2014 + * \author Donatien Garnier + */ + +#ifndef PN512_POLL_H_ +#define PN512_POLL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "inc/nfc.h" + + +void pn512_poll_setup(pn512_t* pPN512); +void pn512_poll_hw(pn512_t* pPN512, pn512_cb_t cb); + + +#ifdef __cplusplus +} +#endif + +#endif /* PN512_POLL_H_ */ diff --git a/features/nfc/stack/transceiver/pn512/pn512_registers.c b/features/nfc/stack/transceiver/pn512/pn512_registers.c new file mode 100644 index 0000000000..a674f47a48 --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512_registers.c @@ -0,0 +1,157 @@ + +/* + * 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 pn512_registers.c + * \copyright Copyright (c) ARM Ltd 2013 + * \author Donatien Garnier + * \details Access to PN512 registers + */ + + +#define __DEBUG__ 0 +#ifndef __MODULE__ +#define __MODULE__ "pn512_registers.c" +#endif +#include "inc/nfc.h" + +#include "pn512_registers.h" +#include "pn512_hw.h" +#include "pn512.h" + +#define REGISTER_PAGE(x) ((x)>>4) +#define REGISTER_ADDR(x) ((x)&0xF) + +/** \addtogroup PN512 + * \internal + * @{ + * \name Registers + * @{ + */ + +static void pn512_register_switch_page_intl(pn512_t* pPN512, uint8_t page); + +/** \internal Initialize underlying pn512_registers_t structure + * \param pPN512 pointer to pn512_t structure + */ +void pn512_registers_init(pn512_t* pPN512) +{ + pPN512->registers.registers_page = 0; +} + +#define PN512_CFG_INIT_LEN 9 +static const uint8_t PN512_CFG_INIT_REGS[] = { + PN512_REG_DIVIEN, + PN512_REG_MODE, + PN512_REG_GSNOFF, + PN512_REG_RFCFG, + PN512_REG_CWGSP, + PN512_REG_MIFNFC, + PN512_REG_FELNFC2, + PN512_REG_RXSEL, + PN512_REG_TYPEB +}; +static const uint8_t PN512_CFG_INIT_VALS[] = { + 0x80, + 0x3F, + 0xF2, + 0x68, + 0x3F, + 0x62, + 0x80, + 0x84, + 0x00 +}; //Timer: For now max prescaler, max reload value + +/** \internal Switch to known (0) registers page, reset registers state + * \param pPN512 pointer to pn512_t structure + */ +void pn512_registers_reset(pn512_t* pPN512) +{ + pn512_register_switch_page_intl(pPN512, 0); + for(int i = 0; i < PN512_CFG_INIT_LEN; i++) + { + pn512_register_write(pPN512, PN512_CFG_INIT_REGS[i], PN512_CFG_INIT_VALS[i]); + } +} + +/** \internal Write register + * \param pPN512 pointer to pn512_t structure + * \param address register address + * \param data value to write in register + */ +void pn512_register_write(pn512_t* pPN512, uint8_t address, uint8_t data) +{ + NFC_DBG("Write [%02x] << %02x", address, data); + if(REGISTER_PAGE(address) != pPN512->registers.registers_page) + { + pn512_register_switch_page_intl(pPN512, REGISTER_PAGE(address)); + } + address=REGISTER_ADDR(address); + pn512_hw_write(pPN512, address, &data, 1); +} + +/** \internal Read register + * \param pPN512 pointer to pn512_t structure + * \param address register address + * \return data value read from register + */ +uint8_t pn512_register_read(pn512_t* pPN512, uint8_t address) +{ + uint8_t data; + DBG_BLOCK( + uint8_t __dbg_addr; + __dbg_addr = address; //FIXME + ) + if(REGISTER_PAGE(address) != pPN512->registers.registers_page) + { + pn512_register_switch_page_intl(pPN512, REGISTER_PAGE(address)); + } + address=REGISTER_ADDR(address); + pn512_hw_read(pPN512, address, &data, 1); + NFC_DBG("Read [%02x] >> %02x", __dbg_addr, data); + return data; +} + +void pn512_register_switch_page(pn512_t* pPN512, uint8_t address) +{ + if(REGISTER_PAGE(address) != pPN512->registers.registers_page) + { + pn512_register_switch_page_intl(pPN512, REGISTER_PAGE(address)); + } +} + +/** \internal Switch registers page + * \param pPN512 pointer to pn512_t structure + * \param page registers page + */ +void pn512_register_switch_page_intl(pn512_t* pPN512, uint8_t page) +{ + uint8_t pageRegValue; + pageRegValue = (1 << 7) | page; + + pn512_hw_write(pPN512, PN512_REG_PAGE, &pageRegValue, 1); + + pPN512->registers.registers_page = page; +} + + + +/** + * @} + * @} + * */ diff --git a/features/nfc/stack/transceiver/pn512/pn512_registers.h b/features/nfc/stack/transceiver/pn512/pn512_registers.h new file mode 100644 index 0000000000..cf9c4b2d27 --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512_registers.h @@ -0,0 +1,114 @@ +/* + * 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 pn512_registers.h + * \copyright Copyright (c) ARM Ltd 2013 + * \author Donatien Garnier + */ + +#ifndef PN512_REGISTERS_H_ +#define PN512_REGISTERS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "inc/nfc.h" + +#include "pn512.h" + +//Page 0 - Command and Status +#define PN512_REG_PAGE 0x00 //Selects the register page +#define PN512_REG_COMMAND 0x01 //Starts and stops command execution +#define PN512_REG_COMIEN 0x02 //Controls bits to enable and disable the passing of Interrupt Requests +#define PN512_REG_DIVIEN 0x03 //Controls bits to enable and disable the passing of Interrupt Requests +#define PN512_REG_COMIRQ 0x04 //Contains Interrupt Request bits +#define PN512_REG_DIVIRQ 0x05 //Contains Interrupt Request bits +#define PN512_REG_ERROR 0x06 //Error bits showing the error status of the last command executed +#define PN512_REG_STATUS1 0x07 //Contains status bits for communication +#define PN512_REG_STATUS2 0x08 //Contains status bits of the receiver and transmitter +#define PN512_REG_FIFODATA 0x09 //In- and output of 64 byte FIFO-buffer +#define PN512_REG_FIFOLEVEL 0x0A //Indicates the number of bytes stored in the FIFO +#define PN512_REG_WATERLEVEL 0x0B //Defines the level for FIFO under- and overflow warning +#define PN512_REG_CONTROL 0x0C //Contains miscellaneous Control Registers +#define PN512_REG_BITFRAMING 0x0D //Adjustments for bit oriented frames +#define PN512_REG_COLL 0x0E //Bit position of the first bit collision detected on the RF-interface + +//Page 1 - Command +//#define PN512_REG_PAGE 0x10 //Selects the register page +#define PN512_REG_MODE 0x11 //Defines general modes for transmitting and receiving +#define PN512_REG_TXMODE 0x12 //Defines the data rate and framing during transmission +#define PN512_REG_RXMODE 0x13 //Defines the data rate and framing during receiving +#define PN512_REG_TXCONTROL 0x14 //Controls the logical behavior of the antenna driver pins TX1 and TX2 +#define PN512_REG_TXAUTO 0x15 //Controls the setting of the antenna drivers +#define PN512_REG_TXSEL 0x16 //Selects the internal sources for the antenna driver +#define PN512_REG_RXSEL 0x17 //Selects internal receiver settings +#define PN512_REG_RXTHRESHOLD 0x18 //Selects thresholds for the bit decoder +#define PN512_REG_DEMOD 0x19 //Defines demodulator settings +#define PN512_REG_FELNFC1 0x1A //Defines the length of the valid range for the receive package +#define PN512_REG_FELNFC2 0x1B //Defines the length of the valid range for the receive package +#define PN512_REG_MIFNFC 0x1C //Controls the communication in ISO/IEC 14443/MIFARE and NFC target mode at 106 kbit +#define PN512_REG_MANUALRCV 0x1D //Allows manual fine tuning of the internal receiver +#define PN512_REG_TYPEB 0x1E //Configure the ISO/IEC 14443 type B +#define PN512_REG_SERIALSPEED 0x1F //Selects the speed of the serial UART interface + +//Page 2 - CFG +//#define PN512_REG_PAGE 0x20 //Selects the register page +#define PN512_REG_CRCRESULT_MSB 0x21 //Shows the actual MSB and LSB values of the CRC calculation +#define PN512_REG_CRCRESULT_LSB 0x22 //Shows the actual MSB and LSB values of the CRC calculation +#define PN512_REG_GSNOFF 0x23 //Selects the conductance of the antenna driver pins TX1 and TX2 for modulation, when the driver is switched off +#define PN512_REG_MODWIDTH 0x24 //Controls the setting of the ModWidth +#define PN512_REG_TXBITPHASE 0x25 //Adjust the TX bit phase at 106 kbit +#define PN512_REG_RFCFG 0x26 //Configures the receiver gain and RF level +#define PN512_REG_GSNON 0x27 //Selects the conductance of the antenna driver pins TX1 and TX2 for modulation when the drivers are switched on +#define PN512_REG_CWGSP 0x28 //Selects the conductance of the antenna driver pins TX1 and TX2 for modulation during times of no modulation +#define PN512_REG_MODGSP 0x29 //Selects the conductance of the antenna driver pins TX1 and TX2 for modulation during modulation +#define PN512_REG_TMODE_TPRESCALERHIGH 0x2A //Defines settings for the internal timer +#define PN512_REG_TPRESCALERLOW 0x2B //Defines settings for the internal timer +#define PN512_REG_TRELOADHIGH 0x2C //Describes the 16-bit timer reload value +#define PN512_REG_TRELOADLOW 0x2D //Describes the 16-bit timer reload value +#define PN512_REG_TCOUNTERVALHIGH 0x2E //Shows the 16-bit actual timer value +#define PN512_REG_TCOUNTERVALLOW 0x2F //Shows the 16-bit actual timer value + +//Page 3 - TestRegister +//#define PN512_REG_PAGE 0x30 //Selects the register page +#define PN512_REG_TESTSEL1 0x31 //General test signal configuration +#define PN512_REG_TESTSEL2 0x32 //General test signal configuration and PRBS control +#define PN512_REG_TESTPINEN 0x33 //Enables pin output driver on 8-bit parallel bus (Note: For serial interfaces only) +#define PN512_REG_TESTPINVALUE 0x34 //Defines the values for the 8-bit parallel bus when it is used as I/O bus +#define PN512_REG_TESTBUS 0x35 //Shows the status of the internal testbus +#define PN512_REG_AUTOTEST 0x36 //Controls the digital selftest +#define PN512_REG_VERSION 0x37 //Shows the version +#define PN512_REG_ANALOGTEST 0x38 //Controls the pins AUX1 and AUX2 +#define PN512_REG_TESTDAC1 0x39 //Defines the test value for the TestDAC1 +#define PN512_REG_TESTDAC2 0x3A //Defines the test value for the TestDAC2 +#define PN512_REG_TESTADC 0x3B //Shows the actual value of ADC I and Q + + +void pn512_registers_init(pn512_t* pPN512); +void pn512_registers_reset(pn512_t* pPN512); + +void pn512_register_write(pn512_t* pPN512, uint8_t address, uint8_t data); +uint8_t pn512_register_read(pn512_t* pPN512, uint8_t address); + +void pn512_register_switch_page(pn512_t* pPN512, uint8_t address); + +#ifdef __cplusplus +} +#endif + +#endif /* PN512_REGISTERS_H_ */ diff --git a/features/nfc/stack/transceiver/pn512/pn512_rf.c b/features/nfc/stack/transceiver/pn512/pn512_rf.c new file mode 100644 index 0000000000..fa2d2dd335 --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512_rf.c @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2014-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 pn512_rf.c + * \copyright Copyright (c) ARM Ltd 2014 + * \author Donatien Garnier + */ + +#define __DEBUG__ 0 +#ifndef __MODULE__ +#define __MODULE__ "pn512_rf.c" +#endif + +#include "inc/nfc.h" + +#include "pn512_callback.h" +#include "pn512_rf.h" +#include "pn512_registers.h" +#include "pn512_timer.h" +#include "pn512_irq.h" +#include "pn512.h" +#include "pn512_internal.h" + +#include "stdlib.h" //For rand() func + +#define PN512_FRAMING_REGS 6 +static const uint8_t framing_registers[] = { PN512_REG_MODE, PN512_REG_TXMODE, PN512_REG_RXMODE, PN512_REG_MODGSP, PN512_REG_RXTHRESHOLD, PN512_REG_MODWIDTH }; +static const uint8_t framing_registers_mode_detector[] = { 0x3B, 0x80, 0x80, 0x3F, 0x55, 0x26 }; +static const uint8_t framing_registers_initiator_iso14443a_106k[] = { 0x3D, 0x80, 0x80, 0x3F, 0x55, 0x26 }; +static const uint8_t framing_registers_initiator_iso14443b_106k[] = { 0x3F, 0x83, 0x83, 0x04, 0x50, 0x26 }; +static const uint8_t framing_registers_target_iso14443a_106k[] = { 0x3D, 0x80, 0x80, 0x3F, 0x55, 0x26 }; +static const uint8_t framing_registers_felica_212k[] = { 0x3A, 0x92, 0x92, 0x12, 0x55, 0x15 }; +static const uint8_t framing_registers_felica_414k[] = { 0x3A, 0xA2, 0xA2, 0x12, 0x55, 0x0A }; + +nfc_err_t pn512_framing_set(pn512_t* pPN512, nfc_framing_t framing) +{ + if(framing == pPN512->framing) //No need to do anything + { + return NFC_OK; + } + + NFC_DBG("Switching to %u", framing); + + const uint8_t* framing_registers_values; + switch(framing) + { + case nfc_framing_target_mode_detector: + framing_registers_values = framing_registers_mode_detector; + break; + case nfc_framing_target_a_106: + framing_registers_values = framing_registers_target_iso14443a_106k; + break; + case nfc_framing_initiator_a_106: + framing_registers_values = framing_registers_initiator_iso14443a_106k; + break; + case nfc_framing_initiator_b_106: + framing_registers_values = framing_registers_initiator_iso14443b_106k; + break; + case nfc_framing_target_f_212: + case nfc_framing_initiator_f_212: + framing_registers_values = framing_registers_felica_212k; + break; + case nfc_framing_target_f_424: + case nfc_framing_initiator_f_424: + framing_registers_values = framing_registers_felica_414k; + break; + default: + return NFC_ERR_UNSUPPORTED; + } + + for(int i = 0; i < PN512_FRAMING_REGS; i++) + { + pn512_register_write(pPN512, framing_registers[i], framing_registers_values[i]); + } + + pPN512->framing = framing; + pPN512->crc.out = true; + pPN512->crc.in = true; + + //TODO initiator: PN512_REG_MODGSP + + switch(pPN512->framing) + { + case nfc_framing_initiator_a_106: + case nfc_framing_initiator_b_106: + case nfc_framing_initiator_f_212: + case nfc_framing_initiator_f_424: + pn512_register_write(pPN512, PN512_REG_CONTROL, 0x10); //Act as initiator + break; + case nfc_framing_target_mode_detector: + case nfc_framing_target_a_106: + case nfc_framing_target_f_212: + case nfc_framing_target_f_424: + pn512_register_write(pPN512, PN512_REG_CONTROL, 0x00); //Act as target + break; + default: + return NFC_ERR_UNSUPPORTED; + } +#if 1 + if( (pPN512->framing == nfc_framing_initiator_a_106) /*|| (pPN512->framing == pn512_framing_target_iso14443a_106k)*/ ) + { + //Enable 100% ASK Modulation + pn512_register_write(pPN512, PN512_REG_TXAUTO, pn512_register_read(pPN512, PN512_REG_TXAUTO) | 0x40 ); + } + else + { + pn512_register_write(pPN512, PN512_REG_TXAUTO, pn512_register_read(pPN512, PN512_REG_TXAUTO) & (~0x40) ); + } +#endif + return NFC_OK; +} + +nfc_err_t pn512_framing_crc_set(pn512_t* pPN512, bool out, bool in) +{ + const uint8_t* framing_registers_values; + switch(pPN512->framing) + { + case nfc_framing_target_mode_detector: + framing_registers_values = framing_registers_mode_detector; + break; + case nfc_framing_target_a_106: + framing_registers_values = framing_registers_target_iso14443a_106k; + break; + case nfc_framing_initiator_a_106: + framing_registers_values = framing_registers_initiator_iso14443a_106k; + break; + case nfc_framing_initiator_b_106: + framing_registers_values = framing_registers_initiator_iso14443b_106k; + break; + case nfc_framing_target_f_212: + case nfc_framing_initiator_f_212: + framing_registers_values = framing_registers_felica_212k; + break; + case nfc_framing_target_f_424: + case nfc_framing_initiator_f_424: + framing_registers_values = framing_registers_felica_414k; + break; + default: + return NFC_ERR_UNSUPPORTED; + } + + if(pPN512->crc.out != out) + { + pn512_register_write(pPN512, framing_registers[1], (framing_registers_values[1] & 0x7F) | (out?0x80:0x00)); //TXMODE + pPN512->crc.out = out; + } + if(pPN512->crc.in != in) + { + pn512_register_write(pPN512, framing_registers[2], (framing_registers_values[2] & 0x7F) | (in?0x80:0x00)); //RXMODE + pPN512->crc.in = in; + } + + return NFC_OK; +} + +nfc_err_t pn512_framing_rx_multiple_enable(pn512_t* pPN512) +{ + const uint8_t* framing_registers_values; + switch(pPN512->framing) + { + case nfc_framing_target_mode_detector: + framing_registers_values = framing_registers_mode_detector; + break; + case nfc_framing_target_a_106: + framing_registers_values = framing_registers_target_iso14443a_106k; + break; + case nfc_framing_initiator_a_106: + framing_registers_values = framing_registers_initiator_iso14443a_106k; + break; + case nfc_framing_initiator_b_106: + framing_registers_values = framing_registers_initiator_iso14443b_106k; + break; + case nfc_framing_target_f_212: + case nfc_framing_initiator_f_212: + framing_registers_values = framing_registers_felica_212k; + break; + case nfc_framing_target_f_424: + case nfc_framing_initiator_f_424: + framing_registers_values = framing_registers_felica_414k; + break; + default: + return NFC_ERR_UNSUPPORTED; + } + + pn512_register_write(pPN512, framing_registers[2], (framing_registers_values[2] & 0x7F) | (pPN512->crc.in?0x80:0x00) | 0x04); //RXMODE + + return NFC_OK; +} + +void pn512_rf_field_switch_off(pn512_t* pPN512) +{ + pn512_register_write(pPN512, PN512_REG_TXAUTO, 0x00); + pn512_register_write(pPN512, PN512_REG_TXCONTROL, 0x80); + pPN512->rf_on = false; +} + +void pn512_rf_field_nfcip1_rf_collision_avoidance_complete(uint32_t events, void* pUserData) +{ + pn512_t* pPN512 = (pn512_t*) pUserData; + + uint16_t irq_res = pn512_irq_get(pPN512); + + (void) events; + + pn512_timer_stop(pPN512); + pn512_timer_config(pPN512, false, 0, 0xffff); //Deactivate autostart + pn512_irq_set(pPN512, PN512_IRQ_NONE); + pn512_irq_clear(pPN512, PN512_IRQ_RF_ON | PN512_IRQ_TIMER); + + if(irq_res & PN512_IRQ_RF_ON) + { + NFC_DBG("External field on"); + + //Clear TXAUTO register + pn512_register_write(pPN512, PN512_REG_TXAUTO, 0x00); + + pPN512->rf_on = false; //External field on + pn512_rf_callback(pPN512, NFC_OK); + return; + } + + //Has our RF field been switched on? + if( pn512_register_read(pPN512, PN512_REG_TXAUTO) & 0x40 ) //InitialRFOn bit is cleared automatically, if the RF field is switched on + { + NFC_ERR("InitialRFOn bit still set"); + pn512_rf_callback(pPN512, NFC_ERR_UNKNOWN); + return; + } + + pPN512->rf_on = true; //Own field on and guard time ok + + NFC_DBG("RF field enabled"); + pn512_rf_callback(pPN512, NFC_OK); +} + +void pn512_rf_field_nfcip1_rf_collision_avoidance(pn512_t* pPN512, pn512_cb_t cb) +{ + pPN512->rf.cb = cb; + pn512_irq_clear(pPN512, PN512_IRQ_RF_ON | PN512_IRQ_TIMER); + + //If our field is switched on, Wait TIRFG according to NFC-IP1 = 5ms => 67800 clock edges = (3+1)*8475 + pn512_timer_config(pPN512, true, 3, 8475); + + pn512_irq_set(pPN512, PN512_IRQ_RF_ON /* External field switched on */ + | PN512_IRQ_TIMER /* Timer reached 0 */ ); + + //Try to enable RF field in compliance with NFC-IP1 + pn512_register_write(pPN512, PN512_REG_TXAUTO, 0x0F); + + //Is external RF Field already on? + if( pn512_register_read(pPN512, PN512_REG_STATUS1) & 0x4 ) + { + NFC_DBG("External field already on"); + pPN512->rf_on = false; //External field on + + //Cancel + pn512_timer_stop(pPN512); + pn512_timer_config(pPN512, false, 0, 0xffff); //Deactivate autostart + pn512_irq_clear(pPN512, PN512_IRQ_RF_ON | PN512_IRQ_TIMER); + pn512_rf_callback(pPN512, NFC_OK); + return; + } + + //Queue task to process IRQ + task_init(&pPN512->transceiver.task, EVENT_HW_INTERRUPT | EVENT_TIMEOUT, -1, pn512_rf_field_nfcip1_rf_collision_avoidance_complete, pPN512); + scheduler_queue_task(&pPN512->transceiver.scheduler, &pPN512->transceiver.task); +} + +void pn512_rf_field_wait_for_external_complete_task(uint32_t events, void* pUserData) +{ + pn512_t* pPN512 = (pn512_t*) pUserData; + + NFC_DBG("%lu events", events); + + //Wake up PN512 + pn512_register_write(pPN512, PN512_REG_COMMAND, 0x00); + while( pn512_register_read(pPN512, PN512_REG_COMMAND) & 0x10 ); + + pn512_irq_set(pPN512, PN512_IRQ_NONE); + pn512_irq_clear(pPN512, PN512_IRQ_RF_ON); + + if(events & EVENT_ABORTED) + { + pn512_rf_callback(pPN512, NFC_ERR_ABORTED); + return; + } + + if( events & EVENT_TIMEOUT ) + { + NFC_DBG("Timeout"); + pn512_rf_callback(pPN512, NFC_ERR_TIMEOUT); + return; + } + + NFC_DBG("On"); + pn512_rf_callback(pPN512, NFC_OK); +} + +void pn512_rf_field_wait_for_external(pn512_t* pPN512, int timeout, pn512_cb_t cb) +{ + pPN512->rf.cb = cb; + + pn512_irq_clear(pPN512, PN512_IRQ_RF_ON); + + NFC_DBG("Wait for RF field to come up (timeout %d)", timeout); + + //Is external RF Field already on? + pn512_irq_set(pPN512, PN512_IRQ_RF_ON /* External field switched on */); + if( pn512_register_read(pPN512, PN512_REG_STATUS1) & 0x4 ) + { + NFC_DBG("RF field already on"); + pn512_irq_set(pPN512, PN512_IRQ_NONE); + pn512_irq_clear(pPN512, PN512_IRQ_RF_ON); + + pn512_rf_callback(pPN512, NFC_OK); + return; + } + + //Send PN512 to sleep mode + pn512_register_write(pPN512, PN512_REG_COMMAND, 0x30); //Receiver off + soft power down + + //Queue task to process IRQ + task_init(&pPN512->transceiver.task, EVENT_HW_INTERRUPT | EVENT_TIMEOUT, timeout, pn512_rf_field_wait_for_external_complete_task, pPN512); + scheduler_queue_task(&pPN512->transceiver.scheduler, &pPN512->transceiver.task); +} + + + + diff --git a/features/nfc/stack/transceiver/pn512/pn512_rf.h b/features/nfc/stack/transceiver/pn512/pn512_rf.h new file mode 100644 index 0000000000..ac52f1c25e --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512_rf.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2014-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 pn512_rf.h + * \copyright Copyright (c) ARM Ltd 2014 + * \author Donatien Garnier + */ + +#ifndef PN512_RF_H_ +#define PN512_RF_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "inc/nfc.h" +#include "pn512_callback.h" +#include "pn512.h" +#include "pn512_rf.h" + +typedef struct __pn512 pn512_t; + +nfc_err_t pn512_framing_set(pn512_t* pPN512, nfc_framing_t framing); + +nfc_err_t pn512_framing_crc_set(pn512_t* pPN512, bool out, bool in); + +nfc_err_t pn512_framing_rx_multiple_enable(pn512_t* pPN512); + +#define PN512_FRAMING_IS_TARGET( framing ) ((framing) <= nfc_framing_target_f_424) + +void pn512_rf_field_switch_off(pn512_t* pPN512); + +void pn512_rf_field_nfcip1_rf_collision_avoidance(pn512_t* pPN512, pn512_cb_t cb); + +void pn512_rf_field_wait_for_external(pn512_t* pPN512, int timeout, pn512_cb_t cb); + + +#ifdef __cplusplus +} +#endif + +#endif /* PN512_RF_H_ */ diff --git a/features/nfc/stack/transceiver/pn512/pn512_timer.c b/features/nfc/stack/transceiver/pn512/pn512_timer.c new file mode 100644 index 0000000000..d4d2a6ae9d --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512_timer.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014-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 pn512_timer.c + * \copyright Copyright (c) ARM Ltd 2014 + * \author Donatien Garnier + */ + +#include "inc/nfc.h" + +#include "pn512_timer.h" +#include "pn512_registers.h" + +void pn512_timer_config(pn512_t* pPN512, bool autostart, uint16_t prescaler, uint16_t countdown_value) +{ + pn512_timer_stop(pPN512); //just in case... + + pn512_register_write(pPN512, PN512_REG_TRELOADLOW, countdown_value & 0xFF); + pn512_register_write(pPN512, PN512_REG_TRELOADHIGH, (countdown_value >> 8) & 0xFF); + + pn512_register_write(pPN512, PN512_REG_TPRESCALERLOW, prescaler & 0xFF); + pn512_register_write(pPN512, PN512_REG_TMODE_TPRESCALERHIGH, (autostart?0x80:0x00) | ((prescaler>>8) & 0x0F) ); +} + +void pn512_timer_start(pn512_t* pPN512) +{ + //The control register also contains the initiator bit that we must set correctly + switch(pPN512->framing) + { + case nfc_framing_initiator_a_106: + case nfc_framing_initiator_f_212: + case nfc_framing_initiator_f_424: + pn512_register_write(pPN512, PN512_REG_CONTROL, 0x50); + break; + case nfc_framing_target_mode_detector: + case nfc_framing_target_a_106: + case nfc_framing_target_f_212: + case nfc_framing_target_f_424: + default: + pn512_register_write(pPN512, PN512_REG_CONTROL, 0x40); + break; + } +} + +void pn512_timer_stop(pn512_t* pPN512) +{ + //The control register also contains the initiator bit that we must set correctly + switch(pPN512->framing) + { + case nfc_framing_initiator_a_106: + case nfc_framing_initiator_f_212: + case nfc_framing_initiator_f_424: + pn512_register_write(pPN512, PN512_REG_CONTROL, 0x90); + break; + case nfc_framing_target_mode_detector: + case nfc_framing_target_a_106: + case nfc_framing_target_f_212: + case nfc_framing_target_f_424: + default: + pn512_register_write(pPN512, PN512_REG_CONTROL, 0x80); + break; + } +} diff --git a/features/nfc/stack/transceiver/pn512/pn512_timer.h b/features/nfc/stack/transceiver/pn512/pn512_timer.h new file mode 100644 index 0000000000..d04b524109 --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512_timer.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014-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 pn512_timer.h + * \copyright Copyright (c) ARM Ltd 2014 + * \author Donatien Garnier + */ + +#ifndef PN512_TIMER_H_ +#define PN512_TIMER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "inc/nfc.h" + +typedef struct __pn512 pn512_t; + +void pn512_timer_config(pn512_t* pPN512, bool autostart, uint16_t prescaler, uint16_t countdown_value); + +void pn512_timer_start(pn512_t* pPN512); +void pn512_timer_stop(pn512_t* pPN512); + +#ifdef __cplusplus +} +#endif + +#endif /* PN512_TIMER_H_ */ diff --git a/features/nfc/stack/transceiver/pn512/pn512_transceive.c b/features/nfc/stack/transceiver/pn512/pn512_transceive.c new file mode 100644 index 0000000000..c96c36b8cf --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512_transceive.c @@ -0,0 +1,495 @@ +/* + * Copyright (c) 2014-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 pn512_transceive.c + * \copyright Copyright (c) ARM Ltd 2014 + * \author Donatien Garnier + */ + +#define __DEBUG__ 0 +#ifndef __MODULE__ +#define __MODULE__ "pn512_transceive.c" +#endif + +#include "inc/nfc.h" + +#include "pn512.h" +#include "pn512_transceive.h" +#include "pn512_rf.h" +#include "pn512_irq.h" +#include "pn512_cmd.h" +#include "pn512_registers.h" +#include "pn512_internal.h" + + +#define TIMEOUT 1000 + +void pn512_transceive_hw_tx_iteration(pn512_t* pPN512, bool start) +{ + uint16_t irqs_en = pn512_irq_enabled(pPN512); + + if( buffer_reader_readable(&pPN512->writeBuf) > 0 ) + { + //Fill FIFO + pn512_fifo_write(pPN512, &pPN512->writeBuf); + + if (buffer_reader_readable(&pPN512->writeBuf) > 0) //Did not fit in FIFO + { + pn512_irq_clear(pPN512, PN512_IRQ_LOW_ALERT); + //Has low FIFO alert IRQ already been enabled? + if (!(irqs_en & PN512_IRQ_LOW_ALERT)) + { + irqs_en |= PN512_IRQ_LOW_ALERT; + pn512_irq_set(pPN512, irqs_en); + } + } + else + { + if (irqs_en & PN512_IRQ_LOW_ALERT) + { + //Buffer has been fully sent + irqs_en &= ~PN512_IRQ_LOW_ALERT; + pn512_irq_set(pPN512, irqs_en); + } + } + } + + + if (start) + { + if ( (pPN512->transceive.mode == pn512_transceive_mode_transmit) || (pPN512->transceive.mode == pn512_transceive_mode_transmit_and_target_autocoll) ) + { + //Update bitframing register + pn512_register_write(pPN512, PN512_REG_BITFRAMING, + 0x00 | ((pPN512->readFirstByteAlign & 0x7) << 4) | (pPN512->writeLastByteLength & 0x7)); + + //Use transmit command + pn512_cmd_exec(pPN512, PN512_CMD_TRANSMIT); + } + else + { + NFC_DBG("Bitframing %02X", 0x80 | ((pPN512->readFirstByteAlign & 0x7) << 4) | (pPN512->writeLastByteLength & 0x7)); + //Update bitframing register to start transmission + pn512_register_write(pPN512, PN512_REG_BITFRAMING, + 0x80 | ((pPN512->readFirstByteAlign & 0x7) << 4) | (pPN512->writeLastByteLength & 0x7)); + } + + //Reset last byte length, first byte align + pPN512->writeLastByteLength = 8; + pPN512->readFirstByteAlign = 0; + } + + //Queue task to process IRQ + task_init(&pPN512->transceiver.task, EVENT_HW_INTERRUPT | EVENT_TIMEOUT, TIMEOUT, pn512_transceive_hw_tx_task, pPN512); + scheduler_queue_task(&pPN512->transceiver.scheduler, &pPN512->transceiver.task); +} + +void pn512_transceive_hw_tx_task(uint32_t events, void* pUserData) +{ + pn512_t* pPN512 = (pn512_t*) pUserData; + + if(events & EVENT_ABORTED) + { + //Stop command + pn512_cmd_exec(pPN512, PN512_CMD_IDLE); + pPN512->transceive.mode = pn512_transceive_mode_idle; + + NFC_ERR("Aborted TX"); + + pn512_irq_set(pPN512, PN512_IRQ_NONE); + pn512_irq_clear(pPN512, PN512_IRQ_ALL); + + pn512_transceive_callback(pPN512, NFC_ERR_ABORTED); + return; + } + + NFC_DBG("TX task"); + if(events & EVENT_TIMEOUT) + { + // Check status + NFC_DBG("Status = %02X %02X", pn512_register_read(pPN512, PN512_REG_STATUS1), pn512_register_read(pPN512, PN512_REG_STATUS2)); + + //Stop command + pn512_cmd_exec(pPN512, PN512_CMD_IDLE); + pPN512->transceive.mode = pn512_transceive_mode_idle; + + NFC_ERR("Timeout on TX"); + + pn512_irq_set(pPN512, PN512_IRQ_NONE); + pn512_irq_clear(pPN512, PN512_IRQ_ALL); + + //Call callback + pn512_transceive_callback(pPN512, NFC_ERR_TIMEOUT); + return; + } + + uint16_t irqs_en = pn512_irq_enabled(pPN512); + uint16_t irqs = pn512_irq_get(pPN512); + + if( irqs & PN512_IRQ_RF_OFF ) + { + //Stop command + pn512_cmd_exec(pPN512, PN512_CMD_IDLE); + pPN512->transceive.mode = pn512_transceive_mode_idle; + + pn512_irq_set(pPN512, PN512_IRQ_NONE); + pn512_irq_clear(pPN512, PN512_IRQ_ALL); + NFC_WARN("RF Off"); + pn512_transceive_callback(pPN512, NFC_ERR_FIELD); + return; + } + if( irqs & PN512_IRQ_TX ) + { + if( irqs_en & PN512_IRQ_LOW_ALERT ) + { + //If the transmission has been completed without us getting a chance to fill the buffer up it means that we had a buffer underflow + NFC_ERR("Buffer underflow"); + pn512_irq_set(pPN512, PN512_IRQ_NONE); + pn512_irq_clear(pPN512, PN512_IRQ_ALL); + + pn512_transceive_callback(pPN512, NFC_ERR_UNDERFLOW); + return; + } + + //Transmission complete + pn512_irq_set(pPN512, PN512_IRQ_NONE); + pn512_irq_clear(pPN512, PN512_IRQ_TX | PN512_IRQ_LOW_ALERT); + + //Start receiving + NFC_DBG("Transmission complete"); + if(pPN512->transceive.mode != pn512_transceive_mode_transmit) + { + if(pPN512->transceive.mode == pn512_transceive_mode_transmit_and_target_autocoll) + { + //Make sure bitframing reg is clean + pn512_register_write(pPN512, PN512_REG_BITFRAMING, 0x00); + + pn512_cmd_exec(pPN512, PN512_CMD_IDLE); + pn512_transceive_hw_rx_start(pPN512); + + //Start autocoll + pn512_cmd_exec(pPN512, PN512_CMD_AUTOCOLL); + } + else + { + pn512_transceive_hw_rx_start(pPN512); + } + return; + } + else + { + pn512_irq_set(pPN512, PN512_IRQ_NONE); + pn512_irq_clear(pPN512, PN512_IRQ_RX | PN512_IRQ_HIGH_ALERT); + + pn512_transceive_callback(pPN512, NFC_OK); + return; + } + } + if( (irqs & PN512_IRQ_LOW_ALERT) && (buffer_reader_readable(&pPN512->writeBuf) > 0) ) + { + //Continue to fill FIFO + pn512_irq_clear(pPN512, PN512_IRQ_LOW_ALERT); + + pn512_transceive_hw_tx_iteration(pPN512, false); + return; + } + + if( irqs & PN512_IRQ_IDLE ) + { + pn512_irq_clear(pPN512, PN512_IRQ_ERR); + + NFC_ERR("Modem went to idle"); + + pn512_cmd_exec(pPN512, PN512_CMD_IDLE); + pPN512->transceive.mode = pn512_transceive_mode_idle; + + pn512_irq_set(pPN512, PN512_IRQ_NONE); + pn512_irq_clear(pPN512, PN512_IRQ_ALL); + pn512_transceive_callback(pPN512, NFC_ERR_WRONG_COMM); + return; + } + + //Call back function + pn512_transceive_hw_tx_iteration(pPN512, false); +} + +void pn512_transceive_hw_rx_start(pn512_t* pPN512) +{ + uint16_t irqs_en = PN512_IRQ_RX | PN512_IRQ_HIGH_ALERT | PN512_IRQ_ERR; + if(PN512_FRAMING_IS_TARGET(pPN512->framing)) + { + irqs_en |= PN512_IRQ_RF_OFF; + } + + pn512_irq_set(pPN512, irqs_en); + + //Reset buffer except if data should be appended to this -- TODO + buffer_builder_reset(&pPN512->readBufBldr); + + //Queue task to process IRQ + task_init(&pPN512->transceiver.task, EVENT_HW_INTERRUPT | EVENT_TIMEOUT, + pPN512->timeout, pn512_transceive_hw_rx_task, pPN512); + scheduler_queue_task(&pPN512->transceiver.scheduler, + &pPN512->transceiver.task); +} + +void pn512_transceive_hw_rx_task(uint32_t events, void* pUserData) +{ + pn512_t* pPN512 = (pn512_t*) pUserData; + + NFC_DBG("RX task"); + if(events & EVENT_ABORTED) + { + //Stop command + pn512_cmd_exec(pPN512, PN512_CMD_IDLE); + pPN512->transceive.mode = pn512_transceive_mode_idle; + + NFC_ERR("Aborted RX"); + + pn512_irq_set(pPN512, PN512_IRQ_NONE); + pn512_irq_clear(pPN512, PN512_IRQ_ALL); + + pn512_transceive_callback(pPN512, NFC_ERR_ABORTED); + return; + } + + if(events & EVENT_TIMEOUT) + { + NFC_WARN("Timeout"); + //Stop command + pn512_cmd_exec(pPN512, PN512_CMD_IDLE); + pPN512->transceive.mode = pn512_transceive_mode_idle; + + pn512_irq_set(pPN512, PN512_IRQ_NONE); + pn512_irq_clear(pPN512, PN512_IRQ_ALL); + + //Call callback + pn512_transceive_callback(pPN512, NFC_ERR_TIMEOUT); + return; + } + + uint16_t irqs = pn512_irq_get(pPN512); + NFC_DBG("irqs %04x", irqs); + bool collision_detected = false; + if( irqs & PN512_IRQ_ERR ) + { + pn512_irq_clear(pPN512, PN512_IRQ_ERR); + + uint8_t err_reg = pn512_register_read(pPN512, PN512_REG_ERROR); + NFC_ERR("Got error - error reg is %02X", err_reg); + // if err_reg == 0, sticky error that must have been cleared automatically, continue + if( err_reg != 0 ) + { + //If it's a collsision, flag it but still carry on with RX procedure + collision_detected = true; + + if( (err_reg == 0x08) || (err_reg == 0x0A) ) // Collision (and maybe parity) (and no other error) + { + irqs &= ~PN512_IRQ_ERR; + irqs |= PN512_IRQ_RX; + } + else + { + DBG_BLOCK( + //Empty FIFO into buffer + pn512_fifo_read(pPN512, &pPN512->readBufBldr); + + NFC_DBG("Received"); + buffer_dump( buffer_builder_buffer(&pPN512->readBufBldr) ); + + NFC_DBG("Computed CRC = %02X %02X", pn512_register_read(pPN512, PN512_REG_CRCRESULT_MSB), pn512_register_read(pPN512, PN512_REG_CRCRESULT_LSB)); + + ) + + //Stop command + pn512_cmd_exec(pPN512, PN512_CMD_IDLE); + pPN512->transceive.mode = pn512_transceive_mode_idle; + + pn512_irq_set(pPN512, PN512_IRQ_NONE); + pn512_irq_clear(pPN512, PN512_IRQ_ALL); + + //Call callback + pn512_transceive_callback(pPN512, NFC_ERR_WRONG_COMM); + return; + } + } + } + if( (irqs & PN512_IRQ_RX) || (irqs & PN512_IRQ_HIGH_ALERT) ) + { + //Empty FIFO into buffer + pn512_fifo_read(pPN512, &pPN512->readBufBldr); + + if( (buffer_builder_writeable(&pPN512->readBufBldr) == 0) && (pn512_fifo_length(pPN512) > 0) ) + { + //Stop command + pn512_cmd_exec(pPN512, PN512_CMD_IDLE); + pPN512->transceive.mode = pn512_transceive_mode_idle; + + NFC_WARN("RX buffer overflow"); + + pn512_irq_set(pPN512, PN512_IRQ_NONE); + pn512_irq_clear(pPN512, PN512_IRQ_ALL); + //Call callback + pn512_transceive_callback(pPN512, NFC_ERR_BUFFER_TOO_SMALL); + return; //overflow + } + + if( irqs & PN512_IRQ_HIGH_ALERT ) + { + NFC_DBG("High alert"); + pn512_irq_clear(pPN512, PN512_IRQ_HIGH_ALERT); + } + + if( irqs & PN512_IRQ_RX ) + { + pn512_irq_clear(pPN512, PN512_IRQ_RX); + + size_t last_byte_length = pn512_register_read(pPN512, PN512_REG_CONTROL) & 0x7; + if( last_byte_length == 0 ) + { + last_byte_length = 8; + } + pPN512->readLastByteLength = last_byte_length; + + pn512_irq_set(pPN512, PN512_IRQ_NONE); + pn512_irq_clear(pPN512, PN512_IRQ_RX | PN512_IRQ_HIGH_ALERT); + + NFC_DBG("Received:"); + DBG_BLOCK( buffer_dump( buffer_builder_buffer(&pPN512->readBufBldr) ); ) + + if( (pPN512->transceive.mode == pn512_transceive_mode_target_autocoll) || (pPN512->transceive.mode == pn512_transceive_mode_transmit_and_target_autocoll) ) + { + //Check if target was activated + if( !(pn512_register_read(pPN512, PN512_REG_STATUS2) & 0x10) ) + { + pPN512->transceive.mode = pn512_transceive_mode_idle; + + pn512_irq_set(pPN512, PN512_IRQ_NONE); + pn512_irq_clear(pPN512, PN512_IRQ_ALL); + //Call callback + pn512_transceive_callback(pPN512, NFC_ERR_PROTOCOL); + return; + } + //PN512 switches to transceive automatically + pPN512->transceive.mode = pn512_transceive_mode_transceive; + } + else if( pPN512->transceive.mode == pn512_transceive_mode_receive ) + { + pPN512->transceive.mode = pn512_transceive_mode_transceive; + //pn512_cmd_exec(pPN512, PN512_CMD_IDLE); //Useful? + } + + if(!collision_detected) + { + pn512_transceive_callback(pPN512, NFC_OK); + } + else + { + pn512_transceive_callback(pPN512, NFC_ERR_COLLISION); + } + + return; + } + } + if( irqs & PN512_IRQ_RF_OFF ) + { + //Stop command + pn512_cmd_exec(pPN512, PN512_CMD_IDLE); + pPN512->transceive.mode = pn512_transceive_mode_idle; + + pn512_irq_set(pPN512, PN512_IRQ_NONE); + pn512_irq_clear(pPN512, PN512_IRQ_ALL); + + //Call callback + pn512_transceive_callback(pPN512, NFC_ERR_FIELD); + return; + } + + //Queue task to process IRQ + task_init(&pPN512->transceiver.task, EVENT_HW_INTERRUPT | EVENT_TIMEOUT, + pPN512->timeout, pn512_transceive_hw_rx_task, pPN512); + scheduler_queue_task(&pPN512->transceiver.scheduler, + &pPN512->transceiver.task); +} + +void pn512_transceive_hw(pn512_t* pPN512, pn512_transceive_mode_t mode, pn512_cb_t cb) +{ + uint16_t irqs_en; + + //Store callback + pPN512->transceive.cb = cb; + + //Clear FIFO + pn512_fifo_clear(pPN512); + + //Clear previous IRQs if present + pn512_irq_clear(pPN512, PN512_IRQ_RX | PN512_IRQ_TX | PN512_IRQ_HIGH_ALERT | PN512_IRQ_LOW_ALERT | PN512_IRQ_ERR | PN512_IRQ_IDLE | PN512_IRQ_RF_OFF ); + + if( PN512_FRAMING_IS_TARGET(pPN512->framing) ) + { + //RF off? + if(!(pn512_register_read(pPN512, PN512_REG_STATUS1) & 0x04)) + { + //Call callback + pn512_transceive_callback(pPN512, NFC_ERR_FIELD); + return; + } + } + else if((pPN512->transceive.mode != mode) && (mode == pn512_transceive_mode_transceive)) + { + pn512_cmd_exec(pPN512, PN512_CMD_TRANSCEIVE); + } + + pPN512->transceive.mode = mode; + + if( mode == pn512_transceive_mode_receive ) + { + pn512_cmd_exec(pPN512, PN512_CMD_IDLE); + pn512_transceive_hw_rx_start(pPN512); + pn512_cmd_exec(pPN512, PN512_CMD_TRANSCEIVE); + } + else if(mode == pn512_transceive_mode_target_autocoll) + { + //Make sure bitframing reg is clean + pn512_register_write(pPN512, PN512_REG_BITFRAMING, 0x00); + + pn512_transceive_hw_rx_start(pPN512); + + //Start autocoll + pn512_cmd_exec(pPN512, PN512_CMD_AUTOCOLL); + return; + } + else + { + NFC_DBG("Sending:"); + DBG_BLOCK( buffer_dump(&pPN512->writeBuf); ) + + //Transmit a frame to remote target/initiator + irqs_en = PN512_IRQ_TX | PN512_IRQ_IDLE; + if( PN512_FRAMING_IS_TARGET(pPN512->framing) ) + { + irqs_en |= PN512_IRQ_RF_OFF; + } + + pn512_irq_set(pPN512, irqs_en); + + pn512_transceive_hw_tx_iteration(pPN512, true); + } +} + + + diff --git a/features/nfc/stack/transceiver/pn512/pn512_transceive.h b/features/nfc/stack/transceiver/pn512/pn512_transceive.h new file mode 100644 index 0000000000..88afac83f2 --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512_transceive.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014-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 pn512_transceive.h + * \copyright Copyright (c) ARM Ltd 2014 + * \author Donatien Garnier + */ + +#ifndef PN512_TRANSCEIVE_H_ +#define PN512_TRANSCEIVE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "pn512.h" + +void pn512_transceive_hw(pn512_t* pPN512, pn512_transceive_mode_t mode, pn512_cb_t cb); + +void pn512_transceive_hw_tx_task(uint32_t events, void* pUserData); +void pn512_transceive_hw_tx_iteration(pn512_t* pPN512, bool start); + +void pn512_transceive_hw_rx_start(pn512_t* pPN512); +void pn512_transceive_hw_rx_task(uint32_t events, void* pUserData); + + +#ifdef __cplusplus +} +#endif + +#endif /* PN512_TRANSCEIVE_H_ */ diff --git a/features/nfc/stack/transceiver/pn512/pn512_types.h b/features/nfc/stack/transceiver/pn512/pn512_types.h new file mode 100644 index 0000000000..413319a2ce --- /dev/null +++ b/features/nfc/stack/transceiver/pn512/pn512_types.h @@ -0,0 +1,39 @@ +/* + * 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 pn512_types.h + * \copyright Copyright (c) ARM Ltd 2015 + * \author Donatien Garnier + */ + +#ifndef TRANSCEIVER_PN512_PN512_TYPES_H_ +#define TRANSCEIVER_PN512_PN512_TYPES_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct __pn512_registers +{ + int8_t registers_page; +} pn512_registers_t; + +#ifdef __cplusplus +} +#endif + +#endif /* TRANSCEIVER_PN512_PN512_TYPES_H_ */ diff --git a/features/nfc/stack/transceiver/protocols.h b/features/nfc/stack/transceiver/protocols.h new file mode 100644 index 0000000000..1e007d829e --- /dev/null +++ b/features/nfc/stack/transceiver/protocols.h @@ -0,0 +1,89 @@ +/* + * 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 protocols.h + * \copyright Copyright (c) ARM Ltd 2013 + * \author Donatien Garnier + * \details List of RF protocols + */ + +/** \addtogroup Transceiver + * @{ + * \name Protocols and RF configuration + * @{ + */ + +#ifndef PROTOCOLS_H_ +#define PROTOCOLS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum __RF_PROTOCOL +{ + __RF_PROTOCOL_UNKNOWN = 0, + //Reader + RF_PROTOCOL_ISO_14443_A_READER, + RF_PROTOCOL_ISO_14443_B_READER, + RF_PROTOCOL_INNOVATRON_READER, + RF_PROTOCOL_ISO_15693_READER, + RF_PROTOCOL_FELICA_READER, + //... add other protocols here + RF_PROTOCOL_ISO_14443_A_TARGET, + RF_PROTOCOL_ISO_14443_B_TARGET, + RF_PROTOCOL_INNOVATRON_TARGET, + RF_PROTOCOL_ISO_15693_TARGET, + RF_PROTOCOL_FELICA_TARGET, + RF_PROTOCOL_ISO_DEP_TARGET, //ISO 14443-4 transport protocol + RF_PROTOCOL_NFC_DEP_TARGET, //NFC-IP 1 transport protocol + //... add other protocols here + +} RF_PROTOCOL; + +#define RF_PROTOCOL_IS_TARGET(x) ((x)>=RF_PROTOCOL_ISO_14443_A_TARGET) +#define RF_PROTOCOL_IS_READER(x) (!RF_PROTOCOL_IS_TARGET(x)) + +typedef uint32_t RF_OPTION; +//These options can be ORed +#define RF_OPTION_NONE 0x00 +#define RF_OPTION_COMPUTE_CRC 0x01 +#define RF_OPTION_COMPUTE_PARITY 0x02 +#define RF_OPTION_CHECK_CRC 0x04 +#define RF_OPTION_CHECK_PARITY 0x08 +#define RF_OPTION_CLOSE 0x10 //Last frame + +typedef enum __RF_BITRATE +{ + RF_BITRATE_106K=0x00, + RF_BITRATE_212K=0x01, + RF_BITRATE_424K=0x02, + RF_BITRATE_848K=0x03, + +} RF_BITRATE; + +#ifdef __cplusplus +} +#endif + +#endif /* PROTOCOLS_H_ */ + +/** + * @} + * @} + * */ + diff --git a/features/nfc/stack/transceiver/transceiver.c b/features/nfc/stack/transceiver/transceiver.c new file mode 100644 index 0000000000..6c35cc7911 --- /dev/null +++ b/features/nfc/stack/transceiver/transceiver.c @@ -0,0 +1,47 @@ +/* + * 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 transceiver.c + * \copyright Copyright (c) ARM Ltd 2013 + * \author Donatien Garnier + * \details Transceiver + */ + +/** \addtogroup Transceiver + * @{ + * \name Initialization + * @{ + */ + +#include "transceiver.h" + +/** Initialize nfc_transceiver_t structure + * \param pTransceiver pointer to nfc_transceiver_t structure to initialize + * \param pTransport pointer to already initialized nfc_transport_t structure + * \param pImpl pointer to the structure implementing the transceiver interface (eg pn512_t or pn532_t) + */ +void transceiver_init(nfc_transceiver_t* pTransceiver, nfc_transport_t* pTransport, nfc_scheduler_timer_t* pTimer) +{ + pTransceiver->pTransport = pTransport; + scheduler_init(&pTransceiver->scheduler, pTimer); +} + + +/** + * @} + * @} + * */ diff --git a/features/nfc/stack/transceiver/transceiver.h b/features/nfc/stack/transceiver/transceiver.h new file mode 100644 index 0000000000..70660b625b --- /dev/null +++ b/features/nfc/stack/transceiver/transceiver.h @@ -0,0 +1,280 @@ +/* + * 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 transceiver.h + * \copyright Copyright (c) ARM Ltd 2013 + * \author Donatien Garnier + */ + +#ifndef TRANSCEIVER_H_ +#define TRANSCEIVER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "inc/nfc.h" +typedef struct __nfc_tech nfc_tech_t; +typedef struct __transceiver nfc_transceiver_t; +typedef struct __transceiver_impl transceiver_impl_t; + +#include "protocols.h" +#include "platform/nfc_transport.h" + +typedef enum __nfc_framing nfc_framing_t; +enum __nfc_framing { + nfc_framing_unknown, + + nfc_framing_target_mode_detector, //Framing is unknown and will be detected by the hardware + nfc_framing_target_a_106, + nfc_framing_target_b_106, + nfc_framing_target_f_212, + nfc_framing_target_f_424, + + nfc_framing_initiator_a_106, + nfc_framing_initiator_b_106, + nfc_framing_initiator_f_212, + nfc_framing_initiator_f_424, +}; + +typedef struct __nfc_tech +{ + unsigned int nfc_type1 : 1; + unsigned int nfc_type2 : 1; + unsigned int nfc_type3 : 1; + unsigned int nfc_iso_dep_a : 1; + unsigned int nfc_iso_dep_b : 1; + unsigned int nfc_nfc_dep_a : 1; + unsigned int nfc_nfc_dep_f_212 : 1; + unsigned int nfc_nfc_dep_f_424 : 1; +} nfc_tech_t; + + +typedef struct __polling_options polling_options_t; +struct __polling_options +{ + unsigned int bail_at_first_target : 1; + unsigned int bail_at_first_tech : 1; + int32_t listen_for; +}; + +typedef void (*transceiver_cb_t)(nfc_transceiver_t* pTransceiver, nfc_err_t ret, void* pUserData); +typedef void (*set_protocols_fn_t)(nfc_transceiver_t* pTransceiver, nfc_tech_t initiators, nfc_tech_t targets, polling_options_t options); +typedef void (*poll_fn_t)(nfc_transceiver_t* pTransceiver); +typedef void (*set_crc_fn_t)(nfc_transceiver_t* pTransceiver, bool crcOut, bool crcIn); +typedef void (*set_timeout_fn_t)(nfc_transceiver_t* pTransceiver, int timeout); +typedef void (*set_transceive_options_fn_t)(nfc_transceiver_t* pTransceiver, bool transmit, bool receive, bool repoll); +typedef void (*set_transceive_framing_fn_t)(nfc_transceiver_t* pTransceiver, nfc_framing_t framing); +typedef void (*set_write_fn_t)(nfc_transceiver_t* pTransceiver, buffer_t* pWriteBuf); //Set write buffer +typedef buffer_t* (*get_read_fn_t)(nfc_transceiver_t* pTransceiver); //Get read buffer +typedef size_t (*get_last_byte_length_fn_t)(nfc_transceiver_t* pTransceiver); +typedef void (*set_last_byte_length_fn_t)(nfc_transceiver_t* pTransceiver, size_t lastByteLength); +typedef size_t (*get_first_byte_align_fn_t)(nfc_transceiver_t* pTransceiver); +typedef void (*set_first_byte_align_fn_t)(nfc_transceiver_t* pTransceiver, size_t firstByteAlign); +typedef void (*transceive_fn_t)(nfc_transceiver_t* pTransceiver); +typedef void (*abort_fn_t)(nfc_transceiver_t* pTransceiver); +typedef void (*close_fn_t)(nfc_transceiver_t* pTransceiver); +typedef void (*sleep_fn_t)(nfc_transceiver_t* pTransceiver, bool sleep); + +struct __transceiver_impl +{ + set_protocols_fn_t set_protocols; + poll_fn_t poll; + set_crc_fn_t set_crc; + set_timeout_fn_t set_timeout; + set_transceive_options_fn_t set_transceive_options; + set_transceive_framing_fn_t set_transceive_framing; + set_write_fn_t set_write; + get_read_fn_t get_read; + set_last_byte_length_fn_t set_last_byte_length; + get_last_byte_length_fn_t get_last_byte_length; + set_first_byte_align_fn_t set_first_byte_align; + transceive_fn_t transceive; + abort_fn_t abort; + close_fn_t close; + sleep_fn_t sleep; +}; + +typedef struct __nfc_a_info nfc_a_info_t; +struct __nfc_a_info +{ + uint8_t uid[10]; + size_t uidLength; + uint8_t sak; + uint8_t atqa[2]; +}; + +typedef struct __nfc_b_info nfc_b_info_t; +struct __nfc_b_info +{ + uint8_t pupi[4]; + uint8_t application_data[4]; + uint8_t protocol_info[3]; +}; + +typedef struct __nfc_f_info nfc_f_info_t; +struct __nfc_f_info +{ + uint8_t nfcid2[8]; +}; + +typedef struct __nfc_info nfc_info_t; + +struct __nfc_info +{ + nfc_tech_t type; + union { + nfc_a_info_t nfcA; + nfc_b_info_t nfcB; + nfc_f_info_t nfcF; + }; +}; + +#define MUNFC_MAX_REMOTE_TARGETS 4 +struct __transceiver +{ + const transceiver_impl_t* fn; //vtable + + bool initiator_ntarget; + nfc_info_t remote_targets[MUNFC_MAX_REMOTE_TARGETS]; + size_t remote_targets_count; + + nfc_tech_t active_tech; + + transceiver_cb_t cb; //Callback to upper layer + void* pUserData; + nfc_task_t task; //Task for deferred execution + + nfc_transport_t* pTransport; + nfc_scheduler_t scheduler; +}; + +void transceiver_init(nfc_transceiver_t* pTransceiver, nfc_transport_t* pTransport, nfc_scheduler_timer_t* pTimer); + +static inline void transceiver_set_protocols(nfc_transceiver_t* pTransceiver, nfc_tech_t initiators, nfc_tech_t targets, polling_options_t options) +{ + pTransceiver->fn->set_protocols(pTransceiver, initiators, targets, options); +} + +static inline void transceiver_poll(nfc_transceiver_t* pTransceiver, transceiver_cb_t cb, void* pUserData) +{ + pTransceiver->cb = cb; + pTransceiver->pUserData = pUserData; + pTransceiver->fn->poll(pTransceiver); +} + +static inline void transceiver_set_crc(nfc_transceiver_t* pTransceiver, bool crcOut, bool crcIn) +{ + pTransceiver->fn->set_crc(pTransceiver, crcOut, crcIn); +} + +static inline void transceiver_set_timeout(nfc_transceiver_t* pTransceiver, int timeout) +{ + pTransceiver->fn->set_timeout(pTransceiver, timeout); +} + +static inline void transceiver_set_transceive_options(nfc_transceiver_t* pTransceiver, bool transmit, bool receive, bool repoll) +{ + pTransceiver->fn->set_transceive_options(pTransceiver, transmit, receive, repoll); +} + +static inline void transceiver_set_transceive_framing(nfc_transceiver_t* pTransceiver, nfc_framing_t framing) +{ + pTransceiver->fn->set_transceive_framing(pTransceiver, framing); +} + +static inline void transceiver_set_write(nfc_transceiver_t* pTransceiver, buffer_t* pWriteBuf) +{ + pTransceiver->fn->set_write(pTransceiver, pWriteBuf); +} + +static inline buffer_t* transceiver_get_read(nfc_transceiver_t* pTransceiver) +{ + return pTransceiver->fn->get_read(pTransceiver); +} + +static inline size_t transceiver_get_last_byte_length(nfc_transceiver_t* pTransceiver) +{ + return pTransceiver->fn->get_last_byte_length(pTransceiver); +} + +static inline void transceiver_set_last_byte_length(nfc_transceiver_t* pTransceiver, size_t lastByteLength) +{ + pTransceiver->fn->set_last_byte_length(pTransceiver, lastByteLength); +} + +static inline void transceiver_set_first_byte_align(nfc_transceiver_t* pTransceiver, size_t firstByteAlign) +{ + pTransceiver->fn->set_first_byte_align(pTransceiver, firstByteAlign); +} + +static inline void nfc_transceiver_transceive(nfc_transceiver_t* pTransceiver, transceiver_cb_t cb, void* pUserData) +{ + pTransceiver->cb = cb; + pTransceiver->pUserData = pUserData; + pTransceiver->fn->transceive(pTransceiver); +} + +static inline void transceiver_abort(nfc_transceiver_t* pTransceiver) +{ + pTransceiver->fn->abort(pTransceiver); +} + +static inline void transceiver_close(nfc_transceiver_t* pTransceiver) +{ + pTransceiver->fn->close(pTransceiver); +} + +static inline bool transceiver_is_initiator_mode(nfc_transceiver_t* pTransceiver) +{ + return pTransceiver->initiator_ntarget; +} + +static inline nfc_tech_t transceiver_get_active_techs(nfc_transceiver_t* pTransceiver) +{ + return pTransceiver->active_tech; +} + +static inline nfc_scheduler_t* transceiver_get_scheduler(nfc_transceiver_t* pTransceiver) +{ + return &pTransceiver->scheduler; +} + +static inline const nfc_info_t* transceiver_get_remote_target_info(nfc_transceiver_t* pTransceiver, size_t number) +{ + if( number > pTransceiver->remote_targets_count ) + { + return NULL; + } + return &pTransceiver->remote_targets[number]; +} + +static inline size_t transceiver_get_remote_targets_count(nfc_transceiver_t* pTransceiver) +{ + return pTransceiver->remote_targets_count; +} + +static inline void transceiver_sleep(nfc_transceiver_t* pTransceiver, bool sleep) +{ + pTransceiver->fn->sleep(pTransceiver, sleep); +} + +#ifdef __cplusplus +} +#endif + +#endif /* TRANSCEIVER_H_ */ diff --git a/features/nfc/stack/transceiver/transceiver_internal.h b/features/nfc/stack/transceiver/transceiver_internal.h new file mode 100644 index 0000000000..ac9507f0fa --- /dev/null +++ b/features/nfc/stack/transceiver/transceiver_internal.h @@ -0,0 +1,41 @@ +/* + * 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 transceiver_internal.h + * \copyright Copyright (c) ARM Ltd 2015 + * \author Donatien Garnier + */ + +#ifndef TRANSCEIVER_INTERNAL_H_ +#define TRANSCEIVER_INTERNAL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "transceiver.h" + +static inline void transceiver_callback(nfc_transceiver_t* pTransceiver, nfc_err_t ret) +{ + pTransceiver->cb(pTransceiver, ret, pTransceiver->pUserData); +} + +#ifdef __cplusplus +} +#endif + +#endif /* TRANSCEIVER_INTERNAL_H_ */