7#include <QtCore/QLoggingCategory>
8#include <QtCore/QThread>
9#include <QtCore/QTimer>
13Q_DECLARE_LOGGING_CATEGORY(QT_NFC_PCSC)
15static constexpr auto PollIntervalEnvVar =
"QT_NFC_POLL_INTERVAL_MS";
20 qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
23 QByteArray intervalEnv = qgetenv(PollIntervalEnvVar);
24 if (!intervalEnv.isEmpty()) {
25 if (
int intervalFromEnv = intervalEnv.toInt(); intervalFromEnv > 0)
26 pollInterval = intervalFromEnv;
28 qCWarning(QT_NFC_PCSC) << PollIntervalEnvVar <<
"set to an invalid value";
31 m_stateUpdateTimer =
new QTimer(
this);
32 m_stateUpdateTimer->setInterval(pollInterval);
33 connect(m_stateUpdateTimer, &QTimer::timeout,
this, &QPcscManager::onStateUpdate);
38 qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
41 for (
auto slot : std::as_const(m_slots))
42 slot->invalidateInsertedCard();
43 SCardReleaseContext(m_context);
52 for (
auto &state : m_slotStates) {
53 auto slot =
static_cast<QPcscSlot *>(state.pvUserData);
54 Q_ASSERT(slot !=
nullptr);
56 if ((state.dwEventState & SCARD_STATE_UNKNOWN) != 0)
59 if (state.dwEventState == state.dwCurrentState)
62 qCDebug(QT_NFC_PCSC) << Qt::hex << state.dwCurrentState <<
"=>" << state.dwEventState <<
":"
65 state.dwCurrentState = state.dwEventState;
66 slot->processStateChange(state.dwEventState, m_targetDetectionRunning);
71
72
75 for (
auto &state : m_slotStates) {
76 auto slot =
static_cast<QPcscSlot *>(state.pvUserData);
77 Q_ASSERT(slot !=
nullptr);
81 if ((state.dwEventState & SCARD_STATE_UNKNOWN) != 0
82 || !(m_targetDetectionRunning || slot->hasCard())) {
83 qCDebug(QT_NFC_PCSC) <<
"Removing slot:" << slot;
84 state.dwEventState = SCARD_STATE_UNKNOWN;
85 slot->invalidateInsertedCard();
86 m_slots.remove(slot->name());
88 state.pvUserData =
nullptr;
93 m_slotStates.removeIf(
94 [](
const auto &state) {
return (state.dwEventState & SCARD_STATE_UNKNOWN) != 0; });
98
99
100
103 Q_ASSERT(m_hasContext);
105#ifndef SCARD_AUTOALLOCATE
108#define LIST_READER_BUFFER_EXTRA 1024
111 buf.resize(listSize);
112 QPcscSlotName::Ptr list = buf.ptr();
114 auto ret = SCardListReaders(m_context,
nullptr, list, &listSize);
116 QPcscSlotName::Ptr list;
117 DWORD listSize = SCARD_AUTOALLOCATE;
118 auto ret = SCardListReaders(m_context,
nullptr,
reinterpret_cast<QPcscSlotName::Ptr>(&list),
122 if (ret == LONG(SCARD_E_NO_READERS_AVAILABLE)) {
124 ret = SCARD_S_SUCCESS;
126#ifndef SCARD_AUTOALLOCATE
127 else if (ret == LONG(SCARD_E_INSUFFICIENT_BUFFER)) {
132 buf.resize(listSize);
135 ret = SCardListReaders(m_context,
nullptr, list, &listSize);
136 if (ret == LONG(SCARD_E_NO_READERS_AVAILABLE)) {
138 ret = SCARD_S_SUCCESS;
141#undef LIST_READER_BUFFER_EXTRA
144 if (ret != SCARD_S_SUCCESS) {
145 qCDebug(QT_NFC_PCSC) <<
"Failed to list readers:" << QPcsc::errorMessage(ret);
149#ifdef SCARD_AUTOALLOCATE
150 auto freeList = qScopeGuard([
this, list] {
152 Q_ASSERT(m_hasContext);
153 SCardFreeMemory(m_context, list);
158 QSet<QPcscSlotName> presentSlots;
160 if (list !=
nullptr) {
161 for (
const auto *p = list; *p; p += QPcscSlotName::nameSize(p) + 1)
167 for (
auto &state : m_slotStates) {
168 auto slot =
static_cast<QPcscSlot *>(state.pvUserData);
169 Q_ASSERT(slot !=
nullptr);
171 if (presentSlots.contains(slot->name()))
172 presentSlots.remove(slot->name());
174 state.dwEventState = SCARD_STATE_UNKNOWN;
177 if (!m_targetDetectionRunning)
181 for (
auto &&slotName : std::as_const(presentSlots)) {
182 QPcscSlot *slot =
new QPcscSlot(slotName,
this);
183 qCDebug(QT_NFC_PCSC) <<
"New slot:" << slot;
185 m_slots[slotName] = slot;
187 SCARD_READERSTATE state {};
188 state.pvUserData = slot;
189 state.szReader = slot->name().ptr();
190 state.dwCurrentState = SCARD_STATE_UNAWARE;
192 m_slotStates.append(state);
198 qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
200 Q_ASSERT(!m_hasContext);
202 LONG ret = SCardEstablishContext(SCARD_SCOPE_USER,
nullptr,
nullptr, &m_context);
203 if (ret != SCARD_S_SUCCESS) {
204 qCWarning(QT_NFC_PCSC) <<
"Failed to establish context:" << QPcsc::errorMessage(ret);
215 if (!m_targetDetectionRunning) {
216 m_stateUpdateTimer->stop();
220 if (!establishContext())
227 if (m_slotStates.isEmpty()) {
228 if (!m_targetDetectionRunning) {
230 SCardReleaseContext(m_context);
231 m_hasContext =
false;
233 m_stateUpdateTimer->stop();
248 LONG ret = SCardGetStatusChange(m_context, 0, m_slotStates.data(), m_slotStates.size());
250 if (ret == SCARD_S_SUCCESS || ret == LONG(SCARD_E_UNKNOWN_READER)) {
251 processSlotUpdates();
253 }
else if (ret == LONG(SCARD_E_CANCELLED) || ret == LONG(SCARD_E_UNKNOWN_READER)
254 || ret == LONG(SCARD_E_TIMEOUT)) {
257 qCWarning(QT_NFC_PCSC) <<
"SCardGetStatusChange failed:" << QPcsc::errorMessage(ret);
262 Q_ASSERT(m_hasContext);
263 m_hasContext =
false;
264 for (
auto slot : std::as_const(m_slots)) {
265 slot->invalidateInsertedCard();
268 SCardReleaseContext(m_context);
270 m_slotStates.clear();
274void QPcscManager::onStartTargetDetectionRequest(QNearFieldTarget::AccessMethod accessMethod)
276 qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
278 m_requestedMethod = accessMethod;
280 if (m_targetDetectionRunning)
283 m_targetDetectionRunning =
true;
284 m_stateUpdateTimer->start();
289 qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
290 m_targetDetectionRunning =
false;
295 qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
296 Q_ASSERT(slot !=
nullptr);
297 Q_ASSERT(m_hasContext);
299 SCARDHANDLE cardHandle;
300 DWORD activeProtocol;
302 LONG ret = SCardConnect(m_context, slot->name().ptr(), SCARD_SHARE_SHARED,
303 SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &cardHandle, &activeProtocol);
304 if (ret != SCARD_S_SUCCESS) {
305 qCDebug(QT_NFC_PCSC) <<
"Failed to connect to card:" << QPcsc::errorMessage(ret);
306 retryCardDetection(slot);
310 auto card =
new QPcscCard(cardHandle, activeProtocol,
this);
311 auto uid = card->readUid();
312 auto maxInputLength = card->readMaxInputLength();
314 QNearFieldTarget::AccessMethods accessMethods = QNearFieldTarget::TagTypeSpecificAccess;
315 if (card->supportsNdef())
316 accessMethods |= QNearFieldTarget::NdefAccess;
318 if (m_requestedMethod != QNearFieldTarget::UnknownAccess
319 && (accessMethods & m_requestedMethod) == 0) {
320 qCDebug(QT_NFC_PCSC) <<
"Dropping card without required access support";
325 if (!card->isValid()) {
326 qCDebug(QT_NFC_PCSC) <<
"Card became invalid";
329 retryCardDetection(slot);
334 Q_EMIT cardInserted(card, uid, accessMethods, maxInputLength);
340
341
342
343
344
347 qCDebug(QT_NFC_PCSC) << Q_FUNC_INFO;
349 for (
auto &state : m_slotStates) {
350 if (state.pvUserData == slot) {
351 state.dwCurrentState = SCARD_STATE_UNAWARE;
void onStopTargetDetectionRequest()
QPcscCard * connectToCard(QPcscSlot *slot)
static constexpr int DefaultPollIntervalMs
#define LIST_READER_BUFFER_EXTRA