/*****************************************************************************
@doc            INT EXT
******************************************************************************
* $ProjectName:  $
* $ProjectRevision:  $
*-----------------------------------------------------------------------------
* $Source: z:/pr/cmbp0/sw/cmbp0.ms/rcs/cmbp0scr.c $
* $Revision: 1.7 $
*-----------------------------------------------------------------------------
* $Author: WFrischauf $
*-----------------------------------------------------------------------------
* History: see EOF
*-----------------------------------------------------------------------------
*
* Copyright Đ 2000 OMNIKEY AG
******************************************************************************/

#include <cmbp0wdm.h>
#include <cmbp0scr.h>
#include <cmbp0log.h>


// this is a FI / Fi assignment
const ULONG Fi[] = { 372, 372, 588, 744, 1116, 1488, 1860, 372,
   372, 512, 768, 1024, 1536, 2048, 372, 372};

// this is a DI / Di assignment
const ULONG Di[] = { 1, 1, 2, 4, 8, 16, 32, 1,
   12, 20, 1, 1, 1, 1, 1, 1};



/*****************************************************************************
CMMOB_CorrectAtr

Routine Description:
  This function checks if the received ATR is valid.

Arguments:

Return Value:

*****************************************************************************/
VOID CMMOB_CorrectAtr(
                     PUCHAR pbBuffer,
                     ULONG  ulBufferSize
                     )
{
   UCHAR bNumberHistoricalBytes;
   UCHAR bXorChecksum;
   ULONG i;

   if (ulBufferSize < 0x09)  // mininmum length of a modified ATR
      return ;               // ATR is ok

   // variant 1
   if (pbBuffer[0] == 0x3b   &&
       pbBuffer[1] == 0xb4   &&
       pbBuffer[2] == 0x11   &&
       pbBuffer[3] == 0x00   &&
       pbBuffer[4] == 0x81   &&
       pbBuffer[5] == 0x31   &&
       pbBuffer[6] == 0x90   &&
       pbBuffer[7] == 0x73   &&
       ulBufferSize == 13      )
      {
      // correct checksum byte
      bXorChecksum = pbBuffer[1];
      for (i=2;i<ulBufferSize-1;i++)
         bXorChecksum ^= pbBuffer[i];

      if (pbBuffer[ulBufferSize -1 ] != bXorChecksum )
         {
         pbBuffer[ulBufferSize -1 ] = bXorChecksum;
         SmartcardDebug(DEBUG_ATR,
                        ("%s!CorrectAtr: Correcting SAMOS ATR (variant 1)\n", DRIVER_NAME));
         }
      }

   // variant 2
   if (pbBuffer[0] == 0x3b   &&
       pbBuffer[1] == 0xbf   &&
       pbBuffer[2] == 0x11   &&
       pbBuffer[3] == 0x00   &&
       pbBuffer[4] == 0x81   &&
       pbBuffer[5] == 0x31   &&
       pbBuffer[6] == 0x90   &&
       pbBuffer[7] == 0x73   &&
       ulBufferSize == 13   )
      {
      // correct number of historical bytes
      bNumberHistoricalBytes = 4;

      pbBuffer[1] &= 0xf0;
      pbBuffer[1] |= bNumberHistoricalBytes;

      // correct checksum byte
      bXorChecksum = pbBuffer[1];
      for (i=2;i<ulBufferSize-1;i++)
         bXorChecksum ^= pbBuffer[i];

      pbBuffer[ulBufferSize -1 ] = bXorChecksum;
      SmartcardDebug(DEBUG_ATR,
                     ("%s!CorrectAtr: Correcting SAMOS ATR (variant 2)\n", DRIVER_NAME));
      }

   // variant 3
   if (pbBuffer[0] == 0x3b   &&
       pbBuffer[1] == 0xbf   &&
       pbBuffer[2] == 0x11   &&
       pbBuffer[3] == 0x00   &&
       pbBuffer[4] == 0x81   &&
       pbBuffer[5] == 0x31   &&
       pbBuffer[6] == 0x90   &&
       pbBuffer[7] == 0x73   &&
       ulBufferSize ==  9      )
      {
      // correct number of historical bytes
      bNumberHistoricalBytes = 0;

      pbBuffer[1] &= 0xf0;
      pbBuffer[1] |= bNumberHistoricalBytes;

      // correct checksum byte
      bXorChecksum = pbBuffer[1];
      for (i=2;i<ulBufferSize-1;i++)
         bXorChecksum ^= pbBuffer[i];

      pbBuffer[ulBufferSize -1 ] = bXorChecksum;
      SmartcardDebug(DEBUG_ATR,
                     ("%s!CorrectAtr: Correcting SAMOS ATR (variant 3)\n",DRIVER_NAME));
      }

}


/*****************************************************************************
CMMOB_CardPower:
   callback handler for SMCLIB RDF_CARD_POWER

Arguments:
   SmartcardExtension   context of call

Return Value:
   STATUS_SUCCESS
   STATUS_NO_MEDIA
   STATUS_TIMEOUT
   STATUS_BUFFER_TOO_SMALL
******************************************************************************/
NTSTATUS CMMOB_CardPower (
                         PSMARTCARD_EXTENSION SmartcardExtension
                         )
{
   NTSTATUS          NTStatus = STATUS_SUCCESS;
   PREADER_EXTENSION ReaderExtension;
   UCHAR             pbAtrBuffer[MAXIMUM_ATR_LENGTH];
   ULONG             ulAtrLength;
   BOOLEAN           fMaxWaitTime=FALSE;

#if DBG || DEBUG
   static PCHAR request[] = { "PowerDown",  "ColdReset", "WarmReset"};
#endif

   SmartcardDebug(DEBUG_TRACE,
                  ("%s!CardPower: Enter, Request = %s\n",
                   DRIVER_NAME,request[SmartcardExtension->MinorIoControlCode]));

   ReaderExtension = SmartcardExtension->ReaderExtension;
#ifdef IOCARD
   ReaderExtension->fTActive=TRUE;
   NTStatus = CMMOB_SetFlags1(ReaderExtension);
   if (NTStatus != STATUS_SUCCESS)
      {
      goto ExitCardPower;
      }
#endif

   switch (SmartcardExtension->MinorIoControlCode)
      {
      case SCARD_WARM_RESET:
      case SCARD_COLD_RESET:
         // try asynchronous cards first
         // because some asynchronous cards
         // do not return 0xFF in the first byte
         NTStatus = CMMOB_PowerOnCard(SmartcardExtension,
                                      pbAtrBuffer,
                                      fMaxWaitTime,
                                      &ulAtrLength);

         if (NTStatus != STATUS_SUCCESS)
            {
            // try a second time, with maximum waiting time
            fMaxWaitTime=TRUE;
            NTStatus = CMMOB_PowerOnCard(SmartcardExtension,
                                         pbAtrBuffer,
                                         fMaxWaitTime,
                                         &ulAtrLength);
            }


/*
         if (NTStatus != STATUS_SUCCESS && NTStatus!= STATUS_NO_MEDIA)
            {
            NTStatus = CMMOB_PowerOnSynchronousCard(SmartcardExtension,
                                                     pbAtrBuffer,
                                                     &ulAtrLength);
            }
*/
         if (NTStatus != STATUS_SUCCESS)
            {
            goto ExitCardPower;
            }

         // correct ATR in case of old Samos cards, with wrong number of historical bytes / checksum
         CMMOB_CorrectAtr(pbAtrBuffer, ulAtrLength);

         if (ReaderExtension->CardParameters.fSynchronousCard == FALSE)
            {
            // copy ATR to smart card structure
            // the lib needs the ATR for evaluation of the card parameters

            RtlCopyBytes((PVOID)SmartcardExtension->CardCapabilities.ATR.Buffer,
                         (PVOID)pbAtrBuffer,
                         ulAtrLength);

            SmartcardExtension->CardCapabilities.ATR.Length = (UCHAR)ulAtrLength;

            SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_NEGOTIABLE;

            SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_UNDEFINED;

            NTStatus = SmartcardUpdateCardCapabilities(SmartcardExtension);
            if (NTStatus != STATUS_SUCCESS)
               {
               if (!fMaxWaitTime)
                  {
                  // try a second time, with maximum waiting time
                  fMaxWaitTime=TRUE;
                  NTStatus = CMMOB_PowerOnCard(SmartcardExtension,
                                               pbAtrBuffer,
                                               fMaxWaitTime,
                                               &ulAtrLength);
                  if (NTStatus != STATUS_SUCCESS)
                     {
                     goto ExitCardPower;
                     }
                  RtlCopyBytes((PVOID)SmartcardExtension->CardCapabilities.ATR.Buffer,
                               (PVOID)pbAtrBuffer,
                               ulAtrLength);
                  SmartcardExtension->CardCapabilities.ATR.Length = (UCHAR)ulAtrLength;
                  SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_NEGOTIABLE;
                  SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_UNDEFINED;

                  NTStatus = SmartcardUpdateCardCapabilities(SmartcardExtension);
                  if (NTStatus != STATUS_SUCCESS)
                     {
                     goto ExitCardPower;
                     }
                  }
               else
                  goto ExitCardPower;
               }

            // -----------------------
            // set parameters
            // -----------------------
            if (SmartcardExtension->CardCapabilities.N != 0xff)
               {
               // 0 <= N <= 254
               ReaderExtension->CardParameters.bStopBits = 2 + SmartcardExtension->CardCapabilities.N;
               }
            else
               {
               // N = 255
               if (SmartcardExtension->CardCapabilities.Protocol.Selected & SCARD_PROTOCOL_T0)
                  {
                  // 12 etu for T=0;
                  ReaderExtension->CardParameters.bStopBits = 2;
                  }
               else
                  {
                  // 11 etu for T=1
                  ReaderExtension->CardParameters.bStopBits = 1;
                  }
               }

            if (SmartcardExtension->CardCapabilities.InversConvention)
               {
               ReaderExtension->CardParameters.fInversRevers = TRUE;
               SmartcardDebug(DEBUG_ATR,
                              ("%s!Card with invers convention !\n",DRIVER_NAME ));
               }

            CMMOB_SetCardParameters (ReaderExtension);

#if DBG
            {
               ULONG i;
               SmartcardDebug(DEBUG_ATR,("%s!ATR : ",DRIVER_NAME));
               for (i = 0;i < ulAtrLength;i++)
                  SmartcardDebug(DEBUG_ATR,("%2.2x ",SmartcardExtension->CardCapabilities.ATR.Buffer[i]));
               SmartcardDebug(DEBUG_ATR,("\n"));
            }
#endif

            }
         else
            {
            SmartcardExtension->CardCapabilities.ATR.Buffer[0] = 0x3B;
            SmartcardExtension->CardCapabilities.ATR.Buffer[1] = 0x04;

            RtlCopyBytes((PVOID)&SmartcardExtension->CardCapabilities.ATR.Buffer[2],
                         (PVOID)pbAtrBuffer,
                         ulAtrLength);

            ulAtrLength += 2;
            SmartcardExtension->CardCapabilities.ATR.Length = (UCHAR)ulAtrLength;

            SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_SPECIFIC;
            SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_T0;

            NTStatus = SmartcardUpdateCardCapabilities(SmartcardExtension);
            if (NTStatus != STATUS_SUCCESS)
               {
               goto ExitCardPower;
               }

            SmartcardDebug(DEBUG_ATR,("ATR of synchronous smart card : %2.2x %2.2x %2.2x %2.2x\n",
                                      pbAtrBuffer[0],pbAtrBuffer[1],pbAtrBuffer[2],pbAtrBuffer[3]));

            }

         // copy ATR to user space
         if (SmartcardExtension->IoRequest.ReplyBufferLength >=
             SmartcardExtension->CardCapabilities.ATR.Length)
            {
            RtlCopyBytes((PVOID)SmartcardExtension->IoRequest.ReplyBuffer,
                         (PVOID)SmartcardExtension->CardCapabilities.ATR.Buffer,
                         SmartcardExtension->CardCapabilities.ATR.Length);

            *SmartcardExtension->IoRequest.Information = SmartcardExtension->CardCapabilities.ATR.Length;
            }
         else
            {
            NTStatus = STATUS_BUFFER_TOO_SMALL;
            *SmartcardExtension->IoRequest.Information = 0;
            }

         break;

      case SCARD_POWER_DOWN:
         NTStatus = CMMOB_PowerOffCard(SmartcardExtension);
         if (NTStatus != STATUS_SUCCESS)
            {
            goto ExitCardPower;
            }

         SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_SWALLOWED;
         SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_UNDEFINED;

         break;
      }


   ExitCardPower:
#ifdef IOCARD
   ReaderExtension->fTActive=FALSE;
   CMMOB_SetFlags1(ReaderExtension);
#endif
   SmartcardDebug(DEBUG_TRACE,
                  ("%s!CardPower: Exit %X\n",DRIVER_NAME,NTStatus ));

   return( NTStatus );

}


