/*++

Copyright (c) 1998 SCM Microsystems, Inc.

Module Name:

   StcCb.c

Abstract:

   Declaration of callback functions - WDM Version


Revision History:


   PP 1.01     01/19/1998
   PP 1.00     12/18/1998     Initial Version

--*/


// Include

#include "common.h"
#include "stccmd.h"
#include "stccb.h"
#include "stcusbnt.h"
#include "usbcom.h"

NTSTATUS
CBCardPower(
   PSMARTCARD_EXTENSION SmartcardExtension)
/*++

CBCardPower:
   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       NTStatus = STATUS_SUCCESS;
   UCHAR          ATRBuffer[ ATR_SIZE ];
   ULONG          Command,
                  ATRLength;
   PREADER_EXTENSION ReaderExtension;

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

   ReaderExtension = SmartcardExtension->ReaderExtension;


   // discard old ATR
   SysFillMemory( SmartcardExtension->CardCapabilities.ATR.Buffer, 0x00, 0x40 );

   SmartcardExtension->CardCapabilities.ATR.Length = 0;
   SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_UNDEFINED;

   Command = SmartcardExtension->MinorIoControlCode;

   switch ( Command )
   {
      case SCARD_WARM_RESET:

         // if the card was not powerd, fall through to cold reset
         if( SmartcardExtension->ReaderCapabilities.CurrentState > SCARD_SWALLOWED )
         {
            // reset the card
            ATRLength = ATR_SIZE;
            NTStatus = STCReset(
               ReaderExtension,
               0,             // not used: ReaderExtension->Device,
               TRUE,          // warm reset
               ATRBuffer,
               &ATRLength);

            break;
         }
         // warm reset not possible because card was not powerd

      case SCARD_COLD_RESET:
         // reset the card
         ATRLength = ATR_SIZE;
         NTStatus = STCReset(
            ReaderExtension,
            0,                // not used: ReaderExtension->Device,
            FALSE,               // cold reset
            ATRBuffer,
            &ATRLength);
         break;

      case SCARD_POWER_DOWN:

         // discard old card status
         ATRLength = 0;
         NTStatus = STCPowerOff( ReaderExtension );

         if(NTStatus == STATUS_SUCCESS)
         {
            SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_PRESENT;
         }
         break;
   }

   // finish the request
   if( NTStatus == STATUS_SUCCESS )
   {

      // update all neccessary data if an ATR was received
      if( ATRLength >= 2 )
      {
         // copy ATR to user buffer
         if( ATRLength <= SmartcardExtension->IoRequest.ReplyBufferLength )
         {
            SysCopyMemory(
               SmartcardExtension->IoRequest.ReplyBuffer,
               ATRBuffer,
               ATRLength);
            *SmartcardExtension->IoRequest.Information = ATRLength;
         }
         else
         {
            NTStatus = STATUS_BUFFER_TOO_SMALL;
         }

         // copy ATR to card capability buffer
         if( ATRLength <= MAXIMUM_ATR_LENGTH )
         {
            SysCopyMemory(
               SmartcardExtension->CardCapabilities.ATR.Buffer,
               ATRBuffer,
               ATRLength);

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

            // let the lib update the card capabilities
            NTStatus = SmartcardUpdateCardCapabilities( SmartcardExtension );

         }
         else
         {
            NTStatus = STATUS_BUFFER_TOO_SMALL;
         }
         if( NTStatus == STATUS_SUCCESS )
         {
            // set the stc registers
            CBSynchronizeSTC( SmartcardExtension );

            // set read timeout
            if( SmartcardExtension->CardCapabilities.Protocol.Selected == SCARD_PROTOCOL_T1 )
            {
               ReaderExtension->ReadTimeout =
                  (ULONG) (SmartcardExtension->CardCapabilities.T1.BWT  / 1000);

            }
            else
            {
               ReaderExtension->ReadTimeout =
                  (ULONG) (SmartcardExtension->CardCapabilities.T0.WT / 1000);
               if(ReaderExtension->ReadTimeout < 50)
               {
                  ReaderExtension->ReadTimeout = 50; //  50 ms minimum timeout
               }
            }
         }
      }
   }


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

NTSTATUS
CBSetProtocol(
   PSMARTCARD_EXTENSION SmartcardExtension
   )
/*++

CBSetProtocol:
   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       NTStatus = STATUS_PENDING;
   UCHAR          PTSRequest[5],
                  PTSReply[5];
   ULONG          NewProtocol;
   PREADER_EXTENSION ReaderExtension;

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

   ReaderExtension = SmartcardExtension->ReaderExtension;
   NewProtocol    = SmartcardExtension->MinorIoControlCode;

   // check if the card is already in specific state
   if( ( SmartcardExtension->ReaderCapabilities.CurrentState == SCARD_SPECIFIC )  &&
      ( SmartcardExtension->CardCapabilities.Protocol.Selected & NewProtocol ))
   {
      NTStatus = STATUS_SUCCESS;
   }

   // protocol supported?
   if( !( SmartcardExtension->CardCapabilities.Protocol.Supported & NewProtocol ) ||
      !( SmartcardExtension->ReaderCapabilities.SupportedProtocols & NewProtocol ))
   {
      NTStatus = STATUS_INVALID_DEVICE_REQUEST;
   }

   // send PTS
   while( NTStatus == STATUS_PENDING )
   {
      // set initial character of PTS
      PTSRequest[0] = 0xFF;

      // set the format character
      if(( NewProtocol & SCARD_PROTOCOL_T1 )&&
         (SmartcardExtension->CardCapabilities.Protocol.Supported & SCARD_PROTOCOL_T1 ))
      {
         PTSRequest[1] = 0x11;
         SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_T1;
      }
      else
      {
         PTSRequest[1] = 0x10;
         SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_T0;
      }

      // PTS1 codes Fl and Dl
      PTSRequest[2] =
         SmartcardExtension->CardCapabilities.PtsData.Fl << 4 |
         SmartcardExtension->CardCapabilities.PtsData.Dl;

      // check character
      PTSRequest[3] = PTSRequest[0] ^ PTSRequest[1] ^ PTSRequest[2];

      // write PTSRequest
      NTStatus = IFWriteSTCData( ReaderExtension, PTSRequest, 4 );

      // get response
      if( NTStatus == STATUS_SUCCESS )
      {
         NTStatus = IFReadSTCData( ReaderExtension, PTSReply, 4 );

         if(( NTStatus == STATUS_SUCCESS ) && !SysCompareMemory( PTSRequest, PTSReply, 4))
         {
            // set the stc registers
            SmartcardExtension->CardCapabilities.Dl =
               SmartcardExtension->CardCapabilities.PtsData.Dl;
            SmartcardExtension->CardCapabilities.Fl =
               SmartcardExtension->CardCapabilities.PtsData.Fl;

            CBSynchronizeSTC( SmartcardExtension );

            // the card replied correctly to the PTS-request
            break;
         }
      }

      //
      // The card did either NOT reply or it replied incorrectly
      // so try default values
      //
      if( SmartcardExtension->CardCapabilities.PtsData.Type != PTS_TYPE_DEFAULT )
      {
         SmartcardExtension->CardCapabilities.PtsData.Type  = PTS_TYPE_DEFAULT;
         SmartcardExtension->MinorIoControlCode          = SCARD_COLD_RESET;
         NTStatus = CBCardPower( SmartcardExtension );

         if( NTStatus == STATUS_SUCCESS )
         {
            NTStatus = STATUS_PENDING;
         }
         else
         {
            NTStatus = STATUS_DEVICE_PROTOCOL_ERROR;
         }
      }
   }

   if( NTStatus == STATUS_TIMEOUT )
   {
      NTStatus = STATUS_IO_TIMEOUT;
   }

   if( NTStatus == STATUS_SUCCESS )
   {
      // card replied correctly to the PTS request
      if( SmartcardExtension->CardCapabilities.Protocol.Selected & SCARD_PROTOCOL_T1 )
      {
         ReaderExtension->ReadTimeout = SmartcardExtension->CardCapabilities.T1.BWT / 1000;
      }
      else
      {
         ULONG ClockRateFactor =
            SmartcardExtension->CardCapabilities.ClockRateConversion[SmartcardExtension->CardCapabilities.PtsData.Fl].F;

         // check for RFU value, and replace by default value
         if( !ClockRateFactor )
            ClockRateFactor = 372;

         ReaderExtension->ReadTimeout = 960 
            * SmartcardExtension->CardCapabilities.T0.WI 
            * ClockRateFactor
            / SmartcardExtension->CardCapabilities.PtsData.CLKFrequency;

         // We need to have a minimum timeout anyway
         if(ReaderExtension->ReadTimeout <50)
         {
            ReaderExtension->ReadTimeout =50; // 50 ms minimum timeout
         }
      }

      // indicate that the card is in specific mode
      SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_SPECIFIC;

      // return the selected protocol to the caller
      *(PULONG) SmartcardExtension->IoRequest.ReplyBuffer = SmartcardExtension->CardCapabilities.Protocol.Selected;
      *SmartcardExtension->IoRequest.Information = sizeof(SmartcardExtension->CardCapabilities.Protocol.Selected);
   }
   else
   {
      SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_UNDEFINED;
      *(PULONG) SmartcardExtension->IoRequest.ReplyBuffer = 0;
      *SmartcardExtension->IoRequest.Information = 0;
   }

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

   return( NTStatus );
}
NTSTATUS
CBGenericIOCTL(
   PSMARTCARD_EXTENSION SmartcardExtension)
/*++

Description:
   Performs generic callbacks to the reader

Arguments:
   SmartcardExtension   context of the call

Return Value:
   STATUS_SUCCESS

--*/
{
   NTSTATUS          NTStatus;
   SmartcardDebug(
      DEBUG_TRACE,
      ( "%s!CBGenericIOCTL: Enter\n",
      DRIVER_NAME));

   //
   // get pointer to current IRP stack location
   //
   //
   // assume error
   //
   NTStatus = STATUS_INVALID_DEVICE_REQUEST;


   //
   // dispatch IOCTL
   //
   switch( SmartcardExtension->MajorIoControlCode )
   {



      case IOCTL_WRITE_STC_REGISTER:


         NTStatus = IFWriteSTCRegister(
            SmartcardExtension->ReaderExtension,
            *(SmartcardExtension->IoRequest.RequestBuffer),             // Address
            (ULONG)(*(SmartcardExtension->IoRequest.RequestBuffer + 1)),   // Size
            SmartcardExtension->IoRequest.RequestBuffer + 2);           // Data

         *SmartcardExtension->IoRequest.Information = 1;
         if(NTStatus == STATUS_SUCCESS)
         {
            *(SmartcardExtension->IoRequest.ReplyBuffer) = 0;
         }
         else
         {
            *(SmartcardExtension->IoRequest.ReplyBuffer) = 1;
         }

         break;

      case IOCTL_READ_STC_REGISTER:

         NTStatus = IFReadSTCRegister(
            SmartcardExtension->ReaderExtension,
            *(SmartcardExtension->IoRequest.RequestBuffer),             // Address
            (ULONG)(*(SmartcardExtension->IoRequest.RequestBuffer + 1)),   // Size
            SmartcardExtension->IoRequest.ReplyBuffer);                 // Data

         if(NTStatus ==STATUS_SUCCESS)
         {
            *SmartcardExtension->IoRequest.Information =
               (ULONG)(*(SmartcardExtension->IoRequest.RequestBuffer + 1));
         }
         else
         {
            SmartcardExtension->IoRequest.Information = 0;
         }

         break;



      case IOCTL_WRITE_STC_DATA:


         NTStatus = IFWriteSTCData(
            SmartcardExtension->ReaderExtension,
            SmartcardExtension->IoRequest.RequestBuffer + 1,            // Data
            (ULONG)(*(SmartcardExtension->IoRequest.RequestBuffer)));      // Size

         *SmartcardExtension->IoRequest.Information = 1;
         if(NTStatus == STATUS_SUCCESS)
         {
            *(SmartcardExtension->IoRequest.ReplyBuffer) = 0;
         }
         else
         {
            *(SmartcardExtension->IoRequest.ReplyBuffer) = 1;
         }

         break;

      case IOCTL_READ_STC_DATA:

         NTStatus = IFReadSTCData(
            SmartcardExtension->ReaderExtension,
            SmartcardExtension->IoRequest.ReplyBuffer,                  // Data
            (ULONG)(*(SmartcardExtension->IoRequest.RequestBuffer)));      // Size

         if(NTStatus ==STATUS_SUCCESS)
         {
            *SmartcardExtension->IoRequest.Information =
               (ULONG)(*(SmartcardExtension->IoRequest.RequestBuffer));
         }
         else
         {
            SmartcardExtension->IoRequest.Information = 0;
         }

         break;

      default:
         break;
   }


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

   return( NTStatus );
}



