Logo Search packages:      
Sourcecode: pcsc-lite version File versions  Download package

LONG SCardGetStatusChange ( SCARDCONTEXT  hContext,
DWORD  dwTimeout,
SCARD_READERSTATE rgReaderStates,
DWORD  cReaders 
)

Blocks execution until the current availability of the cards in a specific set of readers changes.

This function receives a structure or list of structures containing reader names. It then blocks for a change in state to occur for a maximum blocking time of dwTimeout or forever if INFINITE is used.

The new event state will be contained in dwEventState. A status change might be a card insertion or removal event, a change in ATR, etc.

dwEventState also contains a number of events in the upper 16 bits (dwEventState & 0xFFFF0000). This number of events is incremented for each card insertion or removal in the specified reader. This can be used to detect a card removal/insertion between two calls to SCardGetStatusChange()

To wait for a reader event (reader added or removed) you may use the special reader name "\\?PnP?\Notification". If a reader event occurs the state of this reader will change and the bit SCARD_STATE_CHANGED will be set.

 typedef struct {
   LPCSTR szReader;          // Reader name
   LPVOID pvUserData;         // User defined data
   DWORD dwCurrentState;      // Current state of reader
   DWORD dwEventState;        // Reader state after a state change
   DWORD cbAtr;               // ATR Length, usually MAX_ATR_SIZE
   BYTE rgbAtr[MAX_ATR_SIZE]; // ATR Value
 } SCARD_READERSTATE;
 ...
 typedef SCARD_READERSTATE *PSCARD_READERSTATE, **LPSCARD_READERSTATE;
 ...

Value of dwCurrentState and dwEventState:

  • SCARD_STATE_UNAWARE The application is unaware of the current state, and would like to know. The use of this value results in an immediate return from state transition monitoring services. This is represented by all bits set to zero.
  • SCARD_STATE_IGNORE This reader should be ignored
  • SCARD_STATE_CHANGED There is a difference between the state believed by the application, and the state known by the resource manager. When this bit is set, the application may assume a significant state change has occurred on this reader.
  • SCARD_STATE_UNKNOWN The given reader name is not recognized by the resource manager. If this bit is set, then SCARD_STATE_CHANGED and SCARD_STATE_IGNORE will also be set
  • SCARD_STATE_UNAVAILABLE The actual state of this reader is not available. If this bit is set, then all the following bits are clear.
  • SCARD_STATE_EMPTY There is no card in the reader. If this bit is set, all the following bits will be clear
  • SCARD_STATE_PRESENT There is a card in the reader
  • SCARD_STATE_EXCLUSIVE The card in the reader is allocated for exclusive use by another application. If this bit is set, SCARD_STATE_PRESENT will also be set.
  • SCARD_STATE_INUSE The card in the reader is in use by one or more other applications, but may be connected to in shared mode. If this bit is set, SCARD_STATE_PRESENT will also be set.
  • SCARD_STATE_MUTE There is an unresponsive card in the reader.
Parameters:
[in]hContextConnection context to the PC/SC Resource Manager.
[in]dwTimeoutMaximum waiting time (in milliseconds) for status change, zero (or INFINITE) for infinite.
[in,out]rgReaderStatesStructures of readers with current states.
[in]cReadersNumber of structures.
Returns:
Error code.
Return values:
SCARD_S_SUCCESSSuccessful (SCARD_S_SUCCESS)
SCARD_E_NO_SERVICEServer is not running (SCARD_E_NO_SERVICE)
SCARD_E_INVALID_PARAMETERrgReaderStates is NULL and cReaders > 0 (SCARD_E_INVALID_PARAMETER)
SCARD_E_INVALID_VALUEInvalid States, reader name, etc (SCARD_E_INVALID_VALUE)
SCARD_E_INVALID_HANDLEInvalid hContext handle (SCARD_E_INVALID_HANDLE)
SCARD_E_READER_UNAVAILABLEThe reader is unavailable (SCARD_E_READER_UNAVAILABLE)
SCARD_E_TIMEOUTThe user-specified timeout value has expired (SCARD_E_TIMEOUT)
 SCARDCONTEXT hContext;
 SCARD_READERSTATE rgReaderStates[2];
 LONG rv;
 ...
 rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
 ...
 rgReaderStates[0].szReader = "Reader X";
 rgReaderStates[0].dwCurrentState = SCARD_STATE_UNAWARE;

 rgReaderStates[1].szReader = "\\\\?PnP?\\Notification";
 rgReaderStates[1].dwCurrentState = SCARD_STATE_UNAWARE;
 ...
 rv = SCardGetStatusChange(hContext, INFINITE, rgReaderStates, 2);
 printf("reader state: 0x%04X\n", rgReaderStates[0].dwEventState);
 printf("reader state: 0x%04X\n", rgReaderStates[1].dwEventState);