/*****************************************************************************
Routine Description:
   CMMOB_PowerOnCard


Arguments:



Return Value:

*****************************************************************************/
NTSTATUS CMMOB_PowerOnCard (
                           IN  PSMARTCARD_EXTENSION SmartcardExtension,
                           IN  PUCHAR pbATR,
                           IN  BOOLEAN fMaxWaitTime,
                           OUT PULONG pulATRLength
                           )
{
   NTSTATUS             NTStatus = STATUS_SUCCESS;
   KTIMER               TimerWait;
   UCHAR                bPowerCmd;
   ULONG                ulCardType;
   ULONG                ulBytesReceived;
   UCHAR                bFirstByte;
   LONG                 lWaitTime;
   LARGE_INTEGER        liWaitTime;
   BOOLEAN              fTimeExpired;


   SmartcardDebug(DEBUG_TRACE,
                  ("%s!PowerOnCard: Enter\n",DRIVER_NAME));

   SmartcardExtension->ReaderExtension->CardParameters.bStopBits=2;
   SmartcardExtension->ReaderExtension->CardParameters.fSynchronousCard=FALSE;
   SmartcardExtension->ReaderExtension->CardParameters.fInversRevers=FALSE;
   SmartcardExtension->ReaderExtension->CardParameters.bClockFrequency=4;
   SmartcardExtension->ReaderExtension->CardParameters.fT0Mode=FALSE;
   SmartcardExtension->ReaderExtension->CardParameters.fT0Write=FALSE;
   SmartcardExtension->ReaderExtension->fReadCIS = FALSE;

   //   reset the state machine of the reader
   NTStatus = CMMOB_ResetReader (SmartcardExtension->ReaderExtension);
   if (NTStatus!=STATUS_SUCCESS)
      goto ExitPowerOnCard;

   if (CMMOB_CardInserted (SmartcardExtension->ReaderExtension))
      {

      //initialize Timer
      KeInitializeTimer(&TimerWait);

      //we have to differentiate between cold and warm reset
      if (SmartcardExtension->MinorIoControlCode == SCARD_WARM_RESET &&
          CMMOB_CardPowered (SmartcardExtension->ReaderExtension))
         {
         //warm reset
         bPowerCmd=CMD_POWERON_WARM;
         }
      else
         {
         //cold reset
         bPowerCmd=CMD_POWERON_COLD;

         //if card is powerde we have to turn it off for cold reset
         if (CMMOB_CardPowered (SmartcardExtension->ReaderExtension))
            CMMOB_PowerOffCard (SmartcardExtension);
         }

#define MAX_CARD_TYPE 2

      for (ulCardType = 0; ulCardType < MAX_CARD_TYPE; ulCardType++)
         {
         switch (ulCardType)
            {
            case 0:
               // BaudRate divider 372 - 1
               SmartcardExtension->ReaderExtension->CardParameters.bBaudRateHigh = 0x01;
               SmartcardExtension->ReaderExtension->CardParameters.bBaudRateLow = 0x73;
               if (fMaxWaitTime)
                  lWaitTime=1000;
               else
                  lWaitTime=100;
               SmartcardDebug(DEBUG_ATR,
                              ("%s!trying 3.57 Mhz smart card, waiting time %ims\n",DRIVER_NAME,lWaitTime));
               break;

            case 1:
               // BaudRate divider 512 - 1
               SmartcardExtension->ReaderExtension->CardParameters.bBaudRateHigh = 0x01;
               SmartcardExtension->ReaderExtension->CardParameters.bBaudRateLow = 0xFF;
               if (fMaxWaitTime)
                  lWaitTime=1400;
               else
                  lWaitTime=140;
               SmartcardDebug(DEBUG_ATR,
                              ("%s!trying 4.92 Mhz smart card, waiting time %ims\n",DRIVER_NAME,lWaitTime));
               break;
            }

         //set baud rate
         NTStatus=CMMOB_SetCardParameters(SmartcardExtension->ReaderExtension);
         if (NTStatus!=STATUS_SUCCESS)
            goto ExitPowerOnCard;

         //issue power on command
         NTStatus=CMMOB_WriteRegister(SmartcardExtension->ReaderExtension,
                                      ADDR_WRITEREG_FLAGS0, bPowerCmd);
         if (NTStatus!=STATUS_SUCCESS)
            goto ExitPowerOnCard;

         // maximum wait for power on first byte 100 ms
         liWaitTime = RtlConvertLongToLargeInteger(100L * -10000L);
         KeSetTimer(&TimerWait,liWaitTime,NULL);
         do
            {
            fTimeExpired = KeReadStateTimer(&TimerWait);
            NTStatus=CMMOB_BytesReceived (SmartcardExtension->ReaderExtension,&ulBytesReceived);
            if (NTStatus!=STATUS_SUCCESS)
               goto ExitPowerOnCard;
            // wait 1 ms, so that processor is not blocked
            SysDelay(1);
            }
         while (fTimeExpired==FALSE && ulBytesReceived == 0x00);

         if (fTimeExpired)
            {
            NTStatus = STATUS_IO_TIMEOUT;
            }
         else
            {
            ULONG ulBytesReceivedPrevious;

            KeCancelTimer(&TimerWait);

            // maximum wait for power on last byte 1 s for 3.58 card
            // and 1.4 seconds for 4.91 cards
            liWaitTime = RtlConvertLongToLargeInteger(lWaitTime * -10000L);
            do
               {
               KeSetTimer(&TimerWait,liWaitTime,NULL);
               NTStatus=CMMOB_BytesReceived (SmartcardExtension->ReaderExtension,&ulBytesReceivedPrevious);
               if (NTStatus!=STATUS_SUCCESS)
                  goto ExitPowerOnCard;
               do
                  {
                  fTimeExpired = KeReadStateTimer(&TimerWait);
                  NTStatus=CMMOB_BytesReceived (SmartcardExtension->ReaderExtension,&ulBytesReceived);
                  if (NTStatus!=STATUS_SUCCESS)
                     goto ExitPowerOnCard;
                  // wait 1 ms, so that processor is not blocked
                  SysDelay(1);
                  }
               while (fTimeExpired==FALSE && ulBytesReceivedPrevious == ulBytesReceived);

               if (!fTimeExpired)
                  {
                  KeCancelTimer(&TimerWait);
                  }
               }
            while (!fTimeExpired);


            //now we should have received an ATR
            NTStatus=CMMOB_ResetReader (SmartcardExtension->ReaderExtension);
            if (NTStatus!=STATUS_SUCCESS)
               goto ExitPowerOnCard;

            NTStatus=CMMOB_ReadBuffer(SmartcardExtension->ReaderExtension, 0, 1, &bFirstByte);
            if (NTStatus!=STATUS_SUCCESS)
               goto ExitPowerOnCard;

            if ((bFirstByte != 0x3B &&
                 bFirstByte != 0x03 )||
                ulBytesReceived > MAXIMUM_ATR_LENGTH)
               {
               NTStatus=STATUS_UNRECOGNIZED_MEDIA;
               }
            else
               {
               pbATR[0]=bFirstByte;
               NTStatus=CMMOB_ReadBuffer(SmartcardExtension->ReaderExtension,
                                         1, ulBytesReceived, &pbATR[1]);
               if (NTStatus!=STATUS_SUCCESS)
                  goto ExitPowerOnCard;
               *pulATRLength = ulBytesReceived;
               // success leave the loop
               break;
               }
            }
         // if not the last time in the loop power off
         // the card to get a well defined condition
         // (after the last pass the power off is
         // done outside the loop if necessary)
         if (ulCardType < MAX_CARD_TYPE-1)
            {
            CMMOB_PowerOffCard(SmartcardExtension);
            }
         }
      }
   else
      {
      NTStatus=STATUS_NO_MEDIA;
      }

   ExitPowerOnCard:

   if (NTStatus!=STATUS_SUCCESS)
      {
      if (NTStatus != STATUS_NO_MEDIA)
         {
         NTStatus = STATUS_UNRECOGNIZED_MEDIA;
         }
      CMMOB_PowerOffCard(SmartcardExtension);
      }

   SmartcardDebug(DEBUG_TRACE,
                  ("%s!PowerOnCard: Exit %lx\n",DRIVER_NAME,NTStatus));

   return NTStatus;
}


/*****************************************************************************
Routine Description:
   CMMOB_PowerOffCard


Arguments:



Return Value:

*****************************************************************************/
NTSTATUS CMMOB_PowerOffCard (
                            IN PSMARTCARD_EXTENSION SmartcardExtension
                            )
{
   NTSTATUS             NTStatus = STATUS_SUCCESS;
   BYTE*                pbRegsBase;
   KTIMER               TimerWait;
   LARGE_INTEGER        liWaitTime;
   BOOLEAN              fTimeExpired;


   SmartcardDebug(DEBUG_TRACE,
                  ("%s!PowerOffCard: Enter\n",DRIVER_NAME));

   NTStatus = CMMOB_ResetReader (SmartcardExtension->ReaderExtension);
   if (NTStatus!=STATUS_SUCCESS)
      goto ExitPowerOffCard;

   pbRegsBase=SmartcardExtension->ReaderExtension->pbRegsBase;
   if (CMMOB_CardInserted (SmartcardExtension->ReaderExtension))
      {
      // set card state for update thread
      // otherwise a card removal/insertion would be recognized
      if (SmartcardExtension->ReaderExtension->ulOldCardState == POWERED)
         SmartcardExtension->ReaderExtension->ulOldCardState = INSERTED;

      //issue power off command
      CMMOB_WriteRegister(SmartcardExtension->ReaderExtension,
                          ADDR_WRITEREG_FLAGS0, CMD_POWEROFF);

      KeInitializeTimer(&TimerWait);
      // maximum wait for power down 1 second
      liWaitTime = RtlConvertLongToLargeInteger(1000L * -10000L);
      KeSetTimer(&TimerWait,liWaitTime,NULL);
      do
         {
         fTimeExpired = KeReadStateTimer(&TimerWait);
         // wait 1 ms, so that processor is not blocked
         SysDelay(1);
         }
      while (fTimeExpired==FALSE && CMMOB_CardPowered (SmartcardExtension->ReaderExtension));

      if (fTimeExpired)
         {
         NTStatus = STATUS_IO_TIMEOUT;
         }
      else
         {
         KeCancelTimer(&TimerWait);
         }
      }
   else
      {
      NTStatus=STATUS_NO_MEDIA;
      }

   ExitPowerOffCard:

   CMMOB_ResetReader (SmartcardExtension->ReaderExtension);

   SmartcardDebug(DEBUG_TRACE,
                  ("%s!PowerOffCard: Exit %lx\n",DRIVER_NAME,NTStatus));

   return NTStatus;
}

