summaryrefslogtreecommitdiffstats
path: root/src/nfc/targetemulator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/nfc/targetemulator.cpp')
-rw-r--r--src/nfc/targetemulator.cpp382
1 files changed, 382 insertions, 0 deletions
diff --git a/src/nfc/targetemulator.cpp b/src/nfc/targetemulator.cpp
new file mode 100644
index 00000000..7d77aa17
--- /dev/null
+++ b/src/nfc/targetemulator.cpp
@@ -0,0 +1,382 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "targetemulator_p.h"
+
+#include <QtCore/QSettings>
+#include <QtCore/QDateTime>
+
+#include <QtCore/QDebug>
+
+// Implementation of qNfcChecksum
+#include "checksum_p.h"
+
+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;
+}
+
+void NfcTagType1::load(QSettings *settings)
+{
+ settings->beginGroup(QLatin1String("TagType1"));
+
+ hr0 = settings->value(QLatin1String("HR0"), 0x11).toUInt();
+
+ if (!(hr0 & 0x10)) {
+ settings->endGroup();
+ return;
+ }
+
+ hr1 = settings->value(QLatin1String("HR1"), 0x00).toUInt();
+
+ memory = settings->value(QLatin1String("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 (qNfcChecksum(command.constData(), command.length()) != 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 (qNfcChecksum(command.constData(), command.length()) != 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 = qNfcChecksum(response.constData(), response.length());
+ response.append(quint8(crc & 0xff));
+ response.append(quint8(crc >> 8));
+ }
+
+ return response;
+}
+
+
+NfcTagType2::NfcTagType2()
+: memory(64, 0x00), currentSector(0), expectPacket2(false)
+{
+}
+
+void NfcTagType2::load(QSettings *settings)
+{
+ settings->beginGroup(QLatin1String("TagType2"));
+
+ memory = settings->value(QLatin1String("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 (qNfcChecksum(command.constData(), command.length()) != 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" << hex << opcode;
+ qDebug() << "command:" << command.toHex();
+
+ return NACK;
+ ;
+ }
+
+ if (!response.isEmpty()) {
+ quint16 crc = qNfcChecksum(response.constData(), response.length());
+ response.append(quint8(crc & 0xff));
+ response.append(quint8(crc >> 8));
+ }
+
+ return response;
+}