bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
         size_t *nread, int expected_type, int seqno, int timeout,
         int no_debug)
{
  int rc;
  size_t msglen;
  int eagain_retries = 0;

  /* Fixme: The next line for the current Valgrind without support
     for USB IOCTLs. */
  memset (buffer, 0, length);
 retry:
  if (handle->idev)
    {
      rc = usb_bulk_read (handle->idev,
                          handle->ep_bulk_in,
                          (char*)buffer, length,
                          timeout);
      if (rc < 0)
        {
          rc = errno;
          DEBUGOUT_1 ("usb_bulk_read error: %s\n", strerror (rc));
          if (rc == EAGAIN && eagain_retries++ < 3)
            {
              my_sleep (1);
              goto retry;
            }
          return CCID_DRIVER_ERR_CARD_IO_ERROR;
        }
      *nread = msglen = rc;
    }
  else
    {
      rc = read (handle->dev_fd, buffer, length);
      if (rc < 0)
        {
          rc = errno;
          DEBUGOUT_2 ("read from %d failed: %s\n",
                      handle->dev_fd, strerror (rc));
          if (rc == EAGAIN && eagain_retries++ < 5)
            {
              my_sleep (1);
              goto retry;
            }
          return CCID_DRIVER_ERR_CARD_IO_ERROR;
        }
      *nread = msglen = rc;
    }
  eagain_retries = 0;

  if (msglen < 10)
    {
      DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen);
      abort_cmd (handle, seqno);
      return CCID_DRIVER_ERR_INV_VALUE;
    }
  if (buffer[5] != 0)
    {
      DEBUGOUT_1 ("unexpected bulk-in slot (%d)\n", buffer[5]);
      return CCID_DRIVER_ERR_INV_VALUE;
    }
  if (buffer[6] != seqno)
    {
      DEBUGOUT_2 ("bulk-in seqno does not match (%d/%d)\n",
                  seqno, buffer[6]);
      /* Retry until we are synced again.  */
      goto retry;
    }

  /* We need to handle the time extension request before we check that
     we got the expected message type.  This is in particular required
     for the Cherry keyboard which sends a time extension request for
     each key hit.  */
  if ( !(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80)
    {
      /* Card present and active, time extension requested. */
      DEBUGOUT_2 ("time extension requested (%02X,%02X)\n",
                  buffer[7], buffer[8]);
      goto retry;
    }

  if (buffer[0] != expected_type)
    {
      DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]);
      abort_cmd (handle, seqno);
      return CCID_DRIVER_ERR_INV_VALUE;
    }

  if (debug_level && (!no_debug || debug_level >= 3))
    {
      switch (buffer[0])
        {
        case RDR_to_PC_DataBlock:
          print_r2p_datablock (buffer, msglen);
          break;
        case RDR_to_PC_SlotStatus:
          print_r2p_slotstatus (buffer, msglen);
          break;
        case RDR_to_PC_Parameters:
          print_r2p_parameters (buffer, msglen);
          break;
        case RDR_to_PC_Escape:
          print_r2p_escape (buffer, msglen);
          break;
        case RDR_to_PC_DataRate:
          print_r2p_datarate (buffer, msglen);
          break;
        default:
          print_r2p_unknown (buffer, msglen);
          break;
        }
    }
  if (CCID_COMMAND_FAILED (buffer))
    print_command_failed (buffer);

  /* Check whether a card is at all available.  Note: If you add new
     error codes here, check whether they need to be ignored in
     send_escape_cmd. */
  switch ((buffer[7] & 0x03))
    {
    case 0: /* no error */ break;
    case 1: return CCID_DRIVER_ERR_CARD_INACTIVE;
    case 2: return CCID_DRIVER_ERR_NO_CARD;
    case 3: /* RFU */ break;
    }
  return 0;
}