/*****************************************************************************
CMMOB_SetProtocol:
   callback handler for SMCLIB RDF_SET_PROTOCOL

Arguments:
   SmartcardExtension   context of call

Return Value:
   STATUS_SUCCESS
   STATUS_NO_MEDIA
   STATUS_TIMEOUT
   STATUS_BUFFER_TOO_SMALL
   STATUS_INVALID_DEVICE_STATE
   STATUS_INVALID_DEVICE_REQUEST
******************************************************************************/
NTSTATUS CMMOB_SetProtocol(
                          PSMARTCARD_EXTENSION SmartcardExtension
                          )
{
   NTSTATUS          NTStatus;
   PREADER_EXTENSION ReaderExtension;
   USHORT            usSCLibProtocol;
   UCHAR             abPTSRequest[4];
   UCHAR             abPTSReply [4];
   ULONG             ulBytesRead;
   ULONG             ulBaudRateDivider;
   ULONG             ulWaitTime;
   UCHAR             bTemp;
   ULONG             i;

   SmartcardDebug(DEBUG_TRACE,
                  ("%s!SetProtocol: Enter\n",DRIVER_NAME ));

   ReaderExtension = SmartcardExtension->ReaderExtension;

#ifdef IOCARD
   ReaderExtension->fTActive=TRUE;
   NTStatus = CMMOB_SetFlags1(ReaderExtension);
   if (NTStatus != STATUS_SUCCESS)
      {
      goto ExitSetProtocol;
      }
#endif

   NTStatus = STATUS_PENDING;

   usSCLibProtocol = ( USHORT )( SmartcardExtension->MinorIoControlCode );

   //
   //   check card insertion
   //
   if (SmartcardExtension->ReaderCapabilities.CurrentState == SCARD_ABSENT)
      {
      NTStatus = STATUS_NO_MEDIA;
      }
   else
      {
      //
      //    Check if the card is already in specific state and if the caller
      //    wants to have the selected protocol
      //
      if (SmartcardExtension->ReaderCapabilities.CurrentState == SCARD_SPECIFIC)
         {
         if (SmartcardExtension->CardCapabilities.Protocol.Selected == usSCLibProtocol)
            {
            NTStatus = STATUS_SUCCESS;
            }
         }
      }
   if (NTStatus == STATUS_PENDING)
      {

      //
      //    reset the state machine of the reader
      //
      NTStatus=CMMOB_ResetReader(ReaderExtension);
      if (NTStatus==STATUS_SUCCESS)
         {
         // try 2 times,
         // 0 - optimal
         // 1 - default
         for (i=0; i<2; i++)
            {

            // set initial character of PTS
            abPTSRequest[0] = 0xFF;

            // set the format character (PTS0)
            if (SmartcardExtension->CardCapabilities.Protocol.Supported &
                usSCLibProtocol & SCARD_PROTOCOL_T1)
               {
               // select T=1 and indicate that PTS1 follows
               abPTSRequest[1] = 0x11;
               SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_T1;
               }
            else if (SmartcardExtension->CardCapabilities.Protocol.Supported &
                     usSCLibProtocol & SCARD_PROTOCOL_T0)
               {
               // select T=0 and indicate that PTS1 follows
               abPTSRequest[1] = 0x10;
               SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_T0;
               }
            else
               {
               // we do not support other protocols
               NTStatus = STATUS_INVALID_DEVICE_REQUEST;
               goto ExitSetProtocol;
               }


            if (i==0)
               {
               // optimal
               bTemp = (BYTE) (SmartcardExtension->CardCapabilities.PtsData.Fl << 4 |
                               SmartcardExtension->CardCapabilities.PtsData.Dl);
               SmartcardDebug(DEBUG_PROTOCOL,
                              ("%s! from library suggested PTS1(0x%x)\n",DRIVER_NAME,bTemp));

               SmartcardExtension->CardCapabilities.PtsData.Type = PTS_TYPE_OPTIMAL;
               SmartcardExtension->CardCapabilities.PtsData.Fl=SmartcardExtension->CardCapabilities.Fl;
               SmartcardExtension->CardCapabilities.PtsData.Dl=SmartcardExtension->CardCapabilities.Dl;
               }
            else
               {
               // default
               // we donīt know if it is correct to set 4.91Mhz cards to 0x11
               // but we have no card to try now
               SmartcardExtension->CardCapabilities.PtsData.Type = PTS_TYPE_DEFAULT;
               SmartcardExtension->CardCapabilities.PtsData.Fl=1;
               SmartcardExtension->CardCapabilities.PtsData.Dl=1;
               }

            bTemp = (BYTE) (SmartcardExtension->CardCapabilities.PtsData.Fl << 4 |
                            SmartcardExtension->CardCapabilities.PtsData.Dl);
            SmartcardDebug(DEBUG_PROTOCOL,
                           ("%s! trying PTS1(0x%x)\n",DRIVER_NAME,bTemp));
            switch (SmartcardExtension->CardCapabilities.PtsData.Fl)
               {
               case 1:
                  // here we can handle all baudrates
                  break;
               case 2:
               case 3:
                  if (SmartcardExtension->CardCapabilities.PtsData.Dl == 1)
                     {
                     SmartcardDebug(DEBUG_PROTOCOL,
                                    ("%s! overwriting PTS1(0x%x)\n",DRIVER_NAME,bTemp));
                     // we must correct Fl/Dl
                     SmartcardExtension->CardCapabilities.PtsData.Dl = 0x01;
                     SmartcardExtension->CardCapabilities.PtsData.Fl = 0x01;
                     }
                  break;
               case 4:
               case 5:
               case 6:
                  if (SmartcardExtension->CardCapabilities.PtsData.Dl == 1 ||
                      SmartcardExtension->CardCapabilities.PtsData.Dl == 2)
                     {
                     SmartcardDebug(DEBUG_PROTOCOL,
                                    ("%s! overwriting PTS1(0x%x)\n",DRIVER_NAME,bTemp));
                     // we must correct Fl/Dl
                     SmartcardExtension->CardCapabilities.PtsData.Dl = 0x01;
                     SmartcardExtension->CardCapabilities.PtsData.Fl = 0x01;
                     }
                  break;
               case 9:
                  // here we can handle all baudrates
                  break;
               case 10:
               case 11:
                  if (SmartcardExtension->CardCapabilities.PtsData.Dl == 1)
                     {
                     SmartcardDebug(DEBUG_PROTOCOL,
                                    ("%s! overwriting PTS1(0x%x)\n",DRIVER_NAME,bTemp));
                     // we must correct Fl/Dl
                     SmartcardExtension->CardCapabilities.PtsData.Dl = 0x01;
                     SmartcardExtension->CardCapabilities.PtsData.Fl = 0x09;
                     }
                  break;
               case 12:
               case 13:
                  if (SmartcardExtension->CardCapabilities.PtsData.Dl == 1 ||
                      SmartcardExtension->CardCapabilities.PtsData.Dl == 2)
                     {
                     SmartcardDebug(DEBUG_PROTOCOL,
                                    ("%s! overwriting PTS1(0x%x)\n",DRIVER_NAME,bTemp));
                     // we must correct Fl/Dl
                     SmartcardExtension->CardCapabilities.PtsData.Dl = 0x01;
                     SmartcardExtension->CardCapabilities.PtsData.Fl = 0x09;
                     }
                  break;
               default:
                  // this are the RFUs
                  SmartcardDebug(DEBUG_PROTOCOL,
                                 ("%s! overwriting PTS1(0x%x)\n",DRIVER_NAME,bTemp));
                  // we must correct Fl/Dl
                  SmartcardExtension->CardCapabilities.PtsData.Dl = 0x01;
                  SmartcardExtension->CardCapabilities.PtsData.Fl = 0x01;
                  break;
               }


            // set PTS1 with codes Fl and Dl
            abPTSRequest[2] = (BYTE) (SmartcardExtension->CardCapabilities.PtsData.Fl << 4 |
                                      SmartcardExtension->CardCapabilities.PtsData.Dl);


            // set PCK (check character)
            abPTSRequest[3] = (BYTE)(abPTSRequest[0] ^ abPTSRequest[1] ^ abPTSRequest[2]);

            if (ReaderExtension->CardParameters.fInversRevers)
               {
               SmartcardDebug(DEBUG_PROTOCOL,
                              ("%s! PTS request for InversConvention\n",DRIVER_NAME));
               CMMOB_InverseBuffer (abPTSRequest,4);
               }

#if DBG
            {
               ULONG k;
               SmartcardDebug(DEBUG_PROTOCOL,("%s! writing PTS request: ",DRIVER_NAME));
               for (k = 0;k < 4;k++)
                  SmartcardDebug(DEBUG_PROTOCOL,("%2.2x ",abPTSRequest[k]));
               SmartcardDebug(DEBUG_PROTOCOL,("\n"));
            }
#endif

            NTStatus = CMMOB_WriteT1(ReaderExtension,4,abPTSRequest);
            if (NTStatus != STATUS_SUCCESS)
               {
               SmartcardDebug(DEBUG_ERROR,
                              ("%s! writing PTS request failed\n", DRIVER_NAME));
               goto ExitSetProtocol;
               }

            // read back PTS data
            ulWaitTime=1000;
            if (SmartcardExtension->CardCapabilities.PtsData.Fl >= 8)
               ulWaitTime=1400;
            NTStatus = CMMOB_ReadT1(ReaderExtension,4,
                                    ulWaitTime,ulWaitTime,
                                    abPTSReply,&ulBytesRead);
            // in case of an short PTS reply an timeout will occur,
            // but that's not the standard case
            if (NTStatus != STATUS_SUCCESS && NTStatus != STATUS_IO_TIMEOUT)
               {
               SmartcardDebug(DEBUG_ERROR,
                              ("%s! reading PTS reply: failed\n",DRIVER_NAME));
               goto ExitSetProtocol;
               }
#if DBG
            {
               ULONG k;
               SmartcardDebug(DEBUG_PROTOCOL,("%s! reading PTS reply: ",DRIVER_NAME));
               for (k = 0;k < ulBytesRead;k++)
                  SmartcardDebug(DEBUG_PROTOCOL,("%2.2x ",abPTSReply[k]));
               SmartcardDebug(DEBUG_PROTOCOL,("\n"));
            }
#endif

            if (ulBytesRead == 4 &&
                abPTSReply[0] == abPTSRequest[0] &&
                abPTSReply[1] == abPTSRequest[1] &&
                abPTSReply[2] == abPTSRequest[2] &&
                abPTSReply[3] == abPTSRequest[3] )
               {
               SmartcardDebug(DEBUG_PROTOCOL,
                              ("%s! PTS request and reply match\n",DRIVER_NAME));

               if ((SmartcardExtension->CardCapabilities.PtsData.Fl >= 3 &&
                    SmartcardExtension->CardCapabilities.PtsData.Fl < 8) ||
                   (SmartcardExtension->CardCapabilities.PtsData.Fl >= 11 &&
                    SmartcardExtension->CardCapabilities.PtsData.Fl < 16))
                  {
                  ReaderExtension->CardParameters.bClockFrequency=8;
                  }

               ulBaudRateDivider = Fi[SmartcardExtension->CardCapabilities.PtsData.Fl] /
                                   Di[SmartcardExtension->CardCapabilities.PtsData.Dl];
               // decrease by 1, because these values have to be written to CardMan
               ulBaudRateDivider--;
               if (ulBaudRateDivider < 512)
                  {
                  ReaderExtension->CardParameters.bBaudRateLow=(UCHAR)(ulBaudRateDivider & 0xFF);
                  if (ulBaudRateDivider>255)
                     {
                     ReaderExtension->CardParameters.bBaudRateHigh=1;
                     }
                  else
                     {
                     ReaderExtension->CardParameters.bBaudRateHigh=0;
                     }

                  NTStatus = CMMOB_SetCardParameters (ReaderExtension);
                  if (NTStatus == STATUS_SUCCESS)
                     {
                     //
                     // we had success, leave the loop
                     //
                     break;
                     }
                  }
               }

            if (ulBytesRead == 3 &&
                abPTSReply[0] == abPTSRequest[0] &&
                (abPTSReply[1] & 0x7F) == (abPTSRequest[1] & 0x0F) &&
                abPTSReply[2] == (BYTE)(abPTSReply[0] ^ abPTSReply[1] ))
               {
               SmartcardDebug(DEBUG_PROTOCOL,
                              ("%s! Short PTS reply received\n",DRIVER_NAME));

               if (SmartcardExtension->CardCapabilities.PtsData.Fl >= 9)
                  {
                  ulBaudRateDivider = 512;
                  }
               else
                  {
                  ulBaudRateDivider = 372;
                  }
               // decrease by 1, because these values have to be written to CardMan
               ulBaudRateDivider--;

               NTStatus = CMMOB_SetCardParameters (ReaderExtension);
               if (NTStatus == STATUS_SUCCESS)
                  {
                  //
                  // we had success, leave the loop
                  //
                  break;
                  }
               }

            if (i==0)
               {
               // this was the first try
               // we have a second with default values
               SmartcardDebug(DEBUG_PROTOCOL,
                              ("%s! PTS failed : Trying default parameters\n",DRIVER_NAME));

               // the card did either not reply or it replied incorrectly
               // so try default values
               SmartcardExtension->MinorIoControlCode = SCARD_COLD_RESET;
               NTStatus = CMMOB_CardPower(SmartcardExtension);
               }
            else
               {
               // the card failed the PTS request
               NTStatus = STATUS_DEVICE_PROTOCOL_ERROR;
               }
            }
         }
      }

   ExitSetProtocol:

   //
   //   if protocol selection failed, prevent from calling invalid protocols
   //
   if (NTStatus==STATUS_SUCCESS)
      {
      SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_SPECIFIC;
      }
   else
      {
      SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_UNDEFINED;
      }

   //
   //   Return the selected protocol to the caller.
   //
   *(PULONG) (SmartcardExtension->IoRequest.ReplyBuffer) = SmartcardExtension->CardCapabilities.Protocol.Selected;
   *SmartcardExtension->IoRequest.Information = sizeof( ULONG );

#ifdef IOCARD
   ReaderExtension->fTActive=FALSE;
   CMMOB_SetFlags1(ReaderExtension);
#endif
   SmartcardDebug(DEBUG_TRACE,
                  ("%s!SetProtocol: Exit %X\n",DRIVER_NAME,NTStatus ));

   return( NTStatus );
}


/*****************************************************************************
CMMOB_Transmit:
   callback handler for SMCLIB RDF_TRANSMIT

Arguments:
   SmartcardExtension   context of call

Return Value:
   STATUS_SUCCESS
   STATUS_NO_MEDIA
   STATUS_TIMEOUT
   STATUS_INVALID_DEVICE_REQUEST
******************************************************************************/
NTSTATUS CMMOB_Transmit (
                        PSMARTCARD_EXTENSION SmartcardExtension
                        )
{
   NTSTATUS  NTStatus = STATUS_SUCCESS;

   SmartcardDebug(DEBUG_TRACE,
                  ("%s!Transmit: Enter\n",DRIVER_NAME ));
   //
   //   dispatch on the selected protocol
   //
   switch (SmartcardExtension->CardCapabilities.Protocol.Selected)
      {
      case SCARD_PROTOCOL_T0:
         NTStatus = CMMOB_TransmitT0(SmartcardExtension);
         break;

      case SCARD_PROTOCOL_T1:
         NTStatus = CMMOB_TransmitT1(SmartcardExtension);
         break;

         /*
         case SCARD_PROTOCOL_RAW:
            break;
         */

      default:
         NTStatus = STATUS_INVALID_DEVICE_REQUEST;
         break;
      }

   SmartcardDebug(DEBUG_TRACE,
                  ("%s!Transmit: Exit %X\n",DRIVER_NAME,NTStatus ));

   return( NTStatus );
}


/*****************************************************************************
CMMOB_TransmitT0:
   callback handler for SMCLIB RDF_TRANSMIT

Arguments:
   SmartcardExtension   context of call

Return Value:
   STATUS_SUCCESS
   STATUS_NO_MEDIA
   STATUS_TIMEOUT
   STATUS_INVALID_DEVICE_REQUEST
******************************************************************************/
NTSTATUS CMMOB_TransmitT0 (
                          PSMARTCARD_EXTENSION SmartcardExtension
                          )
{
   NTSTATUS NTStatus;
   UCHAR    abWriteBuffer[MIN_BUFFER_SIZE];
   UCHAR    abReadBuffer[MIN_BUFFER_SIZE];
   ULONG    ulBytesToWrite;                  //length written to card
   ULONG    ulBytesToReceive;                //length expected from card
   ULONG    ulBytesToRead;                   //length expected from reader
   ULONG    ulBytesRead;                     //length received from reader
                                             //(without length written)
   ULONG    ulCWTWaitTime;
   BOOLEAN  fDataSent;                        //data longer than T0_HEADER

#ifdef IOCARD
   SmartcardExtension->ReaderExtension->fTActive=TRUE;
   NTStatus = CMMOB_SetFlags1(SmartcardExtension->ReaderExtension);
   if (NTStatus != STATUS_SUCCESS)
      {
      goto ExitTransmitT0;
      }
#endif

   //   reset the state machine of the reader
   NTStatus = CMMOB_ResetReader (SmartcardExtension->ReaderExtension);
   if (NTStatus!=STATUS_SUCCESS)
      {
      // there must be severe error
      goto ExitTransmitT0;
      }

   // set T0 mode
   SmartcardExtension->ReaderExtension->CardParameters.fT0Mode=TRUE;

   // increase timeout for T0 Transmission
   ulCWTWaitTime = SmartcardExtension->CardCapabilities.T0.WT/1000 + 1500;


   //
   // Let the lib build a T=0 packet
   //

   // no bytes additionally needed
   SmartcardExtension->SmartcardRequest.BufferLength = 0;
   NTStatus = SmartcardT0Request(SmartcardExtension);
   if (NTStatus != STATUS_SUCCESS)
      {
      // the lib detected an error in the data to send.
      goto ExitTransmitT0;
      }

   // copy data to the write buffer
   ulBytesToWrite = T0_HEADER_LEN + SmartcardExtension->T0.Lc;
   RtlCopyMemory(abWriteBuffer,SmartcardExtension->SmartcardRequest.Buffer,ulBytesToWrite);
   ulBytesToReceive = SmartcardExtension->T0.Le;

#if DBG
   {
      ULONG i;
      SmartcardDebug(DEBUG_PROTOCOL,("%s!TransmitT0: Request ",DRIVER_NAME));
      for (i = 0;i < ulBytesToWrite;i++)
         SmartcardDebug(DEBUG_PROTOCOL,("%2.2x ",abWriteBuffer[i]));
      SmartcardDebug(DEBUG_PROTOCOL,("\n"));
   }
#endif

   // set T0 write flag correctly
   if (ulBytesToReceive == 0)
      {
      SmartcardExtension->ReaderExtension->CardParameters.fT0Write=TRUE;
      }
   else
      {
      SmartcardExtension->ReaderExtension->CardParameters.fT0Write=FALSE;
      }
   NTStatus=CMMOB_SetCardParameters(SmartcardExtension->ReaderExtension);
   if (NTStatus != STATUS_SUCCESS)
      goto ExitTransmitT0;


   NTStatus = CMMOB_WriteT0 (SmartcardExtension->ReaderExtension,
                             ulBytesToWrite,
                             ulBytesToReceive,
                             abWriteBuffer);
   if (NTStatus != STATUS_SUCCESS)
      {
      goto ExitTransmitT0;
      }

   // bytes to write + answer + SW2
   ulBytesToRead = ulBytesToWrite + ulBytesToReceive + 1;
   NTStatus = CMMOB_ReadT0 (SmartcardExtension->ReaderExtension,
                            ulBytesToRead,
                            ulBytesToWrite,
                            ulCWTWaitTime,
                            abReadBuffer,
                            &ulBytesRead,
                            &fDataSent);

#if DBG
   {
      ULONG i;
      SmartcardDebug(DEBUG_PROTOCOL,("%s!TransmitT0: Reply ",DRIVER_NAME));
      for (i = 0;i < ulBytesRead;i++)
         SmartcardDebug(DEBUG_PROTOCOL,("%2.2x ",abReadBuffer[i]));
      SmartcardDebug(DEBUG_PROTOCOL,("\n"));
   }
#endif

   if (NTStatus != STATUS_SUCCESS)
      {
      SmartcardDebug(DEBUG_PROTOCOL,("%s!TransmitT0: Read failed!\n",DRIVER_NAME));
      goto ExitTransmitT0;
      }

   // copy received bytes
   if (ulBytesRead <= SmartcardExtension->SmartcardReply.BufferSize)
      {
      RtlCopyBytes((PVOID)SmartcardExtension->SmartcardReply.Buffer,
                   (PVOID) abReadBuffer,
                   ulBytesRead);
      SmartcardExtension->SmartcardReply.BufferLength = ulBytesRead;
      }
   else
      {
      NTStatus=STATUS_BUFFER_OVERFLOW;
      goto ExitTransmitT0;
      }

   // let the lib copy the received bytes to the user buffer
   NTStatus = SmartcardT0Reply(SmartcardExtension);
   if (NTStatus != STATUS_SUCCESS)
      {
      goto ExitTransmitT0;
      }


   ExitTransmitT0:
   // ------------------------------------------
   // ITSEC E2 requirements: clear write buffers
   // ------------------------------------------
   RtlFillMemory((PVOID)abWriteBuffer,sizeof(abWriteBuffer),0x00);
   RtlFillMemory((PVOID)SmartcardExtension->SmartcardRequest.Buffer,
                 SmartcardExtension->SmartcardRequest.BufferSize,0x00);

   // set T0 mode back
   SmartcardExtension->ReaderExtension->CardParameters.fT0Mode=FALSE;
   SmartcardExtension->ReaderExtension->CardParameters.fT0Write=FALSE;
   CMMOB_SetCardParameters(SmartcardExtension->ReaderExtension);

#ifdef IOCARD
   SmartcardExtension->ReaderExtension->fTActive=FALSE;
   CMMOB_SetFlags1(SmartcardExtension->ReaderExtension);
#endif

   return NTStatus;
}