NTSTATUS
CBTransmit(
   PSMARTCARD_EXTENSION SmartcardExtension
   )
/*++

CBTransmit:
   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  NTStatus = STATUS_SUCCESS;

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

   // dispatch on the selected protocol
   switch( SmartcardExtension->CardCapabilities.Protocol.Selected )
   {
      case SCARD_PROTOCOL_T0:
         NTStatus = CBT0Transmit( SmartcardExtension );
         break;

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

      case SCARD_PROTOCOL_RAW:
         NTStatus = CBRawTransmit( SmartcardExtension );
         break;

      default:
         NTStatus = STATUS_INVALID_DEVICE_REQUEST;
         break;
   }

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

   return( NTStatus );
}



NTSTATUS
T0_ExchangeData(
   PREADER_EXTENSION ReaderExtension,
   PUCHAR            pRequest,
   ULONG          RequestLen,
   PUCHAR            pReply,
   PULONG            pReplyLen)
/*++

Routine Description:
   T=0 management

Arguments:
   ReaderExtension   Context of the call
   pRequest    Request buffer
   RequestLen     Request buffer length
   pReply         Reply buffer
   pReplyLen      Reply buffer length


Return Value:
   STATUS_SUCCESS
   Status returned by IFReadSTCData or IFWriteSTCData

--*/
{
   NTSTATUS NTStatus = STATUS_SUCCESS;
   BOOLEAN     Direction;
   UCHAR    Ins,
            Pcb = 0;
   ULONG    Len,
            DataIdx;

   // get direction
   Ins = pRequest[ INS_IDX ] & 0xFE;
   Len   = pRequest[ P3_IDX ];

   if( RequestLen == 5 )
   {
      Direction   = ISO_OUT;
      DataIdx     = 0;
      // For an ISO OUT command Len=0 means that the host expect an
      // 256 byte answer
      if( !Len )
      {
         Len = 0x100;
      }
      // Add 2 for SW1 SW2
      Len+=2;
   }
   else
   {
      Direction   = ISO_IN;
      DataIdx     = 5;
   }

   // send header CLASS,INS,P1,P2,P3
   NTStatus = IFWriteSTCData( ReaderExtension, pRequest, 5 );

   if( NTStatus == STATUS_SUCCESS )
   {
      NTStatus = STATUS_MORE_PROCESSING_REQUIRED;
   }

   while( NTStatus == STATUS_MORE_PROCESSING_REQUIRED )
   {
      // PCB reading
      NTStatus = IFReadSTCData( ReaderExtension, &Pcb, 1 );

      if( NTStatus == STATUS_SUCCESS )
      {
         if( Pcb == 0x60 )
         {
            // null byte?
            NTStatus = STATUS_MORE_PROCESSING_REQUIRED;
            continue;
         }
         else if( ( Pcb & 0xFE ) == Ins )
         {
            // transfer all
            if( Direction == ISO_IN )
            {
               // write remaining data
               NTStatus = IFWriteSTCData( ReaderExtension, pRequest + DataIdx, Len );
               if( NTStatus == STATUS_SUCCESS )
               {
                  // if all data successful written the status word is expected
                  NTStatus = STATUS_MORE_PROCESSING_REQUIRED;
                  Direction   = ISO_OUT;
                  DataIdx     = 0;
                  Len         = 2;
               }
            }
            else
            {
               // read remaining data
               NTStatus = IFReadSTCData( ReaderExtension, pReply + DataIdx, Len );

               DataIdx += Len;
            }
         }
         else if( (( Pcb & 0xFE ) ^ Ins ) == 0xFE )
         {
            // transfer next
            if( Direction == ISO_IN )
            {
               // write next

               NTStatus = IFWriteSTCData( ReaderExtension, pRequest + DataIdx, 1 );

               if( NTStatus == STATUS_SUCCESS )
               {
                  DataIdx++;

                  // if all data successful written the status word is expected
                  if( --Len == 0 )
                  {
                     Direction   = ISO_OUT;
                     DataIdx     = 0;
                     Len         = 2;
                  }
                  NTStatus = STATUS_MORE_PROCESSING_REQUIRED;
               }
            }
            else
            {
               // read next
               NTStatus = IFReadSTCData( ReaderExtension, pReply + DataIdx, 1 );


               if( NTStatus == STATUS_SUCCESS )
               {
                  NTStatus = STATUS_MORE_PROCESSING_REQUIRED;
                  Len--;
                  DataIdx++;
               }
            }
         }
         else if( (( Pcb & 0x60 ) == 0x60 ) || (( Pcb & 0x90 ) == 0x90 ) )
         {
            if( Direction == ISO_IN )
            {
               Direction   = ISO_OUT;
               DataIdx     = 0;
            }

            // SW1
            *pReply  = Pcb;

            // read SW2 and leave

            NTStatus = IFReadSTCData( ReaderExtension, &Pcb, 1 );

            *(pReply + 1)  = Pcb;
            DataIdx        += 2;
         }
         else
         {
            NTStatus = STATUS_UNSUCCESSFUL;
         }
      }
   }

   if(( NTStatus == STATUS_SUCCESS ) && ( pReplyLen != NULL ))
   {
      *pReplyLen = DataIdx;
   }

   return( NTStatus );
}


NTSTATUS
CBT0Transmit(
   PSMARTCARD_EXTENSION SmartcardExtension)
/*++

CBT0Transmit:
   finishes the callback RDF_TRANSMIT for the T0 protocol

Arguments:
   SmartcardExtension   context of call

Return Value:
   STATUS_SUCCESS
   STATUS_NO_MEDIA
   STATUS_TIMEOUT
   STATUS_INVALID_DEVICE_REQUEST

--*/
{
    NTSTATUS            NTStatus = STATUS_SUCCESS;

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

   SmartcardExtension->SmartcardRequest.BufferLength = 0;

   // let the lib setup the T=1 APDU & check for errors
   NTStatus = SmartcardT0Request( SmartcardExtension );

   if( NTStatus == STATUS_SUCCESS )
   {
      NTStatus = T0_ExchangeData(
         SmartcardExtension->ReaderExtension,
         SmartcardExtension->SmartcardRequest.Buffer,
         SmartcardExtension->SmartcardRequest.BufferLength,
         SmartcardExtension->SmartcardReply.Buffer,
         &SmartcardExtension->SmartcardReply.BufferLength);

      if( NTStatus == STATUS_SUCCESS )
      {
         // let the lib evaluate the result & tansfer the data
         NTStatus = SmartcardT0Reply( SmartcardExtension );
      }
   }

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

    return( NTStatus );
}





