apdu_open_reader (const char *portstr)
{
  static int pcsc_api_loaded, ct_api_loaded;
  int slot;

  if (DBG_READER)
    log_debug ("enter: apdu_open_reader: portstr=%s\n", portstr);

#ifdef HAVE_LIBUSB
  if (!opt.disable_ccid)
    {
      static int once_available;
      int i;
      const char *s;

      slot = open_ccid_reader (portstr);
      if (slot != -1)
        {
          once_available = 1;
          if (DBG_READER)
            log_debug ("leave: apdu_open_reader => slot=%d [ccid]\n", slot);
          return slot; /* got one */
        }

      /* If we ever loaded successfully loaded a CCID reader we never
         want to fallback to another driver.  This solves a problem
         where ccid was used, the card unplugged and then scdaemon
         tries to find a new reader and will eventually try PC/SC over
         and over again.  To reset this flag "gpgconf --kill scdaemon"
         can be used.  */
      if (once_available)
        {
          if (DBG_READER)
            log_debug ("leave: apdu_open_reader => slot=-1 (once_avail)\n");
          return -1;
        }

      /* If a CCID reader specification has been given, the user does
         not want a fallback to other drivers. */
      if (portstr)
        for (s=portstr, i=0; *s; s++)
          if (*s == ':' && (++i == 3))
            {
              if (DBG_READER)
                log_debug ("leave: apdu_open_reader => slot=-1 (no ccid)\n");
              return -1;
            }
    }

#endif /* HAVE_LIBUSB */

  if (opt.ctapi_driver && *opt.ctapi_driver)
    {
      int port = portstr? atoi (portstr) : 32768;

      if (!ct_api_loaded)
        {
          void *handle;

          handle = dlopen (opt.ctapi_driver, RTLD_LAZY);
          if (!handle)
            {
              log_error ("apdu_open_reader: failed to open driver: %s\n",
                         dlerror ());
              return -1;
            }
          CT_init = dlsym (handle, "CT_init");
          CT_data = dlsym (handle, "CT_data");
          CT_close = dlsym (handle, "CT_close");
          if (!CT_init || !CT_data || !CT_close)
            {
              log_error ("apdu_open_reader: invalid CT-API driver\n");
              dlclose (handle);
              return -1;
            }
          ct_api_loaded = 1;
        }
      return open_ct_reader (port);
    }


  /* No ctAPI configured, so lets try the PC/SC API */
  if (!pcsc_api_loaded)
    {
#ifndef NEED_PCSC_WRAPPER
      void *handle;

      handle = dlopen (opt.pcsc_driver, RTLD_LAZY);
      if (!handle)
        {
          log_error ("apdu_open_reader: failed to open driver '%s': %s\n",
                     opt.pcsc_driver, dlerror ());
          return -1;
        }

      pcsc_establish_context = dlsym (handle, "SCardEstablishContext");
      pcsc_release_context   = dlsym (handle, "SCardReleaseContext");
      pcsc_list_readers      = dlsym (handle, "SCardListReaders");
#if defined(_WIN32) || defined(__CYGWIN__)
      if (!pcsc_list_readers)
        pcsc_list_readers    = dlsym (handle, "SCardListReadersA");
#endif
      pcsc_get_status_change = dlsym (handle, "SCardGetStatusChange");
#if defined(_WIN32) || defined(__CYGWIN__)
      if (!pcsc_get_status_change)
        pcsc_get_status_change = dlsym (handle, "SCardGetStatusChangeA");
#endif
      pcsc_connect           = dlsym (handle, "SCardConnect");
#if defined(_WIN32) || defined(__CYGWIN__)
      if (!pcsc_connect)
        pcsc_connect         = dlsym (handle, "SCardConnectA");
#endif
      pcsc_reconnect         = dlsym (handle, "SCardReconnect");
#if defined(_WIN32) || defined(__CYGWIN__)
      if (!pcsc_reconnect)
        pcsc_reconnect       = dlsym (handle, "SCardReconnectA");
#endif
      pcsc_disconnect        = dlsym (handle, "SCardDisconnect");
      pcsc_status            = dlsym (handle, "SCardStatus");
#if defined(_WIN32) || defined(__CYGWIN__)
      if (!pcsc_status)
        pcsc_status          = dlsym (handle, "SCardStatusA");
#endif
      pcsc_begin_transaction = dlsym (handle, "SCardBeginTransaction");
      pcsc_end_transaction   = dlsym (handle, "SCardEndTransaction");
      pcsc_transmit          = dlsym (handle, "SCardTransmit");
      pcsc_set_timeout       = dlsym (handle, "SCardSetTimeout");
      pcsc_control           = dlsym (handle, "SCardControl");

      if (!pcsc_establish_context
          || !pcsc_release_context
          || !pcsc_list_readers
          || !pcsc_get_status_change
          || !pcsc_connect
          || !pcsc_reconnect
          || !pcsc_disconnect
          || !pcsc_status
          || !pcsc_begin_transaction
          || !pcsc_end_transaction
          || !pcsc_transmit
          || !pcsc_control
          /* || !pcsc_set_timeout */)
        {
          /* Note that set_timeout is currently not used and also not
             available under Windows. */
          log_error ("apdu_open_reader: invalid PC/SC driver "
                     "(%d%d%d%d%d%d%d%d%d%d%d%d%d)\n",
                     !!pcsc_establish_context,
                     !!pcsc_release_context,
                     !!pcsc_list_readers,
                     !!pcsc_get_status_change,
                     !!pcsc_connect,
                     !!pcsc_reconnect,
                     !!pcsc_disconnect,
                     !!pcsc_status,
                     !!pcsc_begin_transaction,
                     !!pcsc_end_transaction,
                     !!pcsc_transmit,
                     !!pcsc_set_timeout,
                     !!pcsc_control );
          dlclose (handle);
          return -1;
        }
#endif /*!NEED_PCSC_WRAPPER*/
      pcsc_api_loaded = 1;
    }

  slot = open_pcsc_reader (portstr);

  if (DBG_READER)
    log_debug ("leave: apdu_open_reader => slot=%d [pc/sc]\n", slot);
  return slot;
}