/*****************************************************************************
CMMOB_TransmitT1:
   callback handler for SMCLIB RDF_TRANSMIT

Arguments:
   SmartcardExtension   context of call

Return Value:
   STATUS_SUCCESS
   STATUS_NO_MEDIA
   STATUS_TIMEOUT
   STATUS_INVALID_DEVICE_REQUEST
******************************************************************************/
NTSTATUS CMMOB_TransmitT1 (
                          PSMARTCARD_EXTENSION SmartcardExtension
                          )
{
   NTSTATUS    NTStatus;
   UCHAR       abReadBuffer[CMMOB_MAXBUFFER];
   LONG        lBytesToRead;
   ULONG       ulBytesRead;
   ULONG       ulCurrentWaitTime;
   ULONG       ulCWTWaitTime;
   ULONG       ulBWTWaitTime;
   ULONG       ulWTXWaitTime;
   ULONG       ulTemp;

   SmartcardDebug(DEBUG_PROTOCOL,
                  ("%s!TransmitT1 CWT = %ld(ms)\n",DRIVER_NAME,
                   SmartcardExtension->CardCapabilities.T1.CWT/1000));
   SmartcardDebug(DEBUG_PROTOCOL,
                  ("%s!TransmitT1 BWT = %ld(ms)\n",DRIVER_NAME,
                   SmartcardExtension->CardCapabilities.T1.BWT/1000));

   ulCWTWaitTime = (ULONG)(100 + 32*(SmartcardExtension->CardCapabilities.T1.CWT/1000));
   ulBWTWaitTime = (ULONG)(1000 + SmartcardExtension->CardCapabilities.T1.BWT/1000);
   ulWTXWaitTime = 0;

#ifdef IOCARD
   SmartcardExtension->ReaderExtension->fTActive=TRUE;
   NTStatus = CMMOB_SetFlags1(SmartcardExtension->ReaderExtension);
   if (NTStatus != STATUS_SUCCESS)
      {
      goto ExitTransmitT1;
      }
#endif

   //   reset the state machine of the reader
   NTStatus = CMMOB_ResetReader (SmartcardExtension->ReaderExtension);
   if (NTStatus!=STATUS_SUCCESS)
      {
      // there must be severe error
      goto ExitTransmitT1;
      }

   do
      {
      // no bytes additionally needed
      SmartcardExtension->SmartcardRequest.BufferLength = 0;

      NTStatus = SmartcardT1Request(SmartcardExtension);
      if (NTStatus != STATUS_SUCCESS)
         {
         // this should never happen, so we return immediately
         goto ExitTransmitT1;
         }

#if DBG
      {
         ULONG i;
         SmartcardDebug(DEBUG_PROTOCOL,("%s!TransmitT1: Request ",DRIVER_NAME));
         for (i = 0;i < SmartcardExtension->SmartcardRequest.BufferLength;i++)
            SmartcardDebug(DEBUG_PROTOCOL,("%2.2x ",SmartcardExtension->SmartcardRequest.Buffer[i]));
         SmartcardDebug(DEBUG_PROTOCOL,("\n"));
      }
#endif

      //    write to the reader
      NTStatus = CMMOB_WriteT1 (SmartcardExtension->ReaderExtension,
                                SmartcardExtension->SmartcardRequest.BufferLength,
                                SmartcardExtension->SmartcardRequest.Buffer);
      if (NTStatus == STATUS_SUCCESS)
         {

         if (ulWTXWaitTime ==  0 ) // use BWT
            {
            /*
            SmartcardDebug(DEBUG_TRACE,
                           ("%s!ulCurrentWaitTime = %ld\n",DRIVER_NAME,ulCurrentWaitTime));
            */
            ulCurrentWaitTime = ulBWTWaitTime;
            }
         else // use WTX time
            {
            /*
            SmartcardDebug(DEBUG_TRACE,
                           ("%s!ulCurrentWaitTime = %ld\n",DRIVER_NAME,ulWTXWaitTime));
            */
            ulCurrentWaitTime = ulWTXWaitTime;
            }


         if (SmartcardExtension->CardCapabilities.T1.EDC == T1_CRC_CHECK)
            {
            // in case of card with CRC check read reply + 5 bytes
            // a negative value indicates a relative number of bytes to read
            lBytesToRead=-5;
            }
         else
            {
            // in case of card with CRC check read reply + 4 bytes
            // a negative value indicates a relative number of bytes to read
            lBytesToRead=-4;
            }

         NTStatus = CMMOB_ReadT1(SmartcardExtension->ReaderExtension,lBytesToRead,
                                 ulCurrentWaitTime,ulCWTWaitTime,abReadBuffer,&ulBytesRead);
         if (NTStatus == STATUS_SUCCESS)
            {

            if (abReadBuffer[1] == T1_WTX_REQUEST)
               {
               ulWTXWaitTime = (ULONG)(1000 +((SmartcardExtension->CardCapabilities.T1.BWT*abReadBuffer[3])/1000));
               SmartcardDebug(DEBUG_PROTOCOL,
                              ("%s!TransmitT1 WTX = %ld(ms)\n",DRIVER_NAME,ulWTXWaitTime));
               }
            else
               {
               ulWTXWaitTime = 0;
               }

#if DBG
            {
               ULONG i;
               SmartcardDebug(DEBUG_PROTOCOL,("%s!TransmitT1: Reply ",DRIVER_NAME));
               for (i = 0;i < ulBytesRead;i++)
                  SmartcardDebug(DEBUG_PROTOCOL,("%2.2x ",abReadBuffer[i]));
               SmartcardDebug(DEBUG_PROTOCOL,("\n"));
            }
#endif
            // copy received bytes
            if (ulBytesRead <= SmartcardExtension->SmartcardReply.BufferSize)
               {
               RtlCopyBytes((PVOID)SmartcardExtension->SmartcardReply.Buffer,
                            (PVOID)abReadBuffer,
                            ulBytesRead);
               SmartcardExtension->SmartcardReply.BufferLength = ulBytesRead;
               }
            else
               {
               NTStatus=STATUS_BUFFER_OVERFLOW;
               goto ExitTransmitT1;
               }
            }
         }

      if (NTStatus != STATUS_SUCCESS)
         {
         SmartcardExtension->SmartcardReply.BufferLength = 0L;
         }

      // bug fix for smclib
      if (SmartcardExtension->T1.State         == T1_IFS_RESPONSE &&
          SmartcardExtension->T1.OriginalState == T1_I_BLOCK)
         {
         SmartcardExtension->T1.State = T1_I_BLOCK;
         }

      NTStatus = SmartcardT1Reply(SmartcardExtension);
      }
   while (NTStatus == STATUS_MORE_PROCESSING_REQUIRED);


   ExitTransmitT1:
   // ------------------------------------------
   // ITSEC E2 requirements: clear write buffers
   // ------------------------------------------
   RtlFillMemory((PVOID)SmartcardExtension->SmartcardRequest.Buffer,
                 SmartcardExtension->SmartcardRequest.BufferSize,0x00);
#ifdef IOCARD
   SmartcardExtension->ReaderExtension->fTActive=FALSE;
   CMMOB_SetFlags1(SmartcardExtension->ReaderExtension);
#endif
   return NTStatus;
}

/*****************************************************************************
CMMOB_IoCtlVendor:
   Performs generic callbacks to the reader

Arguments:
   SmartcardExtension   context of the call

Return Value:
   STATUS_SUCCESS
******************************************************************************/
NTSTATUS CMMOB_IoCtlVendor(
                          PSMARTCARD_EXTENSION SmartcardExtension
                          )
{
   NTSTATUS             NTStatus=STATUS_SUCCESS;
   PIRP                 Irp;
   PIO_STACK_LOCATION   IrpStack;

   SmartcardDebug(DEBUG_TRACE,
                  ("%s!IoCtlVendor: Enter\n",DRIVER_NAME ));

   //
   //   get pointer to current IRP stack location
   //
   Irp = SmartcardExtension->OsData->CurrentIrp;
   IrpStack = IoGetCurrentIrpStackLocation( Irp );
   Irp->IoStatus.Information = 0;

   //
   //   dispatch IOCTL
   //
   switch (IrpStack->Parameters.DeviceIoControl.IoControlCode)
      {
      case CM_IOCTL_GET_FW_VERSION:
         NTStatus = CMMOB_GetFWVersion(SmartcardExtension);
         break;

      case CM_IOCTL_CR80S_SAMOS_SET_HIGH_SPEED:
         NTStatus = CMMOB_SetHighSpeed_CR80S_SAMOS(SmartcardExtension);
         break;

      case CM_IOCTL_SET_READER_9600_BAUD:
         NTStatus = CMMOB_SetReader_9600Baud(SmartcardExtension);
         break;

      case CM_IOCTL_SET_READER_38400_BAUD:
         NTStatus = CMMOB_SetReader_38400Baud(SmartcardExtension);
         break;

      case CM_IOCTL_READ_DEVICE_DESCRIPTION:
         NTStatus = CMMOB_ReadDeviceDescription(SmartcardExtension);
         break;

      default:
         NTStatus = STATUS_INVALID_DEVICE_REQUEST;
         break;
      }

   //
   //   set NTStatus of the packet
   //
   Irp->IoStatus.Status = NTStatus;

   SmartcardDebug(DEBUG_TRACE,
                  ("%s!IoCtlVendor: Exit %X\n",DRIVER_NAME,NTStatus ));

   return( NTStatus );
}



/*****************************************************************************
Routine Description:


Arguments:


Return Value: STATUS_UNSUCCESSFUL
              STATUS_SUCCESS

*****************************************************************************/
NTSTATUS CMMOB_SetReader_9600Baud (
                                  IN PSMARTCARD_EXTENSION SmartcardExtension
                                  )
{
   NTSTATUS    NTStatus = STATUS_SUCCESS;;

   SmartcardDebug(DEBUG_TRACE,
                  ("%s!SetReader_9600Baud: Enter\n",DRIVER_NAME));

   // check if card is already in specific mode
   if (SmartcardExtension->ReaderCapabilities.CurrentState != SCARD_SPECIFIC)
      {
      NTStatus = STATUS_INVALID_DEVICE_REQUEST;
      goto ExitSetReader9600;
      }

   // set 9600 Baud for 3.58 MHz
   SmartcardExtension->ReaderExtension->CardParameters.bBaudRateHigh=0x01;
   SmartcardExtension->ReaderExtension->CardParameters.bBaudRateLow=0x73;
   NTStatus = CMMOB_SetCardParameters (SmartcardExtension->ReaderExtension);

   ExitSetReader9600:
   *SmartcardExtension->IoRequest.Information = 0L;
   SmartcardDebug(DEBUG_TRACE,
                  ("%s!SetReader_9600Baud: Exit %lx\n",DRIVER_NAME,NTStatus));

   return(NTStatus);
}


