diff options
Diffstat (limited to 'tests/auto/nfccommons/qtlv.cpp')
-rw-r--r-- | tests/auto/nfccommons/qtlv.cpp | 496 |
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 |