NTSTATUS
CBT1Transmit(
   PSMARTCARD_EXTENSION SmartcardExtension)
/*++

CBT1Transmit:
   finishes the callback RDF_TRANSMIT for the T1 protocol

Arguments:
   SmartcardExtension   context of call

Return Value:
   STATUS_SUCCESS
   STATUS_NO_MEDIA
   STATUS_TIMEOUT
   STATUS_INVALID_DEVICE_REQUEST

--*/
{
    NTSTATUS   NTStatus = STATUS_SUCCESS;

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

   // smclib workaround
   *(PULONG)&SmartcardExtension->IoRequest.ReplyBuffer[0] = 0x02;
   *(PULONG)&SmartcardExtension->IoRequest.ReplyBuffer[4] = sizeof( SCARD_IO_REQUEST );

   // use the lib support to construct the T=1 packets
   do {
      // no header for the T=1 protocol
      SmartcardExtension->SmartcardRequest.BufferLength = 0;

      SmartcardExtension->T1.NAD = 0;

      // let the lib setup the T=1 APDU & check for errors
      NTStatus = SmartcardT1Request( SmartcardExtension );
      if( NTStatus == STATUS_SUCCESS )
      {
         // send command (don't calculate LRC because CRC may be used!)
         NTStatus = IFWriteSTCData(
            SmartcardExtension->ReaderExtension,
            SmartcardExtension->SmartcardRequest.Buffer,
            SmartcardExtension->SmartcardRequest.BufferLength);

         // extend read timeout if the card issued a WTX request
         if (SmartcardExtension->T1.Wtx)
         {
            SmartcardExtension->ReaderExtension->ReadTimeout = 
               ( SmartcardExtension->T1.Wtx * 
               SmartcardExtension->CardCapabilities.T1.BWT + 999L )/
               1000L;
         }
         else
         {
            // restore timeout
            SmartcardExtension->ReaderExtension->ReadTimeout = 
               (ULONG) (SmartcardExtension->CardCapabilities.T1.BWT  / 1000);
         }

         // get response
         SmartcardExtension->SmartcardReply.BufferLength = 0;

         if( NTStatus == STATUS_SUCCESS )
         {
            NTStatus = IFReadSTCData(
               SmartcardExtension->ReaderExtension,
               SmartcardExtension->SmartcardReply.Buffer,
               3);

            if( NTStatus == STATUS_SUCCESS )
            {
               ULONG Length;

               Length = (ULONG)SmartcardExtension->SmartcardReply.Buffer[ LEN_IDX ] + 1;

               if( Length + 3 < MIN_BUFFER_SIZE )
               {
                  NTStatus = IFReadSTCData(
                     SmartcardExtension->ReaderExtension,
                     &SmartcardExtension->SmartcardReply.Buffer[ DATA_IDX ],
                     Length);

                  SmartcardExtension->SmartcardReply.BufferLength = Length + 3;
               }
               else
               {
                  NTStatus = STATUS_BUFFER_TOO_SMALL;
               }
            }
            //
            // if STCRead detects an LRC error, ignore it (maybe CRC used). Timeouts will
            // be detected by the lib if len=0
            //
            if(( NTStatus == STATUS_CRC_ERROR ) || ( NTStatus == STATUS_IO_TIMEOUT ))
            {
               NTStatus = STATUS_SUCCESS;
            }

            if( NTStatus == STATUS_SUCCESS )
            {
               // let the lib evaluate the result & setup the next APDU
               NTStatus = SmartcardT1Reply( SmartcardExtension );
            }
         }
      }

   // continue if the lib wants to send the next packet
   } while( NTStatus == STATUS_MORE_PROCESSING_REQUIRED );

   if( NTStatus == STATUS_IO_TIMEOUT )
   {
      NTStatus = STATUS_DEVICE_PROTOCOL_ERROR;
   }

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

   return ( NTStatus );
}