/*****************************************************************************
Routine Description:


Arguments:


Return Value: STATUS_UNSUCCESSFUL
              STATUS_SUCCESS

*****************************************************************************/
NTSTATUS CMMOB_SetReader_38400Baud (
                                   IN PSMARTCARD_EXTENSION SmartcardExtension
                                   )
{
   NTSTATUS    NTStatus = STATUS_SUCCESS;;

   SmartcardDebug(DEBUG_TRACE,
                  ("%s!SetReader_38400Baud: Enter\n",DRIVER_NAME));

   // check if card is already in specific mode
   if (SmartcardExtension->ReaderCapabilities.CurrentState != SCARD_SPECIFIC)
      {
      NTStatus = STATUS_INVALID_DEVICE_REQUEST;
      goto ExitSetReader38400;
      }

   // set 384000 Baud for 3.58 MHz card
   SmartcardExtension->ReaderExtension->CardParameters.bBaudRateHigh=0x00;
   SmartcardExtension->ReaderExtension->CardParameters.bBaudRateLow=0x5D;
   NTStatus = CMMOB_SetCardParameters (SmartcardExtension->ReaderExtension);

   ExitSetReader38400:
   *SmartcardExtension->IoRequest.Information = 0L;
   SmartcardDebug(DEBUG_TRACE,
                  ("%s!SetReader_38400Baud: Exit %lx\n",DRIVER_NAME,NTStatus));

   return(NTStatus);
}


/*****************************************************************************
Routine Description:


Arguments:


Return Value: STATUS_UNSUCCESSFUL
              STATUS_SUCCESS

*****************************************************************************/
NTSTATUS CMMOB_SetHighSpeed_CR80S_SAMOS (
                                        IN PSMARTCARD_EXTENSION SmartcardExtension
                                        )
{
   NTSTATUS    NTStatus;
   UCHAR       abCR80S_SAMOS_SET_HIGH_SPEED[4] = {0xFF,0x11,0x94,0x7A};

   SmartcardDebug(DEBUG_TRACE,
                  ("%s!SetHighSpeed_CR80S_SAMOS: Enter\n",DRIVER_NAME));

   NTStatus = CMMOB_SetSpeed (SmartcardExtension,
                              abCR80S_SAMOS_SET_HIGH_SPEED);
   SmartcardDebug(DEBUG_TRACE,
                  ("%s!SetHighSpeed_CR80S_SAMOS: Exit %lx\n",DRIVER_NAME,NTStatus));

   return(NTStatus);
}


/*****************************************************************************
Routine Description:


Arguments:


Return Value: STATUS_UNSUCCESSFUL
              STATUS_SUCCESS

*****************************************************************************/
NTSTATUS CMMOB_SetSpeed (
                        IN PSMARTCARD_EXTENSION SmartcardExtension,
                        IN PUCHAR               abFIDICommand
                        )
{
   NTSTATUS    NTStatus;
   NTSTATUS    DebugStatus;
   UCHAR       abReadBuffer[16];
   ULONG       ulBytesRead;
   ULONG       ulWaitTime;


   SmartcardDebug(DEBUG_TRACE,
                  ("%s!SetSpeed: Enter\n",DRIVER_NAME));


#ifdef IOCARD
   SmartcardExtension->ReaderExtension->fTActive=TRUE;
   NTStatus = CMMOB_SetFlags1(SmartcardExtension->ReaderExtension);
   if (NTStatus != STATUS_SUCCESS)
      {
      goto ExitSetSpeed;
      }
#endif

#if DBG
   {
      ULONG k;
      SmartcardDebug(DEBUG_PROTOCOL,("%s!SetSpeed: writing: ",DRIVER_NAME));
      for (k = 0;k < 4;k++)
         SmartcardDebug(DEBUG_PROTOCOL,("%2.2x ",abFIDICommand[k]));
      SmartcardDebug(DEBUG_PROTOCOL,("\n"));
   }
#endif

   NTStatus = CMMOB_WriteT1(SmartcardExtension->ReaderExtension,4,
                            abFIDICommand);
   if (NTStatus != STATUS_SUCCESS)
      {
      SmartcardDebug(DEBUG_ERROR,
                     ("%s!SetSpeed: writing high speed command failed\n",DRIVER_NAME));
      goto ExitSetSpeed;
      }


   // read back pts data
   // maximim initial waiting time is 9600 * etu
   // clock divider of this card 512 => 1.4 sec is sufficient
   ulWaitTime = 1400;
   NTStatus = CMMOB_ReadT1(SmartcardExtension->ReaderExtension,4,
                           ulWaitTime,ulWaitTime,abReadBuffer,&ulBytesRead);

   SmartcardDebug(DEBUG_PROTOCOL,
                  ("%s!SetSpeed: reading echo: ",DRIVER_NAME));

   if (NTStatus != STATUS_SUCCESS)
      {
      SmartcardDebug(DEBUG_PROTOCOL,("failed\n"));
      goto ExitSetSpeed;
      }

#if DBG
   {
      ULONG k;
      for (k = 0;k < ulBytesRead;k++)
         SmartcardDebug(DEBUG_PROTOCOL,("%2.2x ",abReadBuffer[k]));
      SmartcardDebug(DEBUG_PROTOCOL,("\n"));
   }
#endif

   // if the card has accepted this string , the string is echoed
   if (abReadBuffer[0] == abFIDICommand[0] &&
       abReadBuffer[1] == abFIDICommand[1] &&
       abReadBuffer[2] == abFIDICommand[2] &&
       abReadBuffer[3] == abFIDICommand[3] )
      {
      SmartcardExtension->ReaderExtension->CardParameters.bBaudRateLow=63;
      SmartcardExtension->ReaderExtension->CardParameters.bBaudRateHigh=0;
      NTStatus = CMMOB_SetCardParameters (SmartcardExtension->ReaderExtension);
      }
   else
      {
      SmartcardExtension->MinorIoControlCode = SCARD_COLD_RESET;
      CMMOB_CardPower(SmartcardExtension);

      NTStatus = STATUS_UNSUCCESSFUL;
      }


   ExitSetSpeed:

   *SmartcardExtension->IoRequest.Information = 0L;
   if (NTStatus != STATUS_SUCCESS)
      {
      NTStatus = STATUS_UNSUCCESSFUL;
      }
#ifdef IOCARD
   SmartcardExtension->ReaderExtension->fTActive=FALSE;
   CMMOB_SetFlags1(SmartcardExtension->ReaderExtension);
#endif
   SmartcardDebug(DEBUG_TRACE,
                  ("%s!SetSpeed: Exit %lx\n",DRIVER_NAME,NTStatus));

   return NTStatus;
}


/*****************************************************************************
Routine Description:
This function always returns 'CardManMobile'.


Arguments:     pointer to SMARTCARD_EXTENSION



Return Value:  NT status

*****************************************************************************/
NTSTATUS CMMOB_ReadDeviceDescription(
                                    IN PSMARTCARD_EXTENSION SmartcardExtension
                                    )
{
   NTSTATUS NTStatus = STATUS_SUCCESS;
   BYTE abDeviceDescription[] = "CardManMobile";

   SmartcardDebug(DEBUG_TRACE,
                  ("%s!ReadDeviceDescription : Enter\n",DRIVER_NAME));

   if (SmartcardExtension->IoRequest.ReplyBufferLength  < sizeof(abDeviceDescription))
      {
      NTStatus = STATUS_BUFFER_OVERFLOW;
      *SmartcardExtension->IoRequest.Information = 0L;
      goto ExitReadDeviceDescription;
      }
   else
      {
      RtlCopyBytes((PVOID)SmartcardExtension->IoRequest.ReplyBuffer,
                   (PVOID)abDeviceDescription,sizeof(abDeviceDescription));
      *SmartcardExtension->IoRequest.Information = sizeof(abDeviceDescription);
      }

   ExitReadDeviceDescription:
   SmartcardDebug(DEBUG_TRACE,
                  ("%s!ReadDeviceDescription : Exit %lx\n",DRIVER_NAME,NTStatus));
   return NTStatus;
}



/*****************************************************************************
Routine Description:



Arguments:



Return Value:

*****************************************************************************/
NTSTATUS CMMOB_GetFWVersion (
                            IN PSMARTCARD_EXTENSION SmartcardExtension
                            )
{
   NTSTATUS NTStatus = STATUS_SUCCESS;

   SmartcardDebug(DEBUG_TRACE,
                  ("%s!GetFWVersion : Enter\n",DRIVER_NAME));

   if (SmartcardExtension->IoRequest.ReplyBufferLength  < sizeof (ULONG))
      {
      NTStatus = STATUS_BUFFER_OVERFLOW;
      *SmartcardExtension->IoRequest.Information = 0;
      }
   else
      {
      *(PULONG)(SmartcardExtension->IoRequest.ReplyBuffer) =
      SmartcardExtension->ReaderExtension->ulFWVersion;
      *SmartcardExtension->IoRequest.Information = sizeof(ULONG);
      }


   SmartcardDebug(DEBUG_TRACE,
                  ("%s!GetFWVersion : Exit %lx\n",DRIVER_NAME,NTStatus));

   return NTStatus;
}


/*****************************************************************************
CMMOB_CardTracking:
   callback handler for SMCLIB RDF_CARD_TRACKING. the requested event was
   validated by the smclib (i.e. a card removal request will only be passed
   if a card is present).
   for a win95 build STATUS_PENDING will be returned without any other action.
   for NT the cancel routine for the irp will be set to the drivers cancel
   routine.

Arguments:
   SmartcardExtension   context of call

Return Value:
   STATUS_PENDING
******************************************************************************/
NTSTATUS CMMOB_CardTracking(
                           PSMARTCARD_EXTENSION SmartcardExtension
                           )
{
   KIRQL    CurrentIrql;

   SmartcardDebug(DEBUG_TRACE,
                  ("%s!CardTracking: Enter\n",DRIVER_NAME ));

   //
   //   set cancel routine
   //
   IoAcquireCancelSpinLock( &CurrentIrql );
   IoSetCancelRoutine(SmartcardExtension->OsData->NotificationIrp,
                      CMMOB_CancelCardTracking);
   IoReleaseCancelSpinLock( CurrentIrql );

   //
   // Mark notification irp pending
   //
   IoMarkIrpPending(SmartcardExtension->OsData->NotificationIrp);

   SmartcardDebug(DEBUG_TRACE,
                  ("%s!CardTracking: Exit\n",DRIVER_NAME ));

   return( STATUS_PENDING );
}

/*****************************************************************************
CMMOB_CompleteCardTracking:
   finishes a pending tracking request if the device will be unloaded

Arguments:
   DeviceObject context of the request
   NTStatus     NTStatus to report to the calling process

Return Value:

******************************************************************************/
VOID CMMOB_CompleteCardTracking(
                               PSMARTCARD_EXTENSION SmartcardExtension
                               )
{
   KIRQL ioIrql, keIrql;
   PIRP  NotificationIrp;

   IoAcquireCancelSpinLock(&ioIrql);
   KeAcquireSpinLock(&SmartcardExtension->OsData->SpinLock,
                     &keIrql);

   NotificationIrp = SmartcardExtension->OsData->NotificationIrp;
   SmartcardExtension->OsData->NotificationIrp = NULL;

   KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock,
                     keIrql);

   if (NotificationIrp!=NULL)
      {
      IoSetCancelRoutine(NotificationIrp, NULL);
      }

   IoReleaseCancelSpinLock(ioIrql);

   if (NotificationIrp!=NULL)
      {
      //finish the request
      if (NotificationIrp->Cancel)
         {
         NotificationIrp->IoStatus.Status = STATUS_CANCELLED;
         }
      else
         {
         NotificationIrp->IoStatus.Status = STATUS_SUCCESS;
         }
      NotificationIrp->IoStatus.Information = 0;

      SmartcardDebug(DEBUG_DRIVER,
                     ("%s!CompleteCardTracking: Completing Irp %lx Status=%lx\n",
                      DRIVER_NAME, NotificationIrp,NotificationIrp->IoStatus.Status));

      IoCompleteRequest(NotificationIrp, IO_NO_INCREMENT );
      }
}


/*****************************************************************************
CMMOB_CancelCardTracking
    This routine is called by the I/O system
    when the irp should be cancelled

Arguments:

    DeviceObject    - Pointer to device object for this miniport
    Irp             -    IRP involved.

Return Value:

    STATUS_CANCELLED
******************************************************************************/
NTSTATUS CMMOB_CancelCardTracking(
                                 IN PDEVICE_OBJECT DeviceObject,
                                 IN PIRP Irp
                                 )
{
   PDEVICE_EXTENSION    DeviceExtension = DeviceObject->DeviceExtension;
   PSMARTCARD_EXTENSION SmartcardExtension = &DeviceExtension->SmartcardExtension;

   SmartcardDebug(DEBUG_TRACE,
                  ("%s!CancelCardTracking: Enter\n",DRIVER_NAME));

   ASSERT(Irp == SmartcardExtension->OsData->NotificationIrp);

   IoReleaseCancelSpinLock(Irp->CancelIrql);

   CMMOB_CompleteCardTracking(SmartcardExtension);

   SmartcardDebug(DEBUG_TRACE,
                  ("%s!CancelCardTracking: Exit\n",DRIVER_NAME));

   return STATUS_CANCELLED;
}




/*****************************************************************************
CMMOB_StartCardTracking:

Arguments:
   DeviceObject         context of call

Return Value:
   STATUS_SUCCESS
   NTStatus returned by LowLevel routines
******************************************************************************/
NTSTATUS CMMOB_StartCardTracking(
                                IN PDEVICE_OBJECT DeviceObject
                                )
{
   NTSTATUS             NTStatus = STATUS_SUCCESS;
   HANDLE               hThread;
   PDEVICE_EXTENSION    DeviceExtension;
   PSMARTCARD_EXTENSION SmartcardExtension;


   DeviceExtension = DeviceObject->DeviceExtension;
   SmartcardExtension = &DeviceExtension->SmartcardExtension;

   SmartcardDebug(DEBUG_TRACE,
                  ( "%s!StartCardTracking: Enter\n",DRIVER_NAME));
   SmartcardDebug(DEBUG_DRIVER,
                  ( "%s!StartCardTracking: IRQL %i\n",DRIVER_NAME,KeGetCurrentIrql()));

   KeWaitForSingleObject(&SmartcardExtension->ReaderExtension->CardManIOMutex,
                         Executive,
                         KernelMode,
                         FALSE,
                         NULL);

   // settings for thread synchronization
   SmartcardExtension->ReaderExtension->fTerminateUpdateThread = FALSE;

   // create thread for updating current state
   NTStatus = PsCreateSystemThread(&hThread,
                                   THREAD_ALL_ACCESS,
                                   NULL,
                                   NULL,
                                   NULL,
                                   CMMOB_UpdateCurrentStateThread,
                                   DeviceObject);

   if (NT_SUCCESS(NTStatus))
      {
      //
      // We've got the thread.  Now get a pointer to it.
      //
      NTStatus = ObReferenceObjectByHandle(hThread,
                                           THREAD_ALL_ACCESS,
                                           NULL,
                                           KernelMode,
                                           &SmartcardExtension->ReaderExtension->ThreadObjectPointer,
                                           NULL);

      if (NT_ERROR(NTStatus))
         {
         SmartcardExtension->ReaderExtension->fTerminateUpdateThread = TRUE;
         }
      else
         {
         //
         // Now that we have a reference to the thread
         // we can simply close the handle.
         //
         ZwClose(hThread);

         SmartcardExtension->ReaderExtension->fUpdateThreadRunning = TRUE;
         }
      }


   SmartcardDebug(DEBUG_DRIVER,
                  ("%s!-----------------------------------------------------------\n",DRIVER_NAME));
   SmartcardDebug(DEBUG_DRIVER,
                  ("%s!STARTING THREAD\n",DRIVER_NAME));
   SmartcardDebug(DEBUG_DRIVER,
                  ("%s!-----------------------------------------------------------\n",DRIVER_NAME));

   KeReleaseMutex(&SmartcardExtension->ReaderExtension->CardManIOMutex,FALSE);

   SmartcardDebug(DEBUG_TRACE,
                  ( "%s!CMMOB_StartCardTracking: Exit %lx\n",DRIVER_NAME,NTStatus));

   return NTStatus;
}


