QtNFC: Add PSCSLite/winscard backend

This backend allows to use the QtNFC API with smart card readers
on Linux, macOS and Windows.

This backend currently supports NDEF access only for Type 4
tags, but a framework is provided that can be used to extend
this support to other tags.

[ChangeLog][QtNfc][Platform Specific Changes] Added support for
accessing smartcards using readers supporting PC/SC specification
on Linux, macOS and Windows.

Fixes: QTBUG-97946
Change-Id: I9abd7d5aeae67c7a633200131db9b90573d5f3df
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
This commit is contained in:
Ievgenii Meshcheriakov 2021-12-09 14:11:41 +01:00
parent e27c6600ec
commit 44d474a0fc
23 changed files with 2959 additions and 0 deletions

18
cmake/FindPCSCLite.cmake Normal file
View File

@ -0,0 +1,18 @@
if(WIN32)
add_library(PkgConfig::PCSCLITE INTERFACE IMPORTED)
target_link_libraries(PkgConfig::PCSCLITE INTERFACE winscard)
set(PCSCLITE_FOUND 1)
elseif(MACOS)
qt_internal_find_apple_system_framework(FWPCSC PCSC)
add_library(PkgConfig::PCSCLITE INTERFACE IMPORTED)
target_link_libraries(PkgConfig::PCSCLITE INTERFACE ${FWPCSC})
set(PCSCLITE_FOUND 1)
else()
find_package(PkgConfig QUIET)
pkg_check_modules(PCSCLITE libpcsclite IMPORTED_TARGET)
endif()
if(NOT TARGET PkgConfig::PCSCLITE)
set(PCSCLITE_FOUND 0)
endif()

View File

@ -67,6 +67,27 @@ qt_internal_extend_target(Nfc CONDITION IOS
IOS_NFC
)
if(QT_FEATURE_pcsclite)
set(NFC_BACKEND_AVAILABLE ON)
endif()
qt_internal_extend_target(Nfc CONDITION QT_FEATURE_pcsclite
SOURCES
qnearfieldmanager_pcsc.cpp qnearfieldmanager_pcsc_p.h
qnearfieldtarget_pcsc.cpp qnearfieldtarget_pcsc_p.h
qapduutils.cpp qapduutils_p.h
pcsc/qpcsc.cpp pcsc/qpcsc_p.h
pcsc/qpcscmanager.cpp pcsc/qpcscmanager_p.h
pcsc/qpcscslot.cpp pcsc/qpcscslot_p.h
pcsc/qpcsccard.cpp pcsc/qpcsccard_p.h
ndef/qndefaccessfsm_p.h
ndef/qnfctagtype4ndeffsm.cpp ndef/qnfctagtype4ndeffsm_p.h
DEFINES
PCSC_NFC
LIBRARIES
PkgConfig::PCSCLITE
)
#### Keys ignored in scope 2:.:.:nfc.pro:ANDROID AND NOT ANDROID_EMBEDDED:
# NFC_BACKEND_AVAILABLE = "yes"

5
src/nfc/configure.cmake Normal file
View File

@ -0,0 +1,5 @@
qt_find_package(PCSCLite PROVIDED_TARGETS PkgConfig::PCSCLite)
qt_feature("pcsclite" PUBLIC
LABEL "PCSCLite"
CONDITION PCSCLITE_FOUND)

View File

@ -35,6 +35,8 @@
The NFC API provides connectivity between NFC enabled devices.
Currently the API is supported on \l{Qt for Android}{Android} and \l{Qt for iOS}{iOS}.
This module also provides limited access to readers supporting \l{PC/SC in Qt NFC}{PC/SC}
specification on Linux, macOS, and Windows.
\section1 Overview

View File

