ccid_open_reader (ccid_driver_t *handle, const char *readerid)
{
  int rc = 0;
  struct usb_device *dev = NULL;
  usb_dev_handle *idev = NULL;
  int dev_fd = -1;
  char *rid = NULL;
  unsigned char *ifcdesc_extra = NULL;
  size_t ifcdesc_extra_len;
  int readerno;
  int ifc_no, ep_bulk_out, ep_bulk_in, ep_intr;

  *handle = NULL;

  if (!initialized_usb)
    {
      usb_init ();
      initialized_usb = 1;
    }

  /* See whether we want to use the reader ID string or a reader
     number. A readerno of -1 indicates that the reader ID string is
     to be used. */
  if (readerid && strchr (readerid, ':'))
    readerno = -1; /* We want to use the readerid.  */
  else if (readerid)
    {
      readerno = atoi (readerid);
      if (readerno < 0)
        {
          DEBUGOUT ("no CCID readers found\n");
          rc = CCID_DRIVER_ERR_NO_READER;
          goto leave;
        }
    }
  else
    readerno = 0;  /* Default. */

  if (scan_or_find_devices (readerno, readerid, &rid, &dev,
                            &ifcdesc_extra, &ifcdesc_extra_len,
                            &ifc_no, &ep_bulk_out, &ep_bulk_in, &ep_intr,
                            &idev, &dev_fd) )
    {
      if (readerno == -1)
        DEBUGOUT_1 ("no CCID reader with ID %s\n", readerid );
      else
        DEBUGOUT_1 ("no CCID reader with number %d\n", readerno );
      rc = CCID_DRIVER_ERR_NO_READER;
      goto leave;
    }

  /* Okay, this is a CCID reader. */
  *handle = calloc (1, sizeof **handle);
  if (!*handle)
    {
      DEBUGOUT ("out of memory\n");
      rc = CCID_DRIVER_ERR_OUT_OF_CORE;
      goto leave;
    }
  (*handle)->rid = rid;
  if (idev) /* Regular USB transport. */
    {
      (*handle)->idev = idev;
      (*handle)->dev_fd = -1;
      (*handle)->id_vendor = dev->descriptor.idVendor;
      (*handle)->id_product = dev->descriptor.idProduct;
      (*handle)->bcd_device = dev->descriptor.bcdDevice;
      (*handle)->ifc_no = ifc_no;
      (*handle)->ep_bulk_out = ep_bulk_out;
      (*handle)->ep_bulk_in = ep_bulk_in;
      (*handle)->ep_intr = ep_intr;
    }
  else if (dev_fd != -1) /* Device transport. */
    {
      (*handle)->idev = NULL;
      (*handle)->dev_fd = dev_fd;
      (*handle)->id_vendor = 0;  /* Magic vendor for special transport. */
      (*handle)->id_product = ifc_no; /* Transport type */
      prepare_special_transport (*handle);
    }
  else
    {
      assert (!"no transport"); /* Bug. */
    }

  DEBUGOUT_2 ("using CCID reader %d (ID=%s)\n",  readerno, rid );

  if (idev)
    {
      if (parse_ccid_descriptor (*handle, ifcdesc_extra, ifcdesc_extra_len))
        {
          DEBUGOUT ("device not supported\n");
          rc = CCID_DRIVER_ERR_NO_READER;
          goto leave;
        }

      rc = usb_claim_interface (idev, ifc_no);
      if (rc)
        {
          DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
          rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
          goto leave;
        }
    }

  rc = ccid_vendor_specific_init (*handle);

 leave:
  free (ifcdesc_extra);
  if (rc)
    {
      free (rid);
      if (idev)
        usb_close (idev);
      if (dev_fd != -1)
        close (dev_fd);
      free (*handle);
      *handle = NULL;
    }

  return rc;
}