Definition at line 1789 of file winscard_clnt.c.

References pubReaderStatesList::cardAtr, pubReaderStatesList::cardAtrLength, CMD_STOP_WAITING_READER_STATE_CHANGE, CMD_WAIT_READER_STATE_CHANGE, pubReaderStatesList::eventCounter, INFINITE, MessageReceive(), MessageReceiveTimeout(), MessageSendWithHeader(), PCSCLITE_MAX_READERS_CONTEXTS, PCSCLITE_SHARING_EXCLUSIVE_CONTEXT, PCSCLITE_SHARING_LAST_CONTEXT, PCSCLITE_SHARING_NO_CONTEXT, PCSCLITE_STATUS_POLL_RATE, pubReaderStatesList::readerSharing, pubReaderStatesList::readerState, SCARD_ABSENT, SCARD_E_INVALID_HANDLE, SCARD_E_INVALID_PARAMETER, SCARD_E_INVALID_VALUE, SCARD_E_TIMEOUT, SCARD_PRESENT, SCARD_S_SUCCESS, SCARD_STATE_ATRMATCH, SCARD_STATE_CHANGED, SCARD_STATE_EMPTY, SCARD_STATE_EXCLUSIVE, SCARD_STATE_IGNORE, SCARD_STATE_INUSE, SCARD_STATE_MUTE, SCARD_STATE_PRESENT, SCARD_STATE_UNAVAILABLE, SCARD_STATE_UNAWARE, SCARD_STATE_UNKNOWN, SCARD_SWALLOWED, SCARD_UNKNOWN, SCardGetContext(), SYS_USleep(), time_sub(), and wait_reader_state_change::timeOut.