@ -0,0 +1,53 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** 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 Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\page qtnfc-pcsc.html
\title PC/SC in Qt NFC
\ingroup connectivity-nfc
\inmodule QtNfc
\since 6.4
\brief Notes on PC/SC support in Qt Nfc.
PC/SC support is provided using native APIs on macOS and Windows, and using
\l{https://pcsclite.apdu.fr/}{PCSCLite} library on other platforms. The API
can be used for accessing both wired and wireless smartcards and storage cards.
\section1 Limitations
\list
\li The current API does not provide means to distinguish between separate
readers/slots.
\li NDEF access is only provided for NFC Type 4 tags.
\li Other applications starting transactions on cards may block Qt applications
from using Qt Nfc API.
\li QNearFieldTarget::sendCommand() used with a PC/SC target starts
a transaction that remains active until QNearFieldTarget::disconnect()
is called. This transaction prevents other applications from accessing
this target.
\endlist
*/

View File

@ -0,0 +1,139 @@
/****************************************************************************
**
** Copyright (C) 2022 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$
**
****************************************************************************/
#ifndef QNDEFACCESSFSM_P_H
#define QNDEFACCESSFSM_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qndefmessage.h"
QT_BEGIN_NAMESPACE
/*
Base class for FSMs that can be used to exchange NDEF messages with cards.
The user may call one of these methods to start a task:
- detectNdefSupport()
- readMessages()
- writeMessages()
The user is then expected to perform actions indicated by Action type.
*/
class QNdefAccessFsm
{
Q_DISABLE_COPY_MOVE(QNdefAccessFsm)
public:
QNdefAccessFsm() = default;
virtual ~QNdefAccessFsm() = default;
enum Action {
// The requested task has successfully completed. New tasks can be started.
Done,
// The requested task has failed. New tasks can be started.
Failed,
// An NDEF message was successfully read. The user must call getMessage().
GetMessage,
// The user's call was unexpected. The FSM may be in an invalid state.
Unexpected,
// The user must call getCommand() and then send the returned command to the card.
SendCommand,
// The user must call provideResponse() with the result of the last sent command.
ProvideResponse
};
/*
Returns a command to send to the card.
This method must be called if the FMS has requested SendCommand action.
Next action will be ProvideResponse or Unexpected.
*/
virtual QByteArray getCommand(Action &nextAction) = 0;
/*
This method must be called by the user to provide response for
a completed command.
An empty QByteArray can be provided to indicate that the command
has failed.
*/
virtual Action provideResponse(const QByteArray &response) = 0;
/*
Returns an NDEF message that was read from the card.
This method must be called if the FSM has requested GetMessage action.
*/
virtual QNdefMessage getMessage(Action &nextAction) = 0;
/*
Start NDEF support detection.
*/
virtual Action detectNdefSupport() = 0;
/*
Start reading NDEF messages.
This call also performs NDEF support detection if it was not performed
earlier.
*/
virtual Action readMessages() = 0;
/*
Start writing the given messages to the card.
This call also performs NDEF detection if is was not performed earlier.
*/
virtual Action writeMessages(const QList<QNdefMessage> &messages) = 0;
};
QT_END_NAMESPACE
#endif // QNDEFACCESSFSM_P_H

View File

@ -0,0 +1,410 @@
/****************************************************************************
**
** Copyright (C) 2022 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 "qnfctagtype4ndeffsm_p.h"
#include <QtCore/QtEndian>
#include <QtCore/QLoggingCategory>
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(QT_NFC_T4T, "qt.nfc.t4t")
/*
NDEF support for NFC Type 4 tags.
Based on Type 4 Tag Operation Specification, Version 2.0 (T4TOP 2.0).
*/
QByteArray QNfcTagType4NdefFsm::getCommand(QNdefAccessFsm::Action &nextAction)
{
// ID of the NDEF Tag Application
static constexpr uint8_t NtagApplicationIdV2[] { 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01 };
// Capability container file ID.
static constexpr uint8_t CapabilityContainerId[] { 0xE1, 0x03 };
// Shortcut for specifying zero length of NDEF message data.
static constexpr uint8_t ZeroLength[] { 0x00, 0x00 };
nextAction = ProvideResponse;
switch (m_currentState) {
case SelectApplicationForProbe:
case SelectApplicationForRead:
case SelectApplicationForWrite:
return QCommandApdu::build(0x00, QCommandApdu::Select, 0x04, 0x00,
QByteArrayView::fromArray(NtagApplicationIdV2), 256);
case SelectCCFile:
return QCommandApdu::build(0x00, QCommandApdu::Select, 0x00, 0x0C,
QByteArrayView::fromArray(CapabilityContainerId));
case ReadCCFile:
return QCommandApdu::build(0x00, QCommandApdu::ReadBinary, 0x00, 0x00, {}, 15);
case SelectNdefFileForRead:
case SelectNdefFileForWrite:
return QCommandApdu::build(0x00, QCommandApdu::Select, 0x00, 0x0C, m_ndefFileId);
case ReadNdefMessageLength:
return QCommandApdu::build(0x00, QCommandApdu::ReadBinary, 0x00, 0x00, {}, 2);
case ReadNdefMessage: {
uint16_t readSize = qMin(m_fileSize, m_maxReadSize);
return QCommandApdu::build(0x00, QCommandApdu::ReadBinary, m_fileOffset >> 8,
m_fileOffset & 0xFF, {}, readSize);
}
case ClearNdefLength:
m_fileOffset = 2;
m_fileSize = m_ndefData.size();
return QCommandApdu::build(0x00, QCommandApdu::UpdateBinary, 0x00, 0x00,
QByteArrayView::fromArray(ZeroLength));
case WriteNdefFile: {
uint16_t updateSize = qMin(m_fileSize, m_maxUpdateSize);
uint16_t fileOffset = m_fileOffset;
m_fileOffset += updateSize;
m_fileSize -= updateSize;
return QCommandApdu::build(0x00, QCommandApdu::UpdateBinary, fileOffset >> 8,
fileOffset & 0xFF, m_ndefData.mid(fileOffset - 2, updateSize));
}
case WriteNdefLength: {
QByteArray data(2, Qt::Uninitialized);
qToUnaligned(qToBigEndian<uint16_t>(m_ndefData.size()), data.data());
return QCommandApdu::build(0x00, QCommandApdu::UpdateBinary, 0x00, 0x00, data);
}
default:
nextAction = Unexpected;
return {};
}
}
QNdefMessage QNfcTagType4NdefFsm::getMessage(QNdefAccessFsm::Action &nextAction)
{
if (m_currentState == NdefMessageRead) {
auto message = QNdefMessage::fromByteArray(m_ndefData);
m_ndefData.clear();
m_currentState = NdefSupportDetected;
nextAction = Done;
return message;
}
nextAction = Unexpected;
return {};
}
QNdefAccessFsm::Action QNfcTagType4NdefFsm::detectNdefSupport()
{
switch (m_currentState) {
case SelectApplicationForProbe:
m_targetState = NdefSupportDetected;
return SendCommand;
case NdefSupportDetected:
return Done;
case NdefNotSupported:
return Failed;
default:
return Unexpected;
}
}
QNdefAccessFsm::Action QNfcTagType4NdefFsm::readMessages()
{
switch (m_currentState) {
case SelectApplicationForProbe:
m_targetState = NdefMessageRead;
return SendCommand;
case NdefSupportDetected:
m_currentState = SelectApplicationForRead;
m_targetState = NdefMessageRead;
return SendCommand;
case NdefNotSupported:
return Failed;
default:
return Unexpected;
}
}
QNdefAccessFsm::Action QNfcTagType4NdefFsm::writeMessages(const QList<QNdefMessage> &messages)
{
// Only one message per tag is supported
if (messages.isEmpty() || messages.size() > 1)
return Failed;
auto messageData = messages.first().toByteArray();
if (messageData.size() > m_maxNdefSize - 2)
return Failed;
m_ndefData = messageData;
m_targetState = NdefMessageWritten;
switch (m_currentState) {
case SelectApplicationForProbe:
return SendCommand;
case NdefNotSupported:
return Failed;
case NdefSupportDetected:
if (!m_writable)
return Failed;
m_currentState = SelectApplicationForWrite;
return SendCommand;
default:
return Unexpected;
};
}
QNdefAccessFsm::Action QNfcTagType4NdefFsm::provideResponse(const QByteArray &response)
{
QResponseApdu apdu(response);
switch (m_currentState) {
case SelectApplicationForProbe:
return handleSimpleResponse(apdu, SelectCCFile, NdefNotSupported);
case SelectCCFile:
return handleSimpleResponse(apdu, ReadCCFile, NdefNotSupported);
case ReadCCFile:
return handleReadCCResponse(apdu);
case SelectApplicationForRead:
return handleSimpleResponse(apdu, SelectNdefFileForRead, NdefSupportDetected);
case SelectNdefFileForRead:
return handleSimpleResponse(apdu, ReadNdefMessageLength, NdefSupportDetected);
case ReadNdefMessageLength:
return handleReadFileLengthResponse(apdu);
case ReadNdefMessage:
return handleReadFileResponse(apdu);
case SelectApplicationForWrite:
return handleSimpleResponse(apdu, SelectNdefFileForWrite, NdefSupportDetected);
case SelectNdefFileForWrite:
return handleSimpleResponse(apdu, ClearNdefLength, NdefSupportDetected);
case ClearNdefLength:
return handleSimpleResponse(apdu, WriteNdefFile, NdefSupportDetected);
case WriteNdefFile:
return handleWriteNdefFileResponse(apdu);
case WriteNdefLength:
return handleSimpleResponse(apdu, NdefSupportDetected, NdefSupportDetected, Done);
default:
return Unexpected;
}
}
QNdefAccessFsm::Action QNfcTagType4NdefFsm::handleSimpleResponse(
const QResponseApdu &response, QNfcTagType4NdefFsm::State okState,
QNfcTagType4NdefFsm::State failedState, QNdefAccessFsm::Action okAction)
{
if (!response.isOk()) {
m_currentState = failedState;
return Failed;
}
m_currentState = okState;
return okAction;
}
QNdefAccessFsm::Action QNfcTagType4NdefFsm::handleReadCCResponse(const QResponseApdu &response)
{
m_currentState = NdefNotSupported;
if (!response.isOk())
return Failed;
if (response.data().size() < 15) {
qCDebug(QT_NFC_T4T) << "Invalid response size";
return Failed;
}
qsizetype idx = 0;
auto readU8 = [&data = response.data(), &idx]() {
return static_cast<uint8_t>(data.at(idx++));
};
auto readU16 = [&data = response.data(), &idx]() {
Q_ASSERT(idx >= 0 && idx <= data.size() - 2);
uint16_t res = qFromBigEndian(qFromUnaligned<uint16_t>(data.constData() + idx));
idx += 2;
return res;
};
auto readBytes = [&data = response.data(), &idx](qsizetype count) {
auto res = data.sliced(idx, count);
idx += count;
return res;
};
auto ccLen = readU16();
if (ccLen < 15) {
qCDebug(QT_NFC_T4T) << "CC length is too small";
return Failed;
}
auto mapping = readU8();
if ((mapping & 0xF0) != 0x20) {
qCDebug(QT_NFC_T4T) << "Unsupported mapping:" << Qt::hex << mapping;
return Failed;
}
m_maxReadSize = readU16();
if (m_maxReadSize < 0xF) {
qCDebug(QT_NFC_T4T) << "Invalid maxReadSize" << m_maxReadSize;
return Failed;
}
m_maxUpdateSize = readU16();
auto tlvTag = readU8();
if (tlvTag != 0x04) {
qCDebug(QT_NFC_T4T) << "Invalid TLV tag";
return Failed;
}
auto tlvSize = readU8();
if (tlvSize == 0xFF || tlvSize < 6) {
qCDebug(QT_NFC_T4T) << "Invalid TLV size";
return Failed;
}
m_ndefFileId = readBytes(2);
m_maxNdefSize = readU16();
if (m_maxNdefSize < 2) {
qCDebug(QT_NFC_T4T) << "No space for NDEF file length";
return Failed;
}
/*
The specification defined value 0 for read access and write access
fields to mean that access is granted without any security, all
other values are either reserved, proprietary, or indicate than no
access is granted at all. Here all non-zero value are handled as
no access is granted.
*/
auto readAccess = readU8();
if (readAccess != 0) {
qCDebug(QT_NFC_T4T) << "No read access";
return Failed;
}
auto writeAccess = readU8();
// It's not possible to atomically clear the length field if update
// size is < 2, so handle such tags as read-only. This also simplifies
// the update logic (no need to ever split ClearNdefLength/WriteNdefLength
// states)
m_writable = writeAccess == 0 && m_maxUpdateSize >= 2;
m_currentState = NdefSupportDetected;
if (m_targetState == NdefSupportDetected) {
return Done;
} else if (m_targetState == NdefMessageRead) {
// Skip extra application select
m_currentState = SelectNdefFileForRead;
return SendCommand;
} else if (m_targetState == NdefMessageWritten) {
if (m_writable) {
if (m_ndefData.size() > m_maxNdefSize - 2) {
qCDebug(QT_NFC_T4T) << "Message is too large";
return Failed;
}
// Skip extra application select
m_currentState = SelectNdefFileForWrite;
return SendCommand;
} else {
return Failed;
}
}
return Unexpected;
}
QNdefAccessFsm::Action
QNfcTagType4NdefFsm::handleReadFileLengthResponse(const QResponseApdu &response)
{
if (!response.isOk() || response.data().size() < 2) {
m_currentState = NdefSupportDetected;
return Failed;
}
m_fileSize = qFromBigEndian(qFromUnaligned<uint16_t>(response.data().constData()));
if (m_fileSize > m_maxNdefSize - 2) {
m_currentState = NdefSupportDetected;
return Failed;
}
m_fileOffset = 2;
m_ndefData.clear();
if (m_fileSize == 0) {
m_currentState = NdefMessageRead;
return GetMessage;
}
m_currentState = ReadNdefMessage;
return SendCommand;
}
QNdefAccessFsm::Action QNfcTagType4NdefFsm::handleReadFileResponse(const QResponseApdu &response)
{
if (!response.isOk() || response.data().size() == 0) {
m_currentState = NdefSupportDetected;
return Failed;
}
auto readSize = qMin<qsizetype>(m_fileSize, response.data().size());
m_ndefData.append(response.data().first(readSize));
m_fileOffset += readSize;
m_fileSize -= readSize;
if (m_fileSize == 0) {
m_currentState = NdefMessageRead;
return GetMessage;
}
return SendCommand;
}
QNdefAccessFsm::Action
QNfcTagType4NdefFsm::handleWriteNdefFileResponse(const QResponseApdu &response)
{
if (!response.isOk()) {
m_currentState = NdefSupportDetected;
return Failed;
}
if (m_fileSize == 0)
m_currentState = WriteNdefLength;
return SendCommand;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,118 @@
/****************************************************************************
**
** Copyright (C) 2022 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$
**
****************************************************************************/
#ifndef QNFCTAGTYPE4NDEFFSM_P_H
#define QNFCTAGTYPE4NDEFFSM_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qndefaccessfsm_p.h"
#include "qapduutils_p.h"
QT_BEGIN_NAMESPACE
class QNfcTagType4NdefFsm : public QNdefAccessFsm
{
public:
QByteArray getCommand(Action &nextAction) override;
QNdefMessage getMessage(Action &nextAction) override;
Action provideResponse(const QByteArray &response) override;
Action detectNdefSupport() override;
Action readMessages() override;
Action writeMessages(const QList<QNdefMessage> &messages) override;
private:
enum State {
SelectApplicationForProbe,
SelectCCFile,
ReadCCFile,
NdefSupportDetected,
NdefNotSupported,
SelectApplicationForRead,
SelectNdefFileForRead,
ReadNdefMessageLength,
ReadNdefMessage,
NdefMessageRead,
SelectApplicationForWrite,
SelectNdefFileForWrite,
ClearNdefLength,
WriteNdefFile,
WriteNdefLength,
NdefMessageWritten // Only for target state, it is never actually reached
};
State m_currentState = SelectApplicationForProbe;
State m_targetState = SelectApplicationForProbe;
// Initialized during the detection phase
uint16_t m_maxReadSize;
uint16_t m_maxUpdateSize;
QByteArray m_ndefFileId;
uint16_t m_maxNdefSize = 0xFFFF;
bool m_writable;
// Used during the read and write operations
uint16_t m_fileSize;
uint16_t m_fileOffset;
QByteArray m_ndefData;
Action handleSimpleResponse(const QResponseApdu &response, State okState, State failedState,
Action okAction = SendCommand);
Action handleReadCCResponse(const QResponseApdu &response);
Action handleReadFileLengthResponse(const QResponseApdu &response);
Action handleReadFileResponse(const QResponseApdu &response);
Action handleWriteNdefFileResponse(const QResponseApdu &response);
};
QT_END_NAMESPACE
#endif // QNFCTAGTYPE4NDEFFSM_P_H

66
src/nfc/pcsc/qpcsc.cpp Normal file
View File

@ -0,0 +1,66 @@
/****************************************************************************
**
** Copyright (C) 2022 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 "qpcsc_p.h"
QT_BEGIN_NAMESPACE
namespace QPcsc {
QString errorMessage(LONG error)
{
#ifdef Q_OS_WIN
return (u"0x%1"_qs).arg(error, 8, 16, QLatin1Char('0'));
#else
return QString::fromUtf8(pcsc_stringify_error(error));
#endif
}
} // namespace QPcsc
qsizetype QPcscSlotName::nameSize(QPcscSlotName::CPtr p)
{
#ifdef Q_OS_WIN
return wcslen(p);
#else
return strlen(p);
#endif
}
QT_END_NAMESPACE

108
src/nfc/pcsc/qpcsc_p.h Normal file
View File

@ -0,0 +1,108 @@
/****************************************************************************
**
** Copyright (C) 2022 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$
**
****************************************************************************/
#ifndef QPCSC_P_H
#define QPCSC_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#ifdef Q_OS_WIN
# include <qt_windows.h>
#endif
#include <QtCore/QtGlobal>
#ifdef Q_OS_DARWIN
# include <PCSC/winscard.h>
# include <PCSC/wintypes.h>
#else
# include <winscard.h>
#endif
#include <QtCore/QByteArray>
#include <QtCore/QString>
QT_BEGIN_NAMESPACE
namespace QPcsc {
struct RawCommandResult
{
LONG ret = SCARD_E_READER_UNAVAILABLE;
QByteArray response;
bool isOk() const { return ret == SCARD_S_SUCCESS; }
};
QString errorMessage(LONG error);
} // namespace QPcsc
class QPcscSlotName : public
#ifdef Q_OS_WIN
QString
#else
QByteArray
#endif
{
public:
#ifdef Q_OS_WIN
using CPtr = LPCWSTR;
using Ptr = LPWSTR;
explicit QPcscSlotName(CPtr p) : QString(reinterpret_cast<const QChar *>(p)) { }
#else
using CPtr = LPCSTR;
using Ptr = LPSTR;
explicit QPcscSlotName(CPtr p) : QByteArray(p) { }
#endif
CPtr ptr() const noexcept { return reinterpret_cast<CPtr>(constData()); }
Ptr ptr() { return reinterpret_cast<Ptr>(data()); }
static qsizetype nameSize(CPtr p);
};
QT_END_NAMESPACE
#endif // QPCSC_P_H

455
src/nfc/pcsc/qpcsccard.cpp Normal file
View File

@ -0,0 +1,455 @@
/****************************************************************************
**
** Copyright (C) 2022 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 "qpcsccard_p.h"
#include "ndef/qnfctagtype4ndeffsm_p.h"
#include "qapduutils_p.h"
#include <QtCore/QLoggingCategory>
#include <QtCore/QTimer>
#if defined(Q_OS_DARWIN)
# define SCARD_ATTR_MAXINPUT 0x0007A007
#elif !defined(Q_OS_WIN)
# include <reader.h>
#endif
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(QT_NFC_PCSC)
/*
Windows SCardBeginTransaction() documentation says the following:
If a transaction is held on the card for more than five seconds with no
operations happening on that card, then the card is reset. Calling any
of the Smart Card and Reader Access Functions or Direct Card Access
Functions on the card that is transacted results in the timer being
reset to continue allowing the transaction to be used.
We use a timer to ensure that transactions do not timeout unexpectedly.
*/
static constexpr int KeepAliveIntervalMs = 2500;
/*
Start a temporary transaction if a persistent transaction was not already
started due to call to onSendCommandRequest().
If a temporary transaction was initiated, it is ended when this object is
destroyed.
*/
QPcscCard::Transaction::Transaction(QPcscCard *card) : m_card(card)
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
if (!m_card->isValid() || m_card->m_inAutoTransaction)
return;
auto ret = SCardBeginTransaction(m_card->m_handle);
if (ret != SCARD_S_SUCCESS) {
qCWarning(QT_NFC_PCSC) << "SCardBeginTransaction failed:" << QPcsc::errorMessage(ret);
m_card->invalidate();
return;
}
m_initiated = true;
}
QPcscCard::Transaction::~Transaction()
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
if (!m_initiated || !m_card->isValid())
return;
auto ret = SCardEndTransaction(m_card->m_handle, SCARD_LEAVE_CARD);
if (ret != SCARD_S_SUCCESS) {
qCWarning(QT_NFC_PCSC) << "SCardEndTransaction failed:" << QPcsc::errorMessage(ret);
m_card->invalidate();
}
}
QPcscCard::QPcscCard(SCARDHANDLE handle, DWORD protocol, QObject *parent)
: QObject(parent), m_handle(handle)
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
m_keepAliveTimer = new QTimer(this);
m_keepAliveTimer->setInterval(KeepAliveIntervalMs);
connect(m_keepAliveTimer, &QTimer::timeout, this, &QPcscCard::onKeepAliveTimeout);
m_ioPci.dwProtocol = protocol;
m_ioPci.cbPciLength = sizeof(m_ioPci);
// Assume that everything is NFC Tag Type 4 for now
m_tagDetectionFsm = std::make_unique<QNfcTagType4NdefFsm>();
performNdefDetection();
}
QPcscCard::~QPcscCard()
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
invalidate();
}
void QPcscCard::performNdefDetection()
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
if (!m_isValid)
return;
Transaction transaction(this);
auto action = m_tagDetectionFsm->detectNdefSupport();
while (action == QNdefAccessFsm::SendCommand) {
auto command = m_tagDetectionFsm->getCommand(action);
if (action == QNdefAccessFsm::ProvideResponse) {
auto result = sendCommand(command, NoAutoTransaction);
action = m_tagDetectionFsm->provideResponse(result.response);
}
}
qCDebug(QT_NFC_PCSC) << "NDEF detection result" << action;
m_supportsNdef = action == QNdefAccessFsm::Done;
qCDebug(QT_NFC_PCSC) << "NDEF supported:" << m_supportsNdef;
}
/*
Release the resource associated with the card and notify the NFC target
about disconnection. The card is deleted when automatic deletion is enabled.
*/
void QPcscCard::invalidate()
{
if (!m_isValid)
return;
SCardDisconnect(m_handle, m_inAutoTransaction ? SCARD_RESET_CARD : SCARD_LEAVE_CARD);
m_isValid = false;
m_inAutoTransaction = false;
Q_EMIT disconnected();
Q_EMIT invalidated();
if (m_autodelete)
deleteLater();
}
/*
Send the given command to the card.
Start an automatic transaction if autoTransaction is StartAutoTransaction
and it is not already started. This automatic transaction lasts until the
invalidate() or onDisconnectRequest() is called.
The automatic transaction is used to ensure that other applications do not
interfere with commands sent by the user application.
If autoTransaction is NoAutoTransaction, then the calling code should ensure
that either the command is atomic or that a temporary transaction is started
using Transaction object.
*/
QPcsc::RawCommandResult QPcscCard::sendCommand(const QByteArray &command,
QPcscCard::AutoTransaction autoTransaction)
{
if (!m_isValid)
return {};
if (!m_inAutoTransaction && autoTransaction == StartAutoTransaction) {
qCDebug(QT_NFC_PCSC) << "Starting transaction";
// FIXME: Is there a timeout on this?
auto ret = SCardBeginTransaction(m_handle);
if (ret != SCARD_S_SUCCESS) {
qCWarning(QT_NFC_PCSC) << "SCardBeginTransaction failed:" << QPcsc::errorMessage(ret);
invalidate();
return {};
}
m_inAutoTransaction = true;
m_keepAliveTimer->start();
}
QPcsc::RawCommandResult result;
result.response.resize(0xFFFF + 2);
DWORD recvLength = result.response.size();
qCDebug(QT_NFC_PCSC) << "TX:" << command.toHex(':');
result.ret = SCardTransmit(m_handle, &m_ioPci, reinterpret_cast<LPCBYTE>(command.constData()),
command.size(), nullptr,
reinterpret_cast<LPBYTE>(result.response.data()), &recvLength);
if (result.ret != SCARD_S_SUCCESS) {
qCWarning(QT_NFC_PCSC) << "SCardTransmit failed:" << QPcsc::errorMessage(result.ret);
result.response.clear();
invalidate();
} else {
result.response.resize(recvLength);
qCDebug(QT_NFC_PCSC) << "RX:" << result.response.toHex(':');
}
return result;
}
void QPcscCard::onKeepAliveTimeout()
{
if (!m_isValid || !m_inAutoTransaction) {
m_keepAliveTimer->stop();
return;
}
checkCardPresent();
}
QByteArray QPcscCard::readUid()
{
QByteArray command = QCommandApdu::build(0xFF, QCommandApdu::GetData, 0x00, 0x00, {}, 256);
// Atomic command, no need for transaction.
QResponseApdu res(sendCommand(command, NoAutoTransaction).response);
if (!res.isOk())
return {};
return res.data();
}
void QPcscCard::onReadNdefMessagesRequest(const QNearFieldTarget::RequestId &request)
{
if (!m_isValid) {
Q_EMIT requestCompleted(request, QNearFieldTarget::ConnectionError, {});
return;
}
if (!m_supportsNdef) {
Q_EMIT requestCompleted(request, QNearFieldTarget::UnsupportedError, {});
return;
}
Transaction transaction(this);
QList<QNdefMessage> messages;
auto nextState = m_tagDetectionFsm->readMessages();
while (true) {
if (nextState == QNdefAccessFsm::SendCommand) {
auto command = m_tagDetectionFsm->getCommand(nextState);
if (nextState == QNdefAccessFsm::ProvideResponse) {
auto result = sendCommand(command, NoAutoTransaction);
nextState = m_tagDetectionFsm->provideResponse(result.response);
}
} else if (nextState == QNdefAccessFsm::GetMessage) {
auto message = m_tagDetectionFsm->getMessage(nextState);
Q_EMIT ndefMessageRead(message);
} else {
break;
}
}
qCDebug(QT_NFC_PCSC) << "Final state:" << nextState;
auto errorCode = (nextState == QNdefAccessFsm::Done) ? QNearFieldTarget::NoError
: QNearFieldTarget::NdefReadError;
Q_EMIT requestCompleted(request, errorCode, {});
}
/*
Ends the persistent transaction and resets the card if sendCommand() was
used by user.
Resetting the card ensures that the current state of the card does not get
shared with other processes.
*/
void QPcscCard::onDisconnectRequest()
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
if (!m_isValid)
return;
LONG ret;
if (m_inAutoTransaction) {
// NOTE: PCSCLite does not automatically release transaction in
// SCardReconnect(): https://salsa.debian.org/rousseau/PCSC/-/issues/11
ret = SCardEndTransaction(m_handle, SCARD_RESET_CARD);
if (ret != SCARD_S_SUCCESS) {
qCWarning(QT_NFC_PCSC) << "SCardEndTransaction failed:" << QPcsc::errorMessage(ret);
invalidate();
return;
}
m_inAutoTransaction = false;
}
DWORD activeProtocol;
ret = SCardReconnect(m_handle, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
SCARD_LEAVE_CARD, &activeProtocol);
if (ret != SCARD_S_SUCCESS) {
qCWarning(QT_NFC_PCSC) << "SCardReconnect failed:" << QPcsc::errorMessage(ret);
invalidate();
return;
}
Q_EMIT disconnected();
}
void QPcscCard::onTargetDestroyed()
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
invalidate();
}
void QPcscCard::onSendCommandRequest(const QNearFieldTarget::RequestId &request,
const QByteArray &command)
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
if (!m_isValid) {
Q_EMIT requestCompleted(request, QNearFieldTarget::ConnectionError, {});
return;
}
auto result = sendCommand(command, StartAutoTransaction);
if (result.isOk())
Q_EMIT requestCompleted(request, QNearFieldTarget::NoError, result.response);
else
Q_EMIT requestCompleted(request, QNearFieldTarget::CommandError, {});
}
void QPcscCard::onWriteNdefMessagesRequest(const QNearFieldTarget::RequestId &request,
const QList<QNdefMessage> &messages)
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
if (!m_isValid) {
Q_EMIT requestCompleted(request, QNearFieldTarget::ConnectionError, {});
return;
}
if (!m_supportsNdef) {
Q_EMIT requestCompleted(request, QNearFieldTarget::UnsupportedError, {});
return;
}
Transaction transaction(this);
auto nextState = m_tagDetectionFsm->writeMessages(messages);
while (nextState == QNdefAccessFsm::SendCommand) {
auto command = m_tagDetectionFsm->getCommand(nextState);
if (nextState == QNdefAccessFsm::ProvideResponse) {
auto result = sendCommand(command, NoAutoTransaction);
nextState = m_tagDetectionFsm->provideResponse(result.response);
}
}
auto errorCode = (nextState == QNdefAccessFsm::Done) ? QNearFieldTarget::NoError
: QNearFieldTarget::NdefWriteError;
Q_EMIT requestCompleted(request, errorCode, {});
}
/*
Enable automatic card deletion when the connection is closed by the user
or the card otherwise becomes unavailable.
The automatic deletion is prevented initially so that
QNearFieldManagerPrivate can safely establish connections between this
object and a QNearFieldTargetPrivate proxy.
*/
void QPcscCard::enableAutodelete()
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
m_autodelete = true;
if (!m_isValid)
deleteLater();
}
/*
Check if the card is still present in the reader.
Invalidates the object if card is not present.
*/
bool QPcscCard::checkCardPresent()
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
if (!m_isValid)
return false;
DWORD state;
auto ret = SCardStatus(m_handle, nullptr, nullptr, &state, nullptr, nullptr, nullptr);
if (ret != SCARD_S_SUCCESS) {
qCWarning(QT_NFC_PCSC) << "SCardStatus failed:" << QPcsc::errorMessage(ret);
invalidate();
return false;
}
qCDebug(QT_NFC_PCSC) << "State:" << Qt::hex << state;
return (state & SCARD_PRESENT) != 0;
}
int QPcscCard::readMaxInputLength()
{
if (!m_isValid)
return 0;
// Maximum standard APDU length
static constexpr int DefaultMaxInputLength = 261;
uint32_t maxInput;
DWORD attrSize = sizeof(maxInput);
auto ret = SCardGetAttrib(m_handle, SCARD_ATTR_MAXINPUT, reinterpret_cast<LPBYTE>(&maxInput),
&attrSize);
if (ret != SCARD_S_SUCCESS) {
qCDebug(QT_NFC_PCSC) << "SCardGetAttrib failed:" << QPcsc::errorMessage(ret);
return DefaultMaxInputLength;
}
if (attrSize != sizeof(maxInput)) {
qCWarning(QT_NFC_PCSC) << "Unexpected attribute size for SCARD_ATTR_MAXINPUT:" << attrSize;
return DefaultMaxInputLength;
}
return static_cast<int>(maxInput);
}
QT_END_NAMESPACE

