summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorIvan Solovev <ivan.solovev@qt.io>2021-05-26 12:04:46 +0200
committerIvan Solovev <ivan.solovev@qt.io>2021-05-27 16:56:15 +0200
commitcda030a863aff344b4124f114da32e0c5cbc0640 (patch)
tree0b396b590baccf99e5a6acef9542627ddf63a456 /tests
parenta1aa294733c91881aee0f4416a888d0d269cfcbd (diff)
downloadqtconnectivity-cda030a863aff344b4124f114da32e0c5cbc0640.tar.gz
QtNFC: refactor directory structure
A lot of test-related files were actually located in the src directory. This patch moves them to tests/auto/nfccommons and adjusts the build files accordingly. Task-number: QTBUG-93854 Change-Id: I05641d849f4f07cd39d12ea136b4e7453d6aa883 Reviewed-by: Alex Blasche <alexander.blasche@qt.io>
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/nfccommons/qnearfieldmanager_emulator.cpp104
-rw-r--r--tests/auto/nfccommons/qnearfieldmanager_emulator_p.h90
-rw-r--r--tests/auto/nfccommons/qnearfieldtagtype1.cpp733
-rw-r--r--tests/auto/nfccommons/qnearfieldtagtype1_p.h108
-rw-r--r--tests/auto/nfccommons/qnearfieldtagtype2.cpp328
-rw-r--r--tests/auto/nfccommons/qnearfieldtagtype2_p.h94
-rw-r--r--tests/auto/nfccommons/qnearfieldtarget_emulator.cpp300
-rw-r--r--tests/auto/nfccommons/qnearfieldtarget_emulator_p.h127
-rw-r--r--tests/auto/nfccommons/qtlv.cpp532
-rw-r--r--tests/auto/nfccommons/qtlv_p.h132
-rw-r--r--tests/auto/nfccommons/targetemulator.cpp389
-rw-r--r--tests/auto/nfccommons/targetemulator_p.h124
-rw-r--r--tests/auto/qnearfieldmanager/CMakeLists.txt11
-rw-r--r--tests/auto/qnearfieldmanager/tst_qnearfieldmanager.cpp12
-rw-r--r--tests/auto/qnearfieldtagtype1/CMakeLists.txt10
-rw-r--r--tests/auto/qnearfieldtagtype1/tst_qnearfieldtagtype1.cpp9
-rw-r--r--tests/auto/qnearfieldtagtype2/CMakeLists.txt10
-rw-r--r--tests/auto/qnearfieldtagtype2/tst_qnearfieldtagtype2.cpp8
18 files changed, 3095 insertions, 26 deletions
diff --git a/tests/auto/nfccommons/qnearfieldmanager_emulator.cpp b/tests/auto/nfccommons/qnearfieldmanager_emulator.cpp
new file mode 100644
index 00000000..986658a6
--- /dev/null
+++ b/tests/auto/nfccommons/qnearfieldmanager_emulator.cpp
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnearfieldmanager_emulator_p.h"
+#include "qnearfieldtarget_emulator_p.h"
+
+#include <QtCore/QDebug>
+
+QT_BEGIN_NAMESPACE
+
+QNearFieldManagerPrivateImpl::QNearFieldManagerPrivateImpl()
+{
+ TagActivator *activator = TagActivator::instance();
+ activator->initialize();
+
+ connect(activator, &TagActivator::tagActivated, this, &QNearFieldManagerPrivateImpl::tagActivated);
+ connect(activator, &TagActivator::tagDeactivated, this, &QNearFieldManagerPrivateImpl::tagDeactivated);
+}
+
+QNearFieldManagerPrivateImpl::~QNearFieldManagerPrivateImpl()
+{
+}
+
+bool QNearFieldManagerPrivateImpl::isEnabled() const
+{
+ return true;
+}
+
+bool QNearFieldManagerPrivateImpl::startTargetDetection(QNearFieldTarget::AccessMethod)
+{
+ return true;
+}
+
+void QNearFieldManagerPrivateImpl::reset()
+{
+ TagActivator::instance()->reset();
+}
+
+void QNearFieldManagerPrivateImpl::tagActivated(TagBase *tag)
+{
+ QNearFieldTargetPrivate *target = targets.value(tag).data();
+ if (!target) {
+ if (dynamic_cast<NfcTagType1 *>(tag))
+ target = new TagType1(tag, this);
+ else if (dynamic_cast<NfcTagType2 *>(tag))
+ target = new TagType2(tag, this);
+ else
+ qFatal("Unknown emulator tag type");
+
+ targets.insert(tag, target);
+ }
+
+ Q_EMIT targetDetected(new NearFieldTarget(target, this));
+}
+
+void QNearFieldManagerPrivateImpl::tagDeactivated(TagBase *tag)
+{
+ QNearFieldTargetPrivate *target = targets.value(tag).data();
+ if (!target) {
+ targets.remove(tag);
+ return;
+ }
+
+ Q_EMIT targetLost(target->q_ptr);
+ QMetaObject::invokeMethod(target->q_ptr, &QNearFieldTarget::disconnected);
+}
+
+QT_END_NAMESPACE
diff --git a/tests/auto/nfccommons/qnearfieldmanager_emulator_p.h b/tests/auto/nfccommons/qnearfieldmanager_emulator_p.h
new file mode 100644
index 00000000..50e23895
--- /dev/null
+++ b/tests/auto/nfccommons/qnearfieldmanager_emulator_p.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNEARFIELDMANAGER_EMULATOR_H
+#define QNEARFIELDMANAGER_EMULATOR_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNfc/private/qnearfieldmanager_p.h>
+#include <QtNfc/private/qnearfieldtarget_p.h>
+#include <QtNfc/qndeffilter.h>
+
+#include <QtCore/QMetaMethod>
+#include <QtCore/QObject>
+#include <QtCore/QPointer>
+
+QT_BEGIN_NAMESPACE
+
+class TagBase;
+class QNearFieldManagerPrivateImpl : public QNearFieldManagerPrivate
+{
+ Q_OBJECT
+
+public:
+ QNearFieldManagerPrivateImpl();
+ ~QNearFieldManagerPrivateImpl() override;
+
+ bool isEnabled() const override;
+
+ bool startTargetDetection(QNearFieldTarget::AccessMethod accessMethod) override;
+
+ void reset();
+
+private slots:
+ void tagActivated(TagBase *tag);
+ void tagDeactivated(TagBase *tag);
+
+private:
+ QMap<TagBase *, QPointer<QNearFieldTargetPrivate> > targets;
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QNEARFIELDMANAGER_EMULATOR_H
diff --git a/tests/auto/nfccommons/qnearfieldtagtype1.cpp b/tests/auto/nfccommons/qnearfieldtagtype1.cpp
new file mode 100644
index 00000000..3d1a9b0d
--- /dev/null
+++ b/tests/auto/nfccommons/qnearfieldtagtype1.cpp
@@ -0,0 +1,733 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnearfieldtagtype1_p.h"
+#include "qtlv_p.h"
+
+#include <QtNfc/private/qnearfieldtarget_p.h>
+#include <QtNfc/qndefmessage.h>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QVariant>
+
+#include <QtCore/QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QNearFieldTagType1
+ \brief The QNearFieldTagType1 class provides an interface for communicating with an NFC Tag
+ Type 1 tag.
+
+ \ingroup connectivity-nfc
+ \inmodule QtNfc
+ \internal
+*/
+
+/*!
+ \enum QNearFieldTagType1::WriteMode
+ \brief This enum describes the write modes that are supported.
+
+ \value EraseAndWrite The memory is erased before the new value is written.
+ \value WriteOnly The memory is not erased before the new value is written. The effect of
+ this mode is that the final value store is the bitwise or of the data
+ to be written and the original data value.
+*/
+
+/*!
+ \fn Type QNearFieldTagType1::type() const
+ \reimp
+*/
+
+class QNearFieldTagType1Private
+{
+ Q_DECLARE_PUBLIC(QNearFieldTagType1)
+
+public:
+ QNearFieldTagType1Private(QNearFieldTagType1 *q)
+ : q_ptr(q), m_readNdefMessageState(NotReadingNdefMessage),
+ m_tlvReader(nullptr),
+ m_writeNdefMessageState(NotWritingNdefMessage),
+ m_tlvWriter(nullptr)
+ { }
+
+ QNearFieldTagType1 *q_ptr;
+
+ QMap<QNearFieldTarget::RequestId, QByteArray> m_pendingInternalCommands;
+
+ enum ReadNdefMessageState {
+ NotReadingNdefMessage,
+ NdefReadCheckingIdentification,
+ NdefReadCheckingNdefMagicNumber,
+ NdefReadReadingTlv
+ };
+
+ void progressToNextNdefReadMessageState();
+ ReadNdefMessageState m_readNdefMessageState;
+ QNearFieldTarget::RequestId m_readNdefRequestId;
+
+ QTlvReader *m_tlvReader;
+ QNearFieldTarget::RequestId m_nextExpectedRequestId;
+
+ enum WriteNdefMessageState {
+ NotWritingNdefMessage,
+ NdefWriteCheckingIdentification,
+ NdefWriteCheckingNdefMagicNumber,
+ NdefWriteReadingTlv,
+ NdefWriteWritingTlv,
+ NdefWriteWritingTlvFlush
+ };
+
+ void progressToNextNdefWriteMessageState();
+ WriteNdefMessageState m_writeNdefMessageState;
+ QNearFieldTarget::RequestId m_writeNdefRequestId;
+ QList<QNdefMessage> m_ndefWriteMessages;
+
+ QTlvWriter *m_tlvWriter;
+
+ typedef QPair<quint8, QByteArray> Tlv;
+ QList<Tlv> m_tlvs;
+};
+
+void QNearFieldTagType1Private::progressToNextNdefReadMessageState()
+{
+ Q_Q(QNearFieldTagType1);
+
+ switch (m_readNdefMessageState) {
+ case NotReadingNdefMessage:
+ m_readNdefMessageState = NdefReadCheckingIdentification;
+ m_nextExpectedRequestId = q->readIdentification();
+ break;
+ case NdefReadCheckingIdentification: {
+ const QByteArray data = q->requestResponse(m_nextExpectedRequestId).toByteArray();
+
+ if (data.isEmpty()) {
+ m_readNdefMessageState = NotReadingNdefMessage;
+ m_nextExpectedRequestId = QNearFieldTarget::RequestId();
+ Q_EMIT q->error(QNearFieldTarget::NdefReadError, m_readNdefRequestId);
+ m_readNdefRequestId = QNearFieldTarget::RequestId();
+ break;
+ }
+
+ quint8 hr0 = data.at(0);
+
+ // Check if target is a NFC TagType1 tag
+ if (!(hr0 & 0x10)) {
+ m_readNdefMessageState = NotReadingNdefMessage;
+ m_nextExpectedRequestId = QNearFieldTarget::RequestId();
+ Q_EMIT q->error(QNearFieldTarget::NdefReadError, m_readNdefRequestId);
+ m_readNdefRequestId = QNearFieldTarget::RequestId();
+ break;
+ }
+
+ m_readNdefMessageState = NdefReadCheckingNdefMagicNumber;
+ m_nextExpectedRequestId = q->readByte(8);
+ break;
+ }
+ case NdefReadCheckingNdefMagicNumber: {
+ quint8 ndefMagicNumber = q->requestResponse(m_nextExpectedRequestId).toUInt();
+ m_nextExpectedRequestId = QNearFieldTarget::RequestId();
+
+ if (ndefMagicNumber != 0xe1) {
+ m_readNdefMessageState = NotReadingNdefMessage;
+ Q_EMIT q->error(QNearFieldTarget::NdefReadError, m_readNdefRequestId);
+ m_readNdefRequestId = QNearFieldTarget::RequestId();
+ break;
+ }
+
+ m_readNdefMessageState = NdefReadReadingTlv;
+ delete m_tlvReader;
+ m_tlvReader = new QTlvReader(q);
+
+ Q_FALLTHROUGH(); // fall through
+ }
+ case NdefReadReadingTlv:
+ Q_ASSERT(m_tlvReader);
+ while (!m_tlvReader->atEnd()) {
+ if (!m_tlvReader->readNext())
+ break;
+
+ // NDEF Message TLV
+ if (m_tlvReader->tag() == 0x03) {
+ Q_Q(QNearFieldTagType1);
+
+ Q_EMIT q->ndefMessageRead(QNdefMessage::fromByteArray(m_tlvReader->data()));
+ }
+ }
+
+ m_nextExpectedRequestId = m_tlvReader->requestId();
+ if (!m_nextExpectedRequestId.isValid()) {
+ delete m_tlvReader;
+ m_tlvReader = nullptr;
+ m_readNdefMessageState = NotReadingNdefMessage;
+ Q_EMIT q->requestCompleted(m_readNdefRequestId);
+ m_readNdefRequestId = QNearFieldTarget::RequestId();
+ }
+ break;
+ }
+}
+
+void QNearFieldTagType1Private::progressToNextNdefWriteMessageState()
+{
+ Q_Q(QNearFieldTagType1);
+
+ switch (m_writeNdefMessageState) {
+ case NotWritingNdefMessage:
+ m_writeNdefMessageState = NdefWriteCheckingIdentification;
+ m_nextExpectedRequestId = q->readIdentification();
+ break;
+ case NdefWriteCheckingIdentification: {
+ const QByteArray data = q->requestResponse(m_nextExpectedRequestId).toByteArray();
+
+ if (data.isEmpty()) {
+ m_writeNdefMessageState = NotWritingNdefMessage;
+ m_nextExpectedRequestId = QNearFieldTarget::RequestId();
+ Q_EMIT q->error(QNearFieldTarget::NdefWriteError, m_writeNdefRequestId);
+ m_writeNdefRequestId = QNearFieldTarget::RequestId();
+ break;
+ }
+
+ quint8 hr0 = data.at(0);
+
+ // Check if target is a NFC TagType1 tag
+ if (!(hr0 & 0x10)) {
+ m_writeNdefMessageState = NotWritingNdefMessage;
+ m_nextExpectedRequestId = QNearFieldTarget::RequestId();
+ Q_EMIT q->error(QNearFieldTarget::NdefWriteError, m_writeNdefRequestId);
+ m_writeNdefRequestId = QNearFieldTarget::RequestId();
+ break;
+ }
+
+ m_writeNdefMessageState = NdefWriteCheckingNdefMagicNumber;
+ m_nextExpectedRequestId = q->readByte(8);
+ break;
+ }
+ case NdefWriteCheckingNdefMagicNumber: {
+ quint8 ndefMagicNumber = q->requestResponse(m_nextExpectedRequestId).toUInt();
+ m_nextExpectedRequestId = QNearFieldTarget::RequestId();
+
+ if (ndefMagicNumber != 0xe1) {
+ m_writeNdefMessageState = NotWritingNdefMessage;
+ Q_EMIT q->error(QNearFieldTarget::NdefWriteError, m_writeNdefRequestId);
+ m_writeNdefRequestId = QNearFieldTarget::RequestId();
+ break;
+ }
+
+ m_writeNdefMessageState = NdefWriteReadingTlv;
+ delete m_tlvReader;
+ m_tlvReader = new QTlvReader(q);
+
+ Q_FALLTHROUGH(); // fall through
+ }
+ case NdefWriteReadingTlv:
+ Q_ASSERT(m_tlvReader);
+ while (!m_tlvReader->atEnd()) {
+ if (!m_tlvReader->readNext())
+ break;
+
+ quint8 tag = m_tlvReader->tag();
+ if (tag == 0x01 || tag == 0x02 || tag == 0xfd)
+ m_tlvs.append(qMakePair(tag, m_tlvReader->data()));
+ }
+
+ m_nextExpectedRequestId = m_tlvReader->requestId();
+ if (m_nextExpectedRequestId.isValid())
+ break;
+
+ delete m_tlvReader;
+ m_tlvReader = nullptr;
+ m_writeNdefMessageState = NdefWriteWritingTlv;
+
+ // fall through
+ case NdefWriteWritingTlv:
+ delete m_tlvWriter;
+ m_tlvWriter = new QTlvWriter(q);
+
+ // write old TLVs
+ for (const Tlv &tlv : qAsConst(m_tlvs))
+ m_tlvWriter->writeTlv(tlv.first, tlv.second);
+
+ // write new NDEF message TLVs
+ for (const QNdefMessage &message : qAsConst(m_ndefWriteMessages))
+ m_tlvWriter->writeTlv(0x03, message.toByteArray());
+
+ // write terminator TLV
+ m_tlvWriter->writeTlv(0xfe);
+
+ m_writeNdefMessageState = NdefWriteWritingTlvFlush;
+
+ // fall through
+ case NdefWriteWritingTlvFlush:
+ // flush the writer
+ Q_ASSERT(m_tlvWriter);
+ if (m_tlvWriter->process(true)) {
+ m_nextExpectedRequestId = QNearFieldTarget::RequestId();
+ m_writeNdefMessageState = NotWritingNdefMessage;
+ delete m_tlvWriter;
+ m_tlvWriter = nullptr;
+ Q_EMIT q->requestCompleted(m_writeNdefRequestId);
+ m_writeNdefRequestId = QNearFieldTarget::RequestId();
+ } else {
+ m_nextExpectedRequestId = m_tlvWriter->requestId();
+ if (!m_nextExpectedRequestId.isValid()) {
+ m_writeNdefMessageState = NotWritingNdefMessage;
+ delete m_tlvWriter;
+ m_tlvWriter = nullptr;
+ Q_EMIT q->error(QNearFieldTarget::NdefWriteError, m_writeNdefRequestId);
+ m_writeNdefRequestId = QNearFieldTarget::RequestId();
+ }
+ }
+ break;
+ }
+}
+
+static QVariant decodeResponse(const QByteArray &command, const QByteArray &response)
+{
+ switch (command.at(0)) {
+ case 0x01: // READ
+ if (command.at(1) == response.at(0))
+ return quint8(response.at(1));
+ break;
+ case 0x53: { // WRITE-E
+ quint8 address = command.at(1);
+ quint8 data = command.at(2);
+ quint8 writeAddress = response.at(0);
+ quint8 writeData = response.at(1);
+
+ return ((writeAddress == address) && (writeData == data));
+ }
+ case 0x1a: { // WRITE-NE
+ quint8 address = command.at(1);
+ quint8 data = command.at(2);
+ quint8 writeAddress = response.at(0);
+ quint8 writeData = response.at(1);
+
+ return ((writeAddress == address) && ((writeData & data) == data));
+ }
+ case 0x10: { // RSEG
+ quint8 segmentAddress = quint8(command.at(1)) >> 4;
+ quint8 readSegmentAddress = quint8(response.at(0)) >> 4;
+ if (readSegmentAddress == segmentAddress)
+ return response.mid(1);
+ break;
+ }
+ case 0x02: { // READ8
+ quint8 blockAddress = command.at(1);
+ quint8 readBlockAddress = response.at(0);
+ if (readBlockAddress == blockAddress)
+ return response.mid(1);
+ break;
+ }
+ case 0x54: { // WRITE-E8
+ quint8 blockAddress = command.at(1);
+ QByteArray data = command.mid(2, 8);
+ quint8 writeBlockAddress = response.at(0);
+ QByteArray writeData = response.mid(1);
+
+ return ((writeBlockAddress == blockAddress) && (writeData == data));
+ }
+ case 0x1b: { // WRITE-NE8
+ quint8 blockAddress = command.at(1);
+ QByteArray data = command.mid(2, 8);
+ quint8 writeBlockAddress = response.at(0);
+ QByteArray writeData = response.mid(1);
+
+ if (writeBlockAddress != blockAddress)
+ return false;
+
+ for (int i = 0; i < writeData.length(); ++i) {
+ if ((writeData.at(i) & data.at(i)) != data.at(i))
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ return QVariant();
+}
+
+/*!
+ Constructs a new tag type 1 near field target with \a parent.
+*/
+QNearFieldTagType1::QNearFieldTagType1(QObject *parent)
+: QNearFieldTargetPrivate(parent), d_ptr(new QNearFieldTagType1Private(this))
+{
+}
+
+/*!
+ Destroys the tag type 1 near field target.
+*/
+QNearFieldTagType1::~QNearFieldTagType1()
+{
+ delete d_ptr;
+}
+
+/*!
+ \reimp
+*/
+bool QNearFieldTagType1::hasNdefMessage()
+{
+ QNearFieldTarget::RequestId id = readAll();
+ if (!waitForRequestCompleted(id))
+ return false;
+
+ const QByteArray data = requestResponse(id).toByteArray();
+
+ if (data.isEmpty())
+ return false;
+
+ quint8 hr0 = data.at(0);
+
+ // Check if target is a NFC TagType1 tag
+ if (!(hr0 & 0x10))
+ return false;
+
+ // Check if NDEF Message Magic number is present
+ quint8 nmn = data.at(10);
+ if (nmn != 0xe1)
+ return false;
+
+ // Check if TLV contains NDEF Message
+ return true;
+}
+
+/*!
+ \reimp
+*/
+QNearFieldTarget::RequestId QNearFieldTagType1::readNdefMessages()
+{
+ Q_D(QNearFieldTagType1);
+
+ d->m_readNdefRequestId = QNearFieldTarget::RequestId(new QNearFieldTarget::RequestIdPrivate);
+
+ if (d->m_readNdefMessageState == QNearFieldTagType1Private::NotReadingNdefMessage) {
+ d->progressToNextNdefReadMessageState();
+ } else {
+ reportError(QNearFieldTarget::NdefReadError, d->m_readNdefRequestId);
+ }
+
+ return d->m_readNdefRequestId;
+}
+
+/*!
+ \reimp
+*/
+QNearFieldTarget::RequestId QNearFieldTagType1::writeNdefMessages(const QList<QNdefMessage> &messages)
+{
+ Q_D(QNearFieldTagType1);
+
+ d->m_writeNdefRequestId = QNearFieldTarget::RequestId(new QNearFieldTarget::RequestIdPrivate);
+
+ if (d->m_readNdefMessageState == QNearFieldTagType1Private::NotReadingNdefMessage &&
+ d->m_writeNdefMessageState == QNearFieldTagType1Private::NotWritingNdefMessage) {
+ d->m_ndefWriteMessages = messages;
+ d->progressToNextNdefWriteMessageState();
+ } else {
+ reportError(QNearFieldTarget::NdefWriteError, d->m_readNdefRequestId);
+ }
+
+ return d->m_writeNdefRequestId;
+}
+
+/*!
+ Returns the NFC Tag Type 1 specification version number that the tag supports.
+*/
+quint8 QNearFieldTagType1::version()
+{
+ QNearFieldTarget::RequestId id = readByte(9);
+ if (!waitForRequestCompleted(id))
+ return 0;
+
+ quint8 versionNumber = requestResponse(id).toUInt();
+ return versionNumber;
+}
+
+/*!
+ Returns the memory size in bytes of the tag.
+*/
+int QNearFieldTagType1::memorySize()
+{
+ QNearFieldTarget::RequestId id = readByte(10);
+ if (!waitForRequestCompleted(id))
+ return 0;
+
+ quint8 tms = requestResponse(id).toUInt();
+
+ return 8 * (tms + 1);
+}
+
+/*!
+ Requests the identification bytes from the target. Returns a request id which can be used to
+ track the completion status of the request.
+
+ Once the request completes successfully the response can be retrieved from the
+ requestResponse() function. The response of this request will be a QByteArray containing: HR0,
+ HR1, UID0, UID1, UID2 and UID3 in order.
+
+ \sa requestCompleted(), waitForRequestCompleted()
+*/
+QNearFieldTarget::RequestId QNearFieldTagType1::readIdentification()
+{
+ QByteArray command;
+ command.append(char(0x78)); // RID
+ command.append(char(0x00)); // Address (unused)
+ command.append(char(0x00)); // Data (unused)
+ command.append(uid().left(4)); // 4 bytes of UID
+
+ return sendCommand(command);
+}
+
+/*!
+ Requests all data in the static memory area of the target. Returns a request id which can be
+ used to track the completion status of the request.
+
+ Once the request completes successfully the response can be retrieved from the
+ requestResponse() function. The response of this request will be a QByteArray containing: HR0
+ and HR1 followed by the 120 bytes of data stored in the static memory area of the target.
+
+ \sa requestCompleted(), waitForRequestCompleted()
+*/
+QNearFieldTarget::RequestId QNearFieldTagType1::readAll()
+{
+ QByteArray command;
+ command.append(char(0x00)); // RALL
+ command.append(char(0x00)); // Address (unused)
+ command.append(char(0x00)); // Data (unused)
+ command.append(uid().left(4));// 4 bytes of UID
+
+ return sendCommand(command);
+}
+
+/*!
+ Requests a single byte from the static memory area of the tag. The \a address parameter
+ specifices the linear byte address to read. Returns a request id which can be used to track
+ the completion status of the request.
+
+ Once the request completes successfully the response can be retrieved from the
+ requestResponse() function. The response of this request will be a quint8.
+
+ \sa requestCompleted(), waitForRequestCompleted()
+*/
+QNearFieldTarget::RequestId QNearFieldTagType1::readByte(quint8 address)
+{
+ if (address & 0x80)
+ return QNearFieldTarget::RequestId();
+
+ QByteArray command;
+ command.append(char(0x01)); // READ
+ command.append(char(address)); // Address
+ command.append(char(0x00)); // Data (unused)
+ command.append(uid().left(4)); // 4 bytes of UID
+
+ QNearFieldTarget::RequestId id = sendCommand(command);
+
+ Q_D(QNearFieldTagType1);
+
+ d->m_pendingInternalCommands.insert(id, command);
+
+ return id;
+}
+
+/*!
+ Writes a single \a data byte to the linear byte \a address on the tag. If \a mode is
+ EraseAndWrite the byte will be erased before writing. If \a mode is WriteOnly the contents will
+ not be erased before writing. This is equivelant to writing the result of the bitwise OR of
+ \a data and the original value.
+
+ Returns a request id which can be used to track the completion status of the request.
+
+ Once the request completes the response can be retrieved from the requestResponse() function.
+ The response of this request will be a boolean value, true for success; otherwise false.
+
+ \sa requestCompleted(), waitForRequestCompleted()
+*/
+QNearFieldTarget::RequestId QNearFieldTagType1::writeByte(quint8 address, quint8 data,
+ WriteMode mode)
+{
+ if (address & 0x80)
+ return QNearFieldTarget::RequestId();
+
+ QByteArray command;
+
+ if (mode == EraseAndWrite)
+ command.append(char(0x53)); // WRITE-E
+ else if (mode == WriteOnly)
+ command.append(char(0x1a)); // WRITE-NE
+ else
+ return QNearFieldTarget::RequestId();
+
+ command.append(char(address)); // Address
+ command.append(char(data)); // Data
+ command.append(uid().left(4)); // 4 bytes of UID
+
+ QNearFieldTarget::RequestId id = sendCommand(command);
+
+ Q_D(QNearFieldTagType1);
+
+ d->m_pendingInternalCommands.insert(id, command);
+
+ return id;
+}
+
+/*!
+ Requests 128 bytes of data from the segment specified by \a segmentAddress. Returns a request
+ id which can be used to track the completion status of the request.
+
+ Once the request completes successfully the response can be retrieved from the
+ requestResponse() function. The response of this request will be a QByteArray.
+
+ \sa requestCompleted(), waitForRequestCompleted()
+*/
+QNearFieldTarget::RequestId QNearFieldTagType1::readSegment(quint8 segmentAddress)
+{
+ if (segmentAddress & 0xf0)
+ return QNearFieldTarget::RequestId();
+
+ QByteArray command;
+ command.append(char(0x10)); // RSEG
+ command.append(char(segmentAddress << 4)); // Segment address
+ command.append(QByteArray(8, char(0x00))); // Data (unused)
+ command.append(uid().left(4)); // 4 bytes of UID
+
+ QNearFieldTarget::RequestId id = sendCommand(command);
+
+ Q_D(QNearFieldTagType1);
+
+ d->m_pendingInternalCommands.insert(id, command);
+
+ return id;
+}
+
+/*!
+ Requests 8 bytes of data from the block specified by \a blockAddress. Returns a request id
+ which can be used to track the completion status of the request.
+
+ Once the request completes successfully the response can be retrieved from the
+ requestResponse() function. The response of this request will be a QByteArray.
+
+ \sa requestCompleted(), waitForRequestCompleted()
+*/
+QNearFieldTarget::RequestId QNearFieldTagType1::readBlock(quint8 blockAddress)
+{
+ QByteArray command;
+ command.append(char(0x02)); // READ8
+ command.append(char(blockAddress)); // Block address
+ command.append(QByteArray(8, char(0x00))); // Data (unused)
+ command.append(uid().left(4)); // 4 bytes of UID
+
+ QNearFieldTarget::RequestId id = sendCommand(command);
+
+ Q_D(QNearFieldTagType1);
+
+ d->m_pendingInternalCommands.insert(id, command);
+
+ return id;
+}
+
+/*!
+ Writes 8 bytes of \a data to the block specified by \a blockAddress. If \a mode is
+ EraseAndWrite the bytes will be erased before writing. If \a mode is WriteOnly the contents
+ will not be erased before writing. This is equivelant to writing the result of the bitwise OR
+ of \a data and the original value.
+
+ Returns a request id which can be used to track the completion status of the request.
+
+ Once the request completes the response can be retrieved from the requestResponse() function.
+ The response of this request will be a boolean value, true for success; otherwise false.
+
+ \sa requestCompleted(), waitForRequestCompleted()
+*/
+QNearFieldTarget::RequestId QNearFieldTagType1::writeBlock(quint8 blockAddress,
+ const QByteArray &data,
+ WriteMode mode)
+{
+ if (data.length() != 8)
+ return QNearFieldTarget::RequestId();
+
+ QByteArray command;
+
+ if (mode == EraseAndWrite)
+ command.append(char(0x54)); // WRITE-E8
+ else if (mode == WriteOnly)
+ command.append(char(0x1b)); // WRITE-NE8
+ else
+ return QNearFieldTarget::RequestId();
+
+ command.append(char(blockAddress)); // Block address
+ command.append(data); // Data
+ command.append(uid().left(4)); // 4 bytes of UID
+
+ QNearFieldTarget::RequestId id = sendCommand(command);
+
+ Q_D(QNearFieldTagType1);
+
+ d->m_pendingInternalCommands.insert(id, command);
+
+ return id;
+}
+
+/*!
+ \reimp
+*/
+void QNearFieldTagType1::setResponseForRequest(const QNearFieldTarget::RequestId &id,
+ const QVariant &response,
+ bool emitRequestCompleted)
+{
+ Q_D(QNearFieldTagType1);
+
+ if (d->m_pendingInternalCommands.contains(id)) {
+ const QByteArray command = d->m_pendingInternalCommands.take(id);
+
+ QVariant decodedResponse = decodeResponse(command, response.toByteArray());
+ QNearFieldTargetPrivate::setResponseForRequest(id, decodedResponse, emitRequestCompleted);
+ } else {
+ QNearFieldTargetPrivate::setResponseForRequest(id, response, emitRequestCompleted);
+ }
+
+ // continue reading / writing NDEF message
+ if (d->m_nextExpectedRequestId == id) {
+ if (d->m_readNdefMessageState != QNearFieldTagType1Private::NotReadingNdefMessage)
+ d->progressToNextNdefReadMessageState();
+ else if (d->m_writeNdefMessageState != QNearFieldTagType1Private::NotWritingNdefMessage)
+ d->progressToNextNdefWriteMessageState();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/tests/auto/nfccommons/qnearfieldtagtype1_p.h b/tests/auto/nfccommons/qnearfieldtagtype1_p.h
new file mode 100644
index 00000000..3e170572
--- /dev/null
+++ b/tests/auto/nfccommons/qnearfieldtagtype1_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNEARFIELDTAGTYPE1_H
+#define QNEARFIELDTAGTYPE1_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNfc/private/qnearfieldtarget_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QNearFieldTagType1Private;
+
+class QNearFieldTagType1 : public QNearFieldTargetPrivate
+{
+ Q_OBJECT
+
+ Q_DECLARE_PRIVATE(QNearFieldTagType1)
+
+public:
+ enum WriteMode {
+ EraseAndWrite,
+ WriteOnly
+ };
+ Q_ENUM(WriteMode)
+
+ explicit QNearFieldTagType1(QObject *parent = nullptr);
+ ~QNearFieldTagType1();
+
+ QNearFieldTarget::Type type() const override { return QNearFieldTarget::NfcTagType1; }
+
+ bool hasNdefMessage() override;
+ QNearFieldTarget::RequestId readNdefMessages() override;
+ QNearFieldTarget::RequestId writeNdefMessages(const QList<QNdefMessage> &messages) override;
+
+ quint8 version();
+ virtual int memorySize();
+
+ // DIGPROTO
+ virtual QNearFieldTarget::RequestId readIdentification();
+
+ // static memory functions
+ virtual QNearFieldTarget::RequestId readAll();
+ virtual QNearFieldTarget::RequestId readByte(quint8 address);
+ virtual QNearFieldTarget::RequestId writeByte(quint8 address, quint8 data, WriteMode mode = EraseAndWrite);
+
+ // dynamic memory functions
+ virtual QNearFieldTarget::RequestId readSegment(quint8 segmentAddress);
+ virtual QNearFieldTarget::RequestId readBlock(quint8 blockAddress);
+ virtual QNearFieldTarget::RequestId writeBlock(quint8 blockAddress, const QByteArray &data,
+ WriteMode mode = EraseAndWrite);
+
+protected:
+ void setResponseForRequest(const QNearFieldTarget::RequestId &id, const QVariant &response, bool emitRequestCompleted = true) override;
+
+private:
+ QNearFieldTagType1Private *d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QNEARFIELDTAGTYPE1_H
diff --git a/tests/auto/nfccommons/qnearfieldtagtype2.cpp b/tests/auto/nfccommons/qnearfieldtagtype2.cpp
new file mode 100644
index 00000000..02297892
--- /dev/null
+++ b/tests/auto/nfccommons/qnearfieldtagtype2.cpp
@@ -0,0 +1,328 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnearfieldtagtype2_p.h"
+#include <QtNfc/private/qnearfieldtarget_p.h>
+
+#include <QtCore/QVariant>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QTime>
+
+#include <QtCore/QDebug>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QNearFieldTagType2
+ \brief The QNearFieldTagType2 class provides an interface for communicating with an NFC Tag
+ Type 2 tag.
+
+ \ingroup connectivity-nfc
+ \inmodule QtNfc
+ \internal
+*/
+
+/*!
+ \fn Type QNearFieldTagType2::type() const
+ \reimp
+*/
+
+struct SectorSelectState {
+ int timerId; // id of timer used for passive ack
+ quint8 sector; // sector being selected
+};
+
+class QNearFieldTagType2Private
+{
+public:
+ QNearFieldTagType2Private() : m_currentSector(0) { }
+
+ QMap<QNearFieldTarget::RequestId, QByteArray> m_pendingInternalCommands;
+
+ quint8 m_currentSector;
+
+ QMap<QNearFieldTarget::RequestId, SectorSelectState> m_pendingSectorSelectCommands;
+};
+
+static QVariant decodeResponse(const QByteArray &command, const QByteArray &response)
+{
+ quint8 opcode = command.at(0);
+
+ switch (opcode) {
+ case 0xa2: // WRITE
+ return quint8(response.at(0)) == 0x0a;
+ case 0xc2: // SECTOR SELECT (Command Packet 1)
+ return quint8(response.at(0)) == 0x0a;
+ }
+
+ return QVariant();
+}
+
+/*!
+ Constructs a new tag type 2 near field target with \a parent.
+*/
+QNearFieldTagType2::QNearFieldTagType2(QObject *parent)
+: QNearFieldTargetPrivate(parent), d_ptr(new QNearFieldTagType2Private)
+{
+}
+
+/*!
+ Destroys the tag type 2 near field target.
+*/
+QNearFieldTagType2::~QNearFieldTagType2()
+{
+ delete d_ptr;
+}
+
+/*!
+ \reimp
+*/
+bool QNearFieldTagType2::hasNdefMessage()
+{
+ qWarning() << Q_FUNC_INFO << "is unimplemeted";
+ return false;
+}
+
+/*!
+ \reimp
+*/
+QNearFieldTarget::RequestId QNearFieldTagType2::readNdefMessages()
+{
+ return QNearFieldTarget::RequestId();
+}
+
+/*!
+ \reimp
+*/
+QNearFieldTarget::RequestId QNearFieldTagType2::writeNdefMessages(const QList<QNdefMessage> &messages)
+{
+ Q_UNUSED(messages);
+
+ return QNearFieldTarget::RequestId();
+}
+
+/*!
+ Returns the NFC Tag Type 2 specification version number that the tag supports.
+*/
+quint8 QNearFieldTagType2::version()
+{
+ Q_D(QNearFieldTagType2);
+ if (d->m_currentSector != 0) {
+ QNearFieldTarget::RequestId id = selectSector(0);
+ if (!waitForRequestCompleted(id))
+ return 0;
+ }
+
+ QNearFieldTarget::RequestId id = readBlock(0);
+ if (!waitForRequestCompleted(id))
+ return 0;
+
+ const QByteArray data = requestResponse(id).toByteArray();
+ return data.at(13);
+}
+
+/*!
+ Returns the memory size in bytes of the tag.
+*/
+int QNearFieldTagType2::memorySize()
+{
+ Q_D(QNearFieldTagType2);
+ if (d->m_currentSector != 0) {
+ QNearFieldTarget::RequestId id = selectSector(0);
+ if (!waitForRequestCompleted(id))
+ return 0;
+ }
+
+ QNearFieldTarget::RequestId id = readBlock(0);
+ if (!waitForRequestCompleted(id))
+ return 0;
+
+ const QByteArray data = requestResponse(id).toByteArray();
+ return 8 * quint8(data.at(14));
+}
+
+/*!
+ Requests 16 bytes of data starting at \a blockAddress. Returns a request id which can be used
+ to track the completion status of the request.
+
+ Once the request completes successfully the response can be retrieved from the
+ requestResponse() function. The response of this request will be a QByteArray.
+
+ \sa requestCompleted(), waitForRequestCompleted()
+*/
+QNearFieldTarget::RequestId QNearFieldTagType2::readBlock(quint8 blockAddress)
+{
+ QByteArray command;
+ command.append(char(0x30)); // READ
+ command.append(char(blockAddress)); // Block address
+
+ return sendCommand(command);
+}
+
+/*!
+ Writes 4 bytes of \a data to the block at \a blockAddress. Returns a request id which can be
+ used to track the completion status of the request.
+
+ Once the request completes the response can be retrieved from the requestResponse() function.
+ The response of this request will be a boolean value, true for success; otherwise false.
+
+ Returns true on success; otherwise returns false.
+*/
+QNearFieldTarget::RequestId QNearFieldTagType2::writeBlock(quint8 blockAddress,
+ const QByteArray &data)
+{
+ if (data.length() != 4)
+ return QNearFieldTarget::RequestId();
+
+ QByteArray command;
+ command.append(char(0xa2)); // WRITE
+ command.append(char(blockAddress)); // Block address
+ command.append(data); // Data
+
+ QNearFieldTarget::RequestId id = sendCommand(command);
+
+ Q_D(QNearFieldTagType2);
+
+ d->m_pendingInternalCommands.insert(id, command);
+
+ return id;
+}
+
+/*!
+ Selects the \a sector upon which subsequent readBlock() and writeBlock() operations will act.
+
+ Returns a request id which can be used to track the completion status of the request.
+
+ Once the request completes the response can be retrieved from the requestResponse() function.
+ The response of this request will be a boolean value, true for success; otherwise false.
+
+ \note this request has a passive acknowledgement mechanism. The operation is deemed successful
+ if no response is received within 1ms. It will therefore take a minimum of 1 millisecond for
+ the requestCompleted() signal to be emitted and calling waitForRequestCompleted() on the
+ returned request id may cause the current thread to block for up to 1 millisecond.
+*/
+QNearFieldTarget::RequestId QNearFieldTagType2::selectSector(quint8 sector)
+{
+ QByteArray command;
+ command.append(char(0xc2)); // SECTOR SELECT (Command Packet 1)
+ command.append(char(0xff));
+
+ QNearFieldTarget::RequestId id = sendCommand(command);
+
+ Q_D(QNearFieldTagType2);
+
+ d->m_pendingInternalCommands.insert(id, command);
+
+ SectorSelectState state;
+ state.timerId = -1;
+ state.sector = sector;
+
+ d->m_pendingSectorSelectCommands.insert(id, state);
+
+ return id;
+}
+
+/*!
+ \reimp
+*/
+void QNearFieldTagType2::setResponseForRequest(const QNearFieldTarget::RequestId &id,
+ const QVariant &response,
+ bool emitRequestCompleted)
+{
+ Q_D(QNearFieldTagType2);
+
+ if (d->m_pendingInternalCommands.contains(id)) {
+ const QByteArray command = d->m_pendingInternalCommands.take(id);
+
+ QVariant decodedResponse = decodeResponse(command, response.toByteArray());
+ if (quint8(command.at(0)) == 0xc2 && decodedResponse.toBool()) {
+ // SECTOR SELECT (Command Packet 2)
+ SectorSelectState &state = d->m_pendingSectorSelectCommands[id];
+
+ QByteArray packet2;
+ packet2.append(char(state.sector));
+ packet2.append(QByteArray(3, 0x00));
+
+ sendCommand(packet2);
+
+ state.timerId = startTimer(1);
+ } else {
+ QNearFieldTargetPrivate::setResponseForRequest(id, decodedResponse, emitRequestCompleted);
+ }
+
+ return;
+ }
+
+ if (d->m_pendingSectorSelectCommands.contains(id)) {
+ if (!response.toByteArray().isEmpty()) {
+ d->m_pendingSectorSelectCommands.remove(id);
+ QNearFieldTargetPrivate::setResponseForRequest(id, false, emitRequestCompleted);
+
+ return;
+ }
+ }
+
+ QNearFieldTargetPrivate::setResponseForRequest(id, response, emitRequestCompleted);
+}
+
+/*!
+ \internal
+*/
+void QNearFieldTagType2::timerEvent(QTimerEvent *event)
+{
+ Q_D(QNearFieldTagType2);
+
+ killTimer(event->timerId());
+
+ for (auto i = d->m_pendingSectorSelectCommands.begin(), end = d->m_pendingSectorSelectCommands.end(); i != end; ++i) {
+
+ SectorSelectState &state = i.value();
+
+ if (state.timerId == event->timerId()) {
+ d->m_currentSector = state.sector;
+
+ QNearFieldTargetPrivate::setResponseForRequest(i.key(), true);
+
+ d->m_pendingSectorSelectCommands.erase(i);
+ break;
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/tests/auto/nfccommons/qnearfieldtagtype2_p.h b/tests/auto/nfccommons/qnearfieldtagtype2_p.h
new file mode 100644
index 00000000..5bc49158
--- /dev/null
+++ b/tests/auto/nfccommons/qnearfieldtagtype2_p.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNEARFIELDTAGTYPE2_H
+#define QNEARFIELDTAGTYPE2_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNfc/private/qnearfieldtarget_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QNearFieldTagType2Private;
+
+class QNearFieldTagType2 : public QNearFieldTargetPrivate
+{
+ Q_OBJECT
+
+ Q_DECLARE_PRIVATE(QNearFieldTagType2)
+
+public:
+ explicit QNearFieldTagType2(QObject *parent = nullptr);
+ ~QNearFieldTagType2();
+
+ QNearFieldTarget::Type type() const override { return QNearFieldTarget::NfcTagType2; }
+
+ bool hasNdefMessage() override;
+ QNearFieldTarget::RequestId readNdefMessages() override;
+ QNearFieldTarget::RequestId writeNdefMessages(const QList<QNdefMessage> &messages) override;
+
+ quint8 version();
+ int memorySize();
+
+ virtual QNearFieldTarget::RequestId readBlock(quint8 blockAddress);
+ virtual QNearFieldTarget::RequestId writeBlock(quint8 blockAddress, const QByteArray &data);
+ virtual QNearFieldTarget::RequestId selectSector(quint8 sector);
+
+ void timerEvent(QTimerEvent *event) override;
+
+protected:
+ void setResponseForRequest(const QNearFieldTarget::RequestId &id, const QVariant &response, bool emitRequestCompleted = true) override;
+
+private:
+ QNearFieldTagType2Private *d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QNEARFIELDTAGTYPE2_H
diff --git a/tests/auto/nfccommons/qnearfieldtarget_emulator.cpp b/tests/auto/nfccommons/qnearfieldtarget_emulator.cpp
new file mode 100644
index 00000000..8f969592
--- /dev/null
+++ b/tests/auto/nfccommons/qnearfieldtarget_emulator.cpp
@@ -0,0 +1,300 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnearfieldtarget_emulator_p.h"
+#include <QtNfc/private/qnearfieldtarget_p.h>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QByteArrayView>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDateTime>
+#include <QtCore/QDirIterator>
+#include <QtCore/QMutex>
+#include <QtCore/QSettings>
+
+QT_BEGIN_NAMESPACE
+
+static QMutex tagMutex;
+static QMap<TagBase *, bool> tagMap;
+
+Q_GLOBAL_STATIC(TagActivator, globalTagActivator)
+
+TagType1::TagType1(TagBase *tag, QObject *parent)
+: QNearFieldTagType1(parent), tag(tag)
+{
+}
+
+TagType1::~TagType1()
+{
+}
+
+QByteArray TagType1::uid() const
+{
+ QMutexLocker locker(&tagMutex);
+
+ return tag->uid();
+}
+
+QNearFieldTarget::AccessMethods TagType1::accessMethods() const
+{
+ return QNearFieldTarget::NdefAccess | QNearFieldTarget::TagTypeSpecificAccess;
+}
+
+QNearFieldTarget::RequestId TagType1::sendCommand(const QByteArray &command)
+{
+ QMutexLocker locker(&tagMutex);
+
+ QNearFieldTarget::RequestId id(new QNearFieldTarget::RequestIdPrivate);
+
+ // tag not in proximity
+ if (!tagMap.value(tag)) {
+ reportError(QNearFieldTarget::TargetOutOfRangeError, id);
+ return id;
+ }
+
+ quint16 crc = qChecksum(QByteArrayView(command.constData(), command.length()), Qt::ChecksumItuV41);
+
+ QByteArray response = tag->processCommand(command + char(crc & 0xff) + char(crc >> 8));
+
+ if (response.isEmpty()) {
+ reportError(QNearFieldTarget::NoResponseError, id);
+ return id;
+ }
+
+ // check crc
+ if (qChecksum(QByteArrayView(response.constData(), response.length()), Qt::ChecksumItuV41) != 0) {
+ reportError(QNearFieldTarget::ChecksumMismatchError, id);
+ return id;
+ }
+
+ response.chop(2);
+
+ QMetaObject::invokeMethod(this, [this, id, response] {
+ this->setResponseForRequest(id, response);
+ }, Qt::QueuedConnection);
+
+ return id;
+}
+
+bool TagType1::waitForRequestCompleted(const QNearFieldTarget::RequestId &id, int msecs)
+{
+ QCoreApplication::sendPostedEvents(this, QEvent::MetaCall);
+
+ return QNearFieldTagType1::waitForRequestCompleted(id, msecs);
+}
+
+
+TagType2::TagType2(TagBase *tag, QObject *parent)
+: QNearFieldTagType2(parent), tag(tag)
+{
+}
+
+TagType2::~TagType2()
+{
+}
+
+QByteArray TagType2::uid() const
+{
+ QMutexLocker locker(&tagMutex);
+
+ return tag->uid();
+}
+
+QNearFieldTarget::AccessMethods TagType2::accessMethods() const
+{
+ return QNearFieldTarget::NdefAccess | QNearFieldTarget::TagTypeSpecificAccess;
+}
+
+QNearFieldTarget::RequestId TagType2::sendCommand(const QByteArray &command)
+{
+ QMutexLocker locker(&tagMutex);
+
+ QNearFieldTarget::RequestId id(new QNearFieldTarget::RequestIdPrivate);
+
+ // tag not in proximity
+ if (!tagMap.value(tag)) {
+ reportError(QNearFieldTarget::TargetOutOfRangeError, id);
+ return id;
+ }
+
+ quint16 crc = qChecksum(QByteArrayView(command.constData(), command.length()), Qt::ChecksumItuV41);
+
+ QByteArray response = tag->processCommand(command + char(crc & 0xff) + char(crc >> 8));
+
+ if (response.isEmpty())
+ return id;
+
+ if (response.length() > 1) {
+ // check crc
+ if (qChecksum(QByteArrayView(response.constData(), response.length()), Qt::ChecksumItuV41) != 0) {
+ reportError(QNearFieldTarget::ChecksumMismatchError, id);
+ return id;
+ }
+
+ response.chop(2);
+ }
+
+ QMetaObject::invokeMethod(this, [this, id, response] {
+ this->setResponseForRequest(id, response);
+ }, Qt::QueuedConnection);
+
+ return id;
+}
+
+bool TagType2::waitForRequestCompleted(const QNearFieldTarget::RequestId &id, int msecs)
+{
+ QCoreApplication::sendPostedEvents(this, QEvent::MetaCall);
+
+ return QNearFieldTagType2::waitForRequestCompleted(id, msecs);
+}
+
+
+TagActivator::TagActivator()
+: timerId(-1)
+{
+ qRegisterMetaType<QNearFieldTarget::Error>();
+}
+
+TagActivator::~TagActivator()
+{
+ QMutexLocker locker(&tagMutex);
+ qDeleteAll(tagMap.keys());
+ tagMap.clear();
+}
+
+void TagActivator::initialize()
+{
+ QMutexLocker locker(&tagMutex);
+
+ if (!tagMap.isEmpty())
+ return;
+
+#ifndef BUILTIN_TESTDATA
+ QDirIterator nfcTargets(QDir::currentPath(), QStringList(QStringLiteral("*.nfc")), QDir::Files);
+#else
+ QDirIterator nfcTargets(":/nfcdata", QStringList(QStringLiteral("*.nfc")), QDir::Files);
+#endif
+ while (nfcTargets.hasNext()) {
+ const QString targetFilename = nfcTargets.next();
+
+ QSettings target(targetFilename, QSettings::IniFormat);
+
+ target.beginGroup(QStringLiteral("Target"));
+
+ const QString tagType = target.value(QStringLiteral("Type")).toString();
+
+ target.endGroup();
+
+ if (tagType == QLatin1String("TagType1")) {
+ NfcTagType1 *tag = new NfcTagType1;
+ tag->load(&target);
+
+ tagMap.insert(tag, false);
+ } else if (tagType == QLatin1String("TagType2")) {
+ NfcTagType2 *tag = new NfcTagType2;
+ tag->load(&target);
+
+ tagMap.insert(tag, false);
+ } else {
+ qWarning("Unknown tag type %s\n", qPrintable(tagType));
+ }
+ }
+
+ current = tagMap.end();
+
+ timerId = startTimer(1000);
+}
+
+void TagActivator::reset()
+{
+ QMutexLocker locker(&tagMutex);
+
+ killTimer(timerId);
+ timerId = -1;
+
+ qDeleteAll(tagMap.keys());
+ tagMap.clear();
+}
+
+TagActivator *TagActivator::instance()
+{
+ return globalTagActivator();
+}
+
+void TagActivator::timerEvent(QTimerEvent *e)
+{
+ Q_UNUSED(e);
+
+ tagMutex.lock();
+
+ if (current != tagMap.end()) {
+ if (current.key()->lastAccessTime() + 1500 > QDateTime::currentMSecsSinceEpoch()) {
+ tagMutex.unlock();
+ return;
+ }
+
+ *current = false;
+
+ TagBase *tag = current.key();
+
+ tagMutex.unlock();
+ Q_EMIT tagDeactivated(tag);
+ tagMutex.lock();
+ }
+
+ if (current != tagMap.end())
+ ++current;
+
+ if (current == tagMap.end())
+ current = tagMap.begin();
+
+ if (current != tagMap.end()) {
+ *current = true;
+
+ TagBase *tag = current.key();
+
+ tagMutex.unlock();
+ Q_EMIT tagActivated(tag);
+ tagMutex.lock();
+ }
+
+ tagMutex.unlock();
+}
+
+QT_END_NAMESPACE
diff --git a/tests/auto/nfccommons/qnearfieldtarget_emulator_p.h b/tests/auto/nfccommons/qnearfieldtarget_emulator_p.h
new file mode 100644
index 00000000..cb1e70a7
--- /dev/null
+++ b/tests/auto/nfccommons/qnearfieldtarget_emulator_p.h
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNEARFIELDTARGET_EMULATOR_P_H
+#define QNEARFIELDTARGET_EMULATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qnearfieldtagtype1_p.h"
+#include "qnearfieldtagtype2_p.h"
+#include "targetemulator_p.h"
+
+#include <QtCore/QMap>
+
+QT_BEGIN_NAMESPACE
+
+class TagType1 : public QNearFieldTagType1
+{
+ Q_OBJECT
+
+public:
+ TagType1(TagBase *tag, QObject *parent = nullptr);
+ ~TagType1();
+
+ QByteArray uid() const override;
+
+ QNearFieldTarget::AccessMethods accessMethods() const override;
+
+ QNearFieldTarget::RequestId sendCommand(const QByteArray &command) override;
+ bool waitForRequestCompleted(const QNearFieldTarget::RequestId &id, int msecs);
+
+private:
+ TagBase *tag;
+};
+
+class TagType2 : public QNearFieldTagType2
+{
+ Q_OBJECT
+
+public:
+ TagType2(TagBase *tag, QObject *parent = nullptr);
+ ~TagType2();
+
+ QByteArray uid() const override;
+
+ QNearFieldTarget::AccessMethods accessMethods() const override;
+
+ QNearFieldTarget::RequestId sendCommand(const QByteArray &command) override;
+ bool waitForRequestCompleted(const QNearFieldTarget::RequestId &id, int msecs);
+
+private:
+ TagBase *tag;
+};
+
+class TagActivator : public QObject
+{
+ Q_OBJECT
+
+public:
+ TagActivator();
+ ~TagActivator();
+
+ void initialize();
+ void reset();
+
+ static TagActivator *instance();
+
+protected:
+ void timerEvent(QTimerEvent *e) override;
+
+signals:
+ void tagActivated(TagBase *tag);
+ void tagDeactivated(TagBase *tag);
+
+private:
+ QMap<TagBase *, bool>::Iterator current;
+ int timerId;
+};
+
+QT_END_NAMESPACE
+
+#endif // QNEARFIELDTARGET_EMULATOR_P_H
diff --git a/tests/auto/nfccommons/qtlv.cpp b/tests/auto/nfccommons/qtlv.cpp
new file mode 100644
index 00000000..03c3b022
--- /dev/null
+++ b/tests/auto/nfccommons/qtlv.cpp
@@ -0,0 +1,532 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtlv_p.h"
+
+#include "qnearfieldtagtype1_p.h"
+
+#include <QtCore/QVariant>
+
+#include <QtCore/QDebug>
+
+QT_BEGIN_NAMESPACE
+
+QPair<int, int> qParseReservedMemoryControlTlv(const QByteArray &tlvData)
+{
+ quint8 position = tlvData.at(0);
+ int pageAddr = position >> 4;
+ int byteOffset = position & 0x0f;
+
+ int size = quint8(tlvData.at(1));
+ if (size == 0)
+ size = 256;
+
+ quint8 pageControl = tlvData.at(2);
+ int bytesPerPage = pageControl & 0x0f;
+
+ if (!bytesPerPage)
+ return qMakePair(0, 0);
+
+ int byteAddress = pageAddr * (1 << bytesPerPage) + byteOffset;
+ return qMakePair(byteAddress, size);
+}
+
+QPair<int, int> qParseLockControlTlv(const QByteArray &tlvData)
+{
+ quint8 position = tlvData.at(0);
+ int pageAddr = position >> 4;
+ int byteOffset = position & 0x0f;
+
+ int size = quint8(tlvData.at(1));
+ if (size == 0)
+ size = 256;
+ size = size / 8;
+
+ quint8 pageControl = tlvData.at(2);
+ int bytesPerPage = pageControl & 0x0f;
+
+ if (!bytesPerPage)
+ return qMakePair(0, 0);
+
+ int byteAddress = pageAddr * (1 << bytesPerPage) + byteOffset;
+ return qMakePair(byteAddress, size);
+}
+
+QTlvReader::QTlvReader(QNearFieldTargetPrivate *target)
+: m_target(target), m_index(-1)
+{
+ if (qobject_cast<QNearFieldTagType1 *>(m_target)) {
+ addReservedMemory(0, 12); // skip uid, cc
+ addReservedMemory(104, 16); // skip reserved block D, lock block E
+
+ addReservedMemory(120, 8); // skip reserved block F
+ }
+}
+
+QTlvReader::QTlvReader(const QByteArray &data)
+: m_target(0), m_rawData(data), m_index(-1)
+{
+}
+
+void QTlvReader::addReservedMemory(int offset, int length)
+{
+ m_reservedMemory.insert(offset, length);
+}
+
+/*!
+ Returns the number of bytes of reserved memory found so far. The actual number of reserved
+ bytes will not be known until atEnd() returns true.
+*/
+int QTlvReader::reservedMemorySize() const
+{
+ int total = 0;
+
+ QMap<int, int>::ConstIterator i;
+ for (i = m_reservedMemory.constBegin(); i != m_reservedMemory.constEnd(); ++i)
+ total += i.value();
+
+ return total;
+}
+
+/*!
+ Returns the request id that the TLV reader is currently waiting on.
+*/
+QNearFieldTarget::RequestId QTlvReader::requestId() const
+{
+ return m_requestId;
+}
+
+bool QTlvReader::atEnd() const
+{
+ if (m_index == -1)
+ return false;
+
+ if (m_requestId.isValid())
+ return false;
+
+ return (m_index == m_tlvData.length()) || (tag() == 0xfe);
+}
+
+/*!
+ Moves to the next TLV. Returns true on success; otherwise returns false.
+*/
+bool QTlvReader::readNext()
+{
+ if (atEnd())
+ return false;
+
+ // Move to next TLV
+ if (m_index == -1) {
+ m_index = 0;
+ } else if (m_requestId.isValid()) {
+ // do nothing
+ } else if (tag() == 0x00 || tag() == 0xfe) {
+ ++m_index;
+ } else {
+ int tlvLength = length();
+ m_index += (tlvLength < 0xff) ? tlvLength + 2 : tlvLength + 4;
+ }
+
+ // Ensure that tag byte is available
+ if (!readMoreData(m_index))
+ return false;
+
+ // Ensure that length byte(s) are available
+ if (length() == -1)
+ return false;
+
+ // Ensure that data bytes are available
+ int tlvLength = length();
+
+ int dataOffset = (tlvLength < 0xff) ? m_index + 2 : m_index + 4;
+
+ if (!readMoreData(dataOffset + tlvLength - 1))
+ return false;
+
+ switch (tag()) {
+ case 0x01: { // Lock Control TLV
+ QPair<int, int> locked = qParseLockControlTlv(data());
+ addReservedMemory(locked.first, locked.second);
+ break;
+ }
+ case 0x02: { // Reserved Memory Control TLV
+ QPair<int, int> reserved = qParseReservedMemoryControlTlv(data());
+ addReservedMemory(reserved.first, reserved.second);
+ break;
+ }
+ }
+
+ return true;
+}
+
+quint8 QTlvReader::tag() const
+{
+ return m_tlvData.at(m_index);
+}
+
+int QTlvReader::length()
+{
+ if (tag() == 0x00 || tag() == 0xfe)
+ return 0;
+
+ if (!readMoreData(m_index + 1))
+ return -1;
+
+ quint8 shortLength = m_tlvData.at(m_index + 1);
+ if (shortLength != 0xff)
+ return shortLength;
+
+ if (!readMoreData(m_index + 3))
+ return -1;
+
+ quint16 longLength = (quint8(m_tlvData.at(m_index + 2)) << 8) |
+ quint8(m_tlvData.at(m_index + 3));
+
+ if (longLength < 0xff || longLength == 0xffff) {
+ qWarning("Invalid 3 byte length");
+ return 0;
+ }
+
+ return longLength;
+}
+
+QByteArray QTlvReader::data()
+{
+ int tlvLength = length();
+
+ int dataOffset = (tlvLength < 0xff) ? m_index + 2 : m_index + 4;
+
+ if (!readMoreData(dataOffset + tlvLength - 1))
+ return QByteArray();
+
+ return m_tlvData.mid(dataOffset, tlvLength);
+}
+
+bool QTlvReader::readMoreData(int sparseOffset)
+{
+ while (sparseOffset >= m_tlvData.length()) {
+ int absOffset = absoluteOffset(m_tlvData.length());
+
+ QByteArray data;
+
+ if (!m_rawData.isEmpty()) {
+ data = m_rawData.mid(absOffset, dataLength(absOffset));
+ } else if (QNearFieldTagType1 *tag = qobject_cast<QNearFieldTagType1 *>(m_target)) {
+ quint8 segment = absOffset / 128;
+
+ if (m_requestId.isValid()) {
+ QVariant v = m_target->requestResponse(m_requestId);
+ if (!v.isValid())
+ return false;
+
+ m_requestId = QNearFieldTarget::RequestId();
+
+ data = v.toByteArray();
+
+ if (absOffset < 120)
+ data = data.mid(2);
+
+ int length = dataLength(absOffset);
+
+ data = data.mid(absOffset - (segment * 128), length);
+ } else {
+ m_requestId = (absOffset < 120) ? tag->readAll() : tag->readSegment(segment);
+
+ return false;
+ }
+ }
+
+ if (data.isEmpty() && sparseOffset >= m_tlvData.length())
+ return false;
+
+ m_tlvData.append(data);
+ }
+
+ return true;
+}
+
+int QTlvReader::absoluteOffset(int sparseOffset) const
+{
+ int absoluteOffset = sparseOffset;
+ const QList<int> offsets = m_reservedMemory.keys();
+ for (const int offset : offsets) {
+ if (offset <= absoluteOffset)
+ absoluteOffset += m_reservedMemory.value(offset);
+ }
+
+ return absoluteOffset;
+}
+
+/*!
+ Returns the length of the contiguous non-reserved data block starting from absolute offset
+ \a startOffset. -1 is return as the length of the last contiguous data block.
+*/
+int QTlvReader::dataLength(int startOffset) const
+{
+ const QList<int> offsets = m_reservedMemory.keys();
+ for (const int offset : offsets) {
+ if (offset <= startOffset)
+ continue;
+
+ return offset - startOffset;
+ }
+
+ return -1;
+}
+
+
+QTlvWriter::QTlvWriter(QNearFieldTargetPrivate *target)
+: m_target(target), m_rawData(0), m_index(0), m_tagMemorySize(-1)
+{
+ if (qobject_cast<QNearFieldTagType1 *>(m_target)) {
+ addReservedMemory(0, 12); // skip uid, cc
+ addReservedMemory(104, 16); // skip reserved block D, lock block E
+
+ addReservedMemory(120, 8); // skip reserved block F
+ }
+}
+
+QTlvWriter::QTlvWriter(QByteArray *data)
+: m_target(0), m_rawData(data), m_index(0), m_tagMemorySize(-1)
+{
+}
+
+QTlvWriter::~QTlvWriter()
+{
+ if (m_rawData)
+ process(true);
+}
+
+void QTlvWriter::addReservedMemory(int offset, int length)
+{
+ m_reservedMemory.insert(offset, length);
+}
+
+void QTlvWriter::writeTlv(quint8 tagType, const QByteArray &data)
+{
+ m_buffer.append(tagType);
+
+ if (tagType != 0x00 && tagType != 0xfe) {
+ int length = data.length();
+ if (length < 0xff) {
+ m_buffer.append(quint8(length));
+ } else {
+ m_buffer.append(char(0xff));
+ m_buffer.append(quint16(length) >> 8);
+ m_buffer.append(quint16(length) & 0x00ff);
+ }
+
+ m_buffer.append(data);
+ }
+
+ process();
+
+ switch (tagType) {
+ case 0x01: { // Lock Control TLV
+ QPair<int, int> locked = qParseLockControlTlv(data);
+ addReservedMemory(locked.first, locked.second);
+ break;
+ }
+ case 0x02: { // Reserved Memory Control TLV
+ QPair<int, int> reserved = qParseReservedMemoryControlTlv(data);
+ addReservedMemory(reserved.first, reserved.second);
+ break;
+ }
+ }
+}
+
+/*!
+ Processes more of the TLV writer process. Returns true if the TLVs have been successfully
+ written to the target or buffer; otherwise returns false.
+
+ A false return value indicates that an NFC request is pending (if requestId() returns a valid
+ request identifier) or the write process has failed (requestId() returns an invalid request
+ identifier).
+*/
+bool QTlvWriter::process(bool all)
+{
+ if (m_requestId.isValid()) {
+ QVariant v = m_target->requestResponse(m_requestId);
+ if (!v.isValid())
+ return false;
+ }
+
+ if (m_tagMemorySize == -1) {
+ if (m_rawData)
+ m_tagMemorySize = m_rawData->length();
+ else if (QNearFieldTagType1 *tag = qobject_cast<QNearFieldTagType1 *>(m_target)) {
+ if (m_requestId.isValid()) {
+ m_tagMemorySize = 8 * (tag->requestResponse(m_requestId).toUInt() + 1);
+ m_requestId = QNearFieldTarget::RequestId();
+ } else {
+ m_requestId = tag->readByte(10);
+ return false;
+ }
+ }
+ }
+
+ while (!m_buffer.isEmpty()) {
+ int spaceRemaining = moveToNextAvailable();
+ if (spaceRemaining < 1)
+ return false;
+
+ int length = qMin(spaceRemaining, m_buffer.length());
+
+ if (m_rawData) {
+ m_rawData->replace(m_index, length, m_buffer);
+ m_index += length;
+ m_buffer = m_buffer.mid(length);
+ } else if (QNearFieldTagType1 *tag = qobject_cast<QNearFieldTagType1 *>(m_target)) {
+ int bufferIndex = 0;
+
+ // static memory - can only use writeByte()
+ while (m_index < 120 && bufferIndex < length) {
+ if (m_requestId.isValid()) {
+ if (!m_target->requestResponse(m_requestId).toBool())
+ return false;
+
+ m_requestId = QNearFieldTarget::RequestId();
+
+ ++m_index;
+ ++bufferIndex;
+ } else {
+ m_requestId = tag->writeByte(m_index, m_buffer.at(bufferIndex));
+ m_buffer = m_buffer.mid(bufferIndex);
+ return false;
+ }
+ }
+
+
+ // dynamic memory - writeBlock() full
+ while (m_index >= 120 && (m_index % 8 == 0) && bufferIndex + 8 < length) {
+ if (m_requestId.isValid()) {
+ if (!m_target->requestResponse(m_requestId).toBool())
+ return false;
+
+ m_requestId = QNearFieldTarget::RequestId();
+
+ m_index += 8;
+ bufferIndex += 8;
+ } else {
+ m_requestId = tag->writeBlock(m_index / 8, m_buffer.mid(bufferIndex, 8));
+ m_buffer = m_buffer.mid(bufferIndex);
+ return false;
+ }
+ }
+
+ // partial block
+ int currentBlock = m_index / 8;
+ int nextBlock = currentBlock + 1;
+ int currentBlockStart = currentBlock * 8;
+ int nextBlockStart = nextBlock * 8;
+
+ int fillLength = qMin(nextBlockStart - m_index, spaceRemaining - bufferIndex);
+
+ if (fillLength && (all || m_buffer.length() - bufferIndex >= fillLength) &&
+ (m_buffer.length() != bufferIndex)) {
+ // sufficient data available
+ if (m_requestId.isValid()) {
+ const QVariant v = tag->requestResponse(m_requestId);
+ if (v.typeId() == QMetaType::QByteArray) {
+ // read in block
+ QByteArray block = v.toByteArray();
+
+ int fill = qMin(fillLength, m_buffer.length() - bufferIndex);
+
+ for (int i = m_index - currentBlockStart; i < fill; ++i)
+ block[i] = m_buffer.at(bufferIndex++);
+
+ // now write block
+ m_requestId = tag->writeBlock(currentBlock, block);
+ return false;
+ } else if (v.typeId() == QMetaType::Bool) {
+ m_requestId = QNearFieldTarget::RequestId();
+ int fill = qMin(fillLength, m_buffer.length() - bufferIndex);
+ bufferIndex = fill - (m_index - currentBlockStart);
+
+ // write complete
+ if (!v.toBool())
+ return false;
+ }
+ } else {
+ // read in block
+ m_requestId = tag->readBlock(currentBlock);
+ m_buffer = m_buffer.mid(bufferIndex);
+ return false;
+ }
+ }
+
+ m_buffer = m_buffer.mid(bufferIndex);
+ }
+ }
+
+ return true;
+}
+
+QNearFieldTarget::RequestId QTlvWriter::requestId() const
+{
+ return m_requestId;
+}
+
+int QTlvWriter::moveToNextAvailable()
+{
+ int length = -1;
+
+ // move index to next available byte
+ QMap<int, int>::ConstIterator i;
+ for (i = m_reservedMemory.constBegin(); i != m_reservedMemory.constEnd(); ++i) {
+ if (m_index < i.key()) {
+ length = i.key() - m_index;
+ break;
+ } else if (m_index == i.key()) {
+ m_index += i.value();
+ } else if (m_index > i.key() && m_index < (i.key() + i.value())) {
+ m_index = i.key() + i.value();
+ }
+ }
+
+ if (length == -1)
+ return m_tagMemorySize - m_index;
+
+ Q_ASSERT(length != -1);
+
+ return length;
+}
+
+QT_END_NAMESPACE
diff --git a/tests/auto/nfccommons/qtlv_p.h b/tests/auto/nfccommons/qtlv_p.h
new file mode 100644
index 00000000..6c5e6bdd
--- /dev/null
+++ b/tests/auto/nfccommons/qtlv_p.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTLV_P_H
+#define QTLV_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNfc/qnearfieldtarget.h>
+#include <QtNfc/private/qnearfieldtarget_p.h>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QMap>
+#include <QtCore/QPair>
+
+QT_BEGIN_NAMESPACE
+
+class QNearFieldTarget;
+class QTlvReader
+{
+public:
+ explicit QTlvReader(QNearFieldTargetPrivate *target);
+ explicit QTlvReader(const QByteArray &data);
+
+ void addReservedMemory(int offset, int length);
+ int reservedMemorySize() const;
+
+ QNearFieldTarget::RequestId requestId() const;
+
+ bool atEnd() const;
+
+ bool readNext();
+
+ quint8 tag() const;
+ int length();
+ QByteArray data();
+
+private:
+ bool readMoreData(int sparseOffset);
+ int absoluteOffset(int sparseOffset) const;
+ int dataLength(int startOffset) const;
+
+ QNearFieldTargetPrivate *m_target;
+ QByteArray m_rawData;
+ QNearFieldTarget::RequestId m_requestId;
+
+ QByteArray m_tlvData;
+ int m_index;
+ QMap<int, int> m_reservedMemory;
+};
+
+class QTlvWriter
+{
+public:
+ explicit QTlvWriter(QNearFieldTargetPrivate *target);
+ explicit QTlvWriter(QByteArray *data);
+ ~QTlvWriter();
+
+ void addReservedMemory(int offset, int length);
+
+ void writeTlv(quint8 tag, const QByteArray &data = QByteArray());
+
+ bool process(bool all = false);
+
+ QNearFieldTarget::RequestId requestId() const;
+
+private:
+ int moveToNextAvailable();
+
+ QNearFieldTargetPrivate *m_target;
+ QByteArray *m_rawData;
+
+ int m_index;
+ int m_tagMemorySize;
+ QMap<int, int> m_reservedMemory;
+
+ QByteArray m_buffer;
+
+ QNearFieldTarget::RequestId m_requestId;
+};
+
+QPair<int, int> qParseReservedMemoryControlTlv(const QByteArray &tlvData);
+QPair<int, int> qParseLockControlTlv(const QByteArray &tlvData);
+
+QT_END_NAMESPACE
+
+#endif // QTLV_P_H
diff --git a/tests/auto/nfccommons/targetemulator.cpp b/tests/auto/nfccommons/targetemulator.cpp
new file mode 100644
index 00000000..697fec4b
--- /dev/null
+++ b/tests/auto/nfccommons/targetemulator.cpp
@@ -0,0 +1,389 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "targetemulator_p.h"
+
+#include <QtCore/QSettings>
+#include <QtCore/QDateTime>
+
+#include <QtCore/QDebug>
+
+QT_BEGIN_NAMESPACE
+
+TagBase::TagBase()
+: lastAccess(0)
+{
+}
+
+TagBase::~TagBase()
+{
+}
+
+static inline quint8 blockByteToAddress(quint8 block, quint8 byte)
+{
+ return ((block & 0x0f) << 3) | (byte & 0x07);
+}
+
+NfcTagType1::NfcTagType1()
+: hr0(0x11), hr1(0x00), memory(120, '\0')
+{
+ // Locked blocks
+ memory[(0x0e << 3) | 0x00] = 0x01;
+ memory[(0x0e << 3) | 0x01] = 0x60;
+}
+
+NfcTagType1::~NfcTagType1()
+{
+}
+
+void NfcTagType1::load(QSettings *settings)
+{
+ settings->beginGroup(QStringLiteral("TagType1"));
+
+ hr0 = settings->value(QStringLiteral("HR0"), 0x11).toUInt();
+
+ if (!(hr0 & 0x10)) {
+ settings->endGroup();
+ return;
+ }
+
+ hr1 = settings->value(QStringLiteral("HR1"), 0x00).toUInt();
+
+ memory = settings->value(QStringLiteral("Data")).toByteArray();
+
+ //quint8 nmn = memory.at(8);
+
+ quint8 vno = memory.at(9);
+ if (vno != 0x10)
+ qWarning("Only NFC TagType1 v1.0 behavior is supported.");
+
+ quint8 tms = memory.at(10);
+ if (memory.length() != 8 * (tms + 1))
+ qWarning("Static memory size does not match TMS value.");
+
+ quint8 rwa = memory.at(11);
+ switch (rwa >> 4) {
+ case 0:
+ // Unrestricted read access tag
+ break;
+ default:
+ // tag with unknown read attributes
+ ;
+ }
+
+ switch (rwa & 0x0f) {
+ case 0:
+ // Unrestricted write access tag
+ break;
+ case 0x0f:
+ // Read only tag
+ break;
+ default:
+ // tag with unknown write attributes
+ ;
+ }
+
+ //quint16 lock = (quint8(memory[blockByteToAddress(0x0e, 1)]) << 8) |
+ // quint8(memory[blockByteToAddress(0x0e, 0)]);
+
+ settings->endGroup();
+}
+
+QByteArray NfcTagType1::uid() const
+{
+ lastAccess = QDateTime::currentMSecsSinceEpoch();
+
+ return memory.left(7);
+}
+
+quint8 NfcTagType1::readData(quint8 block, quint8 byte)
+{
+ return memory.at((block << 3) | byte);
+}
+
+QByteArray NfcTagType1::processCommand(const QByteArray &command)
+{
+ lastAccess = QDateTime::currentMSecsSinceEpoch();
+
+ QByteArray response;
+
+ bool tagType1 = (hr0 & 0xf0) == 0x10;
+ bool dynamic = (hr0 & 0x0f) != 0x01;
+
+ if (command.length() == 9) {
+ // static memory model command
+ quint8 opcode = command.at(0);
+ quint8 address = command.at(1);
+ quint8 data = command.at(2);
+ QByteArray uid = command.mid(3, 4);
+
+ // check checksum
+ if (qChecksum(QByteArrayView(command.constData(), command.length()), Qt::ChecksumItuV41) != 0)
+ return QByteArray();
+
+ // check UID
+ if (uid != memory.left(4))
+ return QByteArray();
+
+ switch (opcode) {
+ case 0x00: // RALL
+ response.append(hr0);
+ response.append(hr1);
+ response.append(memory.left(120));
+ break;
+ case 0x01: // READ
+ response.append(address);
+ if (address & 0x80)
+ response.append(char(0x00));
+ else
+ response.append(memory.at(address));
+ break;
+ case 0x53: { // WRITE-E
+ quint8 block = address >> 3;
+ if (block == 0x00 || block == 0x0d || block == 0x0e) // locked blocks
+ break;
+
+ quint16 lock = (readData(0x0e, 0x01) << 8) | readData(0x0e, 0x00);
+ if ((0x01 << block) & lock) // locked blocks
+ break;
+
+ // FIXME: Test dynamic lock bytes
+
+ memory[address] = data;
+
+ response.append(address);
+ response.append(data);
+ break;
+ }
+ case 0x1a: { // WRITE-NE
+ quint8 block = address >> 3;
+ if (block == 0x00 || block == 0x0d) // locked blocks
+ break;
+
+ quint16 lock = (readData(0x0e, 0x01) << 8) | readData(0x0e, 0x00);
+ if ((0x01 << block) & lock) // locked blocks
+ break;
+
+
+ // FIXME: Test dynamic lock bytes
+
+ memory[address] = memory.at(address) | data;
+
+ response.append(address);
+ response.append(memory.at(address));
+ break;
+ }
+ case 0x78: // RID
+ response.append(hr0);
+ response.append(hr1);
+ response.append(memory.left(4));
+ break;
+ }
+ } else if (tagType1 && dynamic && command.length() == 16) {
+ // dynamic memory model command
+ quint8 opcode = command.at(0);
+ quint8 address = command.at(1);
+ QByteArray data = command.mid(2, 8);
+ QByteArray uid = command.mid(10, 4);
+
+ // check checksum
+ if (qChecksum(QByteArrayView(command.constData(), command.length()), Qt::ChecksumItuV41) != 0)
+ return QByteArray();
+
+ // check UID
+ if (uid != memory.left(4))
+ return QByteArray();
+
+ switch (opcode) {
+ case 0x10: // RSEG
+ response.append(address);
+ response.append(memory.mid(128 * (address >> 4), 128));
+ break;
+ case 0x02: // READ8
+ response.append(address);
+ response.append(memory.mid(8 * address, 8));
+ break;
+ case 0x54: { // WRITE-E8
+ // locked blocks
+ if (address == 0x00 || address == 0x0d || address == 0x0e || address == 0x0f)
+ break;
+
+ quint16 lock = (readData(0x0e, 0x01) << 8) | readData(0x0e, 0x00);
+ if (address <= 0x0e && ((0x01 << address) & lock)) // locked blocks
+ break;
+
+ // FIXME: Test dynamic lock bytes
+
+ memory.replace(address * 8, 8, data);
+
+ response.append(address);
+ response.append(memory.mid(address * 8, 8));
+ break;
+ }
+ case 0x1b: // WRITE-NE8
+ // locked blocks
+ if (address == 0x00 || address == 0x0d || address == 0x0e || address == 0x0f)
+ break;
+
+ quint16 lock = (readData(0x0e, 0x01) << 8) | readData(0x0e, 0x00);
+ if (address <= 0x0e && ((0x01 << address) & lock)) // locked blocks
+ break;
+
+ // FIXME: Test dynamic lock bytes
+
+ for (int i = 0; i < 8; ++i)
+ memory[address * 8 + i] = memory.at(address * 8 + i) | data.at(i);
+
+ response.append(address);
+ response.append(memory.mid(address * 8, 8));
+ break;
+ }
+ }
+
+ if (!response.isEmpty()) {
+ quint16 crc = qChecksum(QByteArrayView(response.constData(), response.length()), Qt::ChecksumItuV41);
+ response.append(quint8(crc & 0xff));
+ response.append(quint8(crc >> 8));
+ }
+
+ return response;
+}
+
+
+NfcTagType2::NfcTagType2()
+: memory(64, 0x00), currentSector(0), expectPacket2(false)
+{
+}
+
+NfcTagType2::~NfcTagType2()
+{
+}
+
+void NfcTagType2::load(QSettings *settings)
+{
+ settings->beginGroup(QStringLiteral("TagType2"));
+
+ memory = settings->value(QStringLiteral("Data")).toByteArray();
+
+ settings->endGroup();
+}
+
+QByteArray NfcTagType2::uid() const
+{
+ lastAccess = QDateTime::currentMSecsSinceEpoch();
+
+ return memory.left(3) + memory.mid(4, 4);
+}
+
+#define NACK QByteArray("\x05")
+#define ACK QByteArray("\x0a")
+
+QByteArray NfcTagType2::processCommand(const QByteArray &command)
+{
+ lastAccess = QDateTime::currentMSecsSinceEpoch();
+
+ QByteArray response;
+
+ // check checksum
+ if (qChecksum(QByteArrayView(command.constData(), command.length()), Qt::ChecksumItuV41) != 0)
+ return QByteArray();
+
+ if (expectPacket2) {
+ expectPacket2 = false;
+ quint8 sector = command.at(0);
+ if (sector * 1024 > memory.length())
+ return NACK;
+ else {
+ currentSector = sector;
+ return QByteArray();
+ }
+ }
+
+ quint8 opcode = command.at(0);
+
+ switch (opcode) {
+ case 0x30: { // READ BLOCK
+ quint8 block = command.at(1);
+ int absoluteBlock = currentSector * 256 + block;
+
+ response.append(memory.mid(absoluteBlock * 4, 16));
+ if (response.length() != 16)
+ response.append(QByteArray(16 - response.length(), '\0'));
+
+ break;
+ }
+ case 0xa2: { // WRITE BLOCK
+ quint8 block = command.at(1);
+ int absoluteBlock = currentSector * 256 + block;
+
+ // locked blocks
+ if (absoluteBlock == 0 || absoluteBlock == 1)
+ return NACK;
+
+ const QByteArray data = command.mid(2, 4);
+
+ memory.replace(absoluteBlock * 4, 4, data);
+
+ return ACK;
+ }
+ case 0xc2: // SECTOR SELECT - Packet 1
+ if (memory.length() > 1024) {
+ expectPacket2 = true;
+ return ACK;
+ }
+
+ return NACK;
+ default:
+ qDebug() << "Unknown opcode for Tag Type 2" << Qt::hex << opcode;
+ qDebug() << "command:" << command.toHex();
+
+ return NACK;
+ ;
+ }
+
+ if (!response.isEmpty()) {
+ quint16 crc = qChecksum(QByteArrayView(response.constData(), response.length()), Qt::ChecksumItuV41);
+ response.append(quint8(crc & 0xff));
+ response.append(quint8(crc >> 8));
+ }
+
+ return response;
+}
+
+QT_END_NAMESPACE
diff --git a/tests/auto/nfccommons/targetemulator_p.h b/tests/auto/nfccommons/targetemulator_p.h
new file mode 100644
index 00000000..cd35e036
--- /dev/null
+++ b/tests/auto/nfccommons/targetemulator_p.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNfc module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TARGETEMULATOR_P_H
+#define TARGETEMULATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QtGlobal>
+#include <QtCore/QByteArray>
+#include <QtNfc/qtnfcglobal.h>
+#include <QMetaType>
+
+QT_FORWARD_DECLARE_CLASS(QSettings)
+
+QT_BEGIN_NAMESPACE
+
+class TagBase
+{
+public:
+ TagBase();
+ virtual ~TagBase();
+
+ virtual void load(QSettings *settings) = 0;
+
+ virtual QByteArray processCommand(const QByteArray &command) = 0;
+
+ virtual QByteArray uid() const = 0;
+
+ qint64 lastAccessTime() const { return lastAccess; }
+
+protected:
+ mutable qint64 lastAccess;
+};
+
+class NfcTagType1 : public TagBase
+{
+public:
+ NfcTagType1();
+ ~NfcTagType1();
+
+ void load(QSettings *settings) override;
+
+ QByteArray processCommand(const QByteArray &command) override;
+
+ QByteArray uid() const override;
+
+private:
+ quint8 readData(quint8 block, quint8 byte);
+
+ quint8 hr0;
+ quint8 hr1;
+
+ QByteArray memory;
+};
+
+class NfcTagType2 : public TagBase
+{
+public:
+ NfcTagType2();
+ ~NfcTagType2();
+
+ void load(QSettings *settings) override;
+
+ QByteArray processCommand(const QByteArray &command) override;
+
+ QByteArray uid() const override;
+
+private:
+ QByteArray memory;
+ quint8 currentSector;
+ bool expectPacket2;
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(TagBase *)
+
+#endif // TARGETEMULATOR_P_H
diff --git a/tests/auto/qnearfieldmanager/CMakeLists.txt b/tests/auto/qnearfieldmanager/CMakeLists.txt
index 14bb41f2..af94db09 100644
--- a/tests/auto/qnearfieldmanager/CMakeLists.txt
+++ b/tests/auto/qnearfieldmanager/CMakeLists.txt
@@ -12,14 +12,17 @@ list(APPEND test_data "../nfcdata/Qt Website Tag Type1.nfc")
qt_internal_add_test(tst_qnearfieldmanager
SOURCES
- ../../../src/nfc/qnearfieldmanager_emulator.cpp ../../../src/nfc/qnearfieldmanager_emulator_p.h
- ../../../src/nfc/qnearfieldtarget_emulator.cpp ../../../src/nfc/qnearfieldtarget_emulator_p.h
- ../../../src/nfc/targetemulator.cpp ../../../src/nfc/targetemulator_p.h
+ ../nfccommons/qnearfieldmanager_emulator.cpp ../nfccommons/qnearfieldmanager_emulator_p.h
+ ../nfccommons/qnearfieldtarget_emulator.cpp ../nfccommons/qnearfieldtarget_emulator_p.h
+ ../nfccommons/targetemulator.cpp ../nfccommons/targetemulator_p.h
+ ../nfccommons/qnearfieldtagtype1.cpp ../nfccommons/qnearfieldtagtype1_p.h
+ ../nfccommons/qnearfieldtagtype2.cpp ../nfccommons/qnearfieldtagtype2_p.h
+ ../nfccommons/qtlv.cpp ../nfccommons/qtlv_p.h
tst_qnearfieldmanager.cpp
DEFINES
SRCDIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../nfcdata\\\"
INCLUDE_DIRECTORIES
- ../../../src/nfc
+ ../nfccommons
PUBLIC_LIBRARIES
Qt::NfcPrivate
TESTDATA ${test_data}
diff --git a/tests/auto/qnearfieldmanager/tst_qnearfieldmanager.cpp b/tests/auto/qnearfieldmanager/tst_qnearfieldmanager.cpp
index ce8224ef..09bda66a 100644
--- a/tests/auto/qnearfieldmanager/tst_qnearfieldmanager.cpp
+++ b/tests/auto/qnearfieldmanager/tst_qnearfieldmanager.cpp
@@ -28,12 +28,12 @@
#include <QtTest/QtTest>
-#include <private/qnearfieldmanager_emulator_p.h>
-#include <qnearfieldmanager.h>
-#include <qndefnfctextrecord.h>
-#include <qndefnfcurirecord.h>
-#include <qndefmessage.h>
-#include <qndefrecord.h>
+#include <qnearfieldmanager_emulator_p.h>
+#include <QtNfc/qnearfieldmanager.h>
+#include <QtNfc/qndefnfctextrecord.h>
+#include <QtNfc/qndefnfcurirecord.h>
+#include <QtNfc/qndefmessage.h>
+#include <QtNfc/qndefrecord.h>
QT_USE_NAMESPACE
diff --git a/tests/auto/qnearfieldtagtype1/CMakeLists.txt b/tests/auto/qnearfieldtagtype1/CMakeLists.txt
index 00254f70..744a9504 100644
--- a/tests/auto/qnearfieldtagtype1/CMakeLists.txt
+++ b/tests/auto/qnearfieldtagtype1/CMakeLists.txt
@@ -12,14 +12,16 @@ list(APPEND test_data "../nfcdata/Qt Website Tag Type1.nfc")
qt_internal_add_test(tst_qnearfieldtagtype1
SOURCES
- ../../../src/nfc/qnearfieldmanager_emulator.cpp ../../../src/nfc/qnearfieldmanager_emulator_p.h
- ../../../src/nfc/qnearfieldtarget_emulator.cpp ../../../src/nfc/qnearfieldtarget_emulator_p.h
- ../../../src/nfc/targetemulator.cpp ../../../src/nfc/targetemulator_p.h
+ ../nfccommons/qnearfieldtarget_emulator.cpp ../nfccommons/qnearfieldtarget_emulator_p.h
+ ../nfccommons/targetemulator.cpp ../nfccommons/targetemulator_p.h
+ ../nfccommons/qnearfieldtagtype1.cpp ../nfccommons/qnearfieldtagtype1_p.h
+ ../nfccommons/qnearfieldtagtype2.cpp ../nfccommons/qnearfieldtagtype2_p.h
+ ../nfccommons/qtlv.cpp ../nfccommons/qtlv_p.h
tst_qnearfieldtagtype1.cpp
DEFINES
SRCDIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/../nfcdata\\\"
INCLUDE_DIRECTORIES
- ../../../src/nfc
+ ../nfccommons
PUBLIC_LIBRARIES
Qt::NfcPrivate
TESTDATA ${test_data}
diff --git a/tests/auto/qnearfieldtagtype1/tst_qnearfieldtagtype1.cpp b/tests/auto/qnearfieldtagtype1/tst_qnearfieldtagtype1.cpp
index 42a62b7a..1fff7574 100644
--- a/tests/auto/qnearfieldtagtype1/tst_qnearfieldtagtype1.cpp
+++ b/tests/auto/qnearfieldtagtype1/tst_qnearfieldtagtype1.cpp
@@ -28,10 +28,11 @@
#include <QtTest/QtTest>
-#include <private/qnearfieldtarget_emulator_p.h>
-#include <qndefmessage.h>
-#include <private/qnearfieldtagtype1_p.h>
-#include <qndefnfctextrecord.h>
+#include <qnearfieldtarget_emulator_p.h>
+#include <qnearfieldtagtype1_p.h>
+
+#include <QtNfc/qndefmessage.h>
+#include <QtNfc/qndefnfctextrecord.h>
QT_USE_NAMESPACE
diff --git a/tests/auto/qnearfieldtagtype2/CMakeLists.txt b/tests/auto/qnearfieldtagtype2/CMakeLists.txt
index c07238ef..63f28de7 100644
--- a/tests/auto/qnearfieldtagtype2/CMakeLists.txt
+++ b/tests/auto/qnearfieldtagtype2/CMakeLists.txt
@@ -12,14 +12,16 @@ list(APPEND test_data "nfcdata/Empty Tag.nfc")
qt_internal_add_test(tst_qnearfieldtagtype2
SOURCES
- ../../../src/nfc/qnearfieldmanager_emulator.cpp ../../../src/nfc/qnearfieldmanager_emulator_p.h
- ../../../src/nfc/qnearfieldtarget_emulator.cpp ../../../src/nfc/qnearfieldtarget_emulator_p.h
- ../../../src/nfc/targetemulator.cpp ../../../src/nfc/targetemulator_p.h
+ ../nfccommons/qnearfieldtarget_emulator.cpp ../nfccommons/qnearfieldtarget_emulator_p.h
+ ../nfccommons/targetemulator.cpp ../nfccommons/targetemulator_p.h
+ ../nfccommons/qnearfieldtagtype1.cpp ../nfccommons/qnearfieldtagtype1_p.h
+ ../nfccommons/qnearfieldtagtype2.cpp ../nfccommons/qnearfieldtagtype2_p.h
+ ../nfccommons/qtlv.cpp ../nfccommons/qtlv_p.h
tst_qnearfieldtagtype2.cpp
DEFINES
SRCDIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/nfcdata\\\"
INCLUDE_DIRECTORIES
- ../../../src/nfc
+ ../nfccommons
PUBLIC_LIBRARIES
Qt::NfcPrivate
TESTDATA ${test_data}
diff --git a/tests/auto/qnearfieldtagtype2/tst_qnearfieldtagtype2.cpp b/tests/auto/qnearfieldtagtype2/tst_qnearfieldtagtype2.cpp
index 4ea9bc42..2147763c 100644
--- a/tests/auto/qnearfieldtagtype2/tst_qnearfieldtagtype2.cpp
+++ b/tests/auto/qnearfieldtagtype2/tst_qnearfieldtagtype2.cpp
@@ -28,10 +28,10 @@
#include <QtTest/QtTest>
-#include <private/qnearfieldtarget_emulator_p.h>
-#include <qndefmessage.h>
-#include <private/qnearfieldtagtype2_p.h>
-#include <qndefnfctextrecord.h>
+#include <qnearfieldtarget_emulator_p.h>
+#include <qnearfieldtagtype2_p.h>
+#include <QtNfc/qndefmessage.h>
+#include <QtNfc/qndefnfctextrecord.h>
QT_USE_NAMESPACE