diff --git a/CMakeLists.txt b/CMakeLists.txt
index 576f6e7..5d8b5a8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -138,9 +138,12 @@ IF(NOT WIN32)
IF(LIBNFC_DRIVER_PN53X_USB)
SET(PKG_REQ ${PKG_REQ} "libusb")
ENDIF(LIBNFC_DRIVER_PN53X_USB)
- IF(LIBNFC_DRIVER_ACR122)
+ IF(LIBNFC_DRIVER_PCSC)
SET(PKG_REQ ${PKG_REQ} "libpcsclite")
ENDIF(LIBNFC_DRIVER_ACR122)
+ IF(LIBNFC_DRIVER_ACR122_PCSC)
+ SET(PKG_REQ ${PKG_REQ} "libpcsclite")
+ ENDIF(LIBNFC_DRIVER_ACR122_PCSC)
# CMake lists are separated by a semi colon, replace with colon
STRING(REPLACE ";" "," PKG_CONFIG_REQUIRES "${PKG_REQ}")
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libnfc.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libnfc.pc @ONLY)
diff --git a/cmake/modules/LibnfcDrivers.cmake b/cmake/modules/LibnfcDrivers.cmake
index a79c229..e3806d4 100644
--- a/cmake/modules/LibnfcDrivers.cmake
+++ b/cmake/modules/LibnfcDrivers.cmake
@@ -1,3 +1,4 @@
+SET(LIBNFC_DRIVER_PCSC OFF CACHE BOOL "Enable PC/SC reader support (Depends on PC/SC)")
SET(LIBNFC_DRIVER_ACR122_PCSC OFF CACHE BOOL "Enable ACR122 support (Depends on PC/SC)")
SET(LIBNFC_DRIVER_ACR122_USB ON CACHE BOOL "Enable ACR122 support (Direct USB connection)")
SET(LIBNFC_DRIVER_ACR122S ON CACHE BOOL "Enable ACR122S support (Use serial port)")
@@ -12,6 +13,12 @@ ENDIF(WIN32)
SET(LIBNFC_DRIVER_PN532_UART ON CACHE BOOL "Enable PN532 UART support (Use serial port)")
SET(LIBNFC_DRIVER_PN53X_USB ON CACHE BOOL "Enable PN531 and PN531 USB support (Depends on libusb)")
+IF(LIBNFC_DRIVER_PCSC)
+ FIND_PACKAGE(PCSC REQUIRED)
+ ADD_DEFINITIONS("-DDRIVER_PCSC_ENABLED")
+ SET(DRIVERS_SOURCES ${DRIVERS_SOURCES} "drivers/pcsc")
+ENDIF(LIBNFC_DRIVER_PCSC)
+
IF(LIBNFC_DRIVER_ACR122_PCSC)
FIND_PACKAGE(PCSC REQUIRED)
ADD_DEFINITIONS("-DDRIVER_ACR122_PCSC_ENABLED")
diff --git a/libnfc/drivers/Makefile.am b/libnfc/drivers/Makefile.am
index 4432c5c..3f4d768 100644
--- a/libnfc/drivers/Makefile.am
+++ b/libnfc/drivers/Makefile.am
@@ -7,6 +7,10 @@ libnfcdrivers_la_SOURCES =
libnfcdrivers_la_CFLAGS = @DRIVERS_CFLAGS@ -I$(top_srcdir)/libnfc -I$(top_srcdir)/libnfc/buses
libnfcdrivers_la_LIBADD =
+if DRIVER_PCSC_ENABLED
+libnfcdrivers_la_SOURCES += pcsc.c pcsc.h
+endif
+
if DRIVER_ACR122_PCSC_ENABLED
libnfcdrivers_la_SOURCES += acr122_pcsc.c acr122_pcsc.h
endif
diff --git a/libnfc/drivers/acr122_pcsc.c b/libnfc/drivers/acr122_pcsc.c
index 1875166..f5cc02e 100644
--- a/libnfc/drivers/acr122_pcsc.c
+++ b/libnfc/drivers/acr122_pcsc.c
@@ -93,7 +93,7 @@ const struct pn53x_io acr122_pcsc_io;
// Prototypes
char *acr122_pcsc_firmware(nfc_device *pnd);
-const char *supported_devices[] = {
+static const char *supported_devices[] = {
"ACS ACR122", // ACR122U & Touchatag, last version
"ACS ACR 38U-CCID", // Touchatag, early version
"ACS ACR38U-CCID", // Touchatag, early version, under MacOSX
diff --git a/libnfc/drivers/pcsc.c b/libnfc/drivers/pcsc.c
new file mode 100644
index 0000000..d89ae7e
--- /dev/null
+++ b/libnfc/drivers/pcsc.c
@@ -0,0 +1,807 @@
+/*-
+ * Free/Libre Near Field Communication (NFC) library
+ *
+ * Libnfc historical contributors:
+ * Copyright (C) 2019 Frank Morgner
+ * See AUTHORS file for a more comprehensive list of contributors.
+ * Additional contributors of this file:
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see
+ */
+
+/**
+ * @file pcsc.c
+ * @brief Driver for non-ACR122 devices behind PC/SC
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif // HAVE_CONFIG_H
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "drivers/pcsc.h"
+#include "nfc-internal.h"
+
+// Bus
+#ifdef __APPLE__
+#include
+#include
+#else
+#ifndef _Win32
+#include
+#endif
+#include
+#endif
+
+#define PCSC_DRIVER_NAME "pcsc"
+
+#include
+
+#define LOG_GROUP NFC_LOG_GROUP_DRIVER
+#define LOG_CATEGORY "libnfc.driver.pcsc"
+
+static const char *supported_devices[] = {
+ "ACS ACR122", // ACR122U & Touchatag, last version
+ "ACS ACR 38U-CCID", // Touchatag, early version
+ "ACS ACR38U-CCID", // Touchatag, early version, under MacOSX
+ "ACS AET65", // Touchatag using CCID driver version >= 1.4.6
+ " CCID USB", // ??
+ NULL
+};
+
+struct pcsc_data {
+ SCARDHANDLE hCard;
+ SCARD_IO_REQUEST ioCard;
+ DWORD dwShareMode;
+ DWORD last_error;
+};
+
+#define DRIVER_DATA(pnd) ((struct pcsc_data*)(pnd->driver_data))
+
+static SCARDCONTEXT _SCardContext;
+static int _iSCardContextRefCount = 0;
+
+const nfc_baud_rate pcsc_supported_brs[] = {NBR_106, NBR_424, 0};
+const nfc_modulation_type pcsc_supported_mts[] = {NMT_ISO14443A, NMT_ISO14443B, 0};
+
+SCARDCONTEXT *
+pcsc_get_scardcontext(void)
+{
+ if (_iSCardContextRefCount == 0) {
+ if (SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &_SCardContext) != SCARD_S_SUCCESS)
+ return NULL;
+ }
+ _iSCardContextRefCount++;
+
+ return &_SCardContext;
+}
+
+void
+pcsc_free_scardcontext(void)
+{
+ if (_iSCardContextRefCount) {
+ _iSCardContextRefCount--;
+ if (!_iSCardContextRefCount) {
+ SCardReleaseContext(_SCardContext);
+ }
+ }
+}
+
+#define ICC_TYPE_UNKNOWN 0
+#define ICC_TYPE_14443A 5
+#define ICC_TYPE_14443B 6
+
+int pcsc_transmit(struct nfc_device *pnd, const uint8_t *tx, const size_t tx_len, uint8_t *rx, size_t *rx_len)
+{
+ struct pcsc_data *data = pnd->driver_data;
+ DWORD dw_rx_len = *rx_len;
+
+ LOG_HEX(NFC_LOG_GROUP_COM, "TX", tx, tx_len);
+
+ data->last_error = SCardTransmit(data->hCard, &data->ioCard, tx, tx_len,
+ NULL, rx, &dw_rx_len);
+ if (data->last_error != SCARD_S_SUCCESS) {
+ log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "PCSC transmit failed");
+ return NFC_EIO;
+ }
+ *rx_len = dw_rx_len;
+
+ LOG_HEX(NFC_LOG_GROUP_COM, "RX", rx, *rx_len);
+
+ return NFC_SUCCESS;
+}
+
+int pcsc_get_status(struct nfc_device *pnd, int *target_present, uint8_t *atr, size_t *atr_len)
+{
+ struct pcsc_data *data = pnd->driver_data;
+ DWORD dw_atr_len = *atr_len, reader_len, state, protocol;
+
+ data->last_error = SCardStatus(data->hCard, NULL, &reader_len, &state, &protocol, atr, &dw_atr_len);
+ if (data->last_error != SCARD_S_SUCCESS
+ && data->last_error != SCARD_W_RESET_CARD) {
+ log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Get status failed");
+ return NFC_EIO;
+ }
+
+ *target_present = state & SCARD_PRESENT;
+ *atr_len = dw_atr_len;
+
+ return NFC_SUCCESS;
+}
+
+int pcsc_reconnect(struct nfc_device *pnd, DWORD share_mode, DWORD protocol, DWORD disposition)
+{
+ struct pcsc_data *data = pnd->driver_data;
+
+ data->last_error = SCardReconnect(data->hCard, share_mode, protocol, disposition, &data->ioCard.dwProtocol);
+ if (data->last_error != SCARD_S_SUCCESS
+ && data->last_error != SCARD_W_RESET_CARD) {
+ log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Reconnect failed");
+ return NFC_EIO;
+ }
+
+ data->dwShareMode = share_mode;
+
+ return NFC_SUCCESS;
+}
+
+uint8_t pcsc_get_icc_type(struct nfc_device *pnd)
+{
+ struct pcsc_data *data = pnd->driver_data;
+ uint8_t it = 0;
+ DWORD dwItLen = sizeof it;
+ data->last_error = SCardGetAttrib(data->hCard, SCARD_ATTR_ICC_TYPE_PER_ATR, &it, &dwItLen);
+ return it;
+}
+
+int pcsc_get_uid(struct nfc_device *pnd, uint8_t *uid, size_t uid_len)
+{
+ const uint8_t get_data[] = {0xFF, 0xCA, 0x00, 0x00, 0x00};
+ uint8_t resp[256 + 2];
+ size_t resp_len = sizeof resp;
+
+ pnd->last_error = pcsc_transmit(pnd, get_data, sizeof get_data, resp, &resp_len);
+ if (pnd->last_error != NFC_SUCCESS)
+ return pnd->last_error;
+
+ if (resp_len < 2) {
+ log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Reader doesn't support request for UID");
+ pnd->last_error = NFC_EDEVNOTSUPP;
+ return pnd->last_error;
+ }
+ if (uid_len < resp_len - 2) {
+ log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "UID too big");
+ pnd->last_error = NFC_ESOFT;
+ return pnd->last_error;
+ }
+
+ memcpy(uid, resp, resp_len - 2);
+ return resp_len - 2;
+}
+
+int pcsc_props_to_target(uint8_t it, const uint8_t *patr, size_t szatr, const uint8_t *puid, int szuid, const nfc_modulation_type nmt, nfc_target *pnt)
+{
+ if (NULL != pnt) {
+ switch (nmt) {
+ case NMT_ISO14443A:
+ if ((it == ICC_TYPE_UNKNOWN || it == ICC_TYPE_14443A)
+ && (szuid <= 0 || szuid == 4 || szuid == 7 || szuid == 10)
+ && NULL != patr && szatr >= 5
+ && patr[0] == 0x3B
+ && patr[1] == (0x80 | ((uint8_t)(szatr - 5)))
+ && patr[2] == 0x80
+ && patr[3] == 0x01) {
+ memset(pnt, 0, sizeof * pnt);
+ pnt->nm.nmt = NMT_ISO14443A;
+ pnt->nm.nbr = pcsc_supported_brs[0];
+ if (szuid > 0) {
+ memcpy(pnt->nti.nai.abtUid, puid, szuid);
+ pnt->nti.nai.szUidLen = szuid;
+ }
+ /* SAK_ISO14443_4_COMPLIANT */
+ pnt->nti.nai.btSak = 0x20;
+ /* Choose TL, TA, TB, TC according to Mifare DESFire */
+ memcpy(pnt->nti.nai.abtAts, "\x75\x77\x81\x02", 4);
+ /* copy historical bytes */
+ memcpy(pnt->nti.nai.abtAts + 4, patr + 4, (uint8_t)(szatr - 5));
+ pnt->nti.nai.szAtsLen = 4 + (uint8_t)(szatr - 5);
+ return NFC_SUCCESS;
+ }
+ break;
+ case NMT_ISO14443B:
+ if ((ICC_TYPE_UNKNOWN == 0 || ICC_TYPE_14443B == 6)
+ && (szuid <= 0 || szuid == 8)
+ && NULL != patr && szatr == 5 + 8
+ && patr[0] == 0x3B
+ && patr[1] == (0x80 | 0x08)
+ && patr[2] == 0x80
+ && patr[3] == 0x01) {
+ memset(pnt, 0, sizeof * pnt);
+ pnt->nm.nmt = NMT_ISO14443B;
+ pnt->nm.nbr = pcsc_supported_brs[0];
+ memcpy(pnt->nti.nbi.abtApplicationData, patr + 4, 4);
+ memcpy(pnt->nti.nbi.abtProtocolInfo, patr + 8, 3);
+ /* PI_ISO14443_4_SUPPORTED */
+ pnt->nti.nbi.abtProtocolInfo[1] = 0x01;
+ return NFC_SUCCESS;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return NFC_EINVARG;
+}
+
+#define PCSC_MAX_DEVICES 16
+/**
+ * @brief List opened devices
+ *
+ * Probe PCSC to find any reader but the ACR122 devices (ACR122U and Touchatag/Tikitag).
+ *
+ * @param connstring array of nfc_connstring where found device's connection strings will be stored.
+ * @param connstrings_len size of connstrings array.
+ * @return number of devices found.
+ */
+static size_t
+pcsc_scan(const nfc_context *context, nfc_connstring connstrings[], const size_t connstrings_len)
+{
+ (void) context;
+ size_t szPos = 0;
+ char acDeviceNames[256 + 64 * PCSC_MAX_DEVICES];
+ size_t szDeviceNamesLen = sizeof(acDeviceNames);
+ SCARDCONTEXT *pscc;
+ int i;
+
+ // Clear the reader list
+ memset(acDeviceNames, '\0', szDeviceNamesLen);
+
+ // Test if context succeeded
+ if (!(pscc = pcsc_get_scardcontext())) {
+ log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_INFO, "Warning: %s", "PCSC context not found (make sure PCSC daemon is running).");
+ return 0;
+ }
+ // Retrieve the string array of all available pcsc readers
+ DWORD dwDeviceNamesLen = szDeviceNamesLen;
+ if (SCardListReaders(*pscc, NULL, acDeviceNames, &dwDeviceNamesLen) != SCARD_S_SUCCESS)
+ return 0;
+
+ size_t device_found = 0;
+ while ((acDeviceNames[szPos] != '\0') && (device_found < connstrings_len)) {
+ bool bSupported = false;
+ for (i = 0; supported_devices[i] && !bSupported; i++) {
+ int l = strlen(supported_devices[i]);
+ bSupported = 0 == !strncmp(supported_devices[i], acDeviceNames + szPos, l);
+ }
+
+ if (bSupported) {
+ // Supported non-ACR122 device found
+ snprintf(connstrings[device_found], sizeof(nfc_connstring), "%s:%s", PCSC_DRIVER_NAME, acDeviceNames + szPos);
+ device_found++;
+ } else {
+ log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Skipping PCSC device [%s] as it is supported by acr122_pcsc driver.", acDeviceNames + szPos);
+ }
+
+ // Find next device name position
+ while (acDeviceNames[szPos++] != '\0');
+ }
+ pcsc_free_scardcontext();
+
+ return device_found;
+}
+
+struct pcsc_descriptor {
+ char *pcsc_device_name;
+};
+
+static nfc_device *
+pcsc_open(const nfc_context *context, const nfc_connstring connstring)
+{
+ struct pcsc_descriptor ndd;
+ int connstring_decode_level = connstring_decode(connstring, PCSC_DRIVER_NAME, "pcsc", &ndd.pcsc_device_name, NULL);
+
+ if (connstring_decode_level < 1) {
+ return NULL;
+ }
+
+ nfc_connstring fullconnstring;
+ if (connstring_decode_level == 1) {
+ // Device was not specified, take the first one we can find
+ size_t szDeviceFound = pcsc_scan(context, &fullconnstring, 1);
+ if (szDeviceFound < 1)
+ return NULL;
+ connstring_decode_level = connstring_decode(fullconnstring, PCSC_DRIVER_NAME, "pcsc", &ndd.pcsc_device_name, NULL);
+ if (connstring_decode_level < 2) {
+ return NULL;
+ }
+ } else {
+ memcpy(fullconnstring, connstring, sizeof(nfc_connstring));
+ }
+ if (strlen(ndd.pcsc_device_name) < 5) { // We can assume it's a reader ID as pcsc_name always ends with "NN NN"
+ // Device was not specified, only ID, retrieve it
+ size_t index;
+ if (sscanf(ndd.pcsc_device_name, "%4" SCNuPTR, &index) != 1) {
+ free(ndd.pcsc_device_name);
+ return NULL;
+ }
+ nfc_connstring *ncs = malloc(sizeof(nfc_connstring) * (index + 1));
+ if (!ncs) {
+ perror("malloc");
+ free(ndd.pcsc_device_name);
+ return NULL;
+ }
+ size_t szDeviceFound = pcsc_scan(context, ncs, index + 1);
+ if (szDeviceFound < index + 1) {
+ free(ncs);
+ free(ndd.pcsc_device_name);
+ return NULL;
+ }
+ strncpy(fullconnstring, ncs[index], sizeof(nfc_connstring));
+ fullconnstring[sizeof(nfc_connstring) - 1] = '\0';
+ free(ncs);
+ connstring_decode_level = connstring_decode(fullconnstring, PCSC_DRIVER_NAME, "pcsc", &ndd.pcsc_device_name, NULL);
+
+ if (connstring_decode_level < 2) {
+ free(ndd.pcsc_device_name);
+ return NULL;
+ }
+ }
+
+ nfc_device *pnd = nfc_device_new(context, fullconnstring);
+ if (!pnd) {
+ perror("malloc");
+ goto error;
+ }
+ pnd->driver_data = malloc(sizeof(struct pcsc_data));
+ if (!pnd->driver_data) {
+ perror("malloc");
+ goto error;
+ }
+
+ SCARDCONTEXT *pscc;
+
+ log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Attempt to open %s", ndd.pcsc_device_name);
+ // Test if context succeeded
+ if (!(pscc = pcsc_get_scardcontext()))
+ goto error;
+ DRIVER_DATA(pnd)->last_error = SCardConnect(*pscc, ndd.pcsc_device_name, SCARD_SHARE_DIRECT, 0, &(DRIVER_DATA(pnd)->hCard), (void *) & (DRIVER_DATA(pnd)->ioCard.dwProtocol));
+ if (DRIVER_DATA(pnd)->last_error != SCARD_S_SUCCESS) {
+ // We can not connect to this device.
+ log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "PCSC connect failed");
+ goto error;
+ }
+ // Configure I/O settings for card communication
+ DRIVER_DATA(pnd)->ioCard.cbPciLength = sizeof(SCARD_IO_REQUEST);
+ DRIVER_DATA(pnd)->dwShareMode = SCARD_SHARE_DIRECT;
+
+ // Done, we found the reader we are looking for
+ snprintf(pnd->name, sizeof(pnd->name), "%s", ndd.pcsc_device_name);
+
+ pnd->driver = &pcsc_driver;
+
+ free(ndd.pcsc_device_name);
+ return pnd;
+
+error:
+ free(ndd.pcsc_device_name);
+ nfc_device_free(pnd);
+ return NULL;
+}
+
+static void
+pcsc_close(nfc_device *pnd)
+{
+ SCardDisconnect(DRIVER_DATA(pnd)->hCard, SCARD_LEAVE_CARD);
+ pcsc_free_scardcontext();
+
+ nfc_device_free(pnd);
+}
+
+static const char *stringify_error(const LONG pcscError)
+{
+ static char strError[75];
+ const char *msg = NULL;
+
+ switch (pcscError) {
+ case SCARD_S_SUCCESS:
+ msg = "Command successful.";
+ break;
+ case SCARD_F_INTERNAL_ERROR:
+ msg = "Internal error.";
+ break;
+ case SCARD_E_CANCELLED:
+ msg = "Command cancelled.";
+ break;
+ case SCARD_E_INVALID_HANDLE:
+ msg = "Invalid handle.";
+ break;
+ case SCARD_E_INVALID_PARAMETER:
+ msg = "Invalid parameter given.";
+ break;
+ case SCARD_E_INVALID_TARGET:
+ msg = "Invalid target given.";
+ break;
+ case SCARD_E_NO_MEMORY:
+ msg = "Not enough memory.";
+ break;
+ case SCARD_F_WAITED_TOO_LONG:
+ msg = "Waited too long.";
+ break;
+ case SCARD_E_INSUFFICIENT_BUFFER:
+ msg = "Insufficient buffer.";
+ break;
+ case SCARD_E_UNKNOWN_READER:
+ msg = "Unknown reader specified.";
+ break;
+ case SCARD_E_TIMEOUT:
+ msg = "Command timeout.";
+ break;
+ case SCARD_E_SHARING_VIOLATION:
+ msg = "Sharing violation.";
+ break;
+ case SCARD_E_NO_SMARTCARD:
+ msg = "No smart card inserted.";
+ break;
+ case SCARD_E_UNKNOWN_CARD:
+ msg = "Unknown card.";
+ break;
+ case SCARD_E_CANT_DISPOSE:
+ msg = "Cannot dispose handle.";
+ break;
+ case SCARD_E_PROTO_MISMATCH:
+ msg = "Card protocol mismatch.";
+ break;
+ case SCARD_E_NOT_READY:
+ msg = "Subsystem not ready.";
+ break;
+ case SCARD_E_INVALID_VALUE:
+ msg = "Invalid value given.";
+ break;
+ case SCARD_E_SYSTEM_CANCELLED:
+ msg = "System cancelled.";
+ break;
+ case SCARD_F_COMM_ERROR:
+ msg = "RPC transport error.";
+ break;
+ case SCARD_F_UNKNOWN_ERROR:
+ msg = "Unknown error.";
+ break;
+ case SCARD_E_INVALID_ATR:
+ msg = "Invalid ATR.";
+ break;
+ case SCARD_E_NOT_TRANSACTED:
+ msg = "Transaction failed.";
+ break;
+ case SCARD_E_READER_UNAVAILABLE:
+ msg = "Reader is unavailable.";
+ break;
+ /* case SCARD_P_SHUTDOWN: */
+ case SCARD_E_PCI_TOO_SMALL:
+ msg = "PCI struct too small.";
+ break;
+ case SCARD_E_READER_UNSUPPORTED:
+ msg = "Reader is unsupported.";
+ break;
+ case SCARD_E_DUPLICATE_READER:
+ msg = "Reader already exists.";
+ break;
+ case SCARD_E_CARD_UNSUPPORTED:
+ msg = "Card is unsupported.";
+ break;
+ case SCARD_E_NO_SERVICE:
+ msg = "Service not available.";
+ break;
+ case SCARD_E_SERVICE_STOPPED:
+ msg = "Service was stopped.";
+ break;
+ /* case SCARD_E_UNEXPECTED: */
+ /* case SCARD_E_ICC_CREATEORDER: */
+ /* case SCARD_E_UNSUPPORTED_FEATURE: */
+ /* case SCARD_E_DIR_NOT_FOUND: */
+ /* case SCARD_E_NO_DIR: */
+ /* case SCARD_E_NO_FILE: */
+ /* case SCARD_E_NO_ACCESS: */
+ /* case SCARD_E_WRITE_TOO_MANY: */
+ /* case SCARD_E_BAD_SEEK: */
+ /* case SCARD_E_INVALID_CHV: */
+ /* case SCARD_E_UNKNOWN_RES_MNG: */
+ /* case SCARD_E_NO_SUCH_CERTIFICATE: */
+ /* case SCARD_E_CERTIFICATE_UNAVAILABLE: */
+ case SCARD_E_NO_READERS_AVAILABLE:
+ msg = "Cannot find a smart card reader.";
+ break;
+ /* case SCARD_E_COMM_DATA_LOST: */
+ /* case SCARD_E_NO_KEY_CONTAINER: */
+ /* case SCARD_E_SERVER_TOO_BUSY: */
+ case SCARD_W_UNSUPPORTED_CARD:
+ msg = "Card is not supported.";
+ break;
+ case SCARD_W_UNRESPONSIVE_CARD:
+ msg = "Card is unresponsive.";
+ break;
+ case SCARD_W_UNPOWERED_CARD:
+ msg = "Card is unpowered.";
+ break;
+ case SCARD_W_RESET_CARD:
+ msg = "Card was reset.";
+ break;
+ case SCARD_W_REMOVED_CARD:
+ msg = "Card was removed.";
+ break;
+ /* case SCARD_W_SECURITY_VIOLATION: */
+ /* case SCARD_W_WRONG_CHV: */
+ /* case SCARD_W_CHV_BLOCKED: */
+ /* case SCARD_W_EOF: */
+ /* case SCARD_W_CANCELLED_BY_USER: */
+ /* case SCARD_W_CARD_NOT_AUTHENTICATED: */
+
+ case SCARD_E_UNSUPPORTED_FEATURE:
+ msg = "Feature not supported.";
+ break;
+ default:
+ (void)snprintf(strError, sizeof(strError) - 1, "Unknown error: 0x%08lX",
+ pcscError);
+ };
+
+ if (msg)
+ (void)strncpy(strError, msg, sizeof(strError));
+ else
+ (void)snprintf(strError, sizeof(strError) - 1, "Unknown error: 0x%08lX",
+ pcscError);
+
+ /* add a null byte */
+ strError[sizeof(strError) - 1] = '\0';
+
+ return strError;
+}
+
+const char *
+pcsc_strerror(const struct nfc_device *pnd)
+{
+ return stringify_error(DRIVER_DATA(pnd)->last_error);
+}
+
+int pcsc_initiator_init(struct nfc_device *pnd)
+{
+ (void) pnd;
+ return NFC_SUCCESS;
+}
+
+int pcsc_initiator_select_passive_target(struct nfc_device *pnd, const nfc_modulation nm, const uint8_t *pbtInitData, const size_t szInitData, nfc_target *pnt)
+{
+ uint8_t atr[MAX_ATR_SIZE];
+ uint8_t uid[10];
+ int target_present;
+ size_t atr_len = sizeof atr;
+
+ (void) pbtInitData;
+ (void) szInitData;
+
+ if (nm.nbr != pcsc_supported_brs[0] && nm.nbr != pcsc_supported_brs[1])
+ return NFC_EINVARG;
+
+ pnd->last_error = pcsc_get_status(pnd, &target_present, atr, &atr_len);
+ if (pnd->last_error != NFC_SUCCESS)
+ return pnd->last_error;
+
+ if (!target_present) {
+ log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "No target present");
+ return NFC_ENOTSUCHDEV;
+ }
+
+ uint8_t icc_type = pcsc_get_icc_type(pnd);
+ int uid_len = pcsc_get_uid(pnd, uid, sizeof uid);
+ if (pcsc_props_to_target(icc_type, atr, atr_len, uid, uid_len, nm.nmt, pnt) != NFC_SUCCESS) {
+ log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Type of target not supported");
+ return NFC_EDEVNOTSUPP;
+ }
+
+ pnd->last_error = pcsc_reconnect(pnd, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, SCARD_LEAVE_CARD);
+ if (pnd->last_error != NFC_SUCCESS)
+ return pnd->last_error;
+
+ return 1;
+}
+
+int pcsc_initiator_deselect_target(struct nfc_device *pnd)
+{
+ pnd->last_error = pcsc_reconnect(pnd, SCARD_SHARE_DIRECT, 0, SCARD_LEAVE_CARD);
+ return pnd->last_error;
+}
+
+int pcsc_initiator_transceive_bytes(struct nfc_device *pnd, const uint8_t *pbtTx, const size_t szTx, uint8_t *pbtRx, const size_t szRx, int timeout)
+{
+ size_t resp_len = szRx;
+
+ // FIXME: timeout is not handled
+ (void) timeout;
+
+ pnd->last_error = pcsc_transmit(pnd, pbtTx, szTx, pbtRx, &resp_len);
+ if (pnd->last_error != NFC_SUCCESS)
+ return pnd->last_error;
+
+ return resp_len;
+}
+
+int pcsc_initiator_target_is_present(struct nfc_device *pnd, const nfc_target *pnt)
+{
+ uint8_t atr[MAX_ATR_SIZE];
+ int target_present;
+ size_t atr_len = sizeof atr;
+ nfc_target nt;
+
+ pnd->last_error = pcsc_get_status(pnd, &target_present, atr, &atr_len);
+ if (pnd->last_error != NFC_SUCCESS)
+ return pnd->last_error;
+
+ if (!target_present) {
+ log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "No target present");
+ return NFC_ENOTSUCHDEV;
+ }
+
+ if (pnt) {
+ if (pcsc_props_to_target(ICC_TYPE_UNKNOWN, atr, atr_len, NULL, 0, pnt->nm.nmt, &nt) != NFC_SUCCESS
+ || pnt->nm.nmt != nt.nm.nmt || pnt->nm.nbr != nt.nm.nbr) {
+ log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Target doesn't meet requirements");
+ return NFC_ENOTSUCHDEV;
+ }
+ }
+ return NFC_SUCCESS;
+}
+
+int pcsc_device_set_property_bool(struct nfc_device *pnd, const nfc_property property, const bool bEnable)
+{
+ (void) pnd;
+ switch (property) {
+ case NP_INFINITE_SELECT:
+ // ignore
+ return NFC_SUCCESS;
+ case NP_AUTO_ISO14443_4:
+ case NP_EASY_FRAMING:
+ case NP_FORCE_ISO14443_A:
+ case NP_HANDLE_CRC:
+ case NP_HANDLE_PARITY:
+ case NP_FORCE_SPEED_106:
+ if (bEnable == true)
+ return NFC_SUCCESS;
+ break;
+ case NP_ACCEPT_INVALID_FRAMES:
+ case NP_ACCEPT_MULTIPLE_FRAMES:
+ if (bEnable == false)
+ return NFC_SUCCESS;
+ break;
+ case NP_ACTIVATE_FIELD:
+ if (bEnable == false) {
+ struct pcsc_data *data = pnd->driver_data;
+ pcsc_reconnect(pnd, data->dwShareMode, data->ioCard.dwProtocol, SCARD_RESET_CARD);
+ }
+ return NFC_SUCCESS;
+ default:
+ break;
+ }
+ return NFC_EDEVNOTSUPP;
+}
+
+int pcsc_get_supported_modulation(struct nfc_device *pnd, const nfc_mode mode, const nfc_modulation_type **const supported_mt)
+{
+ (void) pnd;
+ if (mode == N_TARGET || NULL == supported_mt)
+ return NFC_EINVARG;
+ *supported_mt = pcsc_supported_mts;
+ return NFC_SUCCESS;
+}
+
+int pcsc_get_supported_baud_rate(struct nfc_device *pnd, const nfc_mode mode, const nfc_modulation_type nmt, const nfc_baud_rate **const supported_br)
+{
+ (void) pnd;
+ (void) nmt;
+ if (mode == N_TARGET || NULL == supported_br)
+ return NFC_EINVARG;
+ *supported_br = pcsc_supported_brs;
+ return NFC_SUCCESS;
+}
+
+int
+pcsc_get_information_about(nfc_device *pnd, char **pbuf)
+{
+ struct pcsc_data *data = pnd->driver_data;
+ LPBYTE name = NULL, version = NULL, type = NULL, serial = NULL;
+ DWORD name_len = SCARD_AUTOALLOCATE, version_len = SCARD_AUTOALLOCATE,
+ type_len = SCARD_AUTOALLOCATE, serial_len = SCARD_AUTOALLOCATE;
+ int res = NFC_SUCCESS;
+ SCARDCONTEXT *pscc;
+
+ if (!(pscc = pcsc_get_scardcontext())) {
+ pnd->last_error = NFC_ESOFT;
+ return pnd->last_error;
+ }
+
+ SCardGetAttrib(data->hCard, SCARD_ATTR_VENDOR_NAME, (LPBYTE)&name, &name_len);
+ SCardGetAttrib(data->hCard, SCARD_ATTR_VENDOR_IFD_TYPE, (LPBYTE)&type, &type_len);
+ SCardGetAttrib(data->hCard, SCARD_ATTR_VENDOR_IFD_VERSION, (LPBYTE)&version, &version_len);
+ SCardGetAttrib(data->hCard, SCARD_ATTR_VENDOR_IFD_SERIAL_NO, (LPBYTE)&serial, &serial_len);
+
+ *pbuf = malloc(name_len + type_len + version_len + serial_len + 30);
+ if (! *pbuf) {
+ res = NFC_ESOFT;
+ goto error;
+ }
+ sprintf((char *) *pbuf,
+ "%s" // model
+ "%s%s" // version
+ " (%s)" // vendor
+ "%s%s\n" // serial
+ ,
+ name && name_len > 0 && name[0] != '\0'
+ ? (char *)name : "unknown model",
+ version && version_len > 0 && version[0] != '\0'
+ ? " " : "", version_len > 0 ? (char *)version : "",
+ type && type_len > 0 && type[0] != '\0'
+ ? (char *)type : "unknown vendor",
+ serial && serial_len > 0 && serial[0] != '\0'
+ ? "\nserial: " : "", serial_len > 0 ? (char *)serial : "");
+
+error:
+ SCardFreeMemory(*pscc, name);
+ SCardFreeMemory(*pscc, type);
+ SCardFreeMemory(*pscc, version);
+ SCardFreeMemory(*pscc, serial);
+
+ pnd->last_error = res;
+ return pnd->last_error;
+}
+
+const struct nfc_driver pcsc_driver = {
+ .name = PCSC_DRIVER_NAME,
+ .scan = pcsc_scan,
+ .open = pcsc_open,
+ .close = pcsc_close,
+ .strerror = pcsc_strerror,
+
+ .initiator_init = pcsc_initiator_init,
+ .initiator_init_secure_element = NULL, // No secure-element support
+ .initiator_select_passive_target = pcsc_initiator_select_passive_target,
+ .initiator_poll_target = NULL,
+ .initiator_select_dep_target = NULL,
+ .initiator_deselect_target = NULL,
+ .initiator_transceive_bytes = pcsc_initiator_transceive_bytes,
+ .initiator_transceive_bits = NULL,
+ .initiator_transceive_bytes_timed = NULL,
+ .initiator_transceive_bits_timed = NULL,
+ .initiator_target_is_present = pcsc_initiator_target_is_present,
+
+ .target_init = NULL,
+ .target_send_bytes = NULL,
+ .target_receive_bytes = NULL,
+ .target_send_bits = NULL,
+ .target_receive_bits = NULL,
+
+ .device_set_property_bool = pcsc_device_set_property_bool,
+ .device_set_property_int = NULL,
+ .get_supported_modulation = pcsc_get_supported_modulation,
+ .get_supported_baud_rate = pcsc_get_supported_baud_rate,
+ .device_get_information_about = pcsc_get_information_about,
+
+ .abort_command = NULL, // Abort is not supported in this driver
+ .idle = NULL,
+ .powerdown = NULL,
+};
+
diff --git a/libnfc/drivers/pcsc.h b/libnfc/drivers/pcsc.h
new file mode 100644
index 0000000..f96030d
--- /dev/null
+++ b/libnfc/drivers/pcsc.h
@@ -0,0 +1,35 @@
+/*-
+ * Free/Libre Near Field Communication (NFC) library
+ *
+ * Libnfc historical contributors:
+ * Copyright (C) 2019 Frank Morgner
+ * See AUTHORS file for a more comprehensive list of contributors.
+ * Additional contributors of this file:
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see
+ */
+
+/**
+ * @file pcsc.h
+ * @brief Driver for non-ACR122 devices (behind PC/SC daemon)
+ */
+
+#ifndef __NFC_DRIVER_PCSC_H__
+#define __NFC_DRIVER_PCSC_H__
+
+#include
+
+extern const struct nfc_driver pcsc_driver;
+
+#endif // ! __NFC_DRIVER_PCSC_H__
diff --git a/libnfc/nfc.c b/libnfc/nfc.c
index ce30694..c06da0e 100644
--- a/libnfc/nfc.c
+++ b/libnfc/nfc.c
@@ -87,6 +87,10 @@
#include "target-subr.h"
#include "drivers.h"
+#if defined (DRIVER_PCSC_ENABLED)
+# include "drivers/pcsc.h"
+#endif /* DRIVER_PCSC_ENABLED */
+
#if defined (DRIVER_ACR122_PCSC_ENABLED)
# include "drivers/acr122_pcsc.h"
#endif /* DRIVER_ACR122_PCSC_ENABLED */
@@ -159,6 +163,9 @@ nfc_drivers_init(void)
#if defined (DRIVER_PN53X_USB_ENABLED)
nfc_register_driver(&pn53x_usb_driver);
#endif /* DRIVER_PN53X_USB_ENABLED */
+#if defined (DRIVER_PCSC_ENABLED)
+ nfc_register_driver(&pcsc_driver);
+#endif /* DRIVER_ACR122_PCSC_ENABLED */
#if defined (DRIVER_ACR122_PCSC_ENABLED)
nfc_register_driver(&acr122_pcsc_driver);
#endif /* DRIVER_ACR122_PCSC_ENABLED */
diff --git a/m4/libnfc_drivers.m4 b/m4/libnfc_drivers.m4
index 54be7e7..c8a75ff 100644
--- a/m4/libnfc_drivers.m4
+++ b/m4/libnfc_drivers.m4
@@ -4,7 +4,8 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS],
[
AC_MSG_CHECKING(which drivers to build)
AC_ARG_WITH(drivers,
- AS_HELP_STRING([--with-drivers=DRIVERS], [Use a custom driver set, where DRIVERS is a coma-separated list of drivers to build support for. Available drivers are: 'acr122_pcsc', 'acr122_usb', 'acr122s', 'arygon', 'pn532_i2c', 'pn532_spi', 'pn532_uart', 'pn53x_usb' and 'pn71xx'. Default drivers set is 'acr122_usb,acr122s,arygon,pn532_i2c,pn532_spi,pn532_uart,pn53x_usb'. The special driver set 'all' compile all available drivers.]),
+ AS_HELP_STRING([--with-drivers=DRIVERS], [Use a custom driver set, where DRIVERS is a coma-separated list of drivers to build support for. Available drivers are: 'acr122_pcsc', 'acr122_usb', 'acr122s', 'arygon', 'pcsc', 'pn532_i2c', 'pn532_spi', 'pn532_uart', 'pn53x_usb' and 'pn71xx'. Default drivers set is 'acr122_usb,acr122s,arygon,pn532_i2c,pn532_spi,pn532_uart,pn53x_usb'. The special driver set 'all' compile all available drivers.]),
+
[ case "${withval}" in
yes | no)
dnl ignore calls without any arguments
@@ -36,7 +37,8 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS],
fi
;;
all)
- DRIVER_BUILD_LIST="acr122_pcsc acr122_usb acr122s arygon pn53x_usb pn532_uart pn71xx"
+ DRIVER_BUILD_LIST="acr122_pcsc acr122_usb acr122s arygon pn53x_usb pn532_uart pn71xx pcsc"
+
if test x"$spi_available" = x"yes"
then
DRIVER_BUILD_LIST="$DRIVER_BUILD_LIST pn532_spi"
@@ -50,6 +52,7 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS],
DRIVERS_CFLAGS=""
+ driver_pcsc_enabled="no"
driver_acr122_pcsc_enabled="no"
driver_acr122_usb_enabled="no"
driver_acr122s_enabled="no"
@@ -63,6 +66,11 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS],
for driver in ${DRIVER_BUILD_LIST}
do
case "${driver}" in
+ pcsc)
+ pcsc_required="yes"
+ driver_pcsc_enabled="yes"
+ DRIVERS_CFLAGS="$DRIVERS_CFLAGS -DDRIVER_PCSC_ENABLED"
+ ;;
acr122_pcsc)
pcsc_required="yes"
driver_acr122_pcsc_enabled="yes"
@@ -114,6 +122,7 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS],
done
AC_SUBST(DRIVERS_CFLAGS)
AM_CONDITIONAL(DRIVER_ACR122_PCSC_ENABLED, [test x"$driver_acr122_pcsc_enabled" = xyes])
+ AM_CONDITIONAL(DRIVER_PCSC_ENABLED, [test x"$driver_pcsc_enabled" = xyes])
AM_CONDITIONAL(DRIVER_ACR122_USB_ENABLED, [test x"$driver_acr122_usb_enabled" = xyes])
AM_CONDITIONAL(DRIVER_ACR122S_ENABLED, [test x"$driver_acr122s_enabled" = xyes])
AM_CONDITIONAL(DRIVER_PN53X_USB_ENABLED, [test x"$driver_pn53x_usb_enabled" = xyes])
@@ -127,6 +136,7 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS],
AC_DEFUN([LIBNFC_DRIVERS_SUMMARY],[
echo
echo "Selected drivers:"
+echo " pcsc............. $driver_pcsc_enabled"
echo " acr122_pcsc...... $driver_acr122_pcsc_enabled"
echo " acr122_usb....... $driver_acr122_usb_enabled"
echo " acr122s.......... $driver_acr122s_enabled"