132
src/nfc/pcsc/qpcsccard_p.h Normal file
View File

@ -0,0 +1,132 @@
/****************************************************************************
**
** Copyright (C) 2022 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$
**
****************************************************************************/
#ifndef QPCSCCARD_P_H
#define QPCSCCARD_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qpcsc_p.h"
#include "qndefmessage.h"
#include "qnearfieldtarget.h"
#include "ndef/qndefaccessfsm_p.h"
QT_BEGIN_NAMESPACE
class QTimer;
class QPcscCard : public QObject
{
Q_OBJECT
public:
QPcscCard(SCARDHANDLE handle, DWORD protocol, QObject *parent = nullptr);
~QPcscCard();
bool isValid() const { return m_isValid; }
void invalidate();
bool checkCardPresent();
Q_INVOKABLE void enableAutodelete();
QByteArray readUid();
int readMaxInputLength();
bool supportsNdef() const { return m_supportsNdef; }
private:
SCARDHANDLE m_handle;
SCARD_IO_REQUEST m_ioPci;
bool m_isValid = true;
bool m_supportsNdef;
bool m_autodelete = false;
// Indicates that an _automatic_ transaction was started
bool m_inAutoTransaction = false;
QTimer *m_keepAliveTimer;
std::unique_ptr<QNdefAccessFsm> m_tagDetectionFsm;
enum AutoTransaction { NoAutoTransaction, StartAutoTransaction };
QPcsc::RawCommandResult sendCommand(const QByteArray &command, AutoTransaction autoTransaction);
void performNdefDetection();
class Transaction
{
public:
Transaction(QPcscCard *card);
~Transaction();
private:
QPcscCard *m_card;
bool m_initiated = false;
};
public Q_SLOTS:
void onDisconnectRequest();
void onTargetDestroyed();
void onSendCommandRequest(const QNearFieldTarget::RequestId &request,
const QByteArray &command);
void onReadNdefMessagesRequest(const QNearFieldTarget::RequestId &request);
void onWriteNdefMessagesRequest(const QNearFieldTarget::RequestId &request,
const QList<QNdefMessage> &messages);
private Q_SLOTS:
void onKeepAliveTimeout();
Q_SIGNALS:
void disconnected();
void invalidated();
void requestCompleted(const QNearFieldTarget::RequestId &request,
QNearFieldTarget::Error reason, const QVariant &result);
void ndefMessageRead(const QNdefMessage &message);
};
QT_END_NAMESPACE
#endif // QPCSCCARD_P_H