NTSTATUS
CBRawTransmit(
   PSMARTCARD_EXTENSION SmartcardExtension)
/*++

CBRawTransmit:
   finishes the callback RDF_TRANSMIT for the RAW protocol

Arguments:
   SmartcardExtension   context of call

Return Value:
   STATUS_UNSUCCESSFUL

--*/
{
    NTSTATUS         NTStatus = STATUS_UNSUCCESSFUL;

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


NTSTATUS
CBCardTracking(
   PSMARTCARD_EXTENSION SmartcardExtension)
/*++

CBCardTracking:
   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

--*/
{
   KIRQL CurrentIrql;

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

   // set cancel routine
   IoAcquireCancelSpinLock( &CurrentIrql );

   IoSetCancelRoutine(
      SmartcardExtension->OsData->NotificationIrp,
      StcUsbCancel);

   IoReleaseCancelSpinLock( CurrentIrql );

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

   return( STATUS_PENDING );

}


NTSTATUS
CBUpdateCardState(
   PSMARTCARD_EXTENSION SmartcardExtension
   )
/*++

CBUpdateCardState:
   updates the variable CurrentState in SmartcardExtension

Arguments:
   SmartcardExtension   context of call

Return Value:
   STATUS_SUCCESS

--*/
{
   NTSTATUS status = STATUS_SUCCESS;
   UCHAR    cardStatus = 0;
   KIRQL    irql;
   BOOLEAN     stateChanged = FALSE;
   ULONG    oldState;

   // read card state
   status = IFReadSTCRegister(
      SmartcardExtension->ReaderExtension,
      ADR_IO_CONFIG,
      1,
      &cardStatus
      );

   oldState = SmartcardExtension->ReaderCapabilities.CurrentState;

   switch(status)
   {
      case STATUS_NO_MEDIA:
         SmartcardExtension->ReaderExtension->ErrorCounter = 0;
         SmartcardExtension->ReaderCapabilities.CurrentState =
            SCARD_ABSENT;
         break;

      case STATUS_MEDIA_CHANGED:
         SmartcardExtension->ReaderExtension->ErrorCounter = 0;
         SmartcardExtension->ReaderCapabilities.CurrentState =
            SCARD_PRESENT;
         break;

      case STATUS_SUCCESS:
         SmartcardExtension->ReaderExtension->ErrorCounter = 0;
         cardStatus &= M_SD;
         if( cardStatus == 0 )
         {
            SmartcardExtension->ReaderCapabilities.CurrentState =
               SCARD_ABSENT;
         }
         else if( SmartcardExtension->ReaderCapabilities.CurrentState <=
            SCARD_ABSENT )
         {
            SmartcardExtension->ReaderCapabilities.CurrentState =
               SCARD_PRESENT;
         }
         break;

      default:
         if( ++SmartcardExtension->ReaderExtension->ErrorCounter < ERROR_COUNTER_TRESHOLD )
         {
             // a unknown status was reported from the reader, so use the previous state
             SmartcardExtension->ReaderCapabilities.CurrentState = oldState;
         }
         else
         {
              SmartcardLogError(
                 SmartcardExtension->OsData->DeviceObject,
                 STATUS_DEVICE_DATA_ERROR,
                 NULL,
                 0);

             // a report of SCARD_UNKNOWN will force the resource manager to 
             // disconnect the reader
             SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_UNKNOWN;
         }
         break;
   }
   //
   // we need to update the card state if there was a card before hibernate
   // stand / by or when the current state has changed.
   //
   if (SmartcardExtension->ReaderExtension->CardPresent ||
      oldState <= SCARD_ABSENT &&
      SmartcardExtension->ReaderCapabilities.CurrentState > SCARD_ABSENT ||
      oldState > SCARD_ABSENT &&
      SmartcardExtension->ReaderCapabilities.CurrentState <= SCARD_ABSENT) {

        stateChanged = TRUE;
      SmartcardExtension->ReaderExtension->CardPresent = FALSE;
    }

   KeAcquireSpinLock(&SmartcardExtension->OsData->SpinLock, &irql);
   if(stateChanged && SmartcardExtension->OsData->NotificationIrp != NULL)
   {
      KIRQL CurrentIrql;
      PIRP pIrp;

      IoAcquireCancelSpinLock( &CurrentIrql );
      IoSetCancelRoutine( SmartcardExtension->OsData->NotificationIrp, NULL );
      IoReleaseCancelSpinLock( CurrentIrql );

      SmartcardExtension->OsData->NotificationIrp->IoStatus.Status =
            STATUS_SUCCESS;
      SmartcardExtension->OsData->NotificationIrp->IoStatus.Information = 0;

      SmartcardDebug(
         DEBUG_DRIVER,
         ("%s!CBUpdateCardState: Completing notification irp %lx\n",
         DRIVER_NAME,
         SmartcardExtension->OsData->NotificationIrp));

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

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

      IoCompleteRequest(pIrp, IO_NO_INCREMENT);

   } else {
     KeReleaseSpinLock(&SmartcardExtension->OsData->SpinLock, irql);
   }

   return status;
}

NTSTATUS
CBSynchronizeSTC(
   PSMARTCARD_EXTENSION SmartcardExtension )
/*++

CBSynchronizeSTC:
   updates the card dependend data of the stc (wait times, ETU...)

Arguments:
   SmartcardExtension   context of call

Return Value:
   STATUS_SUCCESS

--*/

{
   NTSTATUS          NTStatus = STATUS_SUCCESS;
   PREADER_EXTENSION    ReaderExtension;
   ULONG             CWT,
                     BWT,
                     CGT,
                     ETU;
   UCHAR             Dl,
                     Fl,
                     N;

   PCLOCK_RATE_CONVERSION  ClockRateConversion;
   PBIT_RATE_ADJUSTMENT BitRateAdjustment;

   ReaderExtension      = SmartcardExtension->ReaderExtension;
   ClockRateConversion  = SmartcardExtension->CardCapabilities.ClockRateConversion;
   BitRateAdjustment = SmartcardExtension->CardCapabilities.BitRateAdjustment;

   // cycle length
   Dl = SmartcardExtension->CardCapabilities.Dl;
   Fl = SmartcardExtension->CardCapabilities.Fl;

   ETU = ClockRateConversion[Fl & 0x0F].F;

   ETU /= BitRateAdjustment[ Dl & 0x0F ].DNumerator;
   ETU *= BitRateAdjustment[ Dl & 0x0F ].DDivisor;

   // ETU += (ETU % 2 == 0) ? 0 : 1;

   // a extra guard time of 0xFF means minimum delay in both directions
   N = SmartcardExtension->CardCapabilities.N;
   if( N == 0xFF )
   {
      N = 0;
   }

   // set character waiting & guard time
   switch ( SmartcardExtension->CardCapabilities.Protocol.Selected )
   {
      case SCARD_PROTOCOL_T0:
         CWT = 960 * SmartcardExtension->CardCapabilities.T0.WI;
         CGT = 14 + N;  // 13 + N;     cryptoflex error
         break;

      case SCARD_PROTOCOL_T1:
         CWT = 11 + ( 0x01 << SmartcardExtension->CardCapabilities.T1.CWI );
         BWT = 11 + ( 0x01 << SmartcardExtension->CardCapabilities.T1.BWI ) * 960;
         CGT = 15 + N ;//13 + N; // 12 + N;     sicrypt error

         NTStatus = STCSetBWT( ReaderExtension, BWT * ETU );

         break;

      default:
         // restore default CGT
         CGT=13;
         STCSetCGT( ReaderExtension, CGT);
         NTStatus = STATUS_UNSUCCESSFUL;
         break;
   }

   if(( NTStatus == STATUS_SUCCESS ) && ETU )
   {
      NTStatus = STCSetETU( ReaderExtension, ETU );

      if( NTStatus == STATUS_SUCCESS )
      {
         NTStatus = STCSetCGT( ReaderExtension, CGT );

         if( NTStatus == STATUS_SUCCESS )
         {
            NTStatus = STCSetCWT( ReaderExtension, CWT * ETU );
         }
      }
   }
   return( NTStatus );
}