summaryrefslogtreecommitdiffstats
path: root/tests/auto/nfccommons/qtlv.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/nfccommons/qtlv.cpp')
-rw-r--r--tests/auto/nfccommons/qtlv.cpp496
1 files changed, 496 insertions, 0 deletions
diff --git a/tests/auto/nfccommons/qtlv.cpp b/tests/auto/nfccommons/qtlv.cpp
new file mode 100644
index 00000000..f7dae52b
--- /dev/null
+++ b/tests/auto/nfccommons/qtlv.cpp
@@ -0,0 +1,496 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#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.size()) || (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.size()) {
+ int absOffset = absoluteOffset(m_tlvData.size());
+
+ 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.size())
+ 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.size();
+ 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->size();
+ 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.size());
+
+ 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.size() - bufferIndex >= fillLength) &&
+ (m_buffer.size() != 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.size() - 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.size() - 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