View File

@ -0,0 +1,383 @@
/****************************************************************************
**
** Copyright (C) 2022 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 "qpcscmanager_p.h"
#include "qpcscslot_p.h"
#include "qpcsccard_p.h"
#include <QtCore/QLoggingCategory>
#include <QtCore/QThread>
#include <QtCore/QTimer>
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(QT_NFC_PCSC)
static constexpr int StateUpdateIntervalMs = 1000;
QPcscManager::QPcscManager(QObject *parent) : QObject(parent)
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
m_stateUpdateTimer = new QTimer(this);
m_stateUpdateTimer->setInterval(StateUpdateIntervalMs);
connect(m_stateUpdateTimer, &QTimer::timeout, this, &QPcscManager::onStateUpdate);
}
QPcscManager::~QPcscManager()
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
if (m_hasContext) {
// Destroy all card handles before destroying the PCSC context.
for (auto slot : std::as_const(m_slots))
slot->invalidateInsertedCard();
SCardReleaseContext(m_context);
}
// Stop the worker thread.
thread()->quit();
}
void QPcscManager::processSlotUpdates()
{
for (auto &state : m_slotStates) {
auto slot = static_cast<QPcscSlot *>(state.pvUserData);
Q_ASSERT(slot != nullptr);
if ((state.dwEventState & SCARD_STATE_UNKNOWN) != 0)
continue;
if (state.dwEventState == state.dwCurrentState)
continue;
qCDebug(QT_NFC_PCSC) << Qt::hex << state.dwCurrentState << "=>" << state.dwEventState << ":"
<< slot->name();
state.dwCurrentState = state.dwEventState;
slot->processStateChange(state.dwEventState, m_targetDetectionRunning);
}
}
/*
Remove slots that no longer need to be tracked.
*/
void QPcscManager::removeSlots()
{
for (auto &state : m_slotStates) {
auto slot = static_cast<QPcscSlot *>(state.pvUserData);
Q_ASSERT(slot != nullptr);
// Remove slots that no longer exist, or all slots without cards if
// target detection is stopped.
if ((state.dwEventState & SCARD_STATE_UNKNOWN) != 0
|| !(m_targetDetectionRunning || slot->hasCard())) {
qCDebug(QT_NFC_PCSC) << "Removing slot:" << slot;
state.dwEventState = SCARD_STATE_UNKNOWN;
slot->invalidateInsertedCard();
m_slots.remove(slot->name());
slot->deleteLater();
state.pvUserData = nullptr;
}
}
// Remove state tracking entries for slots that no longer exist.
m_slotStates.removeIf(
[](const auto &state) { return (state.dwEventState & SCARD_STATE_UNKNOWN) != 0; });
}
/*
Reads the system slot lists and marks slots that no longer exists, also
creates new slot entries if target detection is currently running.
*/
void QPcscManager::updateSlotList()
{
Q_ASSERT(m_hasContext);
#ifndef SCARD_AUTOALLOCATE
// macOS does not support automatic allocation. Try using a fixed-size
// buffer first, extending it if it is not sufficient.
#define LIST_READER_BUFFER_EXTRA 1024
QPcscSlotName buf(nullptr);
DWORD listSize = LIST_READER_BUFFER_EXTRA;
buf.resize(listSize);
QPcscSlotName::Ptr list = buf.ptr();
auto ret = SCardListReaders(m_context, nullptr, list, &listSize);
#else
QPcscSlotName::Ptr list;
DWORD listSize = SCARD_AUTOALLOCATE;
auto ret = SCardListReaders(m_context, nullptr, reinterpret_cast<QPcscSlotName::Ptr>(&list),
&listSize);
#endif
if (ret == LONG(SCARD_E_NO_READERS_AVAILABLE)) {
list = nullptr;
ret = SCARD_S_SUCCESS;
}
#ifndef SCARD_AUTOALLOCATE
else if (ret == LONG(SCARD_E_INSUFFICIENT_BUFFER)) {
// SCardListReaders() has set listSize to the required size. We add
// extra space to reduce possibility of failure if the reader list has
// changed since the last call.
listSize += LIST_READER_BUFFER_EXTRA;
buf.resize(listSize);
list = buf.ptr();
ret = SCardListReaders(m_context, nullptr, list, &listSize);
if (ret == LONG(SCARD_E_NO_READERS_AVAILABLE)) {
list = nullptr;
ret = SCARD_S_SUCCESS;
}
}
#undef LIST_READER_BUFFER_EXTRA
#endif
if (ret != SCARD_S_SUCCESS) {
qCDebug(QT_NFC_PCSC) << "Failed to list readers:" << QPcsc::errorMessage(ret);
return;
}
#ifdef SCARD_AUTOALLOCATE
auto freeList = qScopeGuard([this, list] {
if (list) {
Q_ASSERT(m_hasContext);
SCardFreeMemory(m_context, list);
}
});
#endif
QSet<QPcscSlotName> presentSlots;
if (list != nullptr) {
for (const auto *p = list; *p; p += QPcscSlotName::nameSize(p) + 1)
presentSlots.insert(QPcscSlotName(p));
}
// Check current state list and mark slots that are not present anymore to
// be removed later.
for (auto &state : m_slotStates) {
auto slot = static_cast<QPcscSlot *>(state.pvUserData);
Q_ASSERT(slot != nullptr);
if (presentSlots.contains(slot->name()))
presentSlots.remove(slot->name());
else
state.dwEventState = SCARD_STATE_UNKNOWN;
}
if (!m_targetDetectionRunning)
return;
// Add new slots
for (auto &&slotName : std::as_const(presentSlots)) {
QPcscSlot *slot = new QPcscSlot(slotName, this);
qCDebug(QT_NFC_PCSC) << "New slot:" << slot;
m_slots[slotName] = slot;
SCARD_READERSTATE state {};
state.pvUserData = slot;
state.szReader = slot->name().ptr();
state.dwCurrentState = SCARD_STATE_UNAWARE;
m_slotStates.append(state);
}
}
bool QPcscManager::establishContext()
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
Q_ASSERT(!m_hasContext);
LONG ret = SCardEstablishContext(SCARD_SCOPE_USER, nullptr, nullptr, &m_context);
if (ret != SCARD_S_SUCCESS) {
qCWarning(QT_NFC_PCSC) << "Failed to establish context:" << QPcsc::errorMessage(ret);
return false;
}
m_hasContext = true;
return true;
}
void QPcscManager::onStateUpdate()
{
if (!m_hasContext) {
if (!m_targetDetectionRunning) {
m_stateUpdateTimer->stop();
return;
}
if (!establishContext())
return;
}
updateSlotList();
removeSlots();
if (m_slotStates.isEmpty()) {
if (!m_targetDetectionRunning) {
// Free the context if it is no longer needed to card tracking.
SCardReleaseContext(m_context);
m_hasContext = false;
m_stateUpdateTimer->stop();
}
return;
}
// Both Windows and PCSCLite support interruptable continuos state detection
// where SCardCancel() can be used to abort it from another thread.
// But that introduces a problem of reliable cancelling of the status change
// call and no other. Reliable use of SCardCancel() would probably require
// some form of synchronization and polling, and would make the code much
// more complicated.
// Alternatively, the state detection code could run in a yet another thread
// that will not need to be cancelled too often.
// For simplicity, the current code just checks for status changes every
// second.
LONG ret = SCardGetStatusChange(m_context, 0, m_slotStates.data(), m_slotStates.size());
if (ret == SCARD_S_SUCCESS || ret == LONG(SCARD_E_UNKNOWN_READER)) {
processSlotUpdates();
removeSlots();
} else if (ret == LONG(SCARD_E_CANCELLED) || ret == LONG(SCARD_E_UNKNOWN_READER)
|| ret == LONG(SCARD_E_TIMEOUT)) {
/* ignore */
} else {
qCWarning(QT_NFC_PCSC) << "SCardGetStatusChange failed:" << QPcsc::errorMessage(ret);
// Unknown failure. It is likely that the current context will not
// recover from it, so destroy it and try to create a new context at the
// next iteration.
Q_ASSERT(m_hasContext);
m_hasContext = false;
for (auto slot : std::as_const(m_slots)) {
slot->invalidateInsertedCard();
slot->deleteLater();
}
SCardReleaseContext(m_context);
m_slots.clear();
m_slotStates.clear();
}
}
void QPcscManager::onStartTargetDetectionRequest(QNearFieldTarget::AccessMethod accessMethod)
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
m_requestedMethod = accessMethod;
if (m_targetDetectionRunning)
return;
m_targetDetectionRunning = true;
m_stateUpdateTimer->start();
}
void QPcscManager::onStopTargetDetectionRequest()
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
m_targetDetectionRunning = false;
}
QPcscCard *QPcscManager::connectToCard(QPcscSlot *slot)
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
Q_ASSERT(slot != nullptr);
Q_ASSERT(m_hasContext);
SCARDHANDLE cardHandle;
DWORD activeProtocol;
LONG ret = SCardConnect(m_context, slot->name().ptr(), SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &cardHandle, &activeProtocol);
if (ret != SCARD_S_SUCCESS) {
qCDebug(QT_NFC_PCSC) << "Failed to connect to card:" << QPcsc::errorMessage(ret);
retryCardDetection(slot);
return nullptr;
}
auto card = new QPcscCard(cardHandle, activeProtocol, this);
auto uid = card->readUid();
auto maxInputLength = card->readMaxInputLength();
QNearFieldTarget::AccessMethods accessMethods = QNearFieldTarget::TagTypeSpecificAccess;
if (card->supportsNdef())
accessMethods |= QNearFieldTarget::NdefAccess;
if (m_requestedMethod != QNearFieldTarget::UnknownAccess
&& (accessMethods & m_requestedMethod) == 0) {
qCDebug(QT_NFC_PCSC) << "Dropping card without required access support";
card->deleteLater();
return nullptr;
}
if (!card->isValid()) {
qCDebug(QT_NFC_PCSC) << "Card became invalid";
card->deleteLater();
retryCardDetection(slot);
return nullptr;
}
Q_EMIT cardInserted(card, uid, accessMethods, maxInputLength);
return card;
}
/*
Setup states list so that the card detection for the given slot will
be retried on the next iteration.
This is useful to try to get cards working after reset.
*/
void QPcscManager::retryCardDetection(const QPcscSlot *slot)
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
for (auto &state : m_slotStates) {
if (state.pvUserData == slot) {
state.dwCurrentState = SCARD_STATE_UNAWARE;
break;
}
}
}
QT_END_NAMESPACE

