14#include "keyple/plugin/pcsc/PcscReaderAdapter.hpp"
16#if defined(WIN32) || defined(__MINGW32__) || defined(__MINGW64__)
19#include <PCSC/wintypes.h>
20#include <PCSC/winscard.h>
23#include "keyple/core/plugin/CardIOException.hpp"
24#include "keyple/core/plugin/ReaderIOException.hpp"
25#include "keyple/core/plugin/TaskCanceledException.hpp"
26#include "keyple/core/util/HexUtil.hpp"
27#include "keyple/core/util/cpp/Thread.hpp"
28#include "keyple/core/util/cpp/exception/Exception.hpp"
29#include "keyple/core/util/cpp/exception/IllegalArgumentException.hpp"
30#include "keyple/core/util/cpp/exception/IllegalStateException.hpp"
31#include "keyple/core/util/cpp/exception/InterruptedException.hpp"
32#include "keyple/plugin/pcsc/PcscCardCommunicationProtocol.hpp"
33#include "keyple/plugin/pcsc/PcscPluginAdapter.hpp"
34#include "keyple/plugin/pcsc/cpp/CardTerminals.hpp"
35#include "keyple/plugin/pcsc/cpp/TerminalFactory.hpp"
36#include "keyple/plugin/pcsc/cpp/exception/CardException.hpp"
37#include "keyple/plugin/pcsc/cpp/exception/CardNotPresentException.hpp"
43using keyple::core::plugin::CardIOException;
44using keyple::core::plugin::ReaderIOException;
45using keyple::core::plugin::TaskCanceledException;
46using keyple::core::util::HexUtil;
47using keyple::core::util::cpp::Thread;
48using keyple::core::util::cpp::exception::Exception;
49using keyple::core::util::cpp::exception::IllegalArgumentException;
50using keyple::core::util::cpp::exception::IllegalStateException;
51using keyple::core::util::cpp::exception::InterruptedException;
52using keyple::plugin::pcsc::cpp::CardTerminals;
53using keyple::plugin::pcsc::cpp::TerminalFactory;
54using keyple::plugin::pcsc::cpp::exception::CardException;
55using keyple::plugin::pcsc::cpp::exception::CardNotPresentException;
57PcscReaderAdapter::PcscReaderAdapter(
58 std::shared_ptr<CardTerminal> terminal,
59 std::shared_ptr<PcscPluginAdapter> pluginAdapter,
60 const int cardMonitoringCycleDuration)
61: mIsInitialized(false)
62, mIsPhysicalChannelOpen(false)
63, mCommunicationTerminal(terminal)
64, mName(terminal->getName())
65, mPluginAdapter(pluginAdapter)
66, mCardMonitoringCycleDuration(cardMonitoringCycleDuration)
67, mPingApdu(HexUtil::toByteArray(
"00C0000000"))
68, mIsContactless(false)
69, mProtocol(IsoProtocol::ANY.getValue())
70, mIsModeExclusive(false)
73, mLoopWaitCardRemoval(false)
74, mIsObservationActive(false)
76#if defined(WIN32) || defined(__MINGW32__) || defined(__MINGW64__)
88 mMonitoringTerminal = createMonitoringTerminal(terminal->getName());
91std::shared_ptr<CardTerminal>
92PcscReaderAdapter::createMonitoringTerminal(
const std::string& terminalName)
97 std::shared_ptr<TerminalFactory> monitoringFactory
98 = TerminalFactory::getDefault();
99 std::shared_ptr<CardTerminals> monitoringTerminals
100 = monitoringFactory->terminals();
103 for (
const auto& t : monitoringTerminals->list()) {
104 if (t->getName() == terminalName) {
106 "[readerExt=%] Separate monitoring context created to "
107 "improve Linux compatibility\n",
115 "[readerExt=%] Could not find terminal in separate context, "
116 "using shared context (may cause issues on Linux)\n",
118 return mCommunicationTerminal;
120 }
catch (
const Exception& e) {
123 "[readerExt=%] Could not create separate monitoring context "
124 "[reason=%], using shared context (may cause issues on Linux)\n",
127 return mCommunicationTerminal;
132PcscReaderAdapter::waitForCardInsertion()
135 "[readerExt=%] Starting waiting card insertion [loopLatencyMs=%]\n",
137 mCardMonitoringCycleDuration);
140 mLoopWaitCard =
true;
143 while (mLoopWaitCard) {
144 if (mMonitoringTerminal->waitForCardPresent(
145 mCardMonitoringCycleDuration)) {
147 mLogger->trace(
"[readerExt=%] Card inserted\n", getName());
157 "[readerExt=%] Waiting card insertion stopped\n", getName());
159 }
catch (
const CardException& e) {
161 throw ReaderIOException(
162 "Failed to wait for a card insertion. Reader: " + mName, e);
165 throw TaskCanceledException(
166 "The wait for a card insertion task has been cancelled. Reader: "
171PcscReaderAdapter::stopWaitForCardInsertion()
173 mLoopWaitCard =
false;
177PcscReaderAdapter::isProtocolSupported(
const std::string& readerProtocol)
const
182 const std::string rule
183 = mPluginAdapter->getProtocolRule(readerProtocol);
187 }
catch (
const IllegalArgumentException&) {
195PcscReaderAdapter::activateProtocol(
const std::string& readerProtocol)
198 "[readerExt=%] Activating protocol takes no action [protocol=%]\n",
204PcscReaderAdapter::deactivateProtocol(
const std::string& readerProtocol)
207 "[readerExt=%] de-activating protocol takes no action [protocol=%]\n",
213PcscReaderAdapter::isCurrentProtocol(
const std::string& readerProtocol)
const
220 bool isCurrentProtocol =
false;
222 const std::string protocolRule
223 = mPluginAdapter->getProtocolRule(readerProtocol);
224 if (!protocolRule.empty()) {
225 const std::string atr = HexUtil::toHex(mCard->getATR());
227 = Pattern::compile(protocolRule)->matcher(atr)->matches();
230 return isCurrentProtocol;
232 }
catch (
const IllegalArgumentException&) {
240PcscReaderAdapter::onStartDetection()
242 mIsObservationActive =
true;
246PcscReaderAdapter::onStopDetection()
248 mIsObservationActive =
false;
252PcscReaderAdapter::getName()
const
258PcscReaderAdapter::openPhysicalChannel()
260 if (mCard !=
nullptr) {
270 "[readerExt=%] Opening card physical channel [protocol=%]\n",
274 mCard = mCommunicationTerminal->connect(mProtocol);
275 if (mIsModeExclusive) {
276 mCard->beginExclusive();
278 "[readerExt=%] Card physical channel opened [mode=EXCLUSIVE]"
284 "[readerExt=%] Card physical channel opened [mode=SHARED]\n",
288 mChannel = mCard->getBasicChannel();
290 }
catch (
const CardNotPresentException& e) {
291 throw CardIOException(
"Card removed. Reader: " + mName, e);
293 }
catch (
const CardException& e) {
294 throw ReaderIOException(
295 "Failed to open the physical channel. Reader: " + mName, e);
300PcscReaderAdapter::closePhysicalChannel()
306 if (!mIsObservationActive) {
311void PcscReaderAdapter::disconnect()
314 if (mCard !=
nullptr) {
317 PcscCardCommunicationProtocol::INNOVATRON_B_PRIME
319 ? DisconnectionMode::UNPOWER
320 : mDisconnectionMode;
323 effectiveMode == DisconnectionMode::UNPOWER
324 || effectiveMode == DisconnectionMode::RESET);
326 resetReaderState(effectiveMode);
329 }
catch (
const CardNotPresentException& e) {
331 throw CardIOException(
"Card removed. Reader: " + mName, e);
333 }
catch (
const CardException& e) {
335 throw ReaderIOException(
336 "Failed to close the physical channel. Reader: " + mName, e);
345 case DisconnectionMode::RESET:
346 return SCARD_RESET_CARD;
347 case DisconnectionMode::LEAVE:
348 return SCARD_LEAVE_CARD;
349 case DisconnectionMode::UNPOWER:
350 return SCARD_UNPOWER_CARD;
351 case DisconnectionMode::EJECT:
352 return SCARD_EJECT_CARD;
354 throw IllegalArgumentException(std::string(
"Unknown DisconnectionMode: ") + mode);
361 if (effectiveMode == DisconnectionMode::UNPOWER) {
362 mCommunicationTerminal->connect(
"*")->disconnect(
false);
365 }
catch (
const CardException& ) {
371PcscReaderAdapter::isPhysicalChannelOpen()
const
373 return mIsPhysicalChannelOpen;
377PcscReaderAdapter::checkCardPresence()
380 const bool isCardPresent = mCommunicationTerminal->isCardPresent();
381 closePhysicalChannelSafely();
383 return isCardPresent;
385 }
catch (
const CardException& e) {
386 throw ReaderIOException(
387 "Failed to check card presence. Reader: " + mName, e);
392PcscReaderAdapter::closePhysicalChannelSafely()
397 }
catch (
const Exception&) {
403PcscReaderAdapter::resetContext()
406 mIsPhysicalChannelOpen =
false;
410PcscReaderAdapter::getPowerOnData()
const
412 return HexUtil::toHex(mCard->getATR());
415const std::vector<uint8_t>
416PcscReaderAdapter::transmitApdu(
const std::vector<uint8_t>& apduCommandData)
418 std::vector<uint8_t> apduResponseData;
422 apduResponseData = mChannel->transmit(apduCommandData);
424 }
catch (
const CardNotPresentException& e) {
425 throw CardIOException(
426 "Card is not present. Unable to transmit APDU. Reader: "
430 }
catch (
const CardException& e) {
431 if (e.getMessage().find(
"CARD") != std::string::npos ||
432 e.getMessage().find(
"NOT_TRANSACTED") != std::string::npos ||
433 e.getMessage().find(
"INVALID_ATR") != std::string::npos) {
434 throw CardIOException(
435 "Failed to communicate with card. Unable to transmit "
441 throw ReaderIOException(
442 "Failed to communicate with card reader. Unable to "
443 "transmit APDU. Reader: "
448 }
catch (
const IllegalStateException& e) {
450 throw CardIOException(
451 "Card could have been removed prematurely. Unable to "
452 "transmit APDU. Reader: "
456 }
catch (
const IllegalArgumentException& e) {
458 throw CardIOException(
459 "Card could have been removed prematurely. Unable to "
460 "transmit APDU. Reader: "
467 throw CardIOException(
468 "Card channel is null. Unable to transmit APDU. Reader: "
472 return apduResponseData;
476PcscReaderAdapter::isContactless()
478 if (!mIsInitialized) {
484 mIsContactless = mPluginAdapter->isContactless(getName());
487 return mIsContactless;
491PcscReaderAdapter::onUnregister()
497PcscReaderAdapter::monitorCardPresenceDuringProcessing()
499 doWaitForCardRemoval(
501 PcscCardCommunicationProtocol::INNOVATRON_B_PRIME.getName()));
505PcscReaderAdapter::stopCardPresenceMonitoringDuringProcessing()
507 stopWaitForCardRemoval();
511PcscReaderAdapter::waitForCardRemoval()
513 doWaitForCardRemoval(
true);
517PcscReaderAdapter::doWaitForCardRemoval(
const bool allowPolling)
519 mLogger->trace(
"[readerExt=%] Starting waiting card removal\n", mName);
521 mLoopWaitCardRemoval =
true;
524 if (allowPolling && mDisconnectionMode == DisconnectionMode::UNPOWER) {
525 waitForCardRemovalByPolling();
527 waitForCardRemovalStandard();
530 }
catch (
const ReaderIOException&) {
531 if (mLoopWaitCardRemoval) {
535 }
catch (
const Exception& e) {
537 "[readerExt=%] Failed to disconnect card during card "
538 "removal sequence [reason=%]\n",
547 if (mLoopWaitCardRemoval) {
551 }
catch (
const Exception& e) {
553 "[readerExt=%] Failed to disconnect card during card removal "
554 "sequence [reason=%]\n",
560 if (!mLoopWaitCardRemoval) {
562 "[readerExt=%] Waiting card removal stopped\n", mName);
564 mLogger->trace(
"[readerExt=%] Card removed\n", mName);
567 if (!mLoopWaitCardRemoval) {
568 throw TaskCanceledException(
569 "The wait for the card removal task has been cancelled. "
576PcscReaderAdapter::waitForCardRemovalByPolling()
579 while (mLoopWaitCardRemoval) {
580 transmitApdu(mPingApdu);
587 }
catch (
const CardIOException& e) {
589 "[readerExt=%] Expected IOException received while waiting for "
590 "card removal [reason=%]\n",
594 }
catch (
const ReaderIOException& e) {
596 "[readerExt=%] Expected IOException received while waiting for "
597 "card removal [reason=%]\n",
601 }
catch (
const InterruptedException& e) {
603 "[readerExt=%] InterruptedException received while waiting for "
611void PcscReaderAdapter::waitForCardRemovalStandard()
614 while (mLoopWaitCardRemoval) {
615 if (mMonitoringTerminal->waitForCardAbsent(
616 mCardMonitoringCycleDuration)) {
624 }
catch (
const CardException& e) {
626 "Expected CardException while waiting for card removal: %\n",
629 throw ReaderIOException(
630 "Failed to wait for the card removal. Reader: " + mName, e);
635PcscReaderAdapter::stopWaitForCardRemoval()
637 mLoopWaitCardRemoval =
false;
641PcscReaderAdapter::setSharingMode(
const SharingMode sharingMode)
644 "[readerExt=%] Set sharing mode [value=%]\n", getName(), sharingMode);
646 if (sharingMode == SharingMode::SHARED) {
648 if (mCard !=
nullptr) {
650 mCard->endExclusive();
652 }
catch (
const CardException& e) {
653 throw IllegalStateException(
654 "Failed to disable exclusive mode. Reader: " + mName, e);
658 mIsModeExclusive =
false;
660 }
else if (sharingMode == SharingMode::EXCLUSIVE) {
661 mIsModeExclusive =
true;
668PcscReaderAdapter::setContactless(
const bool contactless)
671 "[readerExt=%] Set contactless type [value=%]\n",
675 mIsContactless = contactless;
676 mIsInitialized =
true;
682PcscReaderAdapter::setIsoProtocol(
const IsoProtocol& isoProtocol)
685 "[readerExt=%] Set ISO protocol [protocol=%, value=%]\n",
688 isoProtocol.getValue());
690 mProtocol = isoProtocol.getValue();
696PcscReaderAdapter::setDisconnectionMode(
700 "[readerExt=%] Set disconnection mode [value=%]\n",
704 mDisconnectionMode = disconnectionMode;
709const std::vector<uint8_t>
710PcscReaderAdapter::transmitControlCommand(
711 const int commandId,
const std::vector<uint8_t>& command)
713 std::vector<uint8_t> response;
714 const int controlCode
715 = mIsWindows ? 0x00310000 | (commandId << 2) : 0x42000000 | commandId;
718 if (mCard !=
nullptr) {
720 response = mCard->transmitControlCommand(controlCode, command);
722 std::shared_ptr<Card> virtualCard
723 = mCommunicationTerminal->connect(
"DIRECT");
724 response = virtualCard->transmitControlCommand(controlCode, command);
725 virtualCard->disconnect(
false);
728 }
catch (
const CardException& e) {
729 throw IllegalStateException(
730 "Failed to transmit control command. Reader: " + mName, e);
737PcscReaderAdapter::getIoctlCcidEscapeCommandId()
const
739 return mIsWindows ? 3500 : 1;
PcscReader::DisconnectionMode DisconnectionMode