LONG EHSpawnEventHandler(PREADER_CONTEXT rContext) { LONG rv; DWORD dwStatus = 0; int i; UCHAR ucAtr[MAX_ATR_SIZE]; DWORD dwAtrLen = 0; secdebug("pcscd", "EHSpawnEventHandler: rContext: 0x%p", rContext); rv = IFDStatusICC(rContext, &dwStatus, ucAtr, &dwAtrLen); if (rv != SCARD_S_SUCCESS) { Log2(PCSC_LOG_ERROR, "Initial Check Failed on %s", rContext->lpcReader); return SCARD_F_UNKNOWN_ERROR; } /* * Find an empty reader slot and insert the new reader */ for (i = 0; i < PCSCLITE_MAX_READERS_CONTEXTS; i++) { PCSCD::SharedReaderState *rstmp = PCSCD::SharedReaderState::overlay(readerStates[i]); if (rstmp->xreaderID() == 0) break; } if (i == PCSCLITE_MAX_READERS_CONTEXTS) return SCARD_F_INTERNAL_ERROR; /* * Set all the attributes to this reader */ PCSCD::SharedReaderState *rs = PCSCD::SharedReaderState::overlay(readerStates[i]); rContext->readerState = readerStates[i]; rs->xreaderName(rContext->lpcReader); rs->xcardAtr(ucAtr, dwAtrLen); // also sets cardAtrLength rs->xreaderID(i + 100); rs->xreaderState(dwStatus); rs->sharing(rContext->dwContexts); rs->xcardProtocol(SCARD_PROTOCOL_UNSET); rv = SYS_ThreadCreate(&rContext->pthThread, THREAD_ATTR_DETACHED, (PCSCLITE_THREAD_FUNCTION( ))EHStatusHandlerThread, (LPVOID) rContext); secdebug("pcscd", "EHSpawnEventHandler after thread create: %d [%04X]", rv, rv); if (rv == 1) return SCARD_S_SUCCESS; else return SCARD_E_NO_MEMORY; }
LONG EHSpawnEventHandler(READER_CONTEXT * rContext) { LONG rv; DWORD dwStatus = 0; rv = IFDStatusICC(rContext, &dwStatus); if (rv != SCARD_S_SUCCESS) { Log2(PCSC_LOG_ERROR, "Initial Check Failed on %s", rContext->readerState->readerName); return SCARD_F_UNKNOWN_ERROR; } rv = ThreadCreate(&rContext->pthThread, 0, (PCSCLITE_THREAD_FUNCTION( ))EHStatusHandlerThread, (LPVOID) rContext); if (rv) { Log2(PCSC_LOG_ERROR, "ThreadCreate failed: %s", strerror(rv)); return SCARD_E_NO_MEMORY; } else return SCARD_S_SUCCESS; }
/** * Power up/down or reset's an ICC located in the IFD. */ LONG IFDPowerICC(PREADER_CONTEXT rContext, DWORD dwAction, const unsigned char *pucAtr, PDWORD pdwAtrLen) { RESPONSECODE rv; short ret; SMARTCARD_EXTENSION sSmartCard; DWORD dwStatus; UCHAR ucValue[1]; #ifndef PCSCLITE_STATIC_DRIVER RESPONSECODE(*IFD_power_icc) (DWORD) = NULL; RESPONSECODE(*IFDH_power_icc) (DWORD, DWORD, PUCHAR, PDWORD) = NULL; #endif /* * Zero out everything */ rv = IFD_SUCCESS; dwStatus = 0; ucValue[0] = 0; /* * Check that the card is inserted first */ (void)IFDStatusICC(rContext, &dwStatus, pucAtr, pdwAtrLen); if (dwStatus & SCARD_ABSENT) return SCARD_W_REMOVED_CARD; #ifndef PCSCLITE_STATIC_DRIVER if (rContext->dwVersion == IFD_HVERSION_1_0) IFD_power_icc = rContext->psFunctions.psFunctions_v1.pvfPowerICC; else IFDH_power_icc = rContext->psFunctions.psFunctions_v2.pvfPowerICC; #endif /* LOCK THIS CODE REGION */ (void)SYS_MutexLock(rContext->mMutex); #ifndef PCSCLITE_STATIC_DRIVER if (rContext->dwVersion == IFD_HVERSION_1_0) { ucValue[0] = rContext->dwSlot; (void)IFDSetCapabilities(rContext, TAG_IFD_SLOTNUM, 1, ucValue); rv = (*IFD_power_icc) (dwAction); } else { rv = (*IFDH_power_icc) (rContext->dwSlot, dwAction, (unsigned char *)pucAtr, pdwAtrLen); ret = ATRDecodeAtr(&sSmartCard, pucAtr, *pdwAtrLen); } #else if (rContext->dwVersion == IFD_HVERSION_1_0) { ucValue[0] = rContext->dwSlot; (void)IFDSetCapabilities(rContext, TAG_IFD_SLOTNUM, 1, ucValue); rv = IFD_Power_ICC(dwAction); } else rv = IFDHPowerICC(rContext->dwSlot, dwAction, pucAtr, pdwAtrLen); #endif /* END OF LOCKED REGION */ (void)SYS_MutexUnLock(rContext->mMutex); /* use clean values in case of error */ if (rv != IFD_SUCCESS) { *pdwAtrLen = 0; // pucAtr[0] = '\0'; if (rv == IFD_NO_SUCH_DEVICE) { // (void)SendHotplugSignal(); return SCARD_E_READER_UNAVAILABLE; } return SCARD_E_NOT_TRANSACTED; } /* * Get the ATR and it's length */ if (rContext->dwVersion == IFD_HVERSION_1_0) (void)IFDStatusICC(rContext, &dwStatus, pucAtr, pdwAtrLen); return rv; }
static void EHStatusHandlerThread(READER_CONTEXT * rContext) { LONG rv; const char *readerName; DWORD dwStatus; uint32_t readerState; int32_t readerSharing; DWORD dwCurrentState; DWORD dwAtrLen; /* * Zero out everything */ dwStatus = 0; readerName = rContext->readerState->readerName; rv = IFDStatusICC(rContext, &dwStatus); if ((SCARD_S_SUCCESS == rv) && (dwStatus & SCARD_PRESENT)) { #ifdef DISABLE_AUTO_POWER_ON rContext->readerState->cardAtrLength = 0; rContext->readerState->cardProtocol = SCARD_PROTOCOL_UNDEFINED; readerState = SCARD_PRESENT; Log1(PCSC_LOG_INFO, "Skip card power on"); #else dwAtrLen = sizeof(rContext->readerState->cardAtr); rv = IFDPowerICC(rContext, IFD_POWER_UP, rContext->readerState->cardAtr, &dwAtrLen); rContext->readerState->cardAtrLength = dwAtrLen; /* the protocol is unset after a power on */ rContext->readerState->cardProtocol = SCARD_PROTOCOL_UNDEFINED; if (rv == IFD_SUCCESS) { readerState = SCARD_PRESENT | SCARD_POWERED | SCARD_NEGOTIABLE; rContext->powerState = POWER_STATE_POWERED; Log1(PCSC_LOG_DEBUG, "powerState: POWER_STATE_POWERED"); if (rContext->readerState->cardAtrLength > 0) { LogXxd(PCSC_LOG_INFO, "Card ATR: ", rContext->readerState->cardAtr, rContext->readerState->cardAtrLength); } else Log1(PCSC_LOG_INFO, "Card ATR: (NULL)"); } else { readerState = SCARD_PRESENT | SCARD_SWALLOWED; rContext->powerState = POWER_STATE_UNPOWERED; Log1(PCSC_LOG_DEBUG, "powerState: POWER_STATE_UNPOWERED"); Log3(PCSC_LOG_ERROR, "Error powering up card: %d 0x%04X", rv, rv); } #endif dwCurrentState = SCARD_PRESENT; } else { readerState = SCARD_ABSENT; rContext->readerState->cardAtrLength = 0; rContext->readerState->cardProtocol = SCARD_PROTOCOL_UNDEFINED; dwCurrentState = SCARD_ABSENT; } /* * Set all the public attributes to this reader */ rContext->readerState->readerState = readerState; rContext->readerState->readerSharing = readerSharing = rContext->contexts; (void)EHSignalEventToClients(); while (1) { dwStatus = 0; rv = IFDStatusICC(rContext, &dwStatus); if (rv != SCARD_S_SUCCESS) { Log2(PCSC_LOG_ERROR, "Error communicating to: %s", readerName); /* * Set error status on this reader while errors occur */ rContext->readerState->readerState = SCARD_UNKNOWN; rContext->readerState->cardAtrLength = 0; rContext->readerState->cardProtocol = SCARD_PROTOCOL_UNDEFINED; dwCurrentState = SCARD_UNKNOWN; (void)EHSignalEventToClients(); } if (dwStatus & SCARD_ABSENT) { if (dwCurrentState == SCARD_PRESENT || dwCurrentState == SCARD_UNKNOWN) { /* * Change the status structure */ Log2(PCSC_LOG_INFO, "Card Removed From %s", readerName); /* * Notify the card has been removed */ (void)RFSetReaderEventState(rContext, SCARD_REMOVED); rContext->readerState->cardAtrLength = 0; rContext->readerState->cardProtocol = SCARD_PROTOCOL_UNDEFINED; rContext->readerState->readerState = SCARD_ABSENT; dwCurrentState = SCARD_ABSENT; rContext->readerState->eventCounter++; (void)EHSignalEventToClients(); } } else if (dwStatus & SCARD_PRESENT) { if (dwCurrentState == SCARD_ABSENT || dwCurrentState == SCARD_UNKNOWN) { #ifdef DISABLE_AUTO_POWER_ON rContext->readerState->cardAtrLength = 0; rContext->readerState->cardProtocol = SCARD_PROTOCOL_UNDEFINED; rContext->readerState->readerState = SCARD_PRESENT; rContext->powerState = POWER_STATE_UNPOWERED; Log1(PCSC_LOG_DEBUG, "powerState: POWER_STATE_UNPOWERED"); readerState = SCARD_PRESENT; rv = IFD_SUCCESS; Log1(PCSC_LOG_INFO, "Skip card power on"); #else /* * Power and reset the card */ dwAtrLen = sizeof(rContext->readerState->cardAtr); rv = IFDPowerICC(rContext, IFD_POWER_UP, rContext->readerState->cardAtr, &dwAtrLen); rContext->readerState->cardAtrLength = dwAtrLen; /* the protocol is unset after a power on */ rContext->readerState->cardProtocol = SCARD_PROTOCOL_UNDEFINED; if (rv == IFD_SUCCESS) { rContext->readerState->readerState = SCARD_PRESENT | SCARD_POWERED | SCARD_NEGOTIABLE; rContext->powerState = POWER_STATE_POWERED; Log1(PCSC_LOG_DEBUG, "powerState: POWER_STATE_POWERED"); } else { rContext->readerState->readerState = SCARD_PRESENT | SCARD_SWALLOWED; rContext->powerState = POWER_STATE_UNPOWERED; Log1(PCSC_LOG_DEBUG, "powerState: POWER_STATE_UNPOWERED"); rContext->readerState->cardAtrLength = 0; } #endif dwCurrentState = SCARD_PRESENT; rContext->readerState->eventCounter++; Log2(PCSC_LOG_INFO, "Card inserted into %s", readerName); (void)EHSignalEventToClients(); if (rv == IFD_SUCCESS) { if (rContext->readerState->cardAtrLength > 0) { LogXxd(PCSC_LOG_INFO, "Card ATR: ", rContext->readerState->cardAtr, rContext->readerState->cardAtrLength); } else Log1(PCSC_LOG_INFO, "Card ATR: (NULL)"); } else Log1(PCSC_LOG_ERROR,"Error powering up card."); } } /* * Sharing may change w/o an event pass it on */ if (readerSharing != rContext->contexts) { readerSharing = rContext->contexts; rContext->readerState->readerSharing = readerSharing; (void)EHSignalEventToClients(); } if (rContext->pthCardEvent) { int ret; int timeout; #ifndef DISABLE_ON_DEMAND_POWER_ON if (POWER_STATE_POWERED == rContext->powerState) /* The card is powered but not yet used */ timeout = PCSCLITE_POWER_OFF_GRACE_PERIOD; else /* The card is already in use or not used at all */ #endif timeout = PCSCLITE_STATUS_EVENT_TIMEOUT; ret = rContext->pthCardEvent(rContext->slot, timeout); if (IFD_SUCCESS != ret) (void)SYS_USleep(PCSCLITE_STATUS_POLL_RATE); } else (void)SYS_USleep(PCSCLITE_STATUS_POLL_RATE); #ifndef DISABLE_ON_DEMAND_POWER_ON /* the card is powered but not used */ (void)pthread_mutex_lock(&rContext->powerState_lock); if (POWER_STATE_POWERED == rContext->powerState) { /* power down */ IFDPowerICC(rContext, IFD_POWER_DOWN, NULL, NULL); rContext->powerState = POWER_STATE_UNPOWERED; Log1(PCSC_LOG_DEBUG, "powerState: POWER_STATE_UNPOWERED"); /* the protocol is unset after a power down */ rContext->readerState->cardProtocol = SCARD_PROTOCOL_UNDEFINED; } /* the card was in use */ if (POWER_STATE_GRACE_PERIOD == rContext->powerState) { /* the next state should be UNPOWERED unless the * card is used again */ rContext->powerState = POWER_STATE_POWERED; Log1(PCSC_LOG_DEBUG, "powerState: POWER_STATE_POWERED"); } (void)pthread_mutex_unlock(&rContext->powerState_lock); #endif if (rContext->hLockId == 0xFFFF) { /* * Exit and notify the caller */ (void)EHSignalEventToClients(); Log1(PCSC_LOG_INFO, "Die"); rContext->hLockId = 0; (void)pthread_exit(NULL); } } }
/** * Power up/down or reset's an ICC located in the IFD. */ LONG IFDPowerICC(READER_CONTEXT * rContext, DWORD dwAction, PUCHAR pucAtr, PDWORD pdwAtrLen) { RESPONSECODE rv; DWORD dwStatus; UCHAR dummyAtr[MAX_ATR_SIZE]; DWORD dummyAtrLen = sizeof(dummyAtr); #ifndef PCSCLITE_STATIC_DRIVER RESPONSECODE(*IFDH_power_icc) (DWORD, DWORD, PUCHAR, PDWORD) = NULL; #endif /* * Zero out everything */ dwStatus = 0; if (NULL == pucAtr) pucAtr = dummyAtr; if (NULL == pdwAtrLen) pdwAtrLen = &dummyAtrLen; /* * Check that the card is inserted first */ rv = IFDStatusICC(rContext, &dwStatus); if (rv != IFD_SUCCESS) { if (rv == IFD_NO_SUCH_DEVICE) return SCARD_E_READER_UNAVAILABLE; return SCARD_E_NOT_TRANSACTED; } if (dwStatus & SCARD_ABSENT) return SCARD_W_REMOVED_CARD; #ifndef PCSCLITE_STATIC_DRIVER IFDH_power_icc = rContext->psFunctions.psFunctions_v2.pvfPowerICC; #endif /* LOCK THIS CODE REGION */ (void)pthread_mutex_lock(rContext->mMutex); #ifndef PCSCLITE_STATIC_DRIVER rv = (*IFDH_power_icc) (rContext->slot, dwAction, pucAtr, pdwAtrLen); #else rv = IFDHPowerICC(rContext->slot, dwAction, pucAtr, pdwAtrLen); #endif /* END OF LOCKED REGION */ (void)pthread_mutex_unlock(rContext->mMutex); /* use clean values in case of error */ if (rv != IFD_SUCCESS) { *pdwAtrLen = 0; pucAtr[0] = '\0'; if (rv == IFD_NO_SUCH_DEVICE) { (void)SendHotplugSignal(); return SCARD_E_READER_UNAVAILABLE; } return SCARD_E_NOT_TRANSACTED; } return rv; }
void EHStatusHandlerThread(PREADER_CONTEXT rContext) { LONG rv; LPCSTR lpcReader; DWORD dwStatus, dwReaderSharing; DWORD dwCurrentState; int pageSize = SYS_GetPageSize(); /* * Zero out everything */ dwStatus = 0; dwReaderSharing = 0; dwCurrentState = 0; secdebug("pcscd", "EHStatusHandlerThread: rContext: 0x%p", rContext); lpcReader = rContext->lpcReader; PCSCD::SharedReaderState *rs = PCSCD::SharedReaderState::overlay(rContext->readerState); DWORD tmpCardAtrLength = MAX_ATR_SIZE; rv = IFDStatusICC(rContext, &dwStatus, rs->xcardAtr(), &tmpCardAtrLength); secdebug("pcscd", "EHStatusHandlerThread: initial call to IFDStatusICC: %d [%04X]", rv, rv); if (dwStatus & SCARD_PRESENT) { tmpCardAtrLength = MAX_ATR_SIZE; rv = IFDPowerICC(rContext, IFD_POWER_UP, rs->xcardAtr(), &tmpCardAtrLength); /* the protocol is unset after a power on */ rs->xcardProtocol(SCARD_PROTOCOL_UNSET); secdebug("pcscd", "EHStatusHandlerThread: initial call to IFDPowerICC: %d [%04X]", rv, rv); if (rv == IFD_SUCCESS) { rs->xcardAtrLength(tmpCardAtrLength); dwStatus |= SCARD_PRESENT; dwStatus &= ~SCARD_ABSENT; dwStatus |= SCARD_POWERED; dwStatus |= SCARD_NEGOTIABLE; dwStatus &= ~SCARD_SPECIFIC; dwStatus &= ~SCARD_SWALLOWED; dwStatus &= ~SCARD_UNKNOWN; if (rs->xcardAtrLength() > 0) { LogXxd(PCSC_LOG_INFO, "Card ATR: ", rs->xcardAtr(), rs->xcardAtrLength()); } else Log1(PCSC_LOG_INFO, "Card ATR: (NULL)"); } else { dwStatus |= SCARD_PRESENT; dwStatus &= ~SCARD_ABSENT; dwStatus |= SCARD_SWALLOWED; dwStatus &= ~SCARD_POWERED; dwStatus &= ~SCARD_NEGOTIABLE; dwStatus &= ~SCARD_SPECIFIC; dwStatus &= ~SCARD_UNKNOWN; Log3(PCSC_LOG_ERROR, "Error powering up card: %d 0x%04X", rv, rv); } dwCurrentState = SCARD_PRESENT; } else { dwStatus |= SCARD_ABSENT; dwStatus &= ~SCARD_PRESENT; dwStatus &= ~SCARD_POWERED; dwStatus &= ~SCARD_NEGOTIABLE; dwStatus &= ~SCARD_SPECIFIC; dwStatus &= ~SCARD_SWALLOWED; dwStatus &= ~SCARD_UNKNOWN; rs->xcardAtrLength(0); rs->xcardProtocol(SCARD_PROTOCOL_UNSET); dwCurrentState = SCARD_ABSENT; } /* * Set all the public attributes to this reader */ rs->xreaderState(dwStatus); dwReaderSharing = rContext->dwContexts; rs->sharing(dwReaderSharing); SYS_MMapSynchronize((void *) rContext->readerState, pageSize); while (1) { dwStatus = 0; // Defensive measure if (!rContext->vHandle) { // Exit and notify the caller secdebug("pcscd", "EHStatusHandlerThread: lost dynamic callbacks ??"); ReaderContextUnlock(rContext); SYS_ThreadDetach(rContext->pthThread); SYS_ThreadExit(0); } DWORD tmpCardAtrLength = MAX_ATR_SIZE; rv = IFDStatusICC(rContext, &dwStatus, rs->xcardAtr(), &tmpCardAtrLength); if (rv != SCARD_S_SUCCESS) { Log2(PCSC_LOG_ERROR, "Error communicating to: %s", lpcReader); /* * Set error status on this reader while errors occur */ DWORD readerStateTmp = rs->xreaderState(); readerStateTmp &= ~SCARD_ABSENT; readerStateTmp &= ~SCARD_PRESENT; readerStateTmp &= ~SCARD_POWERED; readerStateTmp &= ~SCARD_NEGOTIABLE; readerStateTmp &= ~SCARD_SPECIFIC; readerStateTmp &= ~SCARD_SWALLOWED; readerStateTmp |= SCARD_UNKNOWN; rs->xcardAtrLength(0); rs->xcardProtocol(SCARD_PROTOCOL_UNSET); rs->xreaderState(readerStateTmp); dwCurrentState = SCARD_UNKNOWN; SYS_MMapSynchronize((void *) rContext->readerState, pageSize); /* * This code causes race conditions on G4's with USB * insertion */ /* * dwErrorCount += 1; SYS_Sleep(1); */ /* * After 10 seconds of errors, try to reinitialize the reader * This sometimes helps bring readers out of *crazy* states. */ /* * if ( dwErrorCount == 10 ) { RFUnInitializeReader( rContext * ); RFInitializeReader( rContext ); dwErrorCount = 0; } */ /* * End of race condition code block */ } if (dwStatus & SCARD_ABSENT) { if (dwCurrentState == SCARD_PRESENT || dwCurrentState == SCARD_UNKNOWN) { /* * Change the status structure */ Log2(PCSC_LOG_INFO, "Card Removed From %s", lpcReader); /* * Notify the card has been removed */ RFSetReaderEventState(rContext, SCARD_REMOVED); rs->xcardAtrLength(0); rs->xcardProtocol(SCARD_PROTOCOL_UNSET); DWORD readerStateTmp = rs->xreaderState(); readerStateTmp |= SCARD_ABSENT; readerStateTmp &= ~SCARD_UNKNOWN; readerStateTmp &= ~SCARD_PRESENT; readerStateTmp &= ~SCARD_POWERED; readerStateTmp &= ~SCARD_NEGOTIABLE; readerStateTmp &= ~SCARD_SWALLOWED; readerStateTmp &= ~SCARD_SPECIFIC; rs->xreaderState(readerStateTmp); dwCurrentState = SCARD_ABSENT; SYS_MMapSynchronize((void *) rContext->readerState, pageSize); } } else if (dwStatus & SCARD_PRESENT) { if (dwCurrentState == SCARD_ABSENT || dwCurrentState == SCARD_UNKNOWN) { /* * Power and reset the card */ SYS_USleep(PCSCLITE_STATUS_WAIT); DWORD tmpCardAtrLength = MAX_ATR_SIZE; rv = IFDPowerICC(rContext, IFD_POWER_UP, rs->xcardAtr(), &tmpCardAtrLength); /* the protocol is unset after a power on */ rs->xcardProtocol(SCARD_PROTOCOL_UNSET); secdebug("pcscd", "EHStatusHandlerThread: power-and-reset call to IFDPowerICC: %d [%04X]", rv, rv); DWORD readerStateTmp = rs->xreaderState(); if (rv == IFD_SUCCESS) { rs->xcardAtrLength(tmpCardAtrLength); readerStateTmp |= SCARD_PRESENT; readerStateTmp &= ~SCARD_ABSENT; readerStateTmp |= SCARD_POWERED; readerStateTmp |= SCARD_NEGOTIABLE; readerStateTmp &= ~SCARD_SPECIFIC; readerStateTmp &= ~SCARD_UNKNOWN; readerStateTmp &= ~SCARD_SWALLOWED; rs->xreaderState(readerStateTmp); /* * Notify the card has been reset */ RFSetReaderEventState(rContext, SCARD_RESET); } else { readerStateTmp |= SCARD_PRESENT; readerStateTmp &= ~SCARD_ABSENT; readerStateTmp |= SCARD_SWALLOWED; readerStateTmp &= ~SCARD_POWERED; readerStateTmp &= ~SCARD_NEGOTIABLE; readerStateTmp &= ~SCARD_SPECIFIC; readerStateTmp &= ~SCARD_UNKNOWN; rs->xreaderState(readerStateTmp); rs->xcardAtrLength(0); } dwCurrentState = SCARD_PRESENT; SYS_MMapSynchronize((void *) rContext->readerState, pageSize); Log2(PCSC_LOG_INFO, "Card inserted into %s", lpcReader); if (rv == IFD_SUCCESS) { if (rs->xcardAtrLength() > 0) LogXxd(PCSC_LOG_INFO, "Card ATR: ", rs->xcardAtr(), rs->xcardAtrLength()); else Log1(PCSC_LOG_INFO, "Card ATR: (NULL)"); } else Log1(PCSC_LOG_ERROR,"Error powering up card."); } } if (ReaderContextIsLocked(rContext)) { /* * Exit and notify the caller */ secdebug("pcscd", "EHStatusHandlerThread: parent requested shutdown"); ReaderContextUnlock(rContext); SYS_ThreadDetach(rContext->pthThread); SYS_ThreadExit(0); } /* * Sharing may change w/o an event pass it on */ if (dwReaderSharing != (uint32_t)rContext->dwContexts) { dwReaderSharing = rContext->dwContexts; rs->sharing(dwReaderSharing); SYS_MMapSynchronize((void *) rContext->readerState, pageSize); } SYS_USleep(PCSCLITE_STATUS_POLL_RATE); } }