+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdio>
+#include <cstring>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include "smartcard-types.h"
+#include "Debug.h"
+#include "TerminalInterface.h"
+#include "USBTerminal.h"
+
+#ifndef EXPORT_API
+#define EXPORT_API __attribute__((visibility("default")))
+#endif
+
+typedef struct _callback_param_t
+{
+ void *callback;
+ void *param;
+}
+callback_param_t;
+
+using namespace smartcard_service_api;
+
+static const char *se_name = "USB0";
+
+void __attribute__ ((constructor)) lib_init()
+{
+}
+
+void __attribute__ ((destructor)) lib_fini()
+{
+}
+
+/* below three functions must be implemented */
+extern "C" EXPORT_API const char *get_name()
+{
+ return se_name;
+}
+
+extern "C" EXPORT_API void *create_instance()
+{
+ return (void *)USBTerminal::getInstance();
+}
+
+extern "C" EXPORT_API void destroy_instance(void *instance)
+{
+ USBTerminal *inst = (USBTerminal *)instance;
+ if (inst == USBTerminal::getInstance())
+ {
+ inst->finalize();
+ }
+ else
+ {
+ _ERR("instance is invalid : getInstance [%p], instance [%p]",
+ USBTerminal::getInstance(), instance);
+ }
+}
+
+namespace smartcard_service_api
+{
+ USBTerminal::USBTerminal() : Terminal()
+ {
+ name = (char *)se_name;
+ cardConnected = false;
+ readerConnected = false;
+ readerStates[0].szReader = "\\\\?PnP?\\Notification"; // name of the card reader to monitor
+ readerStates[0].dwCurrentState = SCARD_STATE_UNAWARE;
+ readerStates[0].dwEventState = SCARD_STATE_UNKNOWN;
+
+ initialize();
+ }
+
+ USBTerminal::~USBTerminal()
+ {
+ finalize();
+ }
+
+ USBTerminal *USBTerminal::getInstance()
+ {
+ static USBTerminal instance;
+
+ return &instance;
+ }
+
+ bool USBTerminal::initialize()
+ {
+ _BEGIN();
+
+ if (initialized == false)
+ {
+ LONG rv = SCARD_S_SUCCESS;
+ LPSTR readerName = NULL;
+ DWORD readerLen;
+
+ rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &mainContext);
+ if (rv != SCARD_S_SUCCESS)
+ {
+ _ERR("SCardEstablishContext failed : %s", pcsc_stringify_error(rv));
+ return false;
+ }
+
+
+ readerLen = SCARD_AUTOALLOCATE;
+ rv = SCardListReaders(mainContext, NULL, (LPSTR)&readerName, &readerLen);
+ if (rv == SCARD_S_SUCCESS)
+ {
+ _INFO("Reader Connected : %s", readerName);
+ readerStates[1].szReader = readerName;
+ readerStates[1].dwCurrentState = SCARD_STATE_UNAWARE;
+ readerStates[1].dwEventState = SCARD_STATE_UNKNOWN;
+
+ readerConnected = true;
+ }
+
+ monitorEventsThreadId = pthread_create(&monitorEventsThread, NULL, USBTerminal::monitorEvents, NULL);
+
+ if (monitorEventsThreadId >= 0)
+ {
+ initialized = true;
+ }
+ else
+ {
+ _ERR("initialize failed");
+ }
+ }
+
+ _END();
+
+ return initialized;
+ }
+
+ void USBTerminal::finalize()
+ {
+ _BEGIN();
+
+ if (isInitialized())
+ {
+ initialized = false;
+ SCardCancel(monitorEventsThreadContext);
+ pthread_join(monitorEventsThread, NULL);
+
+ if (readerStates[1].szReader != NULL)
+ free((void *)readerStates[1].szReader);
+ }
+
+ _END();
+ }
+
+ bool USBTerminal::open()
+ {
+ _BEGIN();
+
+ _INFO("Nothing to do for open operation");
+
+ _END();
+
+ return true;
+ }
+
+ void USBTerminal::close()
+ {
+ _BEGIN();
+
+ _INFO("Nothing to do for close operation");
+
+ _END();
+ }
+
+ int USBTerminal::transmitSync(const ByteArray &command,
+ ByteArray &response)
+ {
+ int result = SCARD_ERROR_OK;
+
+ _BEGIN();
+
+ if (cardConnected == true)
+ {
+ if (command.size() > 0)
+ {
+ LONG rv = SCARD_S_SUCCESS;
+ const uint8_t *send = command.getBuffer();
+ unsigned int send_len = command.size();
+ uint8_t resp[MAX_BUFFER_SIZE] = {0,};
+ uint32_t resp_len = MAX_BUFFER_SIZE;
+ SCARD_IO_REQUEST pioRecvPci;
+
+ _INFO("Sending: %s", command.toString().c_str());
+
+ rv = SCardTransmit(cardHandle, activeProtocol, send, send_len,
+ &pioRecvPci, resp, (LPDWORD)&resp_len);
+ if (rv == SCARD_S_SUCCESS)
+ {
+ response.assign(resp, resp_len);
+ _INFO("Received: %s", response.toString().c_str());
+ }
+ else
+ {
+ _ERR("SCardTransmit failed, [%s]", pcsc_stringify_error(rv));
+
+ result = SCARD_ERROR_IO_FAILED;
+ }
+ }
+ else
+ {
+ _ERR("apdu is empty");
+
+ result = SCARD_ERROR_ILLEGAL_PARAM;
+ }
+ }
+ else
+ {
+ _ERR("Card is not connected");
+
+ result = SCARD_ERROR_SE_NOT_INITIALIZED;
+ }
+
+ _END();
+
+ return result;
+ }
+
+ int USBTerminal::getATRSync(ByteArray &atr)
+ {
+ int result = SCARD_ERROR_OK;
+
+ _BEGIN();
+
+ if (cardConnected == true)
+ {
+ atr = currentAtr;
+ }
+ else
+ {
+ _ERR("Card is not connected");
+
+ result = SCARD_ERROR_SE_NOT_INITIALIZED;
+ }
+
+ _END();
+
+ return result;
+ }
+
+ int USBTerminal::transmit(const ByteArray &command, terminalTransmitCallback callback, void *userData)
+ {
+ _INFO("transmit Async function is not supported");
+
+ return SCARD_ERROR_NOT_SUPPORTED;
+ }
+
+ int USBTerminal::getATR(terminalGetATRCallback callback, void *userData)
+ {
+ _INFO("Get ATR Async function is not supported");
+
+ return SCARD_ERROR_NOT_SUPPORTED;
+ }
+
+ bool USBTerminal::isSecureElementPresence() const
+ {
+ _BEGIN();
+
+ _INFO("isReaderConnected : %d", isReaderConnected());
+
+ _END();
+
+ return isReaderConnected();
+ }
+
+ gboolean USBTerminal::cardConnect(gpointer user_data)
+ {
+ _BEGIN();
+
+ LONG rv;
+ USBTerminal *mTerminal = USBTerminal::getInstance();
+ SCARDHANDLE hCard;
+ char pbReader[MAX_READERNAME] = "";
+ BYTE pbAtr[MAX_ATR_SIZE] = "";
+ DWORD dwActiveProtocol, dwReaderLen, dwState, dwProt, dwAtrLen;
+ pthread_mutex_t *eventMutex = (pthread_mutex_t *)user_data;
+ const char *readerName = mTerminal->readerStates[1].szReader;
+
+ if (eventMutex == NULL)
+ {
+ _INFO("eventMutex is NULL");
+ return false;
+ }
+
+ pthread_mutex_lock(eventMutex);
+
+ if (mTerminal->isReaderConnected() != true || readerName == NULL)
+ {
+ _INFO("Reader is not connected");
+ pthread_mutex_unlock(eventMutex);
+ return false;
+ }
+
+
+ dwActiveProtocol = -1;
+ rv = SCardConnect(mTerminal->getMainContext(), readerName, SCARD_SHARE_SHARED,
+ SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);
+ if (rv != SCARD_S_SUCCESS)
+ {
+ _INFO("SCardConnect failed : %s", pcsc_stringify_error(rv));
+ pthread_mutex_unlock(eventMutex);
+ return false;
+ }
+
+ _INFO("Card Inserted");
+ mTerminal->setCardConnected(true);
+ mTerminal->setCardHandle(hCard);
+
+ _INFO(" Protocol: %ld\n", dwActiveProtocol);
+ switch(dwActiveProtocol)
+ {
+ case SCARD_PROTOCOL_T0:
+ mTerminal->setActiveProtocol(SCARD_PCI_T0);
+ break;
+ case SCARD_PROTOCOL_T1:
+ mTerminal->setActiveProtocol(SCARD_PCI_T1);
+ break;
+ default:
+ _INFO("Unknown protocol. set default protocol");
+ mTerminal->setActiveProtocol(SCARD_PCI_T1);
+ }
+
+ dwAtrLen = sizeof(pbAtr);
+ dwReaderLen = sizeof(pbReader);
+ rv = SCardStatus(hCard, pbReader, &dwReaderLen, &dwState, &dwProt,
+ pbAtr, &dwAtrLen);
+ if (rv != SCARD_S_SUCCESS)
+ {
+ _INFO("SCardStatus failed : %s", pcsc_stringify_error(rv));
+ pthread_mutex_unlock(eventMutex);
+ return false;
+ }
+
+ _INFO(" Reader: %s (length %ld bytes)", pbReader, dwReaderLen);
+ _INFO(" State: 0x%lX", dwState);
+ _INFO(" Prot: %ld", dwProt);
+ _INFO(" ATR (length %ld bytes):", dwAtrLen);
+
+ ByteArray atrArray(pbAtr, dwAtrLen);
+ _INFO(" %s", atrArray.toString().c_str());
+ mTerminal->setCurrentAtr(atrArray);
+
+ pthread_mutex_unlock(eventMutex);
+
+ if (mTerminal->statusCallback != NULL)
+ mTerminal->statusCallback(mTerminal->getName(), NOTIFY_CARD_AVAILABLE, SCARD_ERROR_OK, NULL);
+
+ _END();
+
+ return false;
+ }
+
+ gboolean USBTerminal::cardDisconnect(gpointer user_data)
+ {
+ _BEGIN();
+
+ USBTerminal *mTerminal = USBTerminal::getInstance();
+ pthread_mutex_t *eventMutex = (pthread_mutex_t *)user_data;
+
+ _INFO("Card Removed");
+
+ pthread_mutex_lock(eventMutex);
+
+ mTerminal->setCardConnected(false);
+
+ pthread_mutex_unlock(eventMutex);
+
+ if (mTerminal->statusCallback != NULL)
+ mTerminal->statusCallback(mTerminal->getName(), NOTIFY_CARD_NOT_AVAILABLE, SCARD_ERROR_OK, NULL);
+
+ _END();
+
+ return false;
+ }
+
+ int USBTerminal::resetReaderStates(SCARDCONTEXT hContext)
+ {
+ int readerCount = 1;
+ LONG rv;
+ LPSTR readerName = NULL;
+ DWORD readerLen;
+
+ USBTerminal *mTerminal = USBTerminal::getInstance();
+
+ _BEGIN();
+
+ readerLen = SCARD_AUTOALLOCATE;
+ rv = SCardListReaders(hContext, NULL, (LPSTR)&readerName, &readerLen);
+ if (rv == SCARD_S_SUCCESS)
+ {
+ _INFO("[Monitor] Reader Connected : %s", readerName);
+
+ if (mTerminal->readerStates[1].szReader != NULL)
+ free((void *)mTerminal->readerStates[1].szReader);
+
+ mTerminal->readerStates[1].szReader = readerName;
+ mTerminal->readerStates[1].dwCurrentState = SCARD_STATE_UNAWARE;
+ mTerminal->readerStates[1].dwEventState = SCARD_STATE_UNKNOWN;
+
+ mTerminal->setReaderConnected(true);
+ readerCount = 2;
+
+ if (mTerminal->statusCallback != NULL)
+ mTerminal->statusCallback(mTerminal->getName(), NOTIFY_SE_AVAILABLE, SCARD_ERROR_OK, NULL);
+ }
+ else
+ {
+ _INFO("[Monitor] Reader Removed");
+ mTerminal->setReaderConnected(false);
+ readerCount = 1;
+
+ if (mTerminal->statusCallback != NULL)
+ mTerminal->statusCallback(mTerminal->getName(), NOTIFY_SE_NOT_AVAILABLE, SCARD_ERROR_OK, NULL);
+ }
+
+ _END();
+
+ return readerCount;
+ }
+
+ void *USBTerminal::monitorEvents(void *data)
+ {
+ LONG rv;
+ DWORD readerCounts = 1;
+ pthread_mutex_t eventMutex;
+
+ SCARDCONTEXT hContext;
+ USBTerminal *mTerminal = USBTerminal::getInstance();
+
+ pthread_mutex_init(&eventMutex, NULL);
+
+ _BEGIN();
+
+ rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
+ if (rv != SCARD_S_SUCCESS)
+ {
+ _ERR("[Monitor] SCardEstablishContext failed : %s", pcsc_stringify_error(rv));
+ return NULL;
+ }
+
+ mTerminal->setMonitorEventsThreadContext(hContext);
+
+ if (mTerminal->isReaderConnected())
+ readerCounts = 2;
+
+ while (mTerminal->isInitialized())
+ {
+ rv = SCardGetStatusChange(hContext, INFINITE, mTerminal->readerStates, readerCounts);
+ if (rv != SCARD_S_SUCCESS)
+ {
+ _INFO("[Monitor] SCardGetStatusChange error : %s", pcsc_stringify_error(rv));
+ break;
+ }
+
+ pthread_mutex_lock(&eventMutex);
+
+ if (mTerminal->readerStates[0].dwEventState & SCARD_STATE_CHANGED)
+ {
+ _INFO("[Monitor] reader state changed");
+ readerCounts = USBTerminal::resetReaderStates(hContext);
+ }
+
+ if (mTerminal->isReaderConnected())
+ {
+ if ((mTerminal->readerStates[1].dwEventState & SCARD_STATE_EMPTY) == SCARD_STATE_EMPTY)
+ {
+ _INFO("[Monitor] Card Removed");
+ g_idle_add(&USBTerminal::cardDisconnect, &eventMutex);
+ }
+ else if ((mTerminal->readerStates[1].dwEventState & SCARD_STATE_PRESENT) == SCARD_STATE_PRESENT)
+ {
+ _INFO("[Monitor] Card Inserted");
+ g_idle_add(&USBTerminal::cardConnect, &eventMutex);
+ }
+
+ mTerminal->readerStates[1].dwCurrentState = mTerminal->readerStates[1].dwEventState;
+ }
+
+ pthread_mutex_unlock(&eventMutex);
+ }
+
+ rv = SCardReleaseContext(hContext);
+ if (rv != SCARD_S_SUCCESS)
+ _ERR("[Monitor] SCardReleaseContext failed : %s", pcsc_stringify_error(rv));
+
+ _END();
+
+ return NULL;
+ }
+} /* namespace smartcard_service_api */