View File

@ -0,0 +1,101 @@
/****************************************************************************
**
** Copyright (C) 2022 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$
**
****************************************************************************/
#ifndef QPCSCMANAGER_P_H
#define QPCSCMANAGER_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qpcsc_p.h"
#include "qnearfieldtarget.h"
QT_BEGIN_NAMESPACE
class QPcscSlot;
class QPcscCard;
class QTimer;
class QPcscManager : public QObject
{
Q_OBJECT
public:
explicit QPcscManager(QObject *parent = nullptr);
~QPcscManager() override;
QPcscCard *connectToCard(QPcscSlot *slot);
private:
QTimer *m_stateUpdateTimer;
bool m_targetDetectionRunning = false;
bool m_hasContext = false;
SCARDCONTEXT m_context;
QMap<QPcscSlotName, QPcscSlot *> m_slots;
QList<SCARD_READERSTATE> m_slotStates;
QNearFieldTarget::AccessMethod m_requestedMethod;
[[nodiscard]] bool establishContext();
void processSlotUpdates();
void updateSlotList();
void removeSlots();
void retryCardDetection(const QPcscSlot *slot);
public Q_SLOTS:
void onStartTargetDetectionRequest(QNearFieldTarget::AccessMethod accessMethod);
void onStopTargetDetectionRequest();
private Q_SLOTS:
void onStateUpdate();
Q_SIGNALS:
void cardInserted(QPcscCard *card, const QByteArray &uid,
QNearFieldTarget::AccessMethods accessMethods, int maxInputLength);
};
QT_END_NAMESPACE
#endif // QPCSCMANAGER_P_H

