parse_ccid_descriptor (ccid_driver_t handle,
                       const unsigned char *buf, size_t buflen)
{
  unsigned int i;
  unsigned int us;
  int have_t1 = 0, have_tpdu=0;


  handle->nonnull_nad = 0;
  handle->auto_ifsd = 0;
  handle->max_ifsd = 32;
  handle->ifsd = 0;
  handle->has_pinpad = 0;
  handle->apdu_level = 0;
  handle->auto_voltage = 0;
  handle->auto_param = 0;
  handle->auto_pps = 0;
  DEBUGOUT_3 ("idVendor: %04X  idProduct: %04X  bcdDevice: %04X\n",
              handle->id_vendor, handle->id_product, handle->bcd_device);
  if (buflen < 54 || buf[0] < 54)
    {
      DEBUGOUT ("CCID device descriptor is too short\n");
      return -1;
    }

  DEBUGOUT   ("ChipCard Interface Descriptor:\n");
  DEBUGOUT_1 ("  bLength             %5u\n", buf[0]);
  DEBUGOUT_1 ("  bDescriptorType     %5u\n", buf[1]);
  DEBUGOUT_2 ("  bcdCCID             %2x.%02x", buf[3], buf[2]);
    if (buf[3] != 1 || buf[2] != 0)
      DEBUGOUT_CONT("  (Warning: Only accurate for version 1.0)");
  DEBUGOUT_LF ();

  DEBUGOUT_1 ("  nMaxSlotIndex       %5u\n", buf[4]);
  DEBUGOUT_2 ("  bVoltageSupport     %5u  %s\n",
              buf[5], (buf[5] == 1? "5.0V" : buf[5] == 2? "3.0V"
                       : buf[5] == 3? "1.8V":"?"));

  us = convert_le_u32 (buf+6);
  DEBUGOUT_1 ("  dwProtocols         %5u ", us);
  if ((us & 1))
    DEBUGOUT_CONT (" T=0");
  if ((us & 2))
    {
      DEBUGOUT_CONT (" T=1");
      have_t1 = 1;
    }
  if ((us & ~3))
    DEBUGOUT_CONT (" (Invalid values detected)");
  DEBUGOUT_LF ();

  us = convert_le_u32(buf+10);
  DEBUGOUT_1 ("  dwDefaultClock      %5u\n", us);
  us = convert_le_u32(buf+14);
  DEBUGOUT_1 ("  dwMaxiumumClock     %5u\n", us);
  DEBUGOUT_1 ("  bNumClockSupported  %5u\n", buf[18]);
  us = convert_le_u32(buf+19);
  DEBUGOUT_1 ("  dwDataRate        %7u bps\n", us);
  us = convert_le_u32(buf+23);
  DEBUGOUT_1 ("  dwMaxDataRate     %7u bps\n", us);
  DEBUGOUT_1 ("  bNumDataRatesSupp.  %5u\n", buf[27]);

  us = convert_le_u32(buf+28);
  DEBUGOUT_1 ("  dwMaxIFSD           %5u\n", us);
  handle->max_ifsd = us;

  us = convert_le_u32(buf+32);
  DEBUGOUT_1 ("  dwSyncProtocols  %08X ", us);
  if ((us&1))
    DEBUGOUT_CONT ( " 2-wire");
  if ((us&2))
    DEBUGOUT_CONT ( " 3-wire");
  if ((us&4))
    DEBUGOUT_CONT ( " I2C");
  DEBUGOUT_LF ();

  us = convert_le_u32(buf+36);
  DEBUGOUT_1 ("  dwMechanical     %08X ", us);
  if ((us & 1))
    DEBUGOUT_CONT (" accept");
  if ((us & 2))
    DEBUGOUT_CONT (" eject");
  if ((us & 4))
    DEBUGOUT_CONT (" capture");
  if ((us & 8))
    DEBUGOUT_CONT (" lock");
  DEBUGOUT_LF ();

  us = convert_le_u32(buf+40);
  DEBUGOUT_1 ("  dwFeatures       %08X\n", us);
  if ((us & 0x0002))
    {
      DEBUGOUT ("    Auto configuration based on ATR (assumes auto voltage)\n");
      handle->auto_voltage = 1;
    }
  if ((us & 0x0004))
    DEBUGOUT ("    Auto activation on insert\n");
  if ((us & 0x0008))
    {
      DEBUGOUT ("    Auto voltage selection\n");
      handle->auto_voltage = 1;
    }
  if ((us & 0x0010))
    DEBUGOUT ("    Auto clock change\n");
  if ((us & 0x0020))
    DEBUGOUT ("    Auto baud rate change\n");
  if ((us & 0x0040))
    {
      DEBUGOUT ("    Auto parameter negotiation made by CCID\n");
      handle->auto_param = 1;
    }
  else if ((us & 0x0080))
    {
      DEBUGOUT ("    Auto PPS made by CCID\n");
      handle->auto_pps = 1;
    }
  if ((us & (0x0040 | 0x0080)) == (0x0040 | 0x0080))
    DEBUGOUT ("    WARNING: conflicting negotiation features\n");

  if ((us & 0x0100))
    DEBUGOUT ("    CCID can set ICC in clock stop mode\n");
  if ((us & 0x0200))
    {
      DEBUGOUT ("    NAD value other than 0x00 accepted\n");
      handle->nonnull_nad = 1;
    }
  if ((us & 0x0400))
    {
      DEBUGOUT ("    Auto IFSD exchange\n");
      handle->auto_ifsd = 1;
    }

  if ((us & 0x00010000))
    {
      DEBUGOUT ("    TPDU level exchange\n");
      have_tpdu = 1;
    }
  else if ((us & 0x00020000))
    {
      DEBUGOUT ("    Short APDU level exchange\n");
      handle->apdu_level = 1;
    }
  else if ((us & 0x00040000))
    {
      DEBUGOUT ("    Short and extended APDU level exchange\n");
      handle->apdu_level = 2;
    }
  else if ((us & 0x00070000))
    DEBUGOUT ("    WARNING: conflicting exchange levels\n");

  us = convert_le_u32(buf+44);
  DEBUGOUT_1 ("  dwMaxCCIDMsgLen     %5u\n", us);

  DEBUGOUT (  "  bClassGetResponse    ");
  if (buf[48] == 0xff)
    DEBUGOUT_CONT ("echo\n");
  else
    DEBUGOUT_CONT_1 ("  %02X\n", buf[48]);

  DEBUGOUT (  "  bClassEnvelope       ");
  if (buf[49] == 0xff)
    DEBUGOUT_CONT ("echo\n");
  else
    DEBUGOUT_CONT_1 ("  %02X\n", buf[48]);

  DEBUGOUT (  "  wlcdLayout           ");
  if (!buf[50] && !buf[51])
    DEBUGOUT_CONT ("none\n");
  else
    DEBUGOUT_CONT_2 ("%u cols %u lines\n", buf[50], buf[51]);

  DEBUGOUT_1 ("  bPINSupport         %5u ", buf[52]);
  if ((buf[52] & 1))
    {
      DEBUGOUT_CONT ( " verification");
      handle->has_pinpad |= 1;
    }
  if ((buf[52] & 2))
    {
      DEBUGOUT_CONT ( " modification");
      handle->has_pinpad |= 2;
    }
  DEBUGOUT_LF ();

  DEBUGOUT_1 ("  bMaxCCIDBusySlots   %5u\n", buf[53]);

  if (buf[0] > 54)
    {
      DEBUGOUT ("  junk             ");
      for (i=54; i < buf[0]-54; i++)
        DEBUGOUT_CONT_1 (" %02X", buf[i]);
      DEBUGOUT_LF ();
    }

  if (!have_t1 || !(have_tpdu  || handle->apdu_level))
    {
      DEBUGOUT ("this drivers requires that the reader supports T=1, "
                "TPDU or APDU level exchange - this is not available\n");
      return -1;
    }


  /* SCM drivers get stuck in their internal USB stack if they try to
     send a frame of n*wMaxPacketSize back to us.  Given that
     wMaxPacketSize is 64 for these readers we set the IFSD to a value
     lower than that:
        64 - 10 CCID header -  4 T1frame - 2 reserved = 48
     Product Ids:
	 0xe001 - SCR 331
	 0x5111 - SCR 331-DI
	 0x5115 - SCR 335
	 0xe003 - SPR 532
     The
         0x5117 - SCR 3320 USB ID-000 reader
     seems to be very slow but enabling this workaround boosts the
     performance to a a more or less acceptable level (tested by David).

  */
  if (handle->id_vendor == VENDOR_SCM
      && handle->max_ifsd > 48
      && (  (handle->id_product == SCM_SCR331   && handle->bcd_device < 0x0516)
          ||(handle->id_product == SCM_SCR331DI && handle->bcd_device < 0x0620)
          ||(handle->id_product == SCM_SCR335   && handle->bcd_device < 0x0514)
          ||(handle->id_product == SCM_SPR532   && handle->bcd_device < 0x0504)
          ||(handle->id_product == SCM_SCR3320  && handle->bcd_device < 0x0522)
          ))
    {
      DEBUGOUT ("enabling workaround for buggy SCM readers\n");
      handle->max_ifsd = 48;
    }

  if (handle->id_vendor == VENDOR_GEMPC && handle->id_product == GEMPC_CT30)
    {
      DEBUGOUT ("enabling product quirk: disable non-null NAD\n");
      handle->nonnull_nad = 0;
    }

  return 0;
}
parse_ccid_descriptor (ccid_driver_t handle,
                       const unsigned char *buf, size_t buflen)
{
  unsigned int i;
  unsigned int us;
  int have_t1 = 0, have_tpdu=0;


  handle->nonnull_nad = 0;
  handle->auto_ifsd = 0;
  handle->max_ifsd = 32;
  handle->ifsd = 0;
  handle->has_pinpad = 0;
  handle->apdu_level = 0;
  handle->auto_voltage = 0;
  handle->auto_param = 0;
  handle->auto_pps = 0;
  DEBUGOUT_3 ("idVendor: %04X  idProduct: %04X  bcdDevice: %04X\n",
              handle->id_vendor, handle->id_product, handle->bcd_device);
  if (buflen < 54 || buf[0] < 54)
    {
      DEBUGOUT ("CCID device descriptor is too short\n");
      return -1;
    }

  DEBUGOUT   ("ChipCard Interface Descriptor:\n");
  DEBUGOUT_1 ("  bLength             %5u\n", buf[0]);
  DEBUGOUT_1 ("  bDescriptorType     %5u\n", buf[1]);
  DEBUGOUT_2 ("  bcdCCID             %2x.%02x", buf[3], buf[2]);
    if (buf[3] != 1 || buf[2] != 0)
      DEBUGOUT_CONT("  (Warning: Only accurate for version 1.0)");
  DEBUGOUT_LF ();

  DEBUGOUT_1 ("  nMaxSlotIndex       %5u\n", buf[4]);
  DEBUGOUT_2 ("  bVoltageSupport     %5u  %s\n",
              buf[5], (buf[5] == 1? "5.0V" : buf[5] == 2? "3.0V"
                       : buf[5] == 3? "1.8V":"?"));

  us = convert_le_u32 (buf+6);
  DEBUGOUT_1 ("  dwProtocols         %5u ", us);
  if ((us & 1))
    DEBUGOUT_CONT (" T=0");
  if ((us & 2))
    {
      DEBUGOUT_CONT (" T=1");
      have_t1 = 1;
    }
  if ((us & ~3))
    DEBUGOUT_CONT (" (Invalid values detected)");
  DEBUGOUT_LF ();

  us = convert_le_u32(buf+10);
  DEBUGOUT_1 ("  dwDefaultClock      %5u\n", us);
  us = convert_le_u32(buf+14);
  DEBUGOUT_1 ("  dwMaxiumumClock     %5u\n", us);
  DEBUGOUT_1 ("  bNumClockSupported  %5u\n", buf[18]);
  us = convert_le_u32(buf+19);
  DEBUGOUT_1 ("  dwDataRate        %7u bps\n", us);
  us = convert_le_u32(buf+23);
  DEBUGOUT_1 ("  dwMaxDataRate     %7u bps\n", us);
  DEBUGOUT_1 ("  bNumDataRatesSupp.  %5u\n", buf[27]);

  us = convert_le_u32(buf+28);
  DEBUGOUT_1 ("  dwMaxIFSD           %5u\n", us);
  handle->max_ifsd = us;

  us = convert_le_u32(buf+32);
  DEBUGOUT_1 ("  dwSyncProtocols  %08X ", us);
  if ((us&1))
    DEBUGOUT_CONT ( " 2-wire");
  if ((us&2))
    DEBUGOUT_CONT ( " 3-wire");
  if ((us&4))
    DEBUGOUT_CONT ( " I2C");
  DEBUGOUT_LF ();

  us = convert_le_u32(buf+36);
  DEBUGOUT_1 ("  dwMechanical     %08X ", us);
  if ((us & 1))
    DEBUGOUT_CONT (" accept");
  if ((us & 2))
    DEBUGOUT_CONT (" eject");
  if ((us & 4))
    DEBUGOUT_CONT (" capture");
  if ((us & 8))
    DEBUGOUT_CONT (" lock");
  DEBUGOUT_LF ();

  us = convert_le_u32(buf+40);
  DEBUGOUT_1 ("  dwFeatures       %08X\n", us);
  if ((us & 0x0002))
    {
      DEBUGOUT ("    Auto configuration based on ATR (assumes auto voltage)\n");
      handle->auto_voltage = 1;
    }
  if ((us & 0x0004))
    DEBUGOUT ("    Auto activation on insert\n");
  if ((us & 0x0008))
    {
      DEBUGOUT ("    Auto voltage selection\n");
      handle->auto_voltage = 1;
    }
  if ((us & 0x0010))
    DEBUGOUT ("    Auto clock change\n");
  if ((us & 0x0020))
    DEBUGOUT ("    Auto baud rate change\n");
  if ((us & 0x0040))
    {
      DEBUGOUT ("    Auto parameter negotiation made by CCID\n");
      handle->auto_param = 1;
    }
  else if ((us & 0x0080))
    {
      DEBUGOUT ("    Auto PPS made by CCID\n");
      handle->auto_pps = 1;
    }
  if ((us & (0x0040 | 0x0080)) == (0x0040 | 0x0080))
    DEBUGOUT ("    WARNING: conflicting negotiation features\n");

  if ((us & 0x0100))
    DEBUGOUT ("    CCID can set ICC in clock stop mode\n");
  if ((us & 0x0200))
    {
      DEBUGOUT ("    NAD value other than 0x00 accepted\n");
      handle->nonnull_nad = 1;
    }
  if ((us & 0x0400))
    {
      DEBUGOUT ("    Auto IFSD exchange\n");
      handle->auto_ifsd = 1;
    }

  if ((us & 0x00010000))
    {
      DEBUGOUT ("    TPDU level exchange\n");
      have_tpdu = 1;
    }
  else if ((us & 0x00020000))
    {
      DEBUGOUT ("    Short APDU level exchange\n");
      handle->apdu_level = 1;
    }
  else if ((us & 0x00040000))
    {
      DEBUGOUT ("    Short and extended APDU level exchange\n");
      handle->apdu_level = 2;
    }
  else if ((us & 0x00070000))
    DEBUGOUT ("    WARNING: conflicting exchange levels\n");

  us = convert_le_u32(buf+44);
  DEBUGOUT_1 ("  dwMaxCCIDMsgLen     %5u\n", us);

  DEBUGOUT (  "  bClassGetResponse    ");
  if (buf[48] == 0xff)
    DEBUGOUT_CONT ("echo\n");
  else
    DEBUGOUT_CONT_1 ("  %02X\n", buf[48]);

  DEBUGOUT (  "  bClassEnvelope       ");
  if (buf[49] == 0xff)
    DEBUGOUT_CONT ("echo\n");
  else
    DEBUGOUT_CONT_1 ("  %02X\n", buf[48]);

  DEBUGOUT (  "  wlcdLayout           ");
  if (!buf[50] && !buf[51])
    DEBUGOUT_CONT ("none\n");
  else
    DEBUGOUT_CONT_2 ("%u cols %u lines\n", buf[50], buf[51]);

  DEBUGOUT_1 ("  bPINSupport         %5u ", buf[52]);
  if ((buf[52] & 1))
    {
      DEBUGOUT_CONT ( " verification");
      handle->has_pinpad |= 1;
    }
  if ((buf[52] & 2))
    {
      DEBUGOUT_CONT ( " modification");
      handle->has_pinpad |= 2;
    }
  DEBUGOUT_LF ();

  DEBUGOUT_1 ("  bMaxCCIDBusySlots   %5u\n", buf[53]);

  if (buf[0] > 54)
    {
      DEBUGOUT ("  junk             ");
      for (i=54; i < buf[0]-54; i++)
        DEBUGOUT_CONT_1 (" %02X", buf[i]);
      DEBUGOUT_LF ();
    }

  if (!have_t1 || !(have_tpdu  || handle->apdu_level))
    {
      DEBUGOUT ("this drivers requires that the reader supports T=1, "
                "TPDU or APDU level exchange - this is not available\n");
      return -1;
    }


   
  if (handle->id_vendor == VENDOR_SCM
      && handle->max_ifsd > 48
      && (  (handle->id_product == SCM_SCR331   && handle->bcd_device < 0x0516)
          ||(handle->id_product == SCM_SCR331DI && handle->bcd_device < 0x0620)
          ||(handle->id_product == SCM_SCR335   && handle->bcd_device < 0x0514)
          ||(handle->id_product == SCM_SPR532   && handle->bcd_device < 0x0504)
          ||(handle->id_product == SCM_SCR3320  && handle->bcd_device < 0x0522)
          ))
    {
      DEBUGOUT ("enabling workaround for buggy SCM readers\n");
      handle->max_ifsd = 48;
    }

  if (handle->id_vendor == VENDOR_GEMPC && handle->id_product == GEMPC_CT30)
    {
      DEBUGOUT ("enabling product quirk: disable non-null NAD\n");
      handle->nonnull_nad = 0;
    }

  return 0;
}