blob: 80555593e45ebff13af0367587958d6fa2677b17 [file] [log] [blame]
/****************************************************************************
**
** 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(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(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(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(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" << hex << opcode;
qDebug() << "command:" << command.toHex();
return NACK;
;
}
if (!response.isEmpty()) {
quint16 crc = qChecksum(response.constData(), response.length(), Qt::ChecksumItuV41);
response.append(quint8(crc & 0xff));
response.append(quint8(crc >> 8));
}
return response;
}
QT_END_NAMESPACE