{
      SCARD_READERSTATE *currReader;
      READER_STATE *rContext;
      long dwTime;
      DWORD dwBreakFlag = 0;
      unsigned int j;
      SCONTEXTMAP * currentContextMap;
      int currentReaderCount = 0;
      LONG rv = SCARD_S_SUCCESS;

      PROFILE_START
      API_TRACE_IN("%ld %ld %d", hContext, dwTimeout, cReaders)
#ifdef DO_TRACE
      for (j=0; j<cReaders; j++)
      {
            API_TRACE_IN("[%d] %s %lX %lX", j, rgReaderStates[j].szReader,
                  rgReaderStates[j].dwCurrentState, rgReaderStates[j].dwEventState)
      }
#endif

      if ((rgReaderStates == NULL && cReaders > 0)
            || (cReaders > PCSCLITE_MAX_READERS_CONTEXTS))
      {
            rv = SCARD_E_INVALID_PARAMETER;
            goto error;
      }

      /* Check the integrity of the reader states structures */
      for (j = 0; j < cReaders; j++)
      {
            if (rgReaderStates[j].szReader == NULL)
                  return SCARD_E_INVALID_VALUE;
      }

      /* return if all readers are SCARD_STATE_IGNORE */
      if (cReaders > 0)
      {
            int nbNonIgnoredReaders = cReaders;

            for (j=0; j<cReaders; j++)
                  if (rgReaderStates[j].dwCurrentState & SCARD_STATE_IGNORE)
                        nbNonIgnoredReaders--;

            if (0 == nbNonIgnoredReaders)
            {
                  rv = SCARD_S_SUCCESS;
                  goto error;
            }
      }
      else
      {
            /* reader list is empty */
            rv = SCARD_S_SUCCESS;
            goto error;
      }

      CHECK_SAME_PROCESS

      /*
       * Make sure this context has been opened
       */
      currentContextMap = SCardGetContext(hContext);
      if (NULL == currentContextMap)
      {
            rv = SCARD_E_INVALID_HANDLE;
            goto error;
      }

      (void)pthread_mutex_lock(currentContextMap->mMutex);

      /* check the context is still opened */
      currentContextMap = SCardGetContext(hContext);
      if (NULL == currentContextMap)
            /* the context is now invalid
             * -> another thread may have called SCardReleaseContext
             * -> so the mMutex has been unlocked */
      {
            rv = SCARD_E_INVALID_HANDLE;
            goto error;
      }

      /* synchronize reader states with daemon */
      rv = getReaderStates(currentContextMap);
      if (rv != SCARD_S_SUCCESS)
            goto end;

      /* Clear the event state for all readers */
      for (j = 0; j < cReaders; j++)
            rgReaderStates[j].dwEventState = 0;

      /* Now is where we start our event checking loop */
      Log2(PCSC_LOG_DEBUG, "Event Loop Start, dwTimeout: %ld", dwTimeout);

      /* Get the initial reader count on the system */
      for (j=0; j < PCSCLITE_MAX_READERS_CONTEXTS; j++)
            if (readerStates[j].readerName[0] != '\0')
                  currentReaderCount++;

      /* catch possible sign extension problems from 32 to 64-bits integers */
      if ((DWORD)-1 == dwTimeout)
            dwTimeout = INFINITE;
      if (INFINITE == dwTimeout)
            dwTime = 60*1000; /* "infinite" timeout */
      else
            dwTime = dwTimeout;

      j = 0;
      do
      {
            currReader = &rgReaderStates[j];

            /* Ignore for IGNORED readers */
            if (!(currReader->dwCurrentState & SCARD_STATE_IGNORE))
            {
                  const char *readerName;
                  int i;

                  /* Looks for correct readernames */
                  readerName = currReader->szReader;
                  for (i = 0; i < PCSCLITE_MAX_READERS_CONTEXTS; i++)
                  {
                        if (strcmp(readerName, readerStates[i].readerName) == 0)
                              break;
                  }

                  /* The requested reader name is not recognized */
                  if (i == PCSCLITE_MAX_READERS_CONTEXTS)
                  {
                        /* PnP special reader? */
                        if (strcasecmp(readerName, "\\\\?PnP?\\Notification") == 0)
                        {
                              int k, newReaderCount = 0;

                              for (k=0; k < PCSCLITE_MAX_READERS_CONTEXTS; k++)
                                    if (readerStates[k].readerName[0] != '\0')
                                          newReaderCount++;

                              if (newReaderCount != currentReaderCount)
                              {
                                    Log1(PCSC_LOG_INFO, "Reader list changed");
                                    currentReaderCount = newReaderCount;

                                    currReader->dwEventState |= SCARD_STATE_CHANGED;
                                    dwBreakFlag = 1;
                              }
                        }
                        else
                        {
                              currReader->dwEventState =
                                    SCARD_STATE_UNKNOWN | SCARD_STATE_UNAVAILABLE;
                              if (!(currReader->dwCurrentState & SCARD_STATE_UNKNOWN))
                              {
                                    currReader->dwEventState |= SCARD_STATE_CHANGED;
                                    /*
                                     * Spec says use SCARD_STATE_IGNORE but a removed USB
                                     * reader with eventState fed into currentState will
                                     * be ignored forever
                                     */
                                    dwBreakFlag = 1;
                              }
                        }
                  }
                  else
                  {
                        uint32_t readerState;

                        /* The reader has come back after being away */
                        if (currReader->dwCurrentState & SCARD_STATE_UNKNOWN)
                        {
                              currReader->dwEventState |= SCARD_STATE_CHANGED;
                              currReader->dwEventState &= ~SCARD_STATE_UNKNOWN;
                              Log0(PCSC_LOG_DEBUG);
                              dwBreakFlag = 1;
                        }

                        /* Set the reader status structure */
                        rContext = &readerStates[i];

                        /* Now we check all the Reader States */
                        readerState = rContext->readerState;

                        /* only if current state has an non null event counter */
                        if (currReader->dwCurrentState & 0xFFFF0000)
                        {
                              unsigned int currentCounter;

                              currentCounter = (currReader->dwCurrentState >> 16) & 0xFFFF;

                              /* has the event counter changed since the last call? */
                              if (rContext->eventCounter != currentCounter)
                              {
                                    currReader->dwEventState |= SCARD_STATE_CHANGED;
                                    Log0(PCSC_LOG_DEBUG);
                                    dwBreakFlag = 1;
                              }
                        }

                        /* add an event counter in the upper word of dwEventState */
                        currReader->dwEventState = ((currReader->dwEventState & 0xffff )
                              | (rContext->eventCounter << 16));

                        /* Check if the reader is in the correct state */
                        if (readerState & SCARD_UNKNOWN)
                        {
                              /* reader is in bad state */
                              currReader->dwEventState = SCARD_STATE_UNAVAILABLE;
                              if (!(currReader->dwCurrentState & SCARD_STATE_UNAVAILABLE))
                              {
                                    /* App thinks reader is in good state and it is not */
                                    currReader->dwEventState |= SCARD_STATE_CHANGED;
                                    Log0(PCSC_LOG_DEBUG);
                                    dwBreakFlag = 1;
                              }
                        }
                        else
                        {
                              /* App thinks reader in bad state but it is not */
                              if (currReader-> dwCurrentState & SCARD_STATE_UNAVAILABLE)
                              {
                                    currReader->dwEventState &= ~SCARD_STATE_UNAVAILABLE;
                                    currReader->dwEventState |= SCARD_STATE_CHANGED;
                                    Log0(PCSC_LOG_DEBUG);
                                    dwBreakFlag = 1;
                              }
                        }

                        /* Check for card presence in the reader */
                        if (readerState & SCARD_PRESENT)
                        {
                              /* card present but not yet powered up */
                              if (0 == rContext->cardAtrLength)
                                    /* Allow the status thread to convey information */
                                    (void)SYS_USleep(PCSCLITE_STATUS_POLL_RATE + 10);

                              currReader->cbAtr = rContext->cardAtrLength;
                              memcpy(currReader->rgbAtr, rContext->cardAtr,
                                    currReader->cbAtr);
                        }
                        else
                              currReader->cbAtr = 0;

                        /* Card is now absent */
                        if (readerState & SCARD_ABSENT)
                        {
                              currReader->dwEventState |= SCARD_STATE_EMPTY;
                              currReader->dwEventState &= ~SCARD_STATE_PRESENT;
                              currReader->dwEventState &= ~SCARD_STATE_UNAWARE;
                              currReader->dwEventState &= ~SCARD_STATE_IGNORE;
                              currReader->dwEventState &= ~SCARD_STATE_UNKNOWN;
                              currReader->dwEventState &= ~SCARD_STATE_UNAVAILABLE;
                              currReader->dwEventState &= ~SCARD_STATE_ATRMATCH;
                              currReader->dwEventState &= ~SCARD_STATE_MUTE;
                              currReader->dwEventState &= ~SCARD_STATE_INUSE;

                              /* After present the rest are assumed */
                              if (currReader->dwCurrentState & SCARD_STATE_PRESENT)
                              {
                                    currReader->dwEventState |= SCARD_STATE_CHANGED;
                                    Log0(PCSC_LOG_DEBUG);
                                    dwBreakFlag = 1;
                              }
                        }
                        /* Card is now present */
                        else if (readerState & SCARD_PRESENT)
                        {
                              currReader->dwEventState |= SCARD_STATE_PRESENT;
                              currReader->dwEventState &= ~SCARD_STATE_EMPTY;
                              currReader->dwEventState &= ~SCARD_STATE_UNAWARE;
                              currReader->dwEventState &= ~SCARD_STATE_IGNORE;
                              currReader->dwEventState &= ~SCARD_STATE_UNKNOWN;
                              currReader->dwEventState &= ~SCARD_STATE_UNAVAILABLE;
                              currReader->dwEventState &= ~SCARD_STATE_MUTE;

                              if (currReader->dwCurrentState & SCARD_STATE_EMPTY)
                              {
                                    currReader->dwEventState |= SCARD_STATE_CHANGED;
                                    Log0(PCSC_LOG_DEBUG);
                                    dwBreakFlag = 1;
                              }

                              if (readerState & SCARD_SWALLOWED)
                              {
                                    currReader->dwEventState |= SCARD_STATE_MUTE;
                                    if (!(currReader->dwCurrentState & SCARD_STATE_MUTE))
                                    {
                                          currReader->dwEventState |= SCARD_STATE_CHANGED;
                                          Log0(PCSC_LOG_DEBUG);
                                          dwBreakFlag = 1;
                                    }
                              }
                              else
                              {
                                    /* App thinks card is mute but it is not */
                                    if (currReader->dwCurrentState & SCARD_STATE_MUTE)
                                    {
                                          currReader->dwEventState |= SCARD_STATE_CHANGED;
                                          Log0(PCSC_LOG_DEBUG);
                                          dwBreakFlag = 1;
                                    }
                              }
                        }

                        /* Now figure out sharing modes */
                        if (rContext->readerSharing == PCSCLITE_SHARING_EXCLUSIVE_CONTEXT)
                        {
                              currReader->dwEventState |= SCARD_STATE_EXCLUSIVE;
                              currReader->dwEventState &= ~SCARD_STATE_INUSE;
                              if (currReader->dwCurrentState & SCARD_STATE_INUSE)
                              {
                                    currReader->dwEventState |= SCARD_STATE_CHANGED;
                                    Log0(PCSC_LOG_DEBUG);
                                    dwBreakFlag = 1;
                              }
                        }
                        else if (rContext->readerSharing >= PCSCLITE_SHARING_LAST_CONTEXT)
                        {
                              /* A card must be inserted for it to be INUSE */
                              if (readerState & SCARD_PRESENT)
                              {
                                    currReader->dwEventState |= SCARD_STATE_INUSE;
                                    currReader->dwEventState &= ~SCARD_STATE_EXCLUSIVE;
                                    if (currReader-> dwCurrentState & SCARD_STATE_EXCLUSIVE)
                                    {
                                          currReader->dwEventState |= SCARD_STATE_CHANGED;
                                          Log0(PCSC_LOG_DEBUG);
                                          dwBreakFlag = 1;
                                    }
                              }
                        }
                        else if (rContext->readerSharing == PCSCLITE_SHARING_NO_CONTEXT)
                        {
                              currReader->dwEventState &= ~SCARD_STATE_INUSE;
                              currReader->dwEventState &= ~SCARD_STATE_EXCLUSIVE;

                              if (currReader->dwCurrentState & SCARD_STATE_INUSE)
                              {
                                    currReader->dwEventState |= SCARD_STATE_CHANGED;
                                    Log0(PCSC_LOG_DEBUG);
                                    dwBreakFlag = 1;
                              }
                              else if (currReader-> dwCurrentState
                                    & SCARD_STATE_EXCLUSIVE)
                              {
                                    currReader->dwEventState |= SCARD_STATE_CHANGED;
                                    Log0(PCSC_LOG_DEBUG);
                                    dwBreakFlag = 1;
                              }
                        }

                        if (currReader->dwCurrentState == SCARD_STATE_UNAWARE)
                        {
                              /*
                               * Break out of the while .. loop and return status
                               * once all the status's for all readers is met
                               */
                              currReader->dwEventState |= SCARD_STATE_CHANGED;
                              Log0(PCSC_LOG_DEBUG);
                              dwBreakFlag = 1;
                        }
                  }     /* End of SCARD_STATE_UNKNOWN */
            }     /* End of SCARD_STATE_IGNORE */

            /* Counter and resetter */
            j++;
            if (j == cReaders)
            {
                  /* go back to the first reader */
                  j = 0;

                  /* Declare all the break conditions */

                  /* Break if UNAWARE is set and all readers have been checked */
                  if (dwBreakFlag == 1)
                        break;

                  /* Only sleep once for each cycle of reader checks. */
                  {
                        struct wait_reader_state_change waitStatusStruct;
                        struct timeval before, after;

                        gettimeofday(&before, NULL);

                        waitStatusStruct.timeOut = dwTime;
                        waitStatusStruct.rv = SCARD_S_SUCCESS;

                        /* another thread can do SCardCancel() */
                        currentContextMap->cancellable = TRUE;

                        rv = MessageSendWithHeader(CMD_WAIT_READER_STATE_CHANGE,
                              currentContextMap->dwClientID,
                              sizeof(waitStatusStruct), &waitStatusStruct);

                        if (rv != SCARD_S_SUCCESS)
                              goto end;

                        /*
                         * Read a message from the server
                         */
                        rv = MessageReceiveTimeout(CMD_WAIT_READER_STATE_CHANGE,
                              &waitStatusStruct, sizeof(waitStatusStruct),
                              currentContextMap->dwClientID, dwTime);

                        /* another thread can do SCardCancel() */
                        currentContextMap->cancellable = FALSE;

                        /* timeout */
                        if (SCARD_E_TIMEOUT == rv)
                        {
                              /* ask server to remove us from the event list */
                              rv = MessageSendWithHeader(CMD_STOP_WAITING_READER_STATE_CHANGE,
                                    currentContextMap->dwClientID,
                                    sizeof(waitStatusStruct), &waitStatusStruct);

                              if (rv != SCARD_S_SUCCESS)
                                    goto end;

                              /* Read a message from the server */
                              rv = MessageReceive(&waitStatusStruct,
                                    sizeof(waitStatusStruct),
                                    currentContextMap->dwClientID);

                              if (rv != SCARD_S_SUCCESS)
                                    goto end;
                        }

                        if (rv != SCARD_S_SUCCESS)
                              goto end;

                        /* an event occurs or SCardCancel() was called */
                        if (SCARD_S_SUCCESS != waitStatusStruct.rv)
                        {
                              rv = waitStatusStruct.rv;
                              goto end;
                        }

                        /* synchronize reader states with daemon */
                        rv = getReaderStates(currentContextMap);
                        if (rv != SCARD_S_SUCCESS)
                              goto end;

                        if (INFINITE != dwTimeout)
                        {
                              long int diff;

                              gettimeofday(&after, NULL);
                              diff = time_sub(&after, &before);
                              dwTime -= diff/1000;
                        }
                  }

                  if (dwTimeout != INFINITE)
                  {
                        /* If time is greater than timeout and all readers have been
                         * checked
                         */
                        if (dwTime <= 0)
                        {
                              rv = SCARD_E_TIMEOUT;
                              goto end;
                        }
                  }
            }
      }
      while (1);

end:
      Log1(PCSC_LOG_DEBUG, "Event Loop End");

      (void)pthread_mutex_unlock(currentContextMap->mMutex);

error:
      PROFILE_END(rv)
#ifdef DO_TRACE
      for (j=0; j<cReaders; j++)
      {
            API_TRACE_OUT("[%d] %s %X %X", j, rgReaderStates[j].szReader,
                  rgReaderStates[j].dwCurrentState, rgReaderStates[j].dwEventState)
      }
#endif

      return rv;
}

Here is the call graph for this function:


Generated by  Doxygen 1.6.0   Back to index