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 "qtlv_p.h"
41
42#include "qnearfieldtagtype1_p.h"
43
44#include <QtCore/QVariant>
45
46#include <QtCore/QDebug>
47
48QT_BEGIN_NAMESPACE
49
50QPair<int, int> qParseReservedMemoryControlTlv(const QByteArray &tlvData)
51{
52 quint8 position = tlvData.at(i: 0);
53 int pageAddr = position >> 4;
54 int byteOffset = position & 0x0f;
55
56 int size = quint8(tlvData.at(i: 1));
57 if (size == 0)
58 size = 256;
59
60 quint8 pageControl = tlvData.at(i: 2);
61 int bytesPerPage = pageControl & 0x0f;
62
63 if (!bytesPerPage)
64 return qMakePair(x: 0, y: 0);
65
66 int byteAddress = pageAddr * (1 << bytesPerPage) + byteOffset;
67 return qMakePair(x: byteAddress, y: size);
68}
69
70QPair<int, int> qParseLockControlTlv(const QByteArray &tlvData)
71{
72 quint8 position = tlvData.at(i: 0);
73 int pageAddr = position >> 4;
74 int byteOffset = position & 0x0f;
75
76 int size = quint8(tlvData.at(i: 1));
77 if (size == 0)
78 size = 256;
79 size = size / 8;
80
81 quint8 pageControl = tlvData.at(i: 2);
82 int bytesPerPage = pageControl & 0x0f;
83
84 if (!bytesPerPage)
85 return qMakePair(x: 0, y: 0);
86
87 int byteAddress = pageAddr * (1 << bytesPerPage) + byteOffset;
88 return qMakePair(x: byteAddress, y: size);
89}
90
91QTlvReader::QTlvReader(QNearFieldTarget *target)
92: m_target(target), m_index(-1)
93{
94 if (qobject_cast<QNearFieldTagType1 *>(object: m_target)) {
95 addReservedMemory(offset: 0, length: 12); // skip uid, cc
96 addReservedMemory(offset: 104, length: 16); // skip reserved block D, lock block E
97
98 addReservedMemory(offset: 120, length: 8); // skip reserved block F
99 }
100}
101
102QTlvReader::QTlvReader(const QByteArray &data)
103: m_target(0), m_rawData(data), m_index(-1)
104{
105}
106
107void QTlvReader::addReservedMemory(int offset, int length)
108{
109 m_reservedMemory.insert(akey: offset, avalue: length);
110}
111
112/*!
113 Returns the number of bytes of reserved memory found so far. The actual number of reserved
114 bytes will not be known until atEnd() returns true.
115*/
116int QTlvReader::reservedMemorySize() const
117{
118 int total = 0;
119
120 QMap<int, int>::ConstIterator i;
121 for (i = m_reservedMemory.constBegin(); i != m_reservedMemory.constEnd(); ++i)
122 total += i.value();
123
124 return total;
125}
126
127/*!
128 Returns the request id that the TLV reader is currently waiting on.
129*/
130QNearFieldTarget::RequestId QTlvReader::requestId() const
131{
132 return m_requestId;
133}
134
135bool QTlvReader::atEnd() const
136{
137 if (m_index == -1)
138 return false;
139
140 if (m_requestId.isValid())
141 return false;
142
143 return (m_index == m_tlvData.length()) || (tag() == 0xfe);
144}
145
146/*!
147 Moves to the next TLV. Returns true on success; otherwise returns false.
148*/
149bool QTlvReader::readNext()
150{
151 if (atEnd())
152 return false;
153
154 // Move to next TLV
155 if (m_index == -1) {
156 m_index = 0;
157 } else if (m_requestId.isValid()) {
158 // do nothing
159 } else if (tag() == 0x00 || tag() == 0xfe) {
160 ++m_index;
161 } else {
162 int tlvLength = length();
163 m_index += (tlvLength < 0xff) ? tlvLength + 2 : tlvLength + 4;
164 }
165
166 // Ensure that tag byte is available
167 if (!readMoreData(sparseOffset: m_index))
168 return false;
169
170 // Ensure that length byte(s) are available
171 if (length() == -1)
172 return false;
173
174 // Ensure that data bytes are available
175 int tlvLength = length();
176
177 int dataOffset = (tlvLength < 0xff) ? m_index + 2 : m_index + 4;
178
179 if (!readMoreData(sparseOffset: dataOffset + tlvLength - 1))
180 return false;
181
182 switch (tag()) {
183 case 0x01: { // Lock Control TLV
184 QPair<int, int> locked = qParseLockControlTlv(tlvData: data());
185 addReservedMemory(offset: locked.first, length: locked.second);
186 break;
187 }
188 case 0x02: { // Reserved Memory Control TLV
189 QPair<int, int> reserved = qParseReservedMemoryControlTlv(tlvData: data());
190 addReservedMemory(offset: reserved.first, length: reserved.second);
191 break;
192 }
193 }
194
195 return true;
196}
197
198quint8 QTlvReader::tag() const
199{
200 return m_tlvData.at(i: m_index);
201}
202
203int QTlvReader::length()
204{
205 if (tag() == 0x00 || tag() == 0xfe)
206 return 0;
207
208 if (!readMoreData(sparseOffset: m_index + 1))
209 return -1;
210
211 quint8 shortLength = m_tlvData.at(i: m_index + 1);
212 if (shortLength != 0xff)
213 return shortLength;
214
215 if (!readMoreData(sparseOffset: m_index + 3))
216 return -1;
217
218 quint16 longLength = (quint8(m_tlvData.at(i: m_index + 2)) << 8) |
219 quint8(m_tlvData.at(i: m_index + 3));
220
221 if (longLength < 0xff || longLength == 0xffff) {
222 qWarning(msg: "Invalid 3 byte length");
223 return 0;
224 }
225
226 return longLength;
227}
228
229QByteArray QTlvReader::data()
230{
231 int tlvLength = length();
232
233 int dataOffset = (tlvLength < 0xff) ? m_index + 2 : m_index + 4;
234
235 if (!readMoreData(sparseOffset: dataOffset + tlvLength - 1))
236 return QByteArray();
237
238 return m_tlvData.mid(index: dataOffset, len: tlvLength);
239}
240
241bool QTlvReader::readMoreData(int sparseOffset)
242{
243 while (sparseOffset >= m_tlvData.length()) {
244 int absOffset = absoluteOffset(sparseOffset: m_tlvData.length());
245
246 QByteArray data;
247
248 if (!m_rawData.isEmpty()) {
249 data = m_rawData.mid(index: absOffset, len: dataLength(startOffset: absOffset));
250 } else if (QNearFieldTagType1 *tag = qobject_cast<QNearFieldTagType1 *>(object: m_target)) {
251 quint8 segment = absOffset / 128;
252
253 if (m_requestId.isValid()) {
254 QVariant v = m_target->requestResponse(id: m_requestId);
255 if (!v.isValid())
256 return false;
257
258 m_requestId = QNearFieldTarget::RequestId();
259
260 data = v.toByteArray();
261
262 if (absOffset < 120)
263 data = data.mid(index: 2);
264
265 int length = dataLength(startOffset: absOffset);
266
267 data = data.mid(index: absOffset - (segment * 128), len: length);
268 } else {
269 m_requestId = (absOffset < 120) ? tag->readAll() : tag->readSegment(segmentAddress: segment);
270
271 return false;
272 }
273 }
274
275 if (data.isEmpty() && sparseOffset >= m_tlvData.length())
276 return false;
277
278 m_tlvData.append(a: data);
279 }
280
281 return true;
282}
283
284int QTlvReader::absoluteOffset(int sparseOffset) const
285{
286 int absoluteOffset = sparseOffset;
287 const QList<int> offsets = m_reservedMemory.keys();
288 for (const int offset : offsets) {
289 if (offset <= absoluteOffset)
290 absoluteOffset += m_reservedMemory.value(akey: offset);
291 }
292
293 return absoluteOffset;
294}
295
296/*!
297 Returns the length of the contiguous non-reserved data block starting from absolute offset
298 \a startOffset. -1 is return as the length of the last contiguous data block.
299*/
300int QTlvReader::dataLength(int startOffset) const
301{
302 const QList<int> offsets = m_reservedMemory.keys();
303 for (const int offset : offsets) {
304 if (offset <= startOffset)
305 continue;
306
307 return offset - startOffset;
308 }
309
310 return -1;
311}
312
313
314QTlvWriter::QTlvWriter(QNearFieldTarget *target)
315: m_target(target), m_rawData(0), m_index(0), m_tagMemorySize(-1)
316{
317 if (qobject_cast<QNearFieldTagType1 *>(object: m_target)) {
318 addReservedMemory(offset: 0, length: 12); // skip uid, cc
319 addReservedMemory(offset: 104, length: 16); // skip reserved block D, lock block E
320
321 addReservedMemory(offset: 120, length: 8); // skip reserved block F
322 }
323}
324
325QTlvWriter::QTlvWriter(QByteArray *data)
326: m_target(0), m_rawData(data), m_index(0), m_tagMemorySize(-1)
327{
328}
329
330QTlvWriter::~QTlvWriter()
331{
332 if (m_rawData)
333 process(all: true);
334}
335
336void QTlvWriter::addReservedMemory(int offset, int length)
337{
338 m_reservedMemory.insert(akey: offset, avalue: length);
339}
340
341void QTlvWriter::writeTlv(quint8 tagType, const QByteArray &data)
342{
343 m_buffer.append(c: tagType);
344
345 if (tagType != 0x00 && tagType != 0xfe) {
346 int length = data.length();
347 if (length < 0xff) {
348 m_buffer.append(c: quint8(length));
349 } else {
350 m_buffer.append(c: char(0xff));
351 m_buffer.append(c: quint16(length) >> 8);
352 m_buffer.append(c: quint16(length) & 0x00ff);
353 }
354
355 m_buffer.append(a: data);
356 }
357
358 process();
359
360 switch (tagType) {
361 case 0x01: { // Lock Control TLV
362 QPair<int, int> locked = qParseLockControlTlv(tlvData: data);
363 addReservedMemory(offset: locked.first, length: locked.second);
364 break;
365 }
366 case 0x02: { // Reserved Memory Control TLV
367 QPair<int, int> reserved = qParseReservedMemoryControlTlv(tlvData: data);
368 addReservedMemory(offset: reserved.first, length: reserved.second);
369 break;
370 }
371 }
372}
373
374/*!
375 Processes more of the TLV writer process. Returns true if the TLVs have been successfully
376 written to the target or buffer; otherwise returns false.
377
378 A false return value indicates that an NFC request is pending (if requestId() returns a valid
379 request identifier) or the write process has failed (requestId() returns an invalid request
380 identifier).
381*/
382bool QTlvWriter::process(bool all)
383{
384 if (m_requestId.isValid()) {
385 QVariant v = m_target->requestResponse(id: m_requestId);
386 if (!v.isValid())
387 return false;
388 }
389
390 if (m_tagMemorySize == -1) {
391 if (m_rawData)
392 m_tagMemorySize = m_rawData->length();
393 else if (QNearFieldTagType1 *tag = qobject_cast<QNearFieldTagType1 *>(object: m_target)) {
394 if (m_requestId.isValid()) {
395 m_tagMemorySize = 8 * (tag->requestResponse(id: m_requestId).toUInt() + 1);
396 m_requestId = QNearFieldTarget::RequestId();
397 } else {
398 m_requestId = tag->readByte(address: 10);
399 return false;
400 }
401 }
402 }
403
404 while (!m_buffer.isEmpty()) {
405 int spaceRemaining = moveToNextAvailable();
406 if (spaceRemaining < 1)
407 return false;
408
409 int length = qMin(a: spaceRemaining, b: m_buffer.length());
410
411 if (m_rawData) {
412 m_rawData->replace(index: m_index, len: length, s: m_buffer);
413 m_index += length;
414 m_buffer = m_buffer.mid(index: length);
415 } else if (QNearFieldTagType1 *tag = qobject_cast<QNearFieldTagType1 *>(object: m_target)) {
416 int bufferIndex = 0;
417
418 // static memory - can only use writeByte()
419 while (m_index < 120 && bufferIndex < length) {
420 if (m_requestId.isValid()) {
421 if (!m_target->requestResponse(id: m_requestId).toBool())
422 return false;
423
424 m_requestId = QNearFieldTarget::RequestId();
425
426 ++m_index;
427 ++bufferIndex;
428 } else {
429 m_requestId = tag->writeByte(address: m_index, data: m_buffer.at(i: bufferIndex));
430 m_buffer = m_buffer.mid(index: bufferIndex);
431 return false;
432 }
433 }
434
435
436 // dynamic memory - writeBlock() full
437 while (m_index >= 120 && (m_index % 8 == 0) && bufferIndex + 8 < length) {
438 if (m_requestId.isValid()) {
439 if (!m_target->requestResponse(id: m_requestId).toBool())
440 return false;
441
442 m_requestId = QNearFieldTarget::RequestId();
443
444 m_index += 8;
445 bufferIndex += 8;
446 } else {
447 m_requestId = tag->writeBlock(blockAddress: m_index / 8, data: m_buffer.mid(index: bufferIndex, len: 8));
448 m_buffer = m_buffer.mid(index: bufferIndex);
449 return false;
450 }
451 }
452
453 // partial block
454 int currentBlock = m_index / 8;
455 int nextBlock = currentBlock + 1;
456 int currentBlockStart = currentBlock * 8;
457 int nextBlockStart = nextBlock * 8;
458
459 int fillLength = qMin(a: nextBlockStart - m_index, b: spaceRemaining - bufferIndex);
460
461 if (fillLength && (all || m_buffer.length() - bufferIndex >= fillLength) &&
462 (m_buffer.length() != bufferIndex)) {
463 // sufficient data available
464 if (m_requestId.isValid()) {
465 const QVariant v = tag->requestResponse(id: m_requestId);
466 if (v.type() == QVariant::ByteArray) {
467 // read in block
468 QByteArray block = v.toByteArray();
469
470 int fill = qMin(a: fillLength, b: m_buffer.length() - bufferIndex);
471
472 for (int i = m_index - currentBlockStart; i < fill; ++i)
473 block[i] = m_buffer.at(i: bufferIndex++);
474
475 // now write block
476 m_requestId = tag->writeBlock(blockAddress: currentBlock, data: block);
477 return false;
478 } else if (v.type() == QVariant::Bool) {
479 m_requestId = QNearFieldTarget::RequestId();
480 int fill = qMin(a: fillLength, b: m_buffer.length() - bufferIndex);
481 bufferIndex = fill - (m_index - currentBlockStart);
482
483 // write complete
484 if (!v.toBool())
485 return false;
486 }
487 } else {
488 // read in block
489 m_requestId = tag->readBlock(blockAddress: currentBlock);
490 m_buffer = m_buffer.mid(index: bufferIndex);
491 return false;
492 }
493 }
494
495 m_buffer = m_buffer.mid(index: bufferIndex);
496 }
497 }
498
499 return true;
500}
501
502QNearFieldTarget::RequestId QTlvWriter::requestId() const
503{
504 return m_requestId;
505}
506
507int QTlvWriter::moveToNextAvailable()
508{
509 int length = -1;
510
511 // move index to next available byte
512 QMap<int, int>::ConstIterator i;
513 for (i = m_reservedMemory.constBegin(); i != m_reservedMemory.constEnd(); ++i) {
514 if (m_index < i.key()) {
515 length = i.key() - m_index;
516 break;
517 } else if (m_index == i.key()) {
518 m_index += i.value();
519 } else if (m_index > i.key() && m_index < (i.key() + i.value())) {
520 m_index = i.key() + i.value();
521 }
522 }
523
524 if (length == -1)
525 return m_tagMemorySize - m_index;
526
527 Q_ASSERT(length != -1);
528
529 return length;
530}
531
532QT_END_NAMESPACE
533

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