1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtNfc module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "targetemulator_p.h"
41
42#include <QtCore/QSettings>
43#include <QtCore/QDateTime>
44
45#include <QtCore/QDebug>
46
47QT_BEGIN_NAMESPACE
48
49TagBase::TagBase()
50: lastAccess(0)
51{
52}
53
54TagBase::~TagBase()
55{
56}
57
58static inline quint8 blockByteToAddress(quint8 block, quint8 byte)
59{
60 return ((block & 0x0f) << 3) | (byte & 0x07);
61}
62
63NfcTagType1::NfcTagType1()
64: hr0(0x11), hr1(0x00), memory(120, '\0')
65{
66 // Locked blocks
67 memory[(0x0e << 3) | 0x00] = 0x01;
68 memory[(0x0e << 3) | 0x01] = 0x60;
69}
70
71NfcTagType1::~NfcTagType1()
72{
73}
74
75void NfcTagType1::load(QSettings *settings)
76{
77 settings->beginGroup(QStringLiteral("TagType1"));
78
79 hr0 = settings->value(QStringLiteral("HR0"), defaultValue: 0x11).toUInt();
80
81 if (!(hr0 & 0x10)) {
82 settings->endGroup();
83 return;
84 }
85
86 hr1 = settings->value(QStringLiteral("HR1"), defaultValue: 0x00).toUInt();
87
88 memory = settings->value(QStringLiteral("Data")).toByteArray();
89
90 //quint8 nmn = memory.at(8);
91
92 quint8 vno = memory.at(i: 9);
93 if (vno != 0x10)
94 qWarning(msg: "Only NFC TagType1 v1.0 behavior is supported.");
95
96 quint8 tms = memory.at(i: 10);
97 if (memory.length() != 8 * (tms + 1))
98 qWarning(msg: "Static memory size does not match TMS value.");
99
100 quint8 rwa = memory.at(i: 11);
101 switch (rwa >> 4) {
102 case 0:
103 // Unrestricted read access tag
104 break;
105 default:
106 // tag with unknown read attributes
107 ;
108 }
109
110 switch (rwa & 0x0f) {
111 case 0:
112 // Unrestricted write access tag
113 break;
114 case 0x0f:
115 // Read only tag
116 break;
117 default:
118 // tag with unknown write attributes
119 ;
120 }
121
122 //quint16 lock = (quint8(memory[blockByteToAddress(0x0e, 1)]) << 8) |
123 // quint8(memory[blockByteToAddress(0x0e, 0)]);
124
125 settings->endGroup();
126}
127
128QByteArray NfcTagType1::uid() const
129{
130 lastAccess = QDateTime::currentMSecsSinceEpoch();
131
132 return memory.left(len: 7);
133}
134
135quint8 NfcTagType1::readData(quint8 block, quint8 byte)
136{
137 return memory.at(i: (block << 3) | byte);
138}
139
140QByteArray NfcTagType1::processCommand(const QByteArray &command)
141{
142 lastAccess = QDateTime::currentMSecsSinceEpoch();
143
144 QByteArray response;
145
146 bool tagType1 = (hr0 & 0xf0) == 0x10;
147 bool dynamic = (hr0 & 0x0f) != 0x01;
148
149 if (command.length() == 9) {
150 // static memory model command
151 quint8 opcode = command.at(i: 0);
152 quint8 address = command.at(i: 1);
153 quint8 data = command.at(i: 2);
154 QByteArray uid = command.mid(index: 3, len: 4);
155
156 // check checksum
157 if (qChecksum(s: command.constData(), len: command.length(), standard: Qt::ChecksumItuV41) != 0)
158 return QByteArray();
159
160 // check UID
161 if (uid != memory.left(len: 4))
162 return QByteArray();
163
164 switch (opcode) {
165 case 0x00: // RALL
166 response.append(c: hr0);
167 response.append(c: hr1);
168 response.append(a: memory.left(len: 120));
169 break;
170 case 0x01: // READ
171 response.append(c: address);
172 if (address & 0x80)
173 response.append(c: char(0x00));
174 else
175 response.append(c: memory.at(i: address));
176 break;
177 case 0x53: { // WRITE-E
178 quint8 block = address >> 3;
179 if (block == 0x00 || block == 0x0d || block == 0x0e) // locked blocks
180 break;
181
182 quint16 lock = (readData(block: 0x0e, byte: 0x01) << 8) | readData(block: 0x0e, byte: 0x00);
183 if ((0x01 << block) & lock) // locked blocks
184 break;
185
186 // FIXME: Test dynamic lock bytes
187
188 memory[address] = data;
189
190 response.append(c: address);
191 response.append(c: data);
192 break;
193 }
194 case 0x1a: { // WRITE-NE
195 quint8 block = address >> 3;
196 if (block == 0x00 || block == 0x0d) // locked blocks
197 break;
198
199 quint16 lock = (readData(block: 0x0e, byte: 0x01) << 8) | readData(block: 0x0e, byte: 0x00);
200 if ((0x01 << block) & lock) // locked blocks
201 break;
202
203
204 // FIXME: Test dynamic lock bytes
205
206 memory[address] = memory.at(i: address) | data;
207
208 response.append(c: address);
209 response.append(c: memory.at(i: address));
210 break;
211 }
212 case 0x78: // RID
213 response.append(c: hr0);
214 response.append(c: hr1);
215 response.append(a: memory.left(len: 4));
216 break;
217 }
218 } else if (tagType1 && dynamic && command.length() == 16) {
219 // dynamic memory model command
220 quint8 opcode = command.at(i: 0);
221 quint8 address = command.at(i: 1);
222 QByteArray data = command.mid(index: 2, len: 8);
223 QByteArray uid = command.mid(index: 10, len: 4);
224
225 // check checksum
226 if (qChecksum(s: command.constData(), len: command.length(), standard: Qt::ChecksumItuV41) != 0)
227 return QByteArray();
228
229 // check UID
230 if (uid != memory.left(len: 4))
231 return QByteArray();
232
233 switch (opcode) {
234 case 0x10: // RSEG
235 response.append(c: address);
236 response.append(a: memory.mid(index: 128 * (address >> 4), len: 128));
237 break;
238 case 0x02: // READ8
239 response.append(c: address);
240 response.append(a: memory.mid(index: 8 * address, len: 8));
241 break;
242 case 0x54: { // WRITE-E8
243 // locked blocks
244 if (address == 0x00 || address == 0x0d || address == 0x0e || address == 0x0f)
245 break;
246
247 quint16 lock = (readData(block: 0x0e, byte: 0x01) << 8) | readData(block: 0x0e, byte: 0x00);
248 if (address <= 0x0e && ((0x01 << address) & lock)) // locked blocks
249 break;
250
251 // FIXME: Test dynamic lock bytes
252
253 memory.replace(index: address * 8, len: 8, s: data);
254
255 response.append(c: address);
256 response.append(a: memory.mid(index: address * 8, len: 8));
257 break;
258 }
259 case 0x1b: // WRITE-NE8
260 // locked blocks
261 if (address == 0x00 || address == 0x0d || address == 0x0e || address == 0x0f)
262 break;
263
264 quint16 lock = (readData(block: 0x0e, byte: 0x01) << 8) | readData(block: 0x0e, byte: 0x00);
265 if (address <= 0x0e && ((0x01 << address) & lock)) // locked blocks
266 break;
267
268 // FIXME: Test dynamic lock bytes
269
270 for (int i = 0; i < 8; ++i)
271 memory[address * 8 + i] = memory.at(i: address * 8 + i) | data.at(i);
272
273 response.append(c: address);
274 response.append(a: memory.mid(index: address * 8, len: 8));
275 break;
276 }
277 }
278
279 if (!response.isEmpty()) {
280 quint16 crc = qChecksum(s: response.constData(), len: response.length(), standard: Qt::ChecksumItuV41);
281 response.append(c: quint8(crc & 0xff));
282 response.append(c: quint8(crc >> 8));
283 }
284
285 return response;
286}
287
288
289NfcTagType2::NfcTagType2()
290: memory(64, 0x00), currentSector(0), expectPacket2(false)
291{
292}
293
294NfcTagType2::~NfcTagType2()
295{
296}
297
298void NfcTagType2::load(QSettings *settings)
299{
300 settings->beginGroup(QStringLiteral("TagType2"));
301
302 memory = settings->value(QStringLiteral("Data")).toByteArray();
303
304 settings->endGroup();
305}
306
307QByteArray NfcTagType2::uid() const
308{
309 lastAccess = QDateTime::currentMSecsSinceEpoch();
310
311 return memory.left(len: 3) + memory.mid(index: 4, len: 4);
312}
313
314#define NACK QByteArray("\x05")
315#define ACK QByteArray("\x0a")
316
317QByteArray NfcTagType2::processCommand(const QByteArray &command)
318{
319 lastAccess = QDateTime::currentMSecsSinceEpoch();
320
321 QByteArray response;
322
323 // check checksum
324 if (qChecksum(s: command.constData(), len: command.length(), standard: Qt::ChecksumItuV41) != 0)
325 return QByteArray();
326
327 if (expectPacket2) {
328 expectPacket2 = false;
329 quint8 sector = command.at(i: 0);
330 if (sector * 1024 > memory.length())
331 return NACK;
332 else {
333 currentSector = sector;
334 return QByteArray();
335 }
336 }
337
338 quint8 opcode = command.at(i: 0);
339
340 switch (opcode) {
341 case 0x30: { // READ BLOCK
342 quint8 block = command.at(i: 1);
343 int absoluteBlock = currentSector * 256 + block;
344
345 response.append(a: memory.mid(index: absoluteBlock * 4, len: 16));
346 if (response.length() != 16)
347 response.append(a: QByteArray(16 - response.length(), '\0'));
348
349 break;
350 }
351 case 0xa2: { // WRITE BLOCK
352 quint8 block = command.at(i: 1);
353 int absoluteBlock = currentSector * 256 + block;
354
355 // locked blocks
356 if (absoluteBlock == 0 || absoluteBlock == 1)
357 return NACK;
358
359 const QByteArray data = command.mid(index: 2, len: 4);
360
361 memory.replace(index: absoluteBlock * 4, len: 4, s: data);
362
363 return ACK;
364 }
365 case 0xc2: // SECTOR SELECT - Packet 1
366 if (memory.length() > 1024) {
367 expectPacket2 = true;
368 return ACK;
369 }
370
371 return NACK;
372 default:
373 qDebug() << "Unknown opcode for Tag Type 2" << hex << opcode;
374 qDebug() << "command:" << command.toHex();
375
376 return NACK;
377 ;
378 }
379
380 if (!response.isEmpty()) {
381 quint16 crc = qChecksum(s: response.constData(), len: response.length(), standard: Qt::ChecksumItuV41);
382 response.append(c: quint8(crc & 0xff));
383 response.append(c: quint8(crc >> 8));
384 }
385
386 return response;
387}
388
389QT_END_NAMESPACE
390

source code of qtconnectivity/src/nfc/targetemulator.cpp