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:
parent
e27c6600ec
commit
44d474a0fc
|
@ -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()
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
qt_find_package(PCSCLite PROVIDED_TARGETS PkgConfig::PCSCLite)
|
||||
|
||||
qt_feature("pcsclite" PUBLIC
|
||||
LABEL "PCSCLite"
|
||||
CONDITION PCSCLITE_FOUND)
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
*/
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue