/*++
                 Copyright (c) 1998 Gemplus Development

Name:
   GIOCTL0A.C (Gemplus IOCTL Smart card Reader module 0A)

Description:
   This module holds the IOCTL functions for a smart card reader driver
    in compliance with PC/SC.


Environment:
   Kernel mode

Revision History :

   dd/mm/yy
   13/03/98: V1.00.001  (GPZ)
      - Start of development.
   26/04/98: V1.00.002  (GPZ)
      - Add the RestoreCommunication function for the Power Management.
   18/06/98: V1.00.003  (GPZ)
      - Send a classic PowerUp command (0x12 only) for a Warm reset
      in case of the Transparent protocol is selected.
      - The functions which use KeAcquireSpinLock/KeAcquireCancelSpinlock
      must be NOT PAGEABLE.
   28/08/98: V1.00.004  (GPZ)
      - Check the size of the output buffers before a RtlCopyMemory.
   22/01/99: V1.00.005 (YN)
     - Change the way to increase com port speed

--*/

#include "gntscr.h"
#include "gntser.h"
#include "gntscr0a.h"



//#pragma alloc_text(PAGEABLE, GDDK_0ASetProtocol)
//#pragma alloc_text(PAGEABLE, GDDK_0ATransmit)
//#pragma alloc_text(PAGEABLE, GDDK_0AVendorIoctl)


//
// Static functions declaration section:
//
static void GDDK_0ASetTransparentConfig(PSMARTCARD_EXTENSION,BYTE);
//
// Static variables declaration section:
//   - dataRatesSupported: holds all the supported data rates.
//
static ULONG
   dataRatesSupported[] = {
      9909,  13212,  14400,  15855,
     19200,  19819,  26425,  28800,
     31710,  38400,  39638,  52851,
     57600,  76800,  79277, 105703,
    115200, 158554
      };


NTSTATUS
GDDK_0AReaderPower(
   PSMARTCARD_EXTENSION SmartcardExtension
   )
/*++

Routine Description:

   This function is called by the Smart card library when a
     IOCTL_SMARTCARD_POWER occurs.
   This function provides 3 differents functionnality, depending of the minor
   IOCTL value
     - Cold reset (SCARD_COLD_RESET),
     - Warm reset (SCARD_WARM_RESET),
     - Power down (SCARD_POWER_DOWN).

Arguments:

   SmartcardExtension   - is a pointer on the SmartCardExtension structure of
                           the current device.

Return Value:

    STATUS_SUCCESS         - We could execute the request.
    STATUS_NOT_SUPPORTED   - We could not support the minor Ioctl.

--*/
{
   NTSTATUS status;
   BYTE cmd[5],rbuff[HOR3GLL_BUFFER_SIZE];
   USHORT rlen;
   KIRQL irql;
   READER_EXTENSION
      *param = SmartcardExtension->ReaderExtension;


   SmartcardDebug(
      DEBUG_IOCTL,
      ("%s!GDDK_0AReaderPower: Enter\n",
        SC_DRIVER_NAME)
      );

   //
   // Since power down triggers the UpdateSerialStatus function, we have
   // to inform it that we forced the change of the status and not the user
   // (who might have removed and inserted a card)
   //
   SmartcardExtension->ReaderExtension->PowerRequest = TRUE;

   // Lock the mutex to avoid a call of an other command.
    GDDK_0ALockExchange(SmartcardExtension);
   switch(SmartcardExtension->MinorIoControlCode) {

    case SCARD_POWER_DOWN:
        SmartcardDebug(
            DEBUG_IOCTL,
            ("%s!GDDK_0AReaderPower: SCARD_POWER_DOWN\n",
            SC_DRIVER_NAME)
            );
        //
        // ICC is powered Down
        //
        rlen = HOR3GLL_BUFFER_SIZE;
        cmd[0] = HOR3GLL_IFD_CMD_ICC_POWER_DOWN;
        status = GDDK_Oros3Exchange(
            param->Handle,
            HOR3GLL_LOW_TIME,
            1,
            cmd,
            &rlen,
            rbuff
            );
        if (status == STATUS_SUCCESS) {

            status = GDDK_Translate(rbuff[0],RDF_CARD_POWER);
        }
        //
        // Set the card CurrentState
        //
        SmartcardExtension->CardCapabilities.Protocol.Selected =
            SCARD_PROTOCOL_UNDEFINED;
        SmartcardExtension->CardCapabilities.ATR.Length = 0;
            rlen = HOR3GLL_BUFFER_SIZE;
        cmd[0] = HOR3GLL_IFD_CMD_ICC_STATUS;
        status = GDDK_Oros3Exchange(
            param->Handle,
            HOR3GLL_LOW_TIME,
            1,
            cmd,
            &rlen,
            rbuff
            );
        if (status == STATUS_SUCCESS) {

            status = GDDK_Translate(rbuff[0],0);
        }
        if (status == STATUS_SUCCESS) {

            KeAcquireSpinLock(
                &SmartcardExtension->OsData->SpinLock,
                &irql
                );
            if ((rbuff[1] & 0x04) == 0) {

                SmartcardExtension->ReaderCapabilities.CurrentState =
                    SCARD_ABSENT;
            } else if ((rbuff[1] & 0x02) == 0) {

                SmartcardExtension->ReaderCapabilities.CurrentState =
                    SCARD_SWALLOWED;
            }
            KeReleaseSpinLock(
                &SmartcardExtension->OsData->SpinLock,
                irql
                );
        }
        break;
    case SCARD_COLD_RESET:
    case SCARD_WARM_RESET:
        SmartcardDebug(
            DEBUG_IOCTL,
            ("%s!GDDK_0AReaderPower: SCARD_COLD_RESET or SCARD_WARM_RESET\n",
            SC_DRIVER_NAME)
            );
        status = GDDK_0AIccReset(
            SmartcardExtension,
            SmartcardExtension->MinorIoControlCode
            );
        if (status == STATUS_SUCCESS) {

            //
            // Check if we have a reply buffer.
            // smclib makes sure that it is big enough
            // if there is a buffer
            //
            if (SmartcardExtension->IoRequest.ReplyBuffer) {

                RtlCopyMemory(
                    SmartcardExtension->IoRequest.ReplyBuffer,
                    SmartcardExtension->CardCapabilities.ATR.Buffer,
                    SmartcardExtension->CardCapabilities.ATR.Length
                    );
                *SmartcardExtension->IoRequest.Information =
                    SmartcardExtension->CardCapabilities.ATR.Length;

            } else {

                status = STATUS_BUFFER_TOO_SMALL;
            }
        }
        break;

    default:
        SmartcardDebug(
            DEBUG_ERROR,
            ("%s!GDDK_0AReaderPower: Minor IOCTL not supported!\n",
            SC_DRIVER_NAME)
            );
        status = STATUS_NOT_SUPPORTED;
    }

   SmartcardExtension->ReaderExtension->PowerRequest = FALSE;
   SmartcardDebug(
      DEBUG_IOCTL,
      ("%s!GDDK_0AReaderPower: Exit=%X(hex)\n",
        SC_DRIVER_NAME,
        status)
      );
   // Unlock the mutex.
    GDDK_0AUnlockExchange(SmartcardExtension);
    return (status);
}


NTSTATUS
GDDK_0AIccReset(
   PSMARTCARD_EXTENSION SmartcardExtension,
   ULONG                ResetType
   )
/*++

Routine Description:

   This function provides 2 differents functionnality
     - Cold reset (SCARD_COLD_RESET),
     - Warm reset (SCARD_WARM_RESET),

Arguments:

   SmartcardExtension   - is a pointer on the SmartCardExtension structure of
                           the current device.
   ResetType            - type of the reset (SCARD_COLD_RESET or SCARD_WARM_RESET)

Return Value:

    STATUS_SUCCESS         - We could execute the request.
    STATUS_NOT_SUPPORTED   - We could not support the minor Ioctl.

--*/
{
    NTSTATUS status;
    BYTE cmd[5],
         rbuff[HOR3GLL_BUFFER_SIZE];
    USHORT rlen;
    KEVENT event;
    LARGE_INTEGER timeout;
    READER_EXTENSION *param = SmartcardExtension->ReaderExtension;

   SmartcardExtension->ReaderExtension->IccConfig.PTSMode = IFD_WITHOUT_PTS_REQUEST;
    switch(ResetType) {

    case SCARD_COLD_RESET:
        if (param->IccConfig.ICCType != ISOCARD) {

            //
            // Defines the type of the card (ISOCARD) and set the card presence
            //
            param->IccConfig.ICCType      = ISOCARD;
            rlen = HOR3GLL_BUFFER_SIZE;
            cmd[0] = HOR3GLL_IFD_CMD_ICC_DEFINE_TYPE;
            cmd[1] = (BYTE) param->IccConfig.ICCType;
            cmd[2] = (BYTE) param->IccConfig.ICCVpp;
            cmd[3] = (BYTE) param->IccConfig.ICCPresence;
            status = GDDK_Oros3Exchange(
                param->Handle,
                HOR3GLL_LOW_TIME,
                4,
                cmd,
                &rlen,
                rbuff
                );
            if (status == STATUS_SUCCESS) {

                status = GDDK_Translate(rbuff[0],RDF_CARD_POWER);
            }
            if (status != STATUS_SUCCESS) {

                return(status);
            }
        }
        //
        // ICC is powered Down
        //
        rlen = HOR3GLL_BUFFER_SIZE;
        cmd[0] = HOR3GLL_IFD_CMD_ICC_POWER_DOWN;
        status = GDDK_Oros3Exchange(
            param->Handle,
            HOR3GLL_LOW_TIME,
            1,
            cmd,
            &rlen,
            rbuff
            );
        if (status == STATUS_SUCCESS) {

            status = GDDK_Translate(rbuff[0],RDF_CARD_POWER);
        }
        if (status != STATUS_SUCCESS) {

            return(status);
        }

        if (param->PowerTimeOut) {

            //
            // Waits for the Power Timeout to be elapsed.
            //
            KeInitializeEvent(&event,NotificationEvent,FALSE);
            timeout.QuadPart = -((LONGLONG) param->PowerTimeOut * 10 * 1000);
            KeWaitForSingleObject(&event,
                Suspended,
                KernelMode,
                FALSE,
                &timeout);
        }

    case SCARD_WARM_RESET:
        //
        // ICC is powered up (GDDK_Oros3IccPowerUp).
        //
        rlen = HOR3GLL_BUFFER_SIZE;
        if (param->IccConfig.ICCType == TRANSPARENT_PROTOCOL) {

            status = GDDK_Oros3IccPowerUp(
                param->Handle,
                param->CmdTimeOut,
                0xFF,
                IFD_DEFAULT_MODE,
                0,
                0,
                0,
                0,
                &rlen,
                rbuff
                );
        } else {

            status = GDDK_Oros3IccPowerUp(
                param->Handle,
                param->CmdTimeOut,
                param->IccConfig.ICCVcc,
                param->IccConfig.PTSMode,
                param->IccConfig.PTS0,
                param->IccConfig.PTS1,
                param->IccConfig.PTS2,
                param->IccConfig.PTS3,
                &rlen,
                rbuff
                );
        }
        if (status == STATUS_SUCCESS) {

            status = GDDK_Translate(rbuff[0],RDF_CARD_POWER);
        }
        if (status != STATUS_SUCCESS) {

            return(status);
        }
        //
        // Copy ATR to smart card struct (remove the reader status byte)
        // The lib needs the ATR for evaluation of the card parameters
        //
        if (
            (SmartcardExtension->SmartcardReply.BufferSize >= (ULONG) (rlen - 1))
            &&
            (sizeof(SmartcardExtension->CardCapabilities.ATR.Buffer) >= (ULONG)(rlen - 1))
            ) {

            RtlCopyMemory(
                SmartcardExtension->SmartcardReply.Buffer,
                rbuff + 1,
                rlen - 1
                );
            SmartcardExtension->SmartcardReply.BufferLength = (ULONG) (rlen - 1);

            RtlCopyMemory(
                SmartcardExtension->CardCapabilities.ATR.Buffer,
                SmartcardExtension->SmartcardReply.Buffer,
                SmartcardExtension->SmartcardReply.BufferLength
                );
            SmartcardExtension->CardCapabilities.ATR.Length =
                (UCHAR) SmartcardExtension->SmartcardReply.BufferLength;

            SmartcardExtension->CardCapabilities.Protocol.Selected =
                SCARD_PROTOCOL_UNDEFINED;
            //
            // Parse the ATR string in order to check if it as valid
            // and to find out if the card uses invers convention
            //
            status = SmartcardUpdateCardCapabilities(SmartcardExtension);
        } else  {
            status = STATUS_BUFFER_TOO_SMALL;
        }
        break;

    default:
        status = STATUS_NOT_SUPPORTED;
    }

    return (status);
}


NTSTATUS
GDDK_0ASetProtocol(
      PSMARTCARD_EXTENSION SmartcardExtension
   )
/*++

Routine Description:

   The smart card lib requires to have this function. It is called
   to set a the transmission protocol and parameters. If this function
    is called with a protocol mask (which means the caller doesn't card
    about a particular protocol to be set) we first look if we can
    set T=1 and the T=0

Arguments:

   SmartcardExtension   - is a pointer on the SmartCardExtension structure of
                           the current device.

Return Value:

    STATUS_SUCCESS               - We could execute the request.
    STATUS_DEVICE_PROTOCOL_ERROR - We could not support the protocol requested.

--*/
{
    NTSTATUS status;
    BYTE     rbuff[HOR3GLL_BUFFER_SIZE];
    USHORT rlen;
    READER_EXTENSION *param = SmartcardExtension->ReaderExtension;
    PSERIAL_READER_CONFIG serialConfigData =
        &SmartcardExtension->ReaderExtension->SerialConfigData;

    PAGED_CODE();

    SmartcardDebug(
        DEBUG_TRACE,
        ("%s!GDDK_0ASetProtocol: Enter\n",
        SC_DRIVER_NAME)
        );

    // Lock the mutex to avoid a call of an other command.
    GDDK_0ALockExchange(SmartcardExtension);
    __try {
        //
        // Check if the card is already in specific state
        // and if the caller wants to have the already selected protocol.
        // We return success if this is the case.
        //
        if (SmartcardExtension->ReaderCapabilities.CurrentState == SCARD_SPECIFIC &&
            (SmartcardExtension->CardCapabilities.Protocol.Selected &
            SmartcardExtension->MinorIoControlCode)
            ) {
            status = STATUS_SUCCESS;
            __leave;
        }

        while(TRUE) {
            //
            // Select T=1 or T=0 and indicate that pts1 follows
            //
            if (  SmartcardExtension->CardCapabilities.Protocol.Supported &
                  SmartcardExtension->MinorIoControlCode                  &
                  SCARD_PROTOCOL_T1
               ) {

                SmartcardExtension->ReaderExtension->IccConfig.PTS0 = IFD_NEGOTIATE_T1 | IFD_NEGOTIATE_PTS1;
                SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_T1;

            } else if (  SmartcardExtension->CardCapabilities.Protocol.Supported &
                         SmartcardExtension->MinorIoControlCode                  &
                         SCARD_PROTOCOL_T0
                      ) {

                SmartcardExtension->ReaderExtension->IccConfig.PTS0 = IFD_NEGOTIATE_T0 | IFD_NEGOTIATE_PTS1;
                SmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_T0;

            } else {

                status = STATUS_INVALID_DEVICE_REQUEST;
                __leave;
            }
            //
            // Set pts1 which codes Fl and Dl
            //
            SmartcardExtension->ReaderExtension->IccConfig.PTS1 =
                SmartcardExtension->CardCapabilities.PtsData.Fl << 4 |
                SmartcardExtension->CardCapabilities.PtsData.Dl;

            param->IccConfig.PTSMode = IFD_NEGOTIATE_PTS_MANUALLY;
            rlen = HOR3GLL_BUFFER_SIZE;
            status = GDDK_Oros3IccPowerUp(
                param->Handle,
                param->CmdTimeOut,
                param->IccConfig.ICCVcc,
                param->IccConfig.PTSMode,
                param->IccConfig.PTS0,
                param->IccConfig.PTS1,
                param->IccConfig.PTS2,
                param->IccConfig.PTS3,
                &rlen,
                rbuff
                );
            if (status == STATUS_SUCCESS) {

                status = GDDK_Translate(rbuff[0],RDF_CARD_POWER);
            }
            if (status == STATUS_SUCCESS ) {

                //The card replied correctly to our pts-request
                SmartcardDebug(
                    DEBUG_TRACE,
                    ("%s!GDDK_0ASetProtocol: PTS Request OK\n",
                    SC_DRIVER_NAME)
                    );
                break;
            } else if (SmartcardExtension->CardCapabilities.PtsData.Type !=
                        PTS_TYPE_DEFAULT
                      ) {

                SmartcardDebug(
                    DEBUG_TRACE,
                    ("%s!GDDK_0ASetProtocol: PTS failed. Trying default parameters...\n",
                    SC_DRIVER_NAME)
                    );
                //
                // The card did either NOT reply or it replied incorrectly
                // so try default values.
                // Set PtsData Type to Default and do a cold reset
                //
                SmartcardExtension->CardCapabilities.PtsData.Type =
                    PTS_TYPE_DEFAULT;

                status = GDDK_0AIccReset(SmartcardExtension,SCARD_COLD_RESET);
                continue;
            }
            //
            // The card failed the pts-request
            //
            status = STATUS_DEVICE_PROTOCOL_ERROR;
            __leave;
        } //End of the while

        //
        // The card replied correctly to the pts request
        // Set the appropriate parameters for the port
        //
        if ( SmartcardExtension->CardCapabilities.Protocol.Selected &
             SCARD_PROTOCOL_T1
           ) {

            serialConfigData->Timeouts.ReadIntervalTimeout = 10 +
                SmartcardExtension->CardCapabilities.T1.CWT / 1000;
        }
        else if ( SmartcardExtension->CardCapabilities.Protocol.Selected &
                 SCARD_PROTOCOL_T0
                ) {

            serialConfigData->Timeouts.ReadIntervalTimeout = 10 +
                SmartcardExtension->CardCapabilities.T0.WT / 1000;
        }
        //
        // Now indicate that we're in specific mode
        // and return the selected protocol to the caller
        //
        SmartcardExtension->ReaderCapabilities.CurrentState = SCARD_SPECIFIC;

        *(PULONG) SmartcardExtension->IoRequest.ReplyBuffer =
            SmartcardExtension->CardCapabilities.Protocol.Selected;

        *SmartcardExtension->IoRequest.Information =
            sizeof(SmartcardExtension->CardCapabilities.Protocol.Selected);
    }

    __finally
    {
        if (status != STATUS_SUCCESS) {

            SmartcardExtension->CardCapabilities.Protocol.Selected =
                SCARD_PROTOCOL_UNDEFINED;
            *SmartcardExtension->IoRequest.Information = 0;
        }
    }
    SmartcardDebug(
        DEBUG_TRACE,
        ("%s!GDDK_0ASetProtocol: Exit=%lX(hex)\n",
        SC_DRIVER_NAME,
        status)
        );
    // Unlock the mutex.
    GDDK_0AUnlockExchange(SmartcardExtension);
    return status;
}


NTSTATUS
GDDK_0ATransmit(
   PSMARTCARD_EXTENSION SmartcardExtension
   )
/*++

Routine Description:

   This function is called by the Smart card library when a
     IOCTL_SMARTCARD_TRANSMIT occurs.
   This function is used to transmit a command to the card.

Arguments:

   SmartcardExtension   - is a pointer on the SmartCardExtension structure of
                           the current device.

Return Value:

    STATUS_SUCCESS                - We could execute the request.
    STATUS_INVALID_DEVICE_STATE   - If the protocol specified is different from
                                    the protocol selected
    STATUS_INVALID_DEVICE_REQUEST - We could not support the protocol specified.

--*/
{
    NTSTATUS status;
    PUCHAR requestBuffer = SmartcardExtension->SmartcardRequest.Buffer,
           replyBuffer = SmartcardExtension->SmartcardReply.Buffer;
    PULONG requestLength = &SmartcardExtension->SmartcardRequest.BufferLength;
    PSCARD_IO_REQUEST scardIoRequest = (PSCARD_IO_REQUEST)
                      SmartcardExtension->OsData->CurrentIrp->AssociatedIrp.SystemBuffer;
    READER_EXTENSION *param = SmartcardExtension->ReaderExtension;
    USHORT rlen,
           rlenCmd;
    BYTE cmd[5],
         rbuff[HOR3GLL_BUFFER_SIZE],
         rbuffCmd[HOR3GLL_BUFFER_SIZE];
    ULONG t1Timeout;

    PAGED_CODE();

    SmartcardDebug(
        DEBUG_IOCTL,
        ("%s!GDDK_0ATransmit: Enter\n",
        SC_DRIVER_NAME)
        );

    //Set the reply buffer length to 0.
    *SmartcardExtension->IoRequest.Information = 0;
    status = STATUS_SUCCESS;

    //
    // Verify if the protocol specified is the same than the protocol selected.
    //
    *requestLength = 0;
    if (SmartcardExtension->CardCapabilities.Protocol.Selected !=
        scardIoRequest->dwProtocol) {

        return (STATUS_INVALID_DEVICE_STATE);
    }
    // Lock the mutex to avoid a call of an other command.
    GDDK_0ALockExchange(SmartcardExtension);
    switch (SmartcardExtension->CardCapabilities.Protocol.Selected) {

    // For RAW protocol we return STATUS_INVALID_DEVICE_STATE
    case SCARD_PROTOCOL_RAW:
        status = STATUS_INVALID_DEVICE_STATE;
        break;

    //
    // T=0 PROTOCOL:
    //   Call the SmartCardT0Request which updates the SmartcardRequest struct.
    //
    case SCARD_PROTOCOL_T0:
        SmartcardExtension->SmartcardRequest.BufferLength = 0;
        status = SmartcardT0Request(SmartcardExtension);
        if (status == STATUS_SUCCESS) {

            rlen = HOR3GLL_BUFFER_SIZE;
            //
            // If the length LEx > 0
            // Then
            //    Is an ISO Out command.
            //    If LEx > SC_IFD_T0_MAXIMUM_LEX (256) we return STATUS_BUFFER_TOO_SMALL
            //   Call the GDDK_Oros3IsoOutput
            //
            if (SmartcardExtension->T0.Le > 0) {

                if (SmartcardExtension->T0.Le > SC_IFD_T0_MAXIMUM_LEX) {

                    status = STATUS_BUFFER_TOO_SMALL;
                }
                if (status == STATUS_SUCCESS) {

                    status = GDDK_Oros3IsoOutput(
                        param->Handle,
                        param->APDUTimeOut,
                        HOR3GLL_IFD_CMD_ICC_ISO_OUT,
                        (BYTE *)SmartcardExtension->SmartcardRequest.Buffer,
                        &rlen,
                        rbuff
                        );
                }
            } else {

                //
                // Else Is an ISO In command.
                //   If LC > SC_IFD_T0_MAXIMUM_LC (255) we return STATUS_BUFFER_TOO_SMALL
                //   Call the GDDK_Oros3IsoInput
                //
                if (SmartcardExtension->T0.Lc > SC_IFD_T0_MAXIMUM_LC) {

                    status = STATUS_BUFFER_TOO_SMALL;
                }
                if (status == STATUS_SUCCESS) {

                    status = GDDK_Oros3IsoInput(
                        param->Handle,
                        param->APDUTimeOut,
                        HOR3GLL_IFD_CMD_ICC_ISO_IN,
                        (BYTE *)SmartcardExtension->SmartcardRequest.Buffer,
                        (BYTE *)SmartcardExtension->SmartcardRequest.Buffer + 5,
                        &rlen,
                        rbuff
                        );
                }
            }
        }
        if (status == STATUS_SUCCESS) {

            status = GDDK_Translate(rbuff[0],RDF_TRANSMIT);
        }
        //
        // If the Status is Success
        //  Copy the response in the SmartcardReply buffer. Remove the status
        //   of the reader.
        //   Call the SmartcardT0reply function to update the IORequest struct.
        //
        if (status == STATUS_SUCCESS){
            ASSERT(SmartcardExtension->SmartcardReply.BufferSize >= (ULONG) (rlen - 1));
            RtlCopyMemory(
                SmartcardExtension->SmartcardReply.Buffer,
                rbuff + 1,
                rlen - 1
                );
            SmartcardExtension->SmartcardReply.BufferLength = (ULONG) (rlen - 1);
            status = SmartcardT0Reply(SmartcardExtension);
        }
        break;

    //
    // T=1 PROTOCOL:
    //
    case SCARD_PROTOCOL_T1:
        //
        // If the current card type <> TRANSPARENT_PROTOCOL,
        //
        if (param->IccConfig.ICCType != TRANSPARENT_PROTOCOL) {

            // We read the status of the card to known the current voltage and the TA1
            rlenCmd = HOR3GLL_BUFFER_SIZE;
            cmd[0] = HOR3GLL_IFD_CMD_ICC_STATUS;
            status = GDDK_Oros3Exchange(
                param->Handle,
                HOR3GLL_LOW_TIME,
                1,
                cmd,
                &rlenCmd,
                rbuffCmd
                );
            param->TransparentConfig.CFG = rbuffCmd[1] & 0x01; //Vcc
            param->TransparentConfig.Fi = rbuffCmd[3] >>4; //Fi
            param->TransparentConfig.Di = 0x0F & rbuffCmd[3]; //Di

            //We define the type of the card.
            rlenCmd = HOR3GLL_BUFFER_SIZE;
            param->IccConfig.ICCType = TRANSPARENT_PROTOCOL;
            cmd[0] = HOR3GLL_IFD_CMD_ICC_DEFINE_TYPE;
            cmd[1] = (BYTE) param->IccConfig.ICCType;
            cmd[2] = (BYTE) param->IccConfig.ICCVpp;
            cmd[3] = (BYTE) param->IccConfig.ICCPresence ;
            status = GDDK_Oros3Exchange(
                param->Handle,
                HOR3GLL_LOW_TIME,
                4,
                cmd,
                &rlenCmd,
                rbuffCmd
                );
            if (status == STATUS_SUCCESS) {

                status = GDDK_Translate(rbuffCmd[0],RDF_TRANSMIT);
            }

            if (status != STATUS_SUCCESS) {

                break;
            }

            // Set the transparent configuration
            GDDK_0ASetTransparentConfig(SmartcardExtension,SmartcardExtension->T1.Wtx);
        }
        //
        // Loop for the T=1 management
        //
        do {
            PULONG requestLength = &SmartcardExtension->SmartcardRequest.BufferLength;

            // Tell the lib function how many bytes I need for the prologue
            //
            *requestLength = 0;
            status = SmartcardT1Request(SmartcardExtension);
            if (status != STATUS_SUCCESS) {
                break;
            }
            if (SmartcardExtension->T1.Wtx) {

                // Compute the timeout for the Oros3Exchange (WTX * BWT)
                t1Timeout = SmartcardExtension->CardCapabilities.T1.BWT / 1000;
                t1Timeout *= SmartcardExtension->T1.Wtx;
                t1Timeout += 500;
                GDDK_0ASetTransparentConfig(SmartcardExtension,SmartcardExtension->T1.Wtx);

            } else {

                t1Timeout = SmartcardExtension->CardCapabilities.T1.BWT / 1000;
                t1Timeout += 500;
            }

         SER_SetPortTimeout(param->Handle, t1Timeout);

            rlen = HOR3GLL_BUFFER_SIZE;
            status = GDDK_Oros3TransparentExchange(
                param->Handle,
                t1Timeout,
                (USHORT) SmartcardExtension->SmartcardRequest.BufferLength,
                SmartcardExtension->SmartcardRequest.Buffer,
                &rlen,
                rbuff
                );
            if (status == STATUS_SUCCESS) {

                status = GDDK_Translate(rbuff[0],RDF_TRANSMIT);
            }
            if (status != STATUS_SUCCESS) {
                rlen = 1;
            }
            if (SmartcardExtension->T1.Wtx) {

                // Set the reader BWI to the default value
                GDDK_0ASetTransparentConfig(SmartcardExtension,0);
            }

            // Copy the response in the reply buffer
            if (SmartcardExtension->SmartcardReply.BufferSize >= (ULONG) (rlen -1)) {
                SmartcardExtension->SmartcardReply.BufferLength = (ULONG) (rlen - 1);

                if (SmartcardExtension->SmartcardReply.BufferLength > 0) {
                    RtlCopyMemory(
                        SmartcardExtension->SmartcardReply.Buffer,
                        rbuff + 1,
                        rlen - 1
                        );
                }
            }
            status = SmartcardT1Reply(SmartcardExtension);
        } while (status == STATUS_MORE_PROCESSING_REQUIRED);
        break;

    default:
        status = STATUS_INVALID_DEVICE_REQUEST;
        break;
    }

   SER_SetPortTimeout(param->Handle, HOR3COMM_CHAR_TIMEOUT);

    // Unlock the mutex.
    GDDK_0AUnlockExchange(SmartcardExtension);
    SmartcardDebug(
        DEBUG_IOCTL,
        ("%s!GDDK_0ATransmit: Exit=%X(hex)\n",
        SC_DRIVER_NAME,
        status)
        );
    if (status != STATUS_SUCCESS) {

        SmartcardDebug(
            DEBUG_ERROR,
            ("%s!GDDK_0ATransmit: failed! status=%X(hex)\n",
            SC_DRIVER_NAME,
            status)
            );
    }
    return (status);
}




NTSTATUS
GDDK_0ACardTracking(
   PSMARTCARD_EXTENSION SmartcardExtension
   )
/*++

Routine Description:

   This function is called by the Smart card library when an
     IOCTL_SMARTCARD_IS_PRESENT or IOCTL_SMARTCARD_IS_ABSENT occurs.

Arguments:

   SmartcardExtension   - is a pointer on the SmartCardExtension structure of
                           the current device.

Return Value:

    STATUS_PENDING                - The request is in a pending mode.

--*/
{
    KIRQL oldIrql;

    //
    // Set cancel routine for the notification irp
    //
    IoAcquireCancelSpinLock(&oldIrql);

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

    IoReleaseCancelSpinLock(oldIrql);

    return STATUS_PENDING;
}




NTSTATUS
GDDK_0AVendorIoctl(
    PSMARTCARD_EXTENSION SmartcardExtension
   )
/*++

Routine Description:

   This routine is called when a vendor IOCTL_SMARTCARD_ is send to the driver.

Arguments:

   SmartcardExtension   - is a pointer on the SmartCardExtension structure of
                           the current device.

Return Value:

    STATUS_SUCCESS          - We could execute the request.
    STATUS_BUFFER_TOO_SMALL - The output buffer is to small.
    STATUS_NOT_SUPPORTED    - We could not support the Ioctl specified.

--*/
{
    NTSTATUS status = STATUS_SUCCESS;
    READER_EXTENSION *param = SmartcardExtension->ReaderExtension;
    USHORT rlen;
    BYTE rbuff[HOR3GLL_BUFFER_SIZE];

    PAGED_CODE();

    ASSERT(SmartcardExtension != NULL);
    SmartcardDebug(
        DEBUG_IOCTL,
        ("%s!GDDK_0AVendorIoctl: Enter, IoControlCode=%lX(hex)\n",
        SC_DRIVER_NAME,
        SmartcardExtension->MajorIoControlCode)
        );
    // Lock the mutex to avoid a call of an other command.
    GDDK_0ALockExchange(SmartcardExtension);
    // Set the reply buffer length to 0.
    *SmartcardExtension->IoRequest.Information = 0;

    switch(SmartcardExtension->MajorIoControlCode) {
    //
    // For IOCTL_SMARTCARD_VENDOR_GET_ATTRIBUTE and IOCTL_VENDOR_SMARTCARD_SET_ATTRIBUTE
    //   Call the GDDK_0AVendorTag function
    //
    case IOCTL_SMARTCARD_VENDOR_GET_ATTRIBUTE:
    case IOCTL_SMARTCARD_VENDOR_SET_ATTRIBUTE:
        status = GDDK_0AVendorTag(
            SmartcardExtension,
            (ULONG)  SmartcardExtension->MajorIoControlCode,
            (ULONG)  SmartcardExtension->IoRequest.RequestBufferLength,
            (PUCHAR) SmartcardExtension->IoRequest.RequestBuffer,
            (ULONG)  SmartcardExtension->IoRequest.ReplyBufferLength,
            (PUCHAR) SmartcardExtension->IoRequest.ReplyBuffer,
            (PULONG) SmartcardExtension->IoRequest.Information
            );
        break;

    //
    // For IOCTL_SMARTCARD_VENDOR_IFD_EXCHANGE
    //   Send the command to the reader
    //
    case IOCTL_SMARTCARD_VENDOR_IFD_EXCHANGE:
        rlen = (USHORT) HOR3GLL_BUFFER_SIZE;
        status = GDDK_Oros3Exchange(
            param->Handle,
            HOR3GLL_LOW_TIME,
            (USHORT) SmartcardExtension->IoRequest.RequestBufferLength,
            (BYTE *) SmartcardExtension->IoRequest.RequestBuffer,
            &rlen,
            rbuff
            );

        if (status != STATUS_SUCCESS) {

            break;
        }
        if (SmartcardExtension->IoRequest.ReplyBufferLength < (ULONG) rlen) {

            status = STATUS_BUFFER_TOO_SMALL;
            break;
        }
        RtlCopyMemory(
            SmartcardExtension->IoRequest.ReplyBuffer,
            rbuff,
            rlen
            );
        *(SmartcardExtension->IoRequest.Information) = (ULONG) rlen;
        status = STATUS_SUCCESS;
        break;

    default:
        status = STATUS_NOT_SUPPORTED;
        break;
    }
    // Unlock the mutex.
    GDDK_0AUnlockExchange(SmartcardExtension);

    SmartcardDebug(
        DEBUG_IOCTL,
        ("%s!GDDK_0AVendorIoctl: Exit=%X(hex)\n",
        SC_DRIVER_NAME,
        status)
        );
    return status;
}


NTSTATUS
GDDK_0AVendorTag(
   PSMARTCARD_EXTENSION   SmartcardExtension,
   ULONG                  IoControlCode,
   ULONG                  BufferInLen,
   PUCHAR                 BufferIn,
   ULONG                  BufferOutLen,
   PUCHAR                 BufferOut,
   PULONG                 LengthOut
   )
/*++

Routine Description:

   This function is called when a specific Tag request occurs.

Arguments:

   SmartcardExtension   - is a pointer on the SmartCardExtension structure of
                           the current device.
   IoControlCode        - holds the Ioctl value.
   BufferInLen          - holds the length of the input data.
   BufferIn             - holds the input data.
   BufferOutLen         - holds the size of the output buffer.
   BufferOut            - the output buffer.
   LengthOut            - holds the length of the output data.

Return Value:

    STATUS_SUCCESS          - We could execute the request.
    STATUS_BUFFER_TOO_SMALL - The output buffer is to small.
    STATUS_NOT_SUPPORTED    - We could not support the Ioctl specified.

--*/
{
    ULONG TagValue;
    PREADER_EXTENSION pReaderExtension = SmartcardExtension->ReaderExtension;

    ASSERT(pReaderExtension != NULL);
    // Set the reply buffer length to 0.
    *LengthOut = 0l;
    // Verify the length of the Tag
    if (BufferInLen < sizeof(TagValue)) {

        return(STATUS_BUFFER_TOO_SMALL);
    }
    TagValue = (ULONG) *((PULONG)BufferIn);

    // Switch for the different IOCTL:
    // Get the value of one tag (IOCTL_SMARTCARD_VENDOR_GET_ATTRIBUTE)
    //      Switch for the different Tags:
    switch(IoControlCode) {

    // Get an attribute
    case IOCTL_SMARTCARD_VENDOR_GET_ATTRIBUTE:
        switch (TagValue) {

        // Baud rate of the reader (SCARD_ATTR_SPEC_BAUD_RATE)
        case SCARD_ATTR_SPEC_BAUD_RATE:
            if (BufferOutLen < (ULONG) sizeof(pReaderExtension->IFDBaudRate)) {

                return(STATUS_BUFFER_TOO_SMALL);
            }
            RtlCopyMemory(
                BufferOut,
                &pReaderExtension->IFDBaudRate,
                sizeof(pReaderExtension->IFDBaudRate)
                );
            *(LengthOut) = (ULONG) sizeof(pReaderExtension->IFDBaudRate);
            return (STATUS_SUCCESS);
            break;

        // Power Timeout (SCARD_ATTR_SPEC_POWER_TIMEOUT)
        case SCARD_ATTR_SPEC_POWER_TIMEOUT:
            if (BufferOutLen < (ULONG) sizeof(pReaderExtension->PowerTimeOut)) {

                return(STATUS_BUFFER_TOO_SMALL);
            }
            RtlCopyMemory(
                BufferOut,
                &pReaderExtension->PowerTimeOut,
                sizeof(pReaderExtension->PowerTimeOut)
                );
            *(LengthOut) = (ULONG) sizeof(pReaderExtension->PowerTimeOut);
            return STATUS_SUCCESS;
            break;

        // Command Timeout (SCARD_ATTR_SPEC_CMD_TIMEOUT)
        case SCARD_ATTR_SPEC_CMD_TIMEOUT:
            if (BufferOutLen < (ULONG) sizeof(pReaderExtension->CmdTimeOut)) {
                return(STATUS_BUFFER_TOO_SMALL);
            }
            RtlCopyMemory(
                BufferOut,
                &pReaderExtension->CmdTimeOut,
                sizeof(pReaderExtension->CmdTimeOut)
                );
            *(LengthOut) = (ULONG) sizeof(pReaderExtension->CmdTimeOut);
            return STATUS_SUCCESS;
            break;
        // APDU Timeout (SCARD_ATTR_SPEC_APDU_TIMEOUT)
        case SCARD_ATTR_SPEC_APDU_TIMEOUT:
            if (BufferOutLen < (ULONG) sizeof(pReaderExtension->APDUTimeOut)) {
                return(STATUS_BUFFER_TOO_SMALL);
            }
            RtlCopyMemory(
                BufferOut,
                &pReaderExtension->APDUTimeOut,
                sizeof(pReaderExtension->APDUTimeOut)
                );
            *(LengthOut) = (ULONG) sizeof(pReaderExtension->APDUTimeOut);
            return STATUS_SUCCESS;
            break;
        // Unknown tag, we return STATUS_NOT_SUPPORTED
        default:
            return STATUS_NOT_SUPPORTED;
            break;
        }
        break;

    // Set the value of one tag (IOCTL_SMARTCARD_VENDOR_SET_ATTRIBUTE)
    case IOCTL_SMARTCARD_VENDOR_SET_ATTRIBUTE:
        switch (TagValue) {

        // Power Timeout (SCARD_ATTR_SPEC_POWER_TIMEOUT)
        case SCARD_ATTR_SPEC_POWER_TIMEOUT:
            if (BufferInLen <(ULONG) (sizeof(pReaderExtension->PowerTimeOut) + sizeof(TagValue))) {

                return(STATUS_BUFFER_TOO_SMALL);
            }
            RtlCopyMemory(
                &pReaderExtension->PowerTimeOut,
                BufferIn + sizeof(TagValue),
                sizeof(pReaderExtension->PowerTimeOut)
                );
            return STATUS_SUCCESS;
            break;

        // Command Timeout (SCARD_ATTR_SPEC_CMD_TIMEOUT)
        case SCARD_ATTR_SPEC_CMD_TIMEOUT:
            if (BufferInLen <(ULONG) (sizeof(pReaderExtension->CmdTimeOut) + sizeof(TagValue))) {

                return(STATUS_BUFFER_TOO_SMALL);
            }
            RtlCopyMemory(
                &pReaderExtension->CmdTimeOut,
                BufferIn + sizeof(TagValue),
                sizeof(pReaderExtension->CmdTimeOut)
                );
            return STATUS_SUCCESS;
            break;

        // Command Timeout (SCARD_ATTR_SPEC_APDU_TIMEOUT)
        case SCARD_ATTR_SPEC_APDU_TIMEOUT:
            if (BufferInLen <(ULONG) (sizeof(pReaderExtension->APDUTimeOut) + sizeof(TagValue))) {

                return(STATUS_BUFFER_TOO_SMALL);
            }
            RtlCopyMemory(
                &pReaderExtension->APDUTimeOut,
                BufferIn + sizeof(TagValue),
                sizeof(pReaderExtension->APDUTimeOut)
                );
            return STATUS_SUCCESS;
            break;

        // Unknown tag, we return STATUS_NOT_SUPPORTED
        default:
            return STATUS_NOT_SUPPORTED;
    }
    break;

    default:
        return STATUS_NOT_SUPPORTED;
        break;
    }
}


NTSTATUS
GDDK_0AOpenChannel(
   PSMARTCARD_EXTENSION SmartcardExtension,
   CONST ULONG          DeviceNumber,
   CONST ULONG          PortSerialNumber,
   CONST ULONG          MaximalBaudRate
   )
/*++

Routine Description:

    This routine try to establish a connection with a reader, and after
     update the characteristic of this reader.

Arguments:

   SmartcardExtension   - is a pointer on the SmartCardExtension structure of
                           the current device.
   DeviceNumber         - holds the current device number (0 to MAX_DEVICES).
   PortSerialNumber     - holds the port serial number (0 to HGTSER_MAX_PORT).
   MaximalBaudRate      - holds the maximal speed specified for the reader.

Return Value:

    STATUS_SUCCESS          - We could execute the request.

--*/
{
    NTSTATUS status = STATUS_SUCCESS;
    short handle,portcom;
    char os_string[HOR3GLL_OS_STRING_SIZE];
    USHORT os_length = HOR3GLL_OS_STRING_SIZE;
    USHORT user;
    TGTSER_PORT comm;
    ULONG br;
    BYTE minorVersion,majorVersion;
    COM_SERIAL serial_channel;
    USHORT rlen;
    BYTE cmd[5],rbuff[HOR3GLL_BUFFER_SIZE];
    LARGE_INTEGER timeout;

    // Update the serial communication channel information:
    serial_channel.Port     = PortSerialNumber + G_COM1;
    serial_channel.BaudRate = MaximalBaudRate;
    serial_channel.pSmartcardExtension  = SmartcardExtension;

    // Initializes a mutex object (in a high level) for the exchange commands with
    // the smart card reader.
    KeInitializeMutex(
        &SmartcardExtension->ReaderExtension->ExchangeMutex,
        3
        );
    // Initializes a mutex object (in a high level) for the long APDU commands with
    // the smart card reader.
    KeInitializeMutex(
        &SmartcardExtension->ReaderExtension->LongAPDUMutex,
        3
        );
    // Open a communication channel (GDDK_Oros3OpenComm).
    //   The reader baudrate is automatically detected by this function.
    handle = (short)DeviceNumber;
    status = GDDK_Oros3OpenComm(&serial_channel,handle);
    if (status == STATUS_DEVICE_ALREADY_ATTACHED) {

        status = GDDK_SerPortAddUser((USHORT) serial_channel.Port,&portcom);
        if (status != STATUS_SUCCESS) {

            return (status);
        }
        GDDK_GBPOpen(handle,2,4,portcom);
    }
    if (status != STATUS_SUCCESS) {

        return (status);
    }

    //
    // Verify the Firmware version: this driver support only the PnP GemCore based
    // readers
    //
    cmd[0] = (BYTE) HOR3GLL_IFD_CMD_MEM_RD;
    cmd[1] = (BYTE)HOR3GLL_IFD_TYP_VERSION;
    cmd[2] = HIBYTE(HOR3GLL_IFD_ADD_VERSION);
    cmd[3] = LOBYTE(HOR3GLL_IFD_ADD_VERSION);
    cmd[4] = (BYTE)HOR3GLL_IFD_LEN_VERSION;
    status = GDDK_Oros3Exchange(
        handle,
        HOR3GLL_LOW_TIME,
        5,
        cmd,
        &os_length,
        os_string
        );
    if (status != STATUS_SUCCESS) {
        GDDK_Oros3CloseComm(handle);
        return (status);
    }
    if (os_length >= (USHORT)strlen(IFD_FIRMWARE_VERSION)) {
        if (memcmp(os_string + 1,IFD_FIRMWARE_VERSION,strlen(IFD_FIRMWARE_VERSION))) {

            GDDK_Oros3CloseComm(handle);
            return (STATUS_INVALID_DEVICE_STATE);
        }
    } else {
        GDDK_Oros3CloseComm(handle);
        return (STATUS_INVALID_DEVICE_STATE);
    }
    // GemCore Rx.yy-yz
    // x = major version
    // y = minor version
    // z = M if masked version
    majorVersion = os_string[strlen(IFD_FIRMWARE_VERSION) + 1] - '0';
    minorVersion = 100 * (os_string[strlen(IFD_FIRMWARE_VERSION) + 3] - '0');
    minorVersion += 10 * (os_string[strlen(IFD_FIRMWARE_VERSION) + 4] - '0');
    minorVersion +=  (os_string[strlen(IFD_FIRMWARE_VERSION) + 6] - '0');
    SmartcardDebug(
        DEBUG_TRACE,
        ("%s!GDDK_0AOpenChannel: GemCore version = %d.%d\n",
        SC_DRIVER_NAME,
        majorVersion,
        minorVersion)
        );
    if ((majorVersion < IFD_VERSION_MAJOR) || (minorVersion < IFD_VERSION_MINOR)) {

        GDDK_Oros3CloseComm(handle);
            SmartcardDebug(
            DEBUG_ERROR,
            ("%s!GDDK_0AOpenChannel: The firmware version is not supported by the driver!\n",
            SC_DRIVER_NAME)
            );
        return (STATUS_BAD_DEVICE_TYPE);
    }

    // Optimizes the baudrate:
    // Initializes the comm variable to modify the used baud rate.
    status = GDDK_GBPChannelToPortComm(handle,&portcom);
    if (status != STATUS_SUCCESS) {

        GDDK_Oros3CloseComm(handle);
        return status;
    }
    status  = GDDK_SerPortGetState(
        portcom,
        &comm,
        &user
        );
    if (status != STATUS_SUCCESS) {

        GDDK_Oros3CloseComm(handle);
        return status;
    }

   //ISV
   // Connection was established
   // Now try to connect at max speed!
   comm.BaudRate = serial_channel.BaudRate;
    SmartcardDebug(
        DEBUG_TRACE,
        ("%s!GDDK_0AOpenChannel: Connection established at %d baud rate\n",
        SC_DRIVER_NAME,comm.BaudRate));

            // Unsupported baud rate
    if(comm.BaudRate >= 38400)
    {
        SmartcardDebug(DEBUG_ERROR,("GDDK_0AOpenChannel: ###### UNSUPPORTED BAUD RATE %d\n", comm.BaudRate));
        GDDK_Oros3CloseComm(handle);
        return (STATUS_INVALID_DEVICE_STATE);
    }
            // Maximum baud rate is already set - nothing to do
    if(comm.BaudRate==38400)
    {
       SmartcardDebug(DEBUG_DRIVER,("GDDK_0AOpenChannel: MAX SPEED SET TO 38400!\n"));
    }
    else
    {
        // Try to negotiate a better baud rate.
        int i=0;
        BOOLEAN bBetterBaudRate=FALSE;
        ULONG brList[3] = {38400, 19200, 9600};
        USHORT brListLength = 3;
        ULONG previousBaudRate = 0;
      KEVENT event;
        previousBaudRate = comm.BaudRate;

        i = 0;
        while ( (i < brListLength) &&
                (bBetterBaudRate == FALSE) )
        {
            br = brList[i];

            // The reader is switched to the selected value (GDDK_Oros3SIOConfigure). The
            // function status is not tested because, as the IFD has switched
            // immediatly, it is not possible to read its response.
            comm.BaudRate = br;
            rlen = 0;
            rbuff[0]=0x0;
            GDDK_Oros3SIOConfigure(
                handle,
                HOR3GLL_LOW_TIME,
                0,
                8,
                comm.BaudRate,
                &rlen,
                rbuff,
            FALSE
                );

            //
            // Waits for the fisrt command is processed by the reader
         // 500 ms is enought
            //
            KeInitializeEvent(&event,NotificationEvent,FALSE);
            timeout.QuadPart = -((LONGLONG)  500 * 1000);
            KeWaitForSingleObject(&event,
                Suspended,
                KernelMode,
                FALSE,
                &timeout);

            // Host is switched to the selected value (GDDK_SerPortSetState).
            // If this call is successful,
            // Then
            //   The last command is re-sent to read the IFD response.
            //   response is optionnaly initialized with the translated IFD status.
            status = GDDK_SerPortSetState(
                portcom,
                &comm
                );
            if (status == STATUS_SUCCESS)
            {
                rlen = HOR3GLL_BUFFER_SIZE;
                rbuff[0]=0x0;
                status = GDDK_Oros3SIOConfigure(
                    handle,
                    HOR3GLL_LOW_TIME,
                    0,
                    8,
                    comm.BaudRate,
                    &rlen,
                    rbuff,
               TRUE
                    );
                if (status == STATUS_SUCCESS)
                {
                    bBetterBaudRate = TRUE;
                }
            }
        } // endwhile

               // Check if a better baudrate was negotiated
        if (bBetterBaudRate==FALSE)
        {
                //*** Can come back to the previous one
                // return an error message right now.
            GDDK_Oros3CloseComm(handle);
            return (STATUS_INVALID_DEVICE_STATE);
        }
    }

    SmartcardDebug(
        DEBUG_TRACE,
        ("%s!GDDK_0AOpenChannel: Reader speed was set to %d baud rate\n",
        SC_DRIVER_NAME,comm.BaudRate));

    // Sends the SetMode command with parameter 0 to disable TLP compatibility.
    rlen = HOR3GLL_BUFFER_SIZE;
    cmd[0] = (BYTE) HOR3GLL_IFD_CMD_MODE_SET;
    cmd[1] = (BYTE) 0x00;
    cmd[2] = (BYTE) 0x00;
    status = GDDK_Oros3Exchange(
        handle,
        HOR3GLL_LOW_TIME,
        3,
        cmd,
        &rlen,
        rbuff
        );
    if (status != STATUS_SUCCESS) {

        GDDK_Oros3CloseComm(handle);
        return (status);
    }

   // Reader capabilities:
   // - the type of the reader (SCARD_READER_TYPE_SERIAL)
   // - the channel for the reader (PortSerialNumber)
   // - the protocols supported by the reader (SCARD_PROTOCOL_T0, SCARD_PROTOCOL_T1)
   // - the mechanical characteristic of the reader:
    SmartcardExtension->ReaderCapabilities.ReaderType =
        SCARD_READER_TYPE_SERIAL;
    SmartcardExtension->ReaderCapabilities.Channel =
        PortSerialNumber;
    SmartcardExtension->ReaderCapabilities.SupportedProtocols =
        SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1;
    SmartcardExtension->ReaderCapabilities.MechProperties = 0;

    // Reader capabilities (continue):
    // - the default clock frequency (SC_IFD_DEFAULT_CLK_FREQUENCY)
    // - the maximum clock frequency (SC_IFD_MAXIMUM_CLK_FREQUENCY)
    // - the default data rate (SC_IFD_DEFAULT_DATA_RATE)
    // - the maximum data rate (SC_IFD_MAXIMUM_DATA_RATE)
    // - the maximum IFSD (SC_IFD_MAXIMUM_IFSD)
    // - the power management is set to 0.
    SmartcardExtension->ReaderCapabilities.CLKFrequency.Default =
        SC_IFD_DEFAULT_CLK_FREQUENCY;
    SmartcardExtension->ReaderCapabilities.CLKFrequency.Max =
        SC_IFD_MAXIMUM_CLK_FREQUENCY;
    SmartcardExtension->ReaderCapabilities.DataRate.Default =
        SC_IFD_DEFAULT_DATA_RATE;
    SmartcardExtension->ReaderCapabilities.DataRate.Max =
        SC_IFD_MAXIMUM_DATA_RATE;
    SmartcardExtension->ReaderCapabilities.MaxIFSD =
        SC_IFD_MAXIMUM_IFSD;
    SmartcardExtension->ReaderCapabilities.PowerMgmtSupport = 0;

    // Reader capabilities (continue):
    // - List all the supported data rates
    SmartcardExtension->ReaderCapabilities.DataRatesSupported.List =
        dataRatesSupported;
    SmartcardExtension->ReaderCapabilities.DataRatesSupported.Entries =
        sizeof(dataRatesSupported) / sizeof(dataRatesSupported[0]);

    // Vendor Attributes:
    // - the vendor information (SC_VENDOR_NAME)
    strcpy(
        SmartcardExtension->VendorAttr.VendorName.Buffer,
        SC_VENDOR_NAME
        );
    SmartcardExtension->VendorAttr.VendorName.Length =
        (USHORT) strlen(SmartcardExtension->VendorAttr.VendorName.Buffer);

    // Vendor Attributes (continue):
    // - the UnitNo information. Is set to the device number.
    // - the IFD serial number (is set to a NULL string).
    // - the IFD version is set.
    strcpy(SmartcardExtension->VendorAttr.IfdType.Buffer,SC_IFD_TYPE);
    SmartcardExtension->VendorAttr.IfdType.Length =
        (USHORT) strlen(SmartcardExtension->VendorAttr.IfdType.Buffer);
    SmartcardExtension->VendorAttr.UnitNo = DeviceNumber;
    SmartcardExtension->VendorAttr.IfdSerialNo.Length = 0;
    SmartcardExtension->VendorAttr.IfdVersion.VersionMajor = (UCHAR)majorVersion;
    SmartcardExtension->VendorAttr.IfdVersion.VersionMinor = (UCHAR)minorVersion;
    SmartcardExtension->VendorAttr.IfdVersion.BuildNumber = 0;

    // Reader Extension:
    // - the Handle of the reader.
    // - the IFD number of the reader.
    // - the ICCType (ISOCARD).
    // - the ICCVpp (HOR3GLL_DEFAULT_VPP).
    // - the ICCPresence.
    // - the command timeout for the reader (HOR3GLL_DEFAULT_TIME).
    // - the IFD baud rate.
    // - the power timeout (0).
    // - the selected VCC power supply voltage value.
    // - the PTS negotiate mode.
    // - the parameter PTS0.
    // - the parameter PTS1.
    // - the parameter PTS2.
    // - the parameter PTS3.
    SmartcardExtension->ReaderExtension->Handle                 = handle;
    SmartcardExtension->ReaderExtension->APDUTimeOut            = HOR3GLL_APDU_TIMEOUT;
    SmartcardExtension->ReaderExtension->CmdTimeOut             = HOR3GLL_DEFAULT_TIME;
    SmartcardExtension->ReaderExtension->IFDBaudRate            = br;
    SmartcardExtension->ReaderExtension->PowerTimeOut           = ICC_DEFAULT_POWER_TIMOUT;
    SmartcardExtension->ReaderExtension->MaximalBaudRate        = MaximalBaudRate;
    SmartcardExtension->ReaderExtension->IccConfig.ICCType      = ISOCARD;
    SmartcardExtension->ReaderExtension->IccConfig.ICCVpp       = HOR3GLL_DEFAULT_VPP;
    SmartcardExtension->ReaderExtension->IccConfig.ICCVcc       = ICC_VCC_5V;
    SmartcardExtension->ReaderExtension->IccConfig.PTSMode      = IFD_DEFAULT_MODE;
    SmartcardExtension->ReaderExtension->IccConfig.PTS0         = 0;
    SmartcardExtension->ReaderExtension->IccConfig.PTS1         = 0;
    SmartcardExtension->ReaderExtension->IccConfig.PTS2         = 0;
    SmartcardExtension->ReaderExtension->IccConfig.PTS3         = 0;
    SmartcardExtension->ReaderExtension->IccConfig.ICCPresence  = 0x0D;

    // Define the type of the card (ISOCARD) and set the card presence
    rlen = HOR3GLL_BUFFER_SIZE;
    cmd[0] = HOR3GLL_IFD_CMD_ICC_DEFINE_TYPE;
    cmd[1] = (BYTE) SmartcardExtension->ReaderExtension->IccConfig.ICCType;
    cmd[2] = (BYTE) SmartcardExtension->ReaderExtension->IccConfig.ICCVpp;
    cmd[3] = (BYTE) SmartcardExtension->ReaderExtension->IccConfig.ICCPresence ;
    status = GDDK_Oros3Exchange(
        handle,
        HOR3GLL_LOW_TIME,
        4,
        cmd,
        &rlen,
        rbuff
        );
    if (status == STATUS_SUCCESS) {

        status = GDDK_Translate(rbuff[0],0);
    }
    if (status != STATUS_SUCCESS) {

        GDDK_Oros3CloseComm(handle);
        return(status);
    }
    // Update the status of the card
    GDDK_0AUpdateCardStatus(SmartcardExtension);

    return(STATUS_SUCCESS);
}


NTSTATUS
GDDK_0ACloseChannel(
   PSMARTCARD_EXTENSION SmartcardExtension
   )
/*++

Routine Description:

    This routine close a conection previously opened with a reader.

Arguments:

   SmartcardExtension   - is a pointer on the SmartCardExtension structure of
                           the current device.

Return Value:

    STATUS_SUCCESS          - We could execute the request.

--*/
{
    READER_EXTENSION *param = SmartcardExtension->ReaderExtension;
    USHORT rlen;
    BYTE cmd[1],rbuff[HOR3GLL_BUFFER_SIZE];

    // Call power down function:
    rlen = HOR3GLL_BUFFER_SIZE;
    cmd[0] = HOR3GLL_IFD_CMD_ICC_POWER_DOWN;
    return(
        GDDK_Oros3Exchange(
            param->Handle,
            HOR3GLL_LOW_TIME,
            1,
            cmd,
            &rlen,
            rbuff
            )
        );
}



NTSTATUS
GDDK_0AUpdateCardStatus(
   PSMARTCARD_EXTENSION pSmartcardExtension
   )
/*++

Routine Description:

   This function send a command to the reader to known the state of the card.

Arguments:

   SmartcardExtension   - is a pointer on the SmartCardExtension structure of
                           the current device.

Return Value:

    STATUS_SUCCESS          - We could execute the request.

--*/
{
    BYTE cmd[1];
    READER_EXTENSION *param = pSmartcardExtension->ReaderExtension;
    USHORT rlen;
    BYTE rbuff[HOR3GLL_BUFFER_SIZE];
    KIRQL irql;
    NTSTATUS  status;

    // Read the status of the reader
    cmd[0] = HOR3GLL_IFD_CMD_ICC_STATUS;
    rlen = HOR3GLL_BUFFER_SIZE;
    status = GDDK_Oros3Exchange(
        param->Handle,
        HOR3GLL_LOW_TIME,
        (const USHORT)1,
        (const BYTE *)cmd,
        &rlen,
        rbuff
        );
    if (status == STATUS_SUCCESS) {

        status = GDDK_Translate(rbuff[0],0);
    }
    if (status != STATUS_SUCCESS) {

        return status;
    }
    KeAcquireSpinLock(
        &pSmartcardExtension->OsData->SpinLock,
        &irql
        );

    if ((rbuff[1] & 0x04) == 0) {

        // The card is absent
        pSmartcardExtension->ReaderCapabilities.CurrentState =  SCARD_ABSENT;
        pSmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_UNDEFINED;
        pSmartcardExtension->CardCapabilities.ATR.Length = 0;

        SmartcardDebug(
            DEBUG_TRACE,
            ("%s!GDDK_0AUpdateCardStatus: Card removed\n",
            SC_DRIVER_NAME)
            );

    } else if ((rbuff[1] & 0x02) == 0) {

        // The card is present
        pSmartcardExtension->ReaderCapabilities.CurrentState =  SCARD_SWALLOWED;
        pSmartcardExtension->CardCapabilities.Protocol.Selected = SCARD_PROTOCOL_UNDEFINED;
        pSmartcardExtension->CardCapabilities.ATR.Length = 0;

        SmartcardDebug(
            DEBUG_TRACE,
            ("%s!GDDK_0AUpdateCardStatus: Card inserted\n",
            SC_DRIVER_NAME)
            );
   }

    KeReleaseSpinLock(
        &pSmartcardExtension->OsData->SpinLock,
        irql
        );
    return status;
}




VOID
GDDK_0ALockExchange(
    PSMARTCARD_EXTENSION   SmartcardExtension
   )
/*++

Routine Description:

   Wait the release of the ExchangeMutex and take this.

Arguments:

   SmartcardExtension   - is a pointer on the SmartCardExtension structure of
                           the current device.

Return Value:

    STATUS_SUCCESS          - We could execute the request.

--*/
{

    KeWaitForMutexObject(
        &SmartcardExtension->ReaderExtension->LongAPDUMutex,
        Executive,
        KernelMode,
        FALSE,
        NULL
        );
}




VOID
GDDK_0AUnlockExchange(
    PSMARTCARD_EXTENSION   SmartcardExtension
   )
/*++

Routine Description:

   Release of the Exchange mutex.

Arguments:

   SmartcardExtension   - is a pointer on the SmartCardExtension structure of
                           the current device.

--*/
{
    KeReleaseMutex(
        &SmartcardExtension->ReaderExtension->LongAPDUMutex,
        FALSE
        );
}





static VOID
GDDK_0ASetTransparentConfig(
    PSMARTCARD_EXTENSION   SmartcardExtension,
    BYTE                   NewWtx
   )
/*++

Routine Description:

   Set the parameters of the transparent mode.

Arguments:

   SmartcardExtension   - is a pointer on the SmartCardExtension structure of
                           the current device.
   NewWtx               - holds the value (ms) of the new Wtx

--*/
{
    READER_EXTENSION *param = SmartcardExtension->ReaderExtension;
    LONG etu;
    BYTE temp,mask,index;
    USHORT rlen;
    BYTE cmd[6],rbuff[HOR3GLL_BUFFER_SIZE];
    NTSTATUS status;

    // Inverse or direct conversion
    if (SmartcardExtension->CardCapabilities.InversConvention)
        param->TransparentConfig.CFG |= 0x20;
    else
        param->TransparentConfig.CFG &= 0xDF;
    // Transparent T=1 like (with 1 byte for the length).
    param->TransparentConfig.CFG |= 0x08;
    // ETU = ((F[Fi]/D[Di]) - 1) / 3
    etu = SmartcardExtension->CardCapabilities.ClockRateConversion[
        (BYTE) param->TransparentConfig.Fi].F;
    if (SmartcardExtension->CardCapabilities.BitRateAdjustment[
        (BYTE) param->TransparentConfig.Fi].DNumerator) {

        etu /= SmartcardExtension->CardCapabilities.BitRateAdjustment[
            (BYTE) param->TransparentConfig.Fi].DNumerator;
    }
    etu -= 1;
    etu /= 3;
    param->TransparentConfig.ETU = (BYTE) ( 0x000000FF & etu);

    if (SmartcardExtension->CardCapabilities.N == 0xFF) {

        param->TransparentConfig.EGT = (BYTE) 0x00;
    } else {
        param->TransparentConfig.EGT = (BYTE) SmartcardExtension->CardCapabilities.N;
    }

    param->TransparentConfig.CWT = (BYTE) SmartcardExtension->CardCapabilities.T1.CWI;
    if (NewWtx) {

        for (mask = 0x80,index = 8; index !=0x00; index--) {
            temp = NewWtx & mask;
            if (temp == mask)
                break;
            mask = mask/2;
        }
        param->TransparentConfig.BWI = SmartcardExtension->CardCapabilities.T1.BWI + index;
    } else {

        param->TransparentConfig.BWI = SmartcardExtension->CardCapabilities.T1.BWI;
    }
    // Now we send the configuration command
    rlen = HOR3GLL_BUFFER_SIZE;
    cmd[0] = HOR3GLL_IFD_CMD_TRANS_CONFIG;
    cmd[1] = param->TransparentConfig.CFG;
    cmd[2] = param->TransparentConfig.ETU;
    cmd[3] = param->TransparentConfig.EGT;
    cmd[4] = param->TransparentConfig.CWT;
    cmd[5] = param->TransparentConfig.BWI;
    status = GDDK_Oros3Exchange(
        param->Handle,
        HOR3GLL_LOW_TIME,
        6,
        cmd,
        &rlen,
        rbuff
        );
}


NTSTATUS
GDDK_0ARestoreCommunication(
    PSMARTCARD_EXTENSION   SmartcardExtension
   )
/*++

Routine Description:

   Restore the communication with the reader at the good speed.

Arguments:

   SmartcardExtension   - is a pointer on the SmartCardExtension structure of
                           the current device.

--*/
{
    TGTSER_PORT comm;
    BYTE cmd[10],rbuff[HOR3GLL_BUFFER_SIZE];
    USHORT user,rlen;
    ULONG  br;
    SHORT portcom;
    KEVENT event;
    LARGE_INTEGER timeout;
    NTSTATUS status;
    READER_EXTENSION *param = SmartcardExtension->ReaderExtension;

    // Loop while a right response has not been received. We start at 9600
    status = GDDK_GBPChannelToPortComm(param->Handle,&portcom);
    if (status != STATUS_SUCCESS) {

        return status;
    }
    status = GDDK_SerPortGetState(portcom,&comm,&user);

    if (status != STATUS_SUCCESS) {
       return status;
    }

    comm.BaudRate = 9600;
    GDDK_SerPortSetState(portcom,&comm);

    GDDK_0ALockExchange(SmartcardExtension);
    __try {
        do {
            // Wait for HOR3COMM_CHAR_TIME ms before any command for IFD to forget any
            // previous received byte.
            KeInitializeEvent(&event,NotificationEvent,FALSE);
            timeout.QuadPart = -((LONGLONG) HOR3COMM_CHAR_TIME*10*1000);
            KeWaitForSingleObject(
                &event,
                Suspended,
                KernelMode,
                FALSE,
                &timeout
                );
            SmartcardDebug(
                DEBUG_TRACE,
                ("%s!GDDK_0ARestoreCommunication: Try at baud rate = %d\n",
                SC_DRIVER_NAME,
                comm.BaudRate)
                );
            // Define the type of the card (ISOCARD) and set the card presence
            rlen = HOR3GLL_BUFFER_SIZE;
            SmartcardExtension->ReaderExtension->IccConfig.ICCType = ISOCARD;
            cmd[0] = HOR3GLL_IFD_CMD_ICC_DEFINE_TYPE;
            cmd[1] = (BYTE) SmartcardExtension->ReaderExtension->IccConfig.ICCType;
            cmd[2] = (BYTE) SmartcardExtension->ReaderExtension->IccConfig.ICCVpp;
            cmd[3] = (BYTE) SmartcardExtension->ReaderExtension->IccConfig.ICCPresence ;
            status = GDDK_Oros3Exchange(
                param->Handle,
                HOR3GLL_LOW_TIME,
                4,
                cmd,
                &rlen,
                rbuff
                );
            if (status != STATUS_SUCCESS) {

                if (comm.BaudRate == 9600lu) {

                    comm.BaudRate = 38400lu;
                } else if (comm.BaudRate == 38400lu) {
                    comm.BaudRate = 19200lu;
                } else {
                    status = STATUS_INVALID_DEVICE_STATE;
                    __leave;
                }
                // The new baud rate configuration is set.
                GDDK_SerPortSetState(portcom,&comm);
            } else {
                break;
            }
        } while (status != STATUS_SUCCESS);
        SmartcardDebug(
            DEBUG_ERROR,
            ("%s!GDDK_0ARestoreCommunication: Baud rate = %d\n",
            SC_DRIVER_NAME,
            comm.BaudRate)
            );

        // Optimizes the baudrate:
        if(param->MaximalBaudRate > comm.BaudRate) {
            br = comm.BaudRate;
            for(;br < param->MaximalBaudRate;) {
                br = br * 2;
                // The reader is switched to the selected value (GDDK_Oros3SIOConfigure). The
                // function status is not tested because, as the IFD has switched
                // immediatly, it is not possible to read its response.
                SmartcardDebug(
                    DEBUG_TRACE,
                    ("%s!GDDK_0ARestoreCommunication: Optimize baud rate = %d\n",
                    SC_DRIVER_NAME,
                    comm.BaudRate)
                    );
                comm.BaudRate = br;
                rlen = 0;
                GDDK_Oros3SIOConfigure(
                    param->Handle,
                    HOR3GLL_LOW_TIME,
                    0,
                    8,
                    comm.BaudRate,
                    &rlen,
                    rbuff,
               FALSE
                    );
            //
            // Waits for the fisrt command is process by the reader
            // 500 ms is enought
            //
            KeInitializeEvent(&event,NotificationEvent,FALSE);
            timeout.QuadPart = -((LONGLONG)  500 * 1000);
            KeWaitForSingleObject(&event,
               Suspended,
               KernelMode,
               FALSE,
               &timeout);

                // Host is switched to the selected value (GDDK_SerPortSetState).
                // If this call is successful,
                // Then
                //   The last command is re-sent to read the IFD response.
                //   response is optionnaly initialized with the translated IFD status.
                status = GDDK_SerPortSetState(portcom,&comm);
                SmartcardDebug(
                    DEBUG_TRACE,
                    ("%s!GDDK_0ARestoreCommunication: Set baud rate = %d\n",
                    SC_DRIVER_NAME,
                    comm.BaudRate)
                    );

                if (status == STATUS_SUCCESS) {
                    rlen = HOR3GLL_BUFFER_SIZE;
                    status = GDDK_Oros3SIOConfigure(
                        param->Handle,
                        HOR3GLL_LOW_TIME,
                        0,
                        8,
                        comm.BaudRate,
                        &rlen,
                        rbuff,
                  TRUE
                        );
                    if (status == STATUS_SUCCESS) {

                        status = GDDK_Translate(rbuff[0],0);
                    }
                    if (status != STATUS_SUCCESS) {

                        break;
                    }
                }
            }
            if ((br > 38400) || (status != STATUS_SUCCESS)) {

                status = STATUS_INVALID_DEVICE_STATE;
                __leave;
            }
        }
        SmartcardDebug(
            DEBUG_TRACE,
            ("%s!GDDK_0ARestoreCommunication: Current baud rate = %d\n",
            SC_DRIVER_NAME,
            comm.BaudRate)
            );

        // Send the SetMode command with parameter 0 to disable TLP compatibility.
        rlen = HOR3GLL_BUFFER_SIZE;
        cmd[0] = (BYTE) HOR3GLL_IFD_CMD_MODE_SET;
        cmd[1] = (BYTE) 0x00;
        cmd[2] = (BYTE) 0x00;
        status = GDDK_Oros3Exchange(
            param->Handle,
            HOR3GLL_LOW_TIME,
            3,
            cmd,
            &rlen,
            rbuff
            );
        if (status != STATUS_SUCCESS) {

            __leave;
        }
        // Update the status of the card
        GDDK_0AUpdateCardStatus(SmartcardExtension);
    }
    __finally {
        if (status != STATUS_SUCCESS) {

            SmartcardDebug(
                DEBUG_ERROR,
                ("%s!GDDK_0ARestoreCommunication: Failed\n",
                SC_DRIVER_NAME,
                comm.BaudRate)
                );
        }
    }
    GDDK_0AUnlockExchange(SmartcardExtension);
    SmartcardDebug(
        DEBUG_TRACE,
        ("%s!GDDK_0ARestoreCommunication: Exit=%X(hex)\n",
        SC_DRIVER_NAME,
        status)
        );
    return status;
}