/*****************************************************************************
CMMOB_StopCardTracking:

Arguments:
   DeviceObject         context of call

Return Value:
******************************************************************************/
VOID CMMOB_StopCardTracking(
                           IN PDEVICE_OBJECT DeviceObject
                           )
{
   PDEVICE_EXTENSION    DeviceExtension;
   PSMARTCARD_EXTENSION SmartcardExtension;

   SmartcardDebug(DEBUG_TRACE,
                  ( "%s!StopCardTracking: Enter\n",DRIVER_NAME));
   SmartcardDebug(DEBUG_DRIVER,
                  ( "%s!StopCardTracking: IRQL %i\n",DRIVER_NAME,KeGetCurrentIrql()));

   DeviceExtension = DeviceObject->DeviceExtension;
   SmartcardExtension = &DeviceExtension->SmartcardExtension;

   if (SmartcardExtension->ReaderExtension->fUpdateThreadRunning)
      {

      // kill thread
      KeWaitForSingleObject(&SmartcardExtension->ReaderExtension->CardManIOMutex,
                            Executive,
                            KernelMode,
                            FALSE,
                            NULL );

      SmartcardExtension->ReaderExtension->fTerminateUpdateThread = TRUE;

      KeReleaseMutex(&SmartcardExtension->ReaderExtension->CardManIOMutex,FALSE);

      /* this doesn't work with Win98
      //
      // Wait on the thread handle, when the wait is satisfied, the
      // thread has gone away.
      //
      KeWaitForSingleObject(SmartcardExtension->ReaderExtension->ThreadObjectPointer,
                            Executive,
                            KernelMode,
                            FALSE,
                            NULL);
      */

      while (SmartcardExtension->ReaderExtension->fUpdateThreadRunning==TRUE)
         {
         SysDelay(1);
         }
      }

   SmartcardDebug(DEBUG_TRACE,
                  ( "%s!StopCardTracking: Exit\n",DRIVER_NAME));

   return;
}

/*****************************************************************************
CMMOB_UpdateCurrentStateThread:

Arguments:
   DeviceObject         context of call

Return Value:
******************************************************************************/
VOID CMMOB_UpdateCurrentStateThread(
                                   IN PVOID Context
                                   )
{
   NTSTATUS                NTStatus = STATUS_SUCCESS;
   PDEVICE_OBJECT          DeviceObject  = Context;
   PDEVICE_EXTENSION       DeviceExtension;
   PSMARTCARD_EXTENSION    SmartcardExtension;
   ULONG                   ulInterval;


   DeviceExtension = DeviceObject->DeviceExtension;
   SmartcardExtension = &DeviceExtension->SmartcardExtension;

   KeWaitForSingleObject(&DeviceExtension->CanRunUpdateThread,
                         Executive,
                         KernelMode,
                         FALSE,
                         NULL);

   SmartcardDebug(DEBUG_DRIVER,
                  ( "%s!UpdateCurrentStateThread: started\n",DRIVER_NAME));

   while (TRUE)
      {
      // every 500 ms  the  NTStatus request is sent
      ulInterval = 500;
      KeWaitForSingleObject(&SmartcardExtension->ReaderExtension->CardManIOMutex,
                            Executive,
                            KernelMode,
                            FALSE,
                            NULL);
      /*
      SmartcardDebug(DEBUG_TRACE,
                     ("%s!UpdateCurrentStateThread executed\n",DRIVER_NAME));
      */
      if (SmartcardExtension->ReaderExtension->fTerminateUpdateThread)
         {
         SmartcardDebug(DEBUG_DRIVER,
                        ("%s!-----------------------------------------------------------\n",DRIVER_NAME));
         SmartcardDebug(DEBUG_DRIVER,
                        ("%s!UpdateCurrentStateThread: STOPPING THREAD\n",DRIVER_NAME));
         SmartcardDebug(DEBUG_DRIVER,
                        ("%s!-----------------------------------------------------------\n",DRIVER_NAME));

         KeReleaseMutex(&SmartcardExtension->ReaderExtension->CardManIOMutex,FALSE);
         SmartcardExtension->ReaderExtension->fUpdateThreadRunning = FALSE;
         PsTerminateSystemThread( STATUS_SUCCESS );
         }


      //
      // get current card state
      //
      NTStatus = CMMOB_UpdateCurrentState(SmartcardExtension);
      if (NTStatus == STATUS_DEVICE_DATA_ERROR)
         {
         SmartcardDebug(DEBUG_DRIVER,
                        ("%s!UpdateCurrentStateThread: setting update interval to 1ms\n",DRIVER_NAME));
         ulInterval = 1;
         }
      else if (NTStatus != STATUS_SUCCESS &&
               NTStatus != STATUS_NO_SUCH_DEVICE)
         {
         SmartcardDebug(DEBUG_DRIVER,
                        ("%s!UpdateCurrentStateThread: UpdateCurrentState failed!\n",DRIVER_NAME));
         }

      KeReleaseMutex(&SmartcardExtension->ReaderExtension->CardManIOMutex,FALSE);

      SysDelay (ulInterval);
      }

}


/*****************************************************************************
CMMOB_UpdateCurrentState:

Arguments:
   DeviceObject         context of call

Return Value:
******************************************************************************/
NTSTATUS CMMOB_UpdateCurrentState(
                                 IN PSMARTCARD_EXTENSION    SmartcardExtension
                                 )
{
   NTSTATUS                NTStatus = STATUS_SUCCESS;
   BOOL                    fCardStateChanged = FALSE;

   /*
   SmartcardDebug(DEBUG_TRACE,
                  ("%s!UpdateCurrentState executed\n",DRIVER_NAME));
   */
   //
   // get card state from cardman
   //
   NTStatus = CMMOB_ResetReader(SmartcardExtension->ReaderExtension);
   if (NTStatus == STATUS_SUCCESS ||
       NTStatus == STATUS_NO_SUCH_DEVICE)
      {
      if (NTStatus == STATUS_SUCCESS)
         {
         if (CMMOB_CardInserted(SmartcardExtension->ReaderExtension))
            {
            if (CMMOB_CardPowered(SmartcardExtension->ReaderExtension))
               SmartcardExtension->ReaderExtension->ulNewCardState = POWERED;
            else
               SmartcardExtension->ReaderExtension->ulNewCardState = INSERTED;
            }
         else
            SmartcardExtension->ReaderExtension->ulNewCardState = REMOVED;
         }
      else
         SmartcardExtension->ReaderExtension->ulNewCardState = REMOVED;

      if (SmartcardExtension->ReaderExtension->ulNewCardState == INSERTED &&
          SmartcardExtension->ReaderExtension->ulOldCardState == POWERED )
         {
         // card has been removed and reinserted
         SmartcardExtension->ReaderExtension->ulNewCardState = REMOVED;
         }

      if ((SmartcardExtension->ReaderExtension->ulNewCardState == INSERTED &&
           (SmartcardExtension->ReaderExtension->ulOldCardState == UNKNOWN ||
            SmartcardExtension->ReaderExtension->ulOldCardState == REMOVED )) ||
          (SmartcardExtension->ReaderExtension->ulNewCardState == POWERED &&
           SmartcardExtension->ReaderExtension->ulOldCardState == UNKNOWN  ))
         {
         // card has been inserted
         SmartcardDebug(DEBUG_DRIVER,
                        ("%s!UpdateCurrentState: smartcard inserted\n",DRIVER_NAME));
         SmartcardExtension->ReaderExtension->ulOldCardState = SmartcardExtension->ReaderExtension->ulNewCardState;
         SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_SWALLOWED;
         SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_UNDEFINED;
         fCardStateChanged = TRUE;
         }

      if (SmartcardExtension->ReaderExtension->ulNewCardState == REMOVED &&
          (SmartcardExtension->ReaderExtension->ulOldCardState == UNKNOWN ||
           SmartcardExtension->ReaderExtension->ulOldCardState == INSERTED ||
           SmartcardExtension->ReaderExtension->ulOldCardState == POWERED ))
         {
         // card has been removed
         SmartcardDebug(DEBUG_DRIVER,
                        ("%s!UpdateCurrentState: smartcard removed\n",DRIVER_NAME));
         SmartcardExtension->ReaderExtension->ulOldCardState = SmartcardExtension->ReaderExtension->ulNewCardState;
         SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_ABSENT;
         SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_UNDEFINED;
         fCardStateChanged = TRUE;

         // clear any cardspecific data
         SmartcardExtension->CardCapabilities.ATR.Length = 0;
         RtlFillMemory((PVOID)&SmartcardExtension->ReaderExtension->CardParameters,
                       sizeof(CARD_PARAMETERS), 0x00);
         }

      // complete IOCTL_SMARTCARD_IS_ABSENT or IOCTL_SMARTCARD_IS_PRESENT
      if (fCardStateChanged == TRUE &&
          SmartcardExtension->OsData->NotificationIrp )
         {
         SmartcardDebug(DEBUG_DRIVER,("%s!UpdateCurrentState: completing IRP\n",DRIVER_NAME));
         CMMOB_CompleteCardTracking(SmartcardExtension);
         }

      }

   return NTStatus;
}



/*****************************************************************************
CMMOB_ResetReader:
   Resets the reader

Arguments:
   ReaderExtension  context of the call

Return Value:
   none
******************************************************************************/
NTSTATUS CMMOB_ResetReader(
                          PREADER_EXTENSION ReaderExtension
                          )
{
   NTSTATUS    NTStatus;
   BOOLEAN     fToggle;
   UCHAR       bFlags1;

   NTStatus = CMMOB_WriteRegister(ReaderExtension,ADDR_WRITEREG_FLAGS0,CMD_RESET_SM);
   if (NTStatus != STATUS_SUCCESS)
      return NTStatus;

#ifdef IOCARD
   // check for reader presence
   bFlags1 = ReaderExtension->bPreviousFlags1;
   bFlags1 |= FLAG_CHECK_PRESENCE;
   NTStatus = CMMOB_WriteRegister(ReaderExtension, ADDR_WRITEREG_FLAGS1, bFlags1);
   // don't check for status because
   // we have to set back fCheckPresence for proper working
   fToggle = CMMOB_GetReceiveFlag(ReaderExtension);
   bFlags1 = ReaderExtension->bPreviousFlags1;
   NTStatus = CMMOB_WriteRegister(ReaderExtension, ADDR_WRITEREG_FLAGS1, bFlags1);
   if (NTStatus != STATUS_SUCCESS)
      return NTStatus;
   if (fToggle == CMMOB_GetReceiveFlag(ReaderExtension))
      {
      SmartcardDebug(DEBUG_DRIVER,
                     ("%s!ResetReader: CardMan Mobile removed!\n",DRIVER_NAME));
      return STATUS_NO_SUCH_DEVICE;
      }
#endif

   return NTStatus;
}



/*****************************************************************************
CMMOB_BytesReceived:
   Reads how many bytes are already received from the card by the reader

Arguments:
   ReaderExtension  context of the call

Return Value:
   NTStatus
******************************************************************************/
NTSTATUS CMMOB_BytesReceived(
                            PREADER_EXTENSION ReaderExtension,
                            PULONG pulBytesReceived
                            )
{
   NTSTATUS          NTStatus = STATUS_SUCCESS;
   ULONG             ulBytesReceived;
   ULONG             ulBytesReceivedCheck;
   UCHAR             bReg;

   /*
   SmartcardDebug(DEBUG_TRACE,
                  ( "%s!BytesReceived Enter\n",DRIVER_NAME));
   */
   *pulBytesReceived=0;
   if (CMMOB_GetReceiveFlag(ReaderExtension) ||
       ReaderExtension->CardParameters.fT0Mode)
      {
      do
         {
         NTStatus=CMMOB_ReadRegister(ReaderExtension,ADDR_READREG_BYTES_RECEIVED,&bReg);
         if (NTStatus!=STATUS_SUCCESS)
            return NTStatus;
         ulBytesReceived=bReg;
         NTStatus=CMMOB_ReadRegister(ReaderExtension,ADDR_READREG_FLAGS0,&bReg);
         if (NTStatus!=STATUS_SUCCESS)
            return NTStatus;
         if ((bReg & FLAG_BYTES_RECEIVED_B9) == FLAG_BYTES_RECEIVED_B9)
            {
            ulBytesReceived+=0x100;
            }

         NTStatus=CMMOB_ReadRegister(ReaderExtension,ADDR_READREG_BYTES_RECEIVED,&bReg);
         if (NTStatus!=STATUS_SUCCESS)
            return NTStatus;
         ulBytesReceivedCheck=bReg;
         NTStatus=CMMOB_ReadRegister(ReaderExtension,ADDR_READREG_FLAGS0,&bReg);
         if (NTStatus!=STATUS_SUCCESS)
            return NTStatus;
         if ((bReg & FLAG_BYTES_RECEIVED_B9) == FLAG_BYTES_RECEIVED_B9)
            {
            ulBytesReceivedCheck+=0x100;
            }
         }
      while (ulBytesReceived!=ulBytesReceivedCheck);
      *pulBytesReceived=ulBytesReceived;
      }
   /*
   SmartcardDebug(DEBUG_TRACE,
                  ( "%s!BytesReceived Exit\n",DRIVER_NAME));
   */
   return NTStatus;
}