View File

@ -0,0 +1,93 @@
/****************************************************************************
**
** Copyright (C) 2022 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 "qpcscslot_p.h"
#include "qpcscmanager_p.h"
#include "qpcsccard_p.h"
#include <QtCore/QLoggingCategory>
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(QT_NFC_PCSC)
QPcscSlot::QPcscSlot(const QPcscSlotName &name, QPcscManager *manager)
: QObject(manager), m_name(name)
{
}
QPcscSlot::~QPcscSlot()
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO << this;
if (m_insertedCard)
m_insertedCard->invalidate();
}
void QPcscSlot::processStateChange(DWORD eventId, bool createCards)
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
// Check if the currently inserted card is still valid
if (!m_insertedCard.isNull()) {
if (m_insertedCard->checkCardPresent())
return;
qCDebug(QT_NFC_PCSC) << "Removing card from slot" << m_name;
m_insertedCard->invalidate();
m_insertedCard.clear();
}
auto manager = qobject_cast<QPcscManager *>(parent());
if (manager != nullptr && createCards
&& (eventId
& (SCARD_STATE_PRESENT | SCARD_STATE_MUTE | SCARD_STATE_UNPOWERED
| SCARD_STATE_EXCLUSIVE))
== SCARD_STATE_PRESENT) {
qCDebug(QT_NFC_PCSC) << "New card in slot" << m_name;
m_insertedCard = manager->connectToCard(this);
}
}
void QPcscSlot::invalidateInsertedCard()
{
if (m_insertedCard)
m_insertedCard->invalidate();
}
QT_END_NAMESPACE

View File

@ -0,0 +1,82 @@
/****************************************************************************
**
** Copyright (C) 2022 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$
**
****************************************************************************/
#ifndef QPCSCSLOT_P_H
#define QPCSCSLOT_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qpcsc_p.h"
#include <QtCore/QObject>
#include <QtCore/QPointer>
QT_BEGIN_NAMESPACE
class QPcscManager;
class QPcscCard;
class QPcscSlot : public QObject
{
Q_OBJECT
public:
QPcscSlot(const QPcscSlotName &name, QPcscManager *manager);
~QPcscSlot() override;
const QPcscSlotName &name() const { return m_name; }
void processStateChange(DWORD eventId, bool createCards);
bool hasCard() const { return !m_insertedCard.isNull(); }
void invalidateInsertedCard();
private:
const QPcscSlotName m_name;
QPointer<QPcscCard> m_insertedCard;
};
QT_END_NAMESPACE
#endif // QPCSCSLOT_P_H

117
src/nfc/qapduutils.cpp Normal file
View File

@ -0,0 +1,117 @@
/****************************************************************************
**
** Copyright (C) 2022 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 "qapduutils_p.h"
#include <QtCore/QtEndian>
QT_BEGIN_NAMESPACE
/*
Utilities for handling smart card application protocol data units (APDU).
The structure of APDUs is defined by ISO/IEC 7816-4 Organization, security
and commands for interchange. The summary can be found on Wikipedia:
https://en.wikipedia.org/wiki/Smart_card_application_protocol_data_unit
*/
/*
Parses a response APDU from the raw data.
If the data is too short to contain SW bytes, the returned responses SW
is set to QResponseApdu::Empty.
*/
QResponseApdu::QResponseApdu(const QByteArray &response)
{
if (response.size() < 2) {
m_status = Empty;
m_data = response;
} else {
const auto dataSize = response.size() - 2;
m_status = qFromBigEndian(qFromUnaligned<uint16_t>(response.constData() + dataSize));
m_data = response.left(dataSize);
}
}
/*
Builds a command APDU from components according to ISO/IEC 7816.
*/
QByteArray QCommandApdu::build(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2,
QByteArrayView data, uint16_t ne)
{
Q_ASSERT(data.size() <= 0xFFFF);
QByteArray apdu;
apdu.append(static_cast<char>(cla));
apdu.append(static_cast<char>(ins));
apdu.append(static_cast<char>(p1));
apdu.append(static_cast<char>(p2));
bool extendedLc = false;
uint16_t nc = data.size();
if (nc > 0) {
if (nc < 256) {
apdu.append(static_cast<char>(nc));
} else {
extendedLc = true;
apdu.append('\0');
apdu.append(static_cast<char>(nc >> 8));
apdu.append(static_cast<char>(nc & 0xFF));
}
apdu.append(data);
}
if (ne) {
if (ne < 256) {
apdu.append(static_cast<char>(ne));
} else if (ne == 256) {
apdu.append(static_cast<char>('\0'));
} else {
if (!extendedLc)
apdu.append('\0');
apdu.append(static_cast<char>(ne >> 8));
apdu.append(static_cast<char>(ne & 0xFF));
}
}
return apdu;
}
QT_END_NAMESPACE

89
src/nfc/qapduutils_p.h Normal file
View File

@ -0,0 +1,89 @@
/****************************************************************************
**
** Copyright (C) 2022 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$
**
****************************************************************************/
#ifndef QAPDUUTILS_P_H
#define QAPDUUTILS_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/QByteArray>
QT_BEGIN_NAMESPACE
class QResponseApdu
{
public:
static constexpr uint16_t Empty = 0x0000;
static constexpr uint16_t Success = 0x9000;
explicit QResponseApdu(const QByteArray &response = {});
const QByteArray &data() const { return m_data; }
uint16_t status() const { return m_status; }
bool isOk() const { return m_status == Success; }
private:
QByteArray m_data;
uint16_t m_status;
};
namespace QCommandApdu {
// INS byte values for command APDUs
constexpr uint8_t Select = 0xA4;
constexpr uint8_t ReadBinary = 0xB0;
constexpr uint8_t GetData = 0xCA;
constexpr uint8_t UpdateBinary = 0xD6;
QByteArray build(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, QByteArrayView data,
uint16_t ne = 0);
};
QT_END_NAMESPACE
#endif // QAPDUUTILS_P_H

View File

@ -46,6 +46,8 @@
#include "qnearfieldmanager_android_p.h"
#elif defined(IOS_NFC)
#include "qnearfieldmanager_ios_p.h"
#elif defined(PCSC_NFC)
#include "qnearfieldmanager_pcsc_p.h"
#else
#include "qnearfieldmanager_generic_p.h"
#endif

View File

@ -0,0 +1,174 @@
/****************************************************************************
**
** Copyright (C) 2022 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 "qnearfieldmanager_pcsc_p.h"
#include "qnearfieldtarget_pcsc_p.h"
#include "pcsc/qpcscmanager_p.h"
#include "pcsc/qpcsccard_p.h"
#include <QtCore/QLoggingCategory>
#include <QtCore/QThread>
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(QT_NFC_PCSC)
/*
Constructs a new near field manager private implementation.
This object creates a worker thread with an instance of QPcscManager in
it. All the communication with QPcscManager is done using signal-slot
mechanism.
*/
QNearFieldManagerPrivateImpl::QNearFieldManagerPrivateImpl()
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
m_worker = new QPcscManager;
m_workerThread = new QThread(this);
m_workerThread->setObjectName(u"QtNfcThread"_qs);
m_worker->moveToThread(m_workerThread);
connect(m_worker, &QPcscManager::cardInserted, this,
&QNearFieldManagerPrivateImpl::onCardInserted);
connect(this, &QNearFieldManagerPrivateImpl::startTargetDetectionRequest, m_worker,
&QPcscManager::onStartTargetDetectionRequest);
connect(this, &QNearFieldManagerPrivateImpl::stopTargetDetectionRequest, m_worker,
&QPcscManager::onStopTargetDetectionRequest);
m_workerThread->start();
}
/*
Destroys the near field manager private implementation.
*/
QNearFieldManagerPrivateImpl::~QNearFieldManagerPrivateImpl()
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
// Destroy the worker. It calls QThread::quit() on the working thread in
// its destructor.
QMetaObject::invokeMethod(m_worker, &QObject::deleteLater, Qt::QueuedConnection);
m_workerThread->wait();
}
bool QNearFieldManagerPrivateImpl::isEnabled() const
{
return true;
}
bool QNearFieldManagerPrivateImpl::isSupported(QNearFieldTarget::AccessMethod accessMethod) const
{
switch (accessMethod) {
case QNearFieldTarget::TagTypeSpecificAccess:
case QNearFieldTarget::NdefAccess:
return true;
default:
return false;
}
}
bool QNearFieldManagerPrivateImpl::startTargetDetection(QNearFieldTarget::AccessMethod accessMethod)
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
Q_EMIT startTargetDetectionRequest(accessMethod);
return true;
}
void QNearFieldManagerPrivateImpl::stopTargetDetection(const QString &errorMessage)
{
Q_UNUSED(errorMessage);
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
Q_EMIT stopTargetDetectionRequest();
}
/*
Invoked when the worker has detected a new card.
The worker will ensure that the card object remains valid until the manager
emits targetCreatedForCard() signal.
*/
void QNearFieldManagerPrivateImpl::onCardInserted(QPcscCard *card, const QByteArray &uid,
QNearFieldTarget::AccessMethods accessMethods,
int maxInputLength)
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
auto priv = new QNearFieldTargetPrivateImpl(uid, accessMethods, maxInputLength, this);
connect(priv, &QNearFieldTargetPrivateImpl::disconnectRequest, card,
&QPcscCard::onDisconnectRequest);
connect(priv, &QNearFieldTargetPrivateImpl::destroyed, card, &QPcscCard::onTargetDestroyed);
connect(priv, &QNearFieldTargetPrivateImpl::sendCommandRequest, card,
&QPcscCard::onSendCommandRequest);
connect(priv, &QNearFieldTargetPrivateImpl::readNdefMessagesRequest, card,
&QPcscCard::onReadNdefMessagesRequest);
connect(priv, &QNearFieldTargetPrivateImpl::writeNdefMessagesRequest, card,
&QPcscCard::onWriteNdefMessagesRequest);
connect(priv, &QNearFieldTargetPrivateImpl::targetLost, this,
&QNearFieldManagerPrivateImpl::onTargetLost);
connect(card, &QPcscCard::disconnected, priv, &QNearFieldTargetPrivateImpl::onDisconnected);
connect(card, &QPcscCard::invalidated, priv, &QNearFieldTargetPrivateImpl::onInvalidated);
connect(card, &QPcscCard::requestCompleted, priv,
&QNearFieldTargetPrivateImpl::onRequestCompleted);
connect(card, &QPcscCard::ndefMessageRead, priv,
&QNearFieldTargetPrivateImpl::onNdefMessageRead);
auto target = new NearFieldTarget(priv, this);
Q_EMIT targetDetected(target);
// Let the worker know that the card object can be deleted if it is no
// longer needed.
QMetaObject::invokeMethod(card, &QPcscCard::enableAutodelete, Qt::QueuedConnection);
}
void QNearFieldManagerPrivateImpl::onTargetLost(QNearFieldTargetPrivate *target)
{
Q_EMIT targetLost(target->q_ptr);
}
Q_LOGGING_CATEGORY(QT_NFC_PCSC, "qt.nfc.pcsc")
QT_END_NAMESPACE

View File

@ -0,0 +1,91 @@
/****************************************************************************
**
** Copyright (C) 2022 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$
**
****************************************************************************/
#ifndef QNEARFIELDMANAGER_PCSC_P_H
#define QNEARFIELDMANAGER_PCSC_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qnearfieldmanager_p.h"
QT_BEGIN_NAMESPACE
class QPcscManager;
class QPcscCard;
class QThread;
class QNearFieldManagerPrivateImpl : public QNearFieldManagerPrivate
{
Q_OBJECT
public:
QNearFieldManagerPrivateImpl();
~QNearFieldManagerPrivateImpl() override;
bool isEnabled() const override;
bool isSupported(QNearFieldTarget::AccessMethod accessMethod) const override;
bool startTargetDetection(QNearFieldTarget::AccessMethod accessMethod) override;
void stopTargetDetection(const QString &errorMessage) override;
public Q_SLOTS:
void onCardInserted(QPcscCard *card, const QByteArray &uid,
QNearFieldTarget::AccessMethods accessMethods, int maxInputLength);
void onTargetLost(QNearFieldTargetPrivate *target);
Q_SIGNALS:
void startTargetDetectionRequest(QNearFieldTarget::AccessMethod accessMethod);
void stopTargetDetectionRequest();
private:
QThread *m_workerThread;
QPcscManager *m_worker;
};
QT_END_NAMESPACE
#endif // QNEARFIELDMANAGER_PCSC_P_H

View File

@ -0,0 +1,197 @@
/****************************************************************************
**
** Copyright (C) 2022 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 "qnearfieldtarget_pcsc_p.h"
#include "qndefmessage.h"
#include <QtCore/QLoggingCategory>
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(QT_NFC_PCSC)
/*
Construct QNearFieldTargetPrivateImpl instance.
This object communicates with a QPcscCard object that lives inside the
worker thread via signal-slot mechanism.
*/
QNearFieldTargetPrivateImpl::QNearFieldTargetPrivateImpl(
const QByteArray &uid, QNearFieldTarget::AccessMethods accessMethods, int maxInputLength,
QObject *parent)
: QNearFieldTargetPrivate(parent),
m_uid(uid),
m_accessMethods(accessMethods),
m_maxInputLength(maxInputLength)
{
qCDebug(QT_NFC_PCSC) << "New card with UID" << m_uid.toHex(':');
}
QNearFieldTargetPrivateImpl::~QNearFieldTargetPrivateImpl()
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
}
QByteArray QNearFieldTargetPrivateImpl::uid() const
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
return m_uid;
}
QNearFieldTarget::Type QNearFieldTargetPrivateImpl::type() const
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
// Currently NDEF access is only supported for Type 4 tags
if (m_accessMethods & QNearFieldTarget::NdefAccess)
return QNearFieldTarget::NfcTagType4;
return QNearFieldTarget::ProprietaryTag;
}
QNearFieldTarget::AccessMethods QNearFieldTargetPrivateImpl::accessMethods() const
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
return m_accessMethods;
}
bool QNearFieldTargetPrivateImpl::disconnect()
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
if (!m_connected || !m_isValid)
return false;
Q_EMIT disconnectRequest();
return true;
}
int QNearFieldTargetPrivateImpl::maxCommandLength() const
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
return m_maxInputLength;
}
void QNearFieldTargetPrivateImpl::onRequestCompleted(const QNearFieldTarget::RequestId &request,
QNearFieldTarget::Error reason,
const QVariant &result)
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
if (reason == QNearFieldTarget::NoError)
setResponseForRequest(request, result);
else
reportError(reason, request);
}
void QNearFieldTargetPrivateImpl::onNdefMessageRead(const QNdefMessage &message)
{
Q_EMIT ndefMessageRead(message);
}
QNearFieldTarget::RequestId QNearFieldTargetPrivateImpl::sendCommand(const QByteArray &command)
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
if (!m_isValid)
return QNearFieldTarget::RequestId(nullptr);
m_connected = true;
QNearFieldTarget::RequestId reqId(new QNearFieldTarget::RequestIdPrivate);
Q_EMIT sendCommandRequest(reqId, command);
return reqId;
}
QNearFieldTarget::RequestId QNearFieldTargetPrivateImpl::readNdefMessages()
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
if (!m_isValid)
return QNearFieldTarget::RequestId(nullptr);
m_connected = true;
QNearFieldTarget::RequestId reqId(new QNearFieldTarget::RequestIdPrivate);
Q_EMIT readNdefMessagesRequest(reqId);
return reqId;
}
QNearFieldTarget::RequestId
QNearFieldTargetPrivateImpl::writeNdefMessages(const QList<QNdefMessage> &messages)
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
if (!m_isValid)
return QNearFieldTarget::RequestId(nullptr);
m_connected = true;
QNearFieldTarget::RequestId reqId(new QNearFieldTarget::RequestIdPrivate);
Q_EMIT writeNdefMessagesRequest(reqId, messages);
return reqId;
}
void QNearFieldTargetPrivateImpl::onDisconnected()
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
if (!m_connected)
return;
m_connected = false;
Q_EMIT disconnected();
}
void QNearFieldTargetPrivateImpl::onInvalidated()
{
qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
if (!m_isValid)
return;
m_isValid = false;
Q_EMIT targetLost(this);
}
QT_END_NAMESPACE

View File

@ -0,0 +1,103 @@
/****************************************************************************
**
** Copyright (C) 2022q 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$
**
****************************************************************************/
#ifndef QNEARFIELDTARGET_PCSC_P_H
#define QNEARFIELDTARGET_PCSC_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qnearfieldtarget_p.h"
QT_BEGIN_NAMESPACE
class QNearFieldTargetPrivateImpl : public QNearFieldTargetPrivate
{
Q_OBJECT
public:
QNearFieldTargetPrivateImpl(const QByteArray &uid,
QNearFieldTarget::AccessMethods accessMethods, int maxInputLength,
QObject *parent);
~QNearFieldTargetPrivateImpl() override;
QByteArray uid() const override;
QNearFieldTarget::Type type() const override;
QNearFieldTarget::AccessMethods accessMethods() const override;
bool disconnect() override;
int maxCommandLength() const override;
QNearFieldTarget::RequestId sendCommand(const QByteArray &command) override;
QNearFieldTarget::RequestId readNdefMessages() override;
QNearFieldTarget::RequestId writeNdefMessages(const QList<QNdefMessage> &messages) override;
private:
const QByteArray m_uid;
QNearFieldTarget::AccessMethods m_accessMethods;
int m_maxInputLength;
bool m_connected = false;
bool m_isValid = true;
public Q_SLOTS:
void onDisconnected();
void onInvalidated();
void onRequestCompleted(const QNearFieldTarget::RequestId &request,
QNearFieldTarget::Error reason, const QVariant &result);
void onNdefMessageRead(const QNdefMessage &message);
Q_SIGNALS:
void disconnectRequest();
void sendCommandRequest(const QNearFieldTarget::RequestId &request, const QByteArray &command);
void readNdefMessagesRequest(const QNearFieldTarget::RequestId &request);
void writeNdefMessagesRequest(const QNearFieldTarget::RequestId &request,
const QList<QNdefMessage> &messages);
void targetLost(QNearFieldTargetPrivate *target);
};
QT_END_NAMESPACE
#endif // QNEARFIELDTARGET_PCSC_P_H