/*****************************************************************************
CMMOB_SetFlags1:
   Sets register Flags1

Arguments:
   ReaderExtension  context of the call

Return Value:
   none
******************************************************************************/
NTSTATUS CMMOB_SetFlags1 (
                         PREADER_EXTENSION ReaderExtension
                         )
{
   NTSTATUS NTStatus = STATUS_SUCCESS;
   UCHAR    bFlags1;

   bFlags1 = ReaderExtension->CardParameters.bBaudRateHigh;
   if (ReaderExtension->CardParameters.fInversRevers)
      bFlags1 |= FLAG_INVERS_PARITY;
   if (ReaderExtension->CardParameters.bClockFrequency==8)
      bFlags1 |= FLAG_CLOCK_8MHZ;
   if (ReaderExtension->CardParameters.fT0Write)
      bFlags1 |= FLAG_T0_WRITE;
#ifdef IOCARD
   if (ReaderExtension->bAddressHigh == 1)
      bFlags1 |= FLAG_BUFFER_ADDR_B9;
   if (ReaderExtension->fTActive)
      bFlags1 |= FLAG_TACTIVE;
   if (ReaderExtension->fReadCIS)
      bFlags1 |= FLAG_READ_CIS;
#endif
   ReaderExtension->bPreviousFlags1=bFlags1;
   NTStatus = CMMOB_WriteRegister(ReaderExtension, ADDR_WRITEREG_FLAGS1, bFlags1);
   return NTStatus;
}



/*****************************************************************************
CMMOB_SetCardParameters:
   Sets card parameters (baudrate, stopbits)

Arguments:
   ReaderExtension  context of the call

Return Value:
   none
******************************************************************************/
NTSTATUS CMMOB_SetCardParameters (
                                 PREADER_EXTENSION ReaderExtension
                                 )
{
   NTSTATUS NTStatus = STATUS_SUCCESS;

   NTStatus = CMMOB_SetFlags1 (ReaderExtension);
   if (NTStatus!=STATUS_SUCCESS)
      return NTStatus;

   NTStatus = CMMOB_WriteRegister(ReaderExtension, ADDR_WRITEREG_BAUDRATE,
                                  ReaderExtension->CardParameters.bBaudRateLow);
   if (NTStatus!=STATUS_SUCCESS)
      return NTStatus;

   NTStatus = CMMOB_WriteRegister(ReaderExtension, ADDR_WRITEREG_STOPBITS,
                                  ReaderExtension->CardParameters.bStopBits);
   return NTStatus;
}


/*****************************************************************************
CMMOB_CardInserted:
   Sets card parameters (baudrate, stopbits)

Arguments:
   ReaderExtension  context of the call

Return Value:
   TRUE if card is inserted
******************************************************************************/
BOOLEAN CMMOB_CardInserted(
                          IN PREADER_EXTENSION ReaderExtension
                          )
{
   NTSTATUS NTStatus=STATUS_SUCCESS;
   UCHAR    bReg;

   NTStatus=CMMOB_ReadRegister(ReaderExtension,ADDR_READREG_FLAGS0,&bReg);
   if (NTStatus!=STATUS_SUCCESS)
      return FALSE;
   /*
   SmartcardDebug(DEBUG_TRACE,
                  ("%s!CardInserted: ReadReg Flags0 = %x\n",DRIVER_NAME, (ULONG)bReg));
   */
   if ((bReg & FLAG_INSERTED)==FLAG_INSERTED)
      {
      return TRUE;
      }
   return FALSE;
}

/*****************************************************************************
CMMOB_CardPowered:
   Sets card parameters (baudrate, stopbits)

Arguments:
   ReaderExtension  context of the call

Return Value:
   TRUE if card is powered
******************************************************************************/
BOOLEAN CMMOB_CardPowered(
                         IN PREADER_EXTENSION ReaderExtension
                         )
{
   NTSTATUS NTStatus=STATUS_SUCCESS;
   UCHAR    bReg;

   NTStatus=CMMOB_ReadRegister(ReaderExtension,ADDR_READREG_FLAGS0,&bReg);
   if (NTStatus!=STATUS_SUCCESS)
      return FALSE;
   if ((bReg & FLAG_POWERED)==FLAG_POWERED)
      {
      return TRUE;
      }
   return FALSE;
}


/*****************************************************************************
CMMOB_ProcedureReceived:

Arguments:
   ReaderExtension  context of the call

Return Value:
   TRUE if a procedure byte has been received
******************************************************************************/
BOOLEAN CMMOB_ProcedureReceived(
                               IN PREADER_EXTENSION ReaderExtension
                               )
{
   NTSTATUS NTStatus=STATUS_SUCCESS;
   UCHAR    bReg;

   NTStatus=CMMOB_ReadRegister(ReaderExtension,ADDR_READREG_FLAGS1,&bReg);
   if (NTStatus!=STATUS_SUCCESS)
      return FALSE;
   if ((bReg & FLAG_NOPROCEDURE_RECEIVED)!=FLAG_NOPROCEDURE_RECEIVED)
      {
      return TRUE;
      }
   return FALSE;
}

/*****************************************************************************
CMMOB_GetReceiveFlag:

Arguments:
   ReaderExtension  context of the call

Return Value:
   TRUE if a receive flag is set
******************************************************************************/
BOOLEAN CMMOB_GetReceiveFlag(
                            IN PREADER_EXTENSION ReaderExtension
                            )
{
   NTSTATUS NTStatus=STATUS_SUCCESS;
   UCHAR    bReg;

   NTStatus=CMMOB_ReadRegister(ReaderExtension,ADDR_READREG_FLAGS0,&bReg);
   /*
   SmartcardDebug(DEBUG_TRACE,
                  ("%s!GetReceiveFlag: ReadReg Flags0 = %x\n",DRIVER_NAME, (ULONG)bReg));
   */
   if (NTStatus!=STATUS_SUCCESS)
      return FALSE;
   if ((bReg & FLAG_RECEIVE)==FLAG_RECEIVE)
      {
      return TRUE;
      }
   return FALSE;
}

/*****************************************************************************
CMMOB_GetProcedureByte:
   Reads how many bytes are already received from the card by the reader

Arguments:
   ReaderExtension  context of the call

Return Value:
   NTStatus
******************************************************************************/
NTSTATUS CMMOB_GetProcedureByte(
                               IN PREADER_EXTENSION ReaderExtension,
                               OUT PUCHAR pbProcedureByte
                               )
{
   NTSTATUS          NTStatus = STATUS_SUCCESS;
   UCHAR             bReg;
   UCHAR             bRegPrevious;

   do
      {
      NTStatus=CMMOB_ReadRegister(ReaderExtension,ADDR_READREG_LASTPROCEDURE_T0,&bRegPrevious);
      if (NTStatus!=STATUS_SUCCESS)
         return NTStatus;
      NTStatus=CMMOB_ReadRegister(ReaderExtension,ADDR_READREG_LASTPROCEDURE_T0,&bReg);
      if (NTStatus!=STATUS_SUCCESS)
         return NTStatus;
      }
   while (bReg!=bRegPrevious);
   *pbProcedureByte=bReg;
   return NTStatus;
}

/*****************************************************************************
CMMOB_WriteT0:
   Writes T0 request to card

Arguments:
   ReaderExtension  context of the call

Return Value:
   NT STATUS
******************************************************************************/
NTSTATUS CMMOB_WriteT0(
                      IN PREADER_EXTENSION ReaderExtension,
                      IN ULONG ulBytesToWrite,
                      IN ULONG ulBytesToReceive,
                      IN PUCHAR pbData
                      )
{
   NTSTATUS       NTStatus = STATUS_SUCCESS;
   UCHAR          bFlags0;
   UCHAR          bReg;

   if (ulBytesToWrite > CMMOB_MAXBUFFER)
      {
      NTStatus = STATUS_BUFFER_OVERFLOW;
      return NTStatus;
      }
   // dummy read, to reset flag procedure received
   NTStatus=CMMOB_ReadRegister(ReaderExtension,ADDR_READREG_FLAGS1,&bReg);

   NTStatus = CMMOB_WriteBuffer(ReaderExtension,ulBytesToWrite,pbData);
   if (NTStatus != STATUS_SUCCESS)
      return NTStatus;

   // write instruction byte to register
   NTStatus = CMMOB_WriteRegister(ReaderExtension,ADDR_WRITEREG_PROCEDURE_T0,pbData[1]);
   if (NTStatus != STATUS_SUCCESS)
      return NTStatus;

   // write message length
   NTStatus = CMMOB_WriteRegister(ReaderExtension,ADDR_WRITEREG_MESSAGE_LENGTH,
                                  (UCHAR)((ulBytesToWrite+ulBytesToReceive) & 0xFF));
   if (NTStatus != STATUS_SUCCESS)
      return NTStatus;
   if ((ulBytesToWrite+ulBytesToReceive) > 0xFF)
      {
      bFlags0=1;
      }
   else
      {
      bFlags0=0;
      }
   bFlags0 |= CMD_WRITE_T0;
   NTStatus = CMMOB_WriteRegister(ReaderExtension,ADDR_WRITEREG_FLAGS0,bFlags0);
   return NTStatus;
}



/*****************************************************************************
CMMOB_WriteT1:
   Writes T1 request to card

Arguments:
   ReaderExtension  context of the call

Return Value:
   NT STATUS
******************************************************************************/
NTSTATUS CMMOB_WriteT1(
                      IN PREADER_EXTENSION ReaderExtension,
                      IN ULONG ulBytesToWrite,
                      IN PUCHAR pbData
                      )
{
   NTSTATUS       NTStatus = STATUS_SUCCESS;
   UCHAR          bFlags0;

   if (ulBytesToWrite > CMMOB_MAXBUFFER)
      {
      NTStatus = STATUS_BUFFER_OVERFLOW;
      return NTStatus;
      }
   NTStatus = CMMOB_WriteBuffer(ReaderExtension,ulBytesToWrite,pbData);
   if (NTStatus != STATUS_SUCCESS)
      return NTStatus;
   NTStatus = CMMOB_WriteRegister(ReaderExtension,ADDR_WRITEREG_MESSAGE_LENGTH,
                                  (UCHAR)(ulBytesToWrite & 0xFF));
   if (NTStatus != STATUS_SUCCESS)
      return NTStatus;
   if (ulBytesToWrite > 0xFF)
      {
      bFlags0=1;
      }
   else
      {
      bFlags0=0;
      }
   bFlags0 |= CMD_WRITE_T1;
   NTStatus = CMMOB_WriteRegister(ReaderExtension,ADDR_WRITEREG_FLAGS0,bFlags0);
   return NTStatus;
}



/*****************************************************************************
CMMOB_ReadT0:
   Reads T0 reply from card

Arguments:
   ReaderExtension  context of the call

Return Value:
   NT STATUS
******************************************************************************/
NTSTATUS CMMOB_ReadT0(
                     IN PREADER_EXTENSION ReaderExtension,
                     IN ULONG ulBytesToRead,
                     IN ULONG ulBytesSent,
                     IN ULONG ulCWT,
                     OUT PUCHAR pbData,
                     OUT PULONG pulBytesRead,
                     OUT PBOOLEAN pfDataSent
                     )
{
   NTSTATUS             NTStatus = STATUS_SUCCESS;
   KTIMER               TimerWait;
   ULONG                ulBytesReceived;
   ULONG                ulBytesReceivedPrevious;
   LARGE_INTEGER        liWaitTime;
   BOOLEAN              fTimeExpired;
   BOOLEAN              fProcedureReceived;
   BOOLEAN              fTransmissionFinished;
   UCHAR                bProcedureByte=0;


   SmartcardDebug(DEBUG_TRACE,
                  ("%s!ReadT0: Enter BytesToRead = %li\n",DRIVER_NAME,ulBytesToRead));

   //initialize Timer
   KeInitializeTimer(&TimerWait);
   liWaitTime = RtlConvertLongToLargeInteger(ulCWT * -10000L);

   *pulBytesRead = 0;
   *pfDataSent = FALSE;

   do
      {
      KeSetTimer(&TimerWait,liWaitTime,NULL);
      NTStatus=CMMOB_BytesReceived (ReaderExtension,&ulBytesReceivedPrevious);
      if (NTStatus!=STATUS_SUCCESS)
         goto ExitReadT0;
      do
         {
         fTimeExpired = KeReadStateTimer(&TimerWait);
         fTransmissionFinished=CMMOB_GetReceiveFlag(ReaderExtension);
         fProcedureReceived=CMMOB_ProcedureReceived(ReaderExtension);
         NTStatus=CMMOB_BytesReceived (ReaderExtension,&ulBytesReceived);
         if (NTStatus!=STATUS_SUCCESS)
            goto ExitReadT0;
         // wait 1 ms, so that processor is not blocked
         SysDelay(1);
         }
      while (fTimeExpired==FALSE &&
             fProcedureReceived==FALSE &&
             ulBytesReceivedPrevious == ulBytesReceived &&
             fTransmissionFinished==FALSE);

      if (fProcedureReceived)
         {
         NTStatus=CMMOB_GetProcedureByte (ReaderExtension,&bProcedureByte);
         if (NTStatus!=STATUS_SUCCESS)
            goto ExitReadT0;
         // check for SW1
         if (ReaderExtension->CardParameters.fInversRevers)
            {
            CMMOB_InverseBuffer(&bProcedureByte,1);
            }
         }

      if (!fTimeExpired)
         {
         KeCancelTimer(&TimerWait);
         }
#ifdef DBG
      else
         {
         SmartcardDebug(DEBUG_PROTOCOL,( "%s!----------------------------------------------\n",DRIVER_NAME));
         SmartcardDebug(DEBUG_PROTOCOL,( "%s!Read T0 timed out\n",DRIVER_NAME));
         SmartcardDebug(DEBUG_PROTOCOL,( "%s!----------------------------------------------\n",DRIVER_NAME));
         }
#endif

      }
   while (fTimeExpired==FALSE &&
          fTransmissionFinished==FALSE);

   // read once more ulBytesReceived
   // this value could have changed in the meantime
   NTStatus=CMMOB_BytesReceived (ReaderExtension,&ulBytesReceived);
   if (NTStatus!=STATUS_SUCCESS)
      goto ExitReadT0;

   SmartcardDebug(DEBUG_PROTOCOL,
                  ("%s!ReadT0: BytesReceived = %li\n",DRIVER_NAME,ulBytesReceived));

   //now we should have received a reply
   NTStatus=CMMOB_ResetReader (ReaderExtension);
   if (NTStatus!=STATUS_SUCCESS)
      goto ExitReadT0;

   // check for valid SW1
   if ((bProcedureByte > 0x60 && bProcedureByte <= 0x6F) ||
       (bProcedureByte >= 0x90 && bProcedureByte <= 0x9F))
      {
      if (ReaderExtension->CardParameters.fInversRevers)
         {
         CMMOB_InverseBuffer(&bProcedureByte,1);
         }
      if (ulBytesReceived > ulBytesSent)
         {
         NTStatus=CMMOB_ReadBuffer(ReaderExtension, ulBytesSent,
                                   ulBytesReceived-ulBytesSent, pbData);
         if (NTStatus==STATUS_SUCCESS)
            {
            // we have to insert the procedure byte (SW1)
            pbData[ulBytesReceived-ulBytesSent]=pbData[ulBytesReceived-ulBytesSent-1];
            pbData[ulBytesReceived-ulBytesSent-1]=bProcedureByte;
            *pulBytesRead=ulBytesReceived-ulBytesSent+1;
            }

         if (ulBytesSent > T0_HEADER_LEN)
            {
            *pfDataSent = TRUE;
            }
         }
      else
         {
         if (ulBytesReceived > T0_HEADER_LEN)
            {
            // it seems not all bytes were accepted by the card
            // but we got SW1 SW2 - return only SW1 SW2
            pbData[0]=bProcedureByte;
            NTStatus=CMMOB_ReadBuffer(ReaderExtension, ulBytesReceived-1,
                                      1, &pbData[1]);
            *pulBytesRead=2;
            }
         else
            {
            NTStatus = STATUS_IO_TIMEOUT;
            }
         }
      }
   else
      {
      NTStatus = STATUS_IO_TIMEOUT;
      }

   ExitReadT0:

   SmartcardDebug(DEBUG_TRACE,
                  ("%s!ReadT0: Exit\n",DRIVER_NAME ));

   return NTStatus;
}

/*****************************************************************************
CMMOB_ReadT1:
   Reads T1 reply from card

Arguments:
   ReaderExtension  context of the call

Return Value:
   NT STATUS
******************************************************************************/
NTSTATUS CMMOB_ReadT1(
                     IN PREADER_EXTENSION ReaderExtension,
                     IN LONG lBytesToRead,
                     IN ULONG ulBWT,
                     IN ULONG ulCWT,
                     OUT PUCHAR pbData,
                     OUT PULONG pulBytesRead
                     )
// a negative value of ulBytesToRead indicates a relative number of bytes to read
{
   NTSTATUS             NTStatus = STATUS_SUCCESS;
   KTIMER               TimerWait;
   ULONG                ulBytesReceived;
   LARGE_INTEGER        liWaitTime;
   BOOLEAN              fTimeExpired;
   ULONG                ulBytesReceivedPrevious;

   /*
   SmartcardDebug(DEBUG_TRACE,
                  ("%s!ReadT1: Enter\n",DRIVER_NAME ));
   */

   //initialize Timer
   KeInitializeTimer(&TimerWait);

   *pulBytesRead = 0;
   // first wait BWT (block waiting time)
   liWaitTime = RtlConvertLongToLargeInteger(ulBWT * -10000L);
   do
      {
      KeSetTimer(&TimerWait,liWaitTime,NULL);
      NTStatus=CMMOB_BytesReceived (ReaderExtension,&ulBytesReceivedPrevious);
      if (NTStatus!=STATUS_SUCCESS)
         goto ExitReadT1;
      do
         {
         fTimeExpired = KeReadStateTimer(&TimerWait);
         NTStatus=CMMOB_BytesReceived (ReaderExtension,&ulBytesReceived);
         if (NTStatus!=STATUS_SUCCESS)
            goto ExitReadT1;
         // wait 1 ms, so that processor is not blocked
         SysDelay(1);

         // make an adjustment of lBytesToRead (only one time)
         if (lBytesToRead<= 0 && ulBytesReceived >= 3)
            {
            // get number of bytes to receive from reader
            UCHAR bReg;
            UCHAR bRegPrevious;
            lBytesToRead = -lBytesToRead;
            do
               {
               NTStatus=CMMOB_ReadRegister(ReaderExtension,ADDR_READREG_BYTESTORECEIVE_T1,&bRegPrevious);
               if (NTStatus!=STATUS_SUCCESS)
                  goto ExitReadT1;
               NTStatus=CMMOB_ReadRegister(ReaderExtension,ADDR_READREG_BYTESTORECEIVE_T1,&bReg);
               if (NTStatus!=STATUS_SUCCESS)
                  goto ExitReadT1;
               }
            while (bReg!=bRegPrevious);
            lBytesToRead += bReg;
            }
         }
      while (fTimeExpired==FALSE &&
             ulBytesReceivedPrevious == ulBytesReceived &&
             (lBytesToRead<=0 || ulBytesReceived!=(ULONG)lBytesToRead));

      if (!fTimeExpired)
         {
         KeCancelTimer(&TimerWait);
         liWaitTime = RtlConvertLongToLargeInteger(ulCWT * -10000L);
         // now wait only CWT (character waiting time)
         }
#ifdef DBG
      else
         {
         SmartcardDebug(DEBUG_PROTOCOL,( "%s!----------------------------------------------\n",DRIVER_NAME));
         SmartcardDebug(DEBUG_PROTOCOL,( "%s!Read T1 timed out\n",DRIVER_NAME));
         SmartcardDebug(DEBUG_PROTOCOL,( "%s!----------------------------------------------\n",DRIVER_NAME));
         }
#endif
      }
   while (!fTimeExpired &&
          (lBytesToRead<=0 || ulBytesReceived!=(ULONG)lBytesToRead));



   //now we should have received a reply
   NTStatus=CMMOB_ResetReader (ReaderExtension);
   if (NTStatus!=STATUS_SUCCESS)
      goto ExitReadT1;

   if (ulBytesReceived==(ULONG)lBytesToRead && lBytesToRead > 0)
      {
      NTStatus=CMMOB_ReadBuffer(ReaderExtension, 0, (ULONG)lBytesToRead, pbData);
      if (NTStatus==STATUS_SUCCESS)
         {
         *pulBytesRead=(ULONG)lBytesToRead;
         }
      }
   else
      {
      NTStatus=CMMOB_ReadBuffer(ReaderExtension, 0, ulBytesReceived, pbData);
      if (NTStatus==STATUS_SUCCESS)
         {
         *pulBytesRead=ulBytesReceived;
         }
      NTStatus = STATUS_IO_TIMEOUT;
      }

   ExitReadT1:

   /*
   SmartcardDebug(DEBUG_TRACE,
                  ("%s!ReadT1: Exit\n",DRIVER_NAME ));
   */
   return NTStatus;
}

/*****************************************************************************
CMMOB_ReadRegister:
   Sets card parameters (baudrate, stopbits)

Arguments:
   ReaderExtension  context of the call

Return Value:
   NT STATUS
******************************************************************************/
NTSTATUS CMMOB_ReadRegister(
                           IN PREADER_EXTENSION ReaderExtension,
                           IN USHORT usAddress,
                           OUT PUCHAR pbData
                           )
{
#ifdef MEMORYCARD
   *pbData = READ_REGISTER_UCHAR(ReaderExtension->pbRegsBase+usAddress);
#endif

#ifdef IOCARD
   *pbData = READ_PORT_UCHAR(ReaderExtension->pbRegsBase+usAddress);
#endif

   return STATUS_SUCCESS;
}


/*****************************************************************************
CMMOB_WriteRegister:
   Sets card parameters (baudrate, stopbits)

Arguments:
   ReaderExtension  context of the call

Return Value:
   NT STATUS
******************************************************************************/
NTSTATUS CMMOB_WriteRegister(
                            IN PREADER_EXTENSION ReaderExtension,
                            IN USHORT usAddress,
                            IN UCHAR bData
                            )
{
#ifdef MEMORYCARD
   WRITE_REGISTER_UCHAR(ReaderExtension->pbRegsBase+usAddress,bData);
#endif

#ifdef IOCARD
   WRITE_PORT_UCHAR(ReaderExtension->pbRegsBase+usAddress,bData);
#endif

   return STATUS_SUCCESS;
}


/*****************************************************************************
CMMOB_ReadBuffer:
   Sets card parameters (baudrate, stopbits)

Arguments:
   ReaderExtension  context of the call

Return Value:
   NT STATUS
******************************************************************************/
NTSTATUS CMMOB_ReadBuffer(
                         IN PREADER_EXTENSION ReaderExtension,
                         IN ULONG ulOffset,
                         IN ULONG ulLength,
                         OUT PUCHAR pbData
                         )
{
   NTSTATUS NTStatus = STATUS_SUCCESS;
   ULONG    i;

#ifdef IOCARD
   if ((ulOffset & 0x100) == 0x100)
      {
      ReaderExtension->bAddressHigh=1;
      }
   else
      {
      ReaderExtension->bAddressHigh=0;
      }
   NTStatus = CMMOB_SetFlags1(ReaderExtension);
   if (NTStatus!=STATUS_SUCCESS)
      {
      goto ExitReadBuffer;
      }
#endif

   for (i=0; i<ulLength; i++)
      {
#ifdef MEMORYCARD
      *(pbData+i)=READ_REGISTER_UCHAR(ReaderExtension->pbDataBase+(2*(i+ulOffset)));
      // erase buffer - required for certification
      WRITE_REGISTER_UCHAR(ReaderExtension->pbDataBase+(2*(i+ulOffset)),0);
#endif

#ifdef IOCARD
      WRITE_PORT_UCHAR(ReaderExtension->pbRegsBase+ADDR_WRITEREG_BUFFER_ADDR,
                       (BYTE)((ulOffset+i)&0xFF));
      // because we are counting up in a loop we have to set
      // bit 9 of address only once
      if (ulOffset+i == 0x100)
         {
         ReaderExtension->bAddressHigh=1;
         NTStatus = CMMOB_SetFlags1(ReaderExtension);
         if (NTStatus!=STATUS_SUCCESS)
            {
            goto ExitReadBuffer;
            }
         }
      *(pbData+i)=READ_PORT_UCHAR(ReaderExtension->pbRegsBase+ADDR_WRITEREG_BUFFER_DATA);
      // erase buffer - required for certification
      WRITE_PORT_UCHAR(ReaderExtension->pbRegsBase+ADDR_WRITEREG_BUFFER_DATA,0);
#endif
      }
   ExitReadBuffer:
   return NTStatus;
}


/*****************************************************************************
CMMOB_WriteBuffer:
   Sets card parameters (baudrate, stopbits)

Arguments:
   ReaderExtension  context of the call

Return Value:
   NT STATUS
******************************************************************************/
NTSTATUS CMMOB_WriteBuffer(
                          IN PREADER_EXTENSION ReaderExtension,
                          IN ULONG ulLength,
                          IN PUCHAR pbData
                          )
{
   NTSTATUS NTStatus = STATUS_SUCCESS;
   ULONG    i;

#ifdef IOCARD
   ReaderExtension->bAddressHigh=0;
   NTStatus = CMMOB_SetFlags1(ReaderExtension);
   if (NTStatus!=STATUS_SUCCESS)
      {
      goto ExitWriteBuffer;
      }
#endif

   for (i=0; i<ulLength; i++)
      {
#ifdef MEMORYCARD
      WRITE_REGISTER_UCHAR(ReaderExtension->pbDataBase+(2*i),*(pbData+i));
#endif

#ifdef IOCARD
      WRITE_PORT_UCHAR(ReaderExtension->pbRegsBase+ADDR_WRITEREG_BUFFER_ADDR,
                       (BYTE)(i & 0xFF));
      // because we are counting up in a loop we have to set
      // bit 9 of address only once
      if (i == 0x100)
         {
         ReaderExtension->bAddressHigh=1;
         NTStatus = CMMOB_SetFlags1(ReaderExtension);
         if (NTStatus!=STATUS_SUCCESS)
            {
            goto ExitWriteBuffer;
            }
         }
      WRITE_PORT_UCHAR(ReaderExtension->pbRegsBase+ADDR_WRITEREG_BUFFER_DATA,*(pbData+i));
#endif
      }

   ExitWriteBuffer:
   return NTStatus;
}


/*****************************************************************************
Routine Description:
This routine inverts the buffer
Bit0 -> Bit 7
Bit1 -> Bit 6
Bit2 -> Bit 5
Bit3 -> Bit 4
Bit4 -> Bit 3
Bit5 -> Bit 2
Bit6 -> Bit 1
Bit7 -> Bit 0


Arguments: pbBuffer     ... pointer to buffer
           ulBufferSize ... size of buffer


Return Value: none

*****************************************************************************/
VOID CMMOB_InverseBuffer (
                         PUCHAR pbBuffer,
                         ULONG  ulBufferSize
                         )
{
   ULONG i,j;
   UCHAR bRevers;
   UCHAR bTemp;

   for (i=0; i<ulBufferSize; i++)
      {
      bRevers = 0;
      for (j=0; j<8; j++)
         {
         bTemp = pbBuffer[i] << j;
         bTemp &= 0x80;
         bRevers |= bTemp >> (7-j);
         }
      pbBuffer[i] = ~bRevers;
      }

   return;
}


/*****************************************************************************
* History:
* $Log: cmbp0scr.c $
* Revision 1.7  2001/01/22 07:12:36  WFrischauf
* No comment given
*
* Revision 1.6  2000/09/25 14:24:31  WFrischauf
* No comment given
*
* Revision 1.5  2000/08/24 09:05:13  TBruendl
* No comment given
*
* Revision 1.4  2000/08/09 12:45:57  WFrischauf
* No comment given
*
* Revision 1.3  2000/07/27 13:53:03  WFrischauf
* No comment given
*
*
******************************************************************************/

