//****************************************************************************
//                             File NTDEMOCL.C
//
//                  Windows NT kernel mode sample client
//
//                    Copyright (c) APSoft., 1993-2000.
//                          All rights reserved.
//
//****************************************************************************



#include <ntddk.h>                          // Windows NT DDK main include file
#include <cs_sdk.h>                         // Include CS definitions

#define NT_DEVICE_NAME      L"\\Device\\NTDEMOCL"
#define DOS_DEVICE_NAME     L"\\DosDevices\\CW_CLIENT"

CS_HND ClientHandle = 0;                    // Client handle



//****************************************************************************
//                        --- CallBackFunction ---
//
// Purpose: Callback function to be provided by RegisterClient. Print number
//          of callback function in the Kernel Debugger and returns SUCCESS
//          to caller.
//   Input: CB_REGS  * Parameters - Pointer to callback register structure.
//  Output: none
// Written: by Timo Geusch 2/25/97
//****************************************************************************
void __cdecl CallBackFunction(CB_REGS *Parameters)
{
    if (Parameters != NULL)                 // Validate pointer
    {
        switch (Parameters->wStatus)
        {
            // Only MTD drivers need to answer MTD request. This driver should
            // never receive such request and therefore case below is added only
            // for security.

            case MTD_REQUEST:
                Parameters->wStatus = BAD_FUNCTION;
                break;

            // Add events to process there...


            // Please return status SUCCESS for all unprocessed events
            default:
                DbgPrint("NTDEMOCL callback function called with function code %u\n",
                        Parameters->wFunction);
                Parameters->wStatus = SUCCESS;
                break;
        }                                   //  switch()...
    }
}


//****************************************************************************
//                         --- DoCardServices ---
//
// Purpose: Call Windows NT Card Services driver. Provides the same call inter-
//          face as the DOS/Win16 function does.
//   Input: enum CS_SUBFUNC   SubFunc - CS function to call; see file cs_sdk.h
//          CS_HND          * pHandle - Pointer to client handle storage
//          void            * * fp    - Pointer to Misc pointer storage
//          WORD              wArgLen - Argument length
//          void            * pArg    - Pointer to Argument buffer
//  Output: enum RETCODE              - Return code
// Written: by Timo Geusch 2/25/97
//****************************************************************************
enum RETCODE DoCardServices(enum CS_SUBFUNC SubFunc, WORD *pHandle,
                            void * * fp, WORD wArgLen, void * pArg)
{
    enum RETCODE   retcode = GENERAL_FAILURE;
                                            // Assumes error

    PDEVICE_OBJECT pCS        = 0;          // Pointer to CS device object
    PFILE_OBJECT   pFileObjCS = 0;          // Pointer to CS file   object

    DWORD          dwParsSize;              // Size of IOCTL parameters block
    NT_CSPARS     *pCSParsIn;               // Pointer to IOCTL parameters block
    NT_CSPARS     *pCSParsOut;              // Pointer to IOCTL parameters block

    UNICODE_STRING uszCSDeviceName;         // Unicode name of CS Device

//-------------------------- Attach to CS driver -----------------------------

    // Note: In real case you will attach to CS in DriverEntry routine and
    //       the code below will be moved there

    RtlInitUnicodeString(&uszCSDeviceName, L"\\Device\\" CS_DEVICE_NAME);

    if (IoGetDeviceObjectPointer(&uszCSDeviceName, FILE_ANY_ACCESS,
                                    &pFileObjCS, &pCS) != STATUS_SUCCESS)
    {
        pCS        = 0;                     // Just for be sure
        return retcode;                     // Return error code
    }

//-------------------- Allocate communication buffers ------------------------

    dwParsSize = sizeof(NT_CSPARS) +wArgLen;// Size of parameters for the
                                            // request

    pCSParsIn  = ExAllocatePool(NonPagedPool, dwParsSize);
    pCSParsOut = ExAllocatePool(NonPagedPool, dwParsSize);

//----------- Copy request parameters to communication buffer ----------------

    if (pCSParsIn && pCSParsOut)
    {
        RtlZeroMemory(pCSParsIn, dwParsSize);
        pCSParsIn->SubFunc           = SubFunc;

        if (pHandle != NULL)                // Security checking
            pCSParsIn->Handle        = *pHandle;

        if (fp != NULL)                     // Security checking
            pCSParsIn->fp            = *fp;

        pCSParsIn->dwArgBufferLength = wArgLen;

        if (wArgLen && pArg != NULL)        // Security checking
            RtlCopyMemory(&pCSParsIn->ArgBuffer[0], pArg, wArgLen);

//------------------------- Build IRP for CS call ----------------------------

        if (pCS != 0)
        {
            PIRP            pIrp     = 0;   // Pointer to Request Structure
            IO_STATUS_BLOCK IOStatus;       // Status block
            KEVENT          Event;          // Event Object

            KeInitializeEvent(&Event, NotificationEvent, FALSE );
                                            // Initialize Event Object
            pIrp = IoBuildDeviceIoControlRequest(
                    (ULONG)IOCTL_PCMCIA_CardServices,
                    pCS,                    // Command to process
                    pCSParsIn,              // Input parameter buffer
                    dwParsSize,             // Size of data transferred
                    pCSParsOut,             // Output parameter buffer
                    dwParsSize,             // Size of return buffer
                    FALSE,                  // No overlapped I/O
                    &Event,                 //
                    &IOStatus);             //

//-------------------------- Call Card Services ------------------------------

            if (pIrp != NULL)               // IRP was allocated successfully
            {                               // Execute CS request
                // Note: IoCallDriver not necessary returns STATUS_SUCCESS.
                // However NT_SUCCESS() returns TRUE, if value is correct.
                if (NT_SUCCESS(IoCallDriver(pCS, pIrp)))
                    retcode = pCSParsOut->retcode;
            }
        }

//----------- Copy request parameters from communication buffer --------------

        if(retcode == SUCCESS)
        {
            if (pHandle != NULL)            // Security checking
                *pHandle = (WORD)pCSParsOut->Handle;

            if (fp != NULL)                 // Security checking
                *fp      = pCSParsOut->fp;

            if (wArgLen && pArg != NULL)    // Security checking
                RtlCopyMemory(pArg, &pCSParsOut->ArgBuffer[0], wArgLen);
        }
    }

//-------------------- Free used communication buffers -----------------------

    if (pCSParsIn)
        ExFreePool(pCSParsIn);

    if (pCSParsOut)
        ExFreePool(pCSParsOut);

//----------------------- Detach from Card Services --------------------------

    // Note: In real case you will dettach from CS in DriverUnload routine and
    //       the code below will be moved there

    if (pCS != 0 && pFileObjCS != 0)
    {
        ObDereferenceObject(pFileObjCS);
    }
    return retcode;                         // Status returned
}


//****************************************************************************
//                          --- DemoOpenClose ---
//
// Purpose: This standard NT routine called by OS in response to Open/Close
//          requests. This code is copied from NT DDK sample of generic driver
//   Input: IN PDEVICE_OBJECT DeviceObject - Pointer to device object
//          IN PIRP           Irp          - Pointer to IRP packet
//  Output: NTSTATUS                       - Always STATUS_SUCCESS
//****************************************************************************
NTSTATUS DemoOpenClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
    //
    // No need to do anything.
    //

    //
    // Fill these in before calling IoCompleteRequest.
    //
    // DON'T get cute and try to use the status field of
    // the irp in the return status.  That IRP IS GONE as
    // soon as you call IoCompleteRequest.
    //

    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;

    IoCompleteRequest( Irp, IO_NO_INCREMENT );

    return STATUS_SUCCESS;
}


//****************************************************************************
//                           --- DemoUnload ---
//
// Purpose: This code is copied from NT DDK sample of generic driver. NT call
//          this routine during driver unload
//   Input: IN PDRIVER_OBJECT DriverObject - Pointer to the driver object
//  Output: none
//    Note: Typical CS aware driver will deregister client and detach from CS
//          in this routine. This sample just deregister the client. Please
//          refer to DoCardServices() function above for code which detach from
//          CS
//****************************************************************************
VOID DemoUnload(IN PDRIVER_OBJECT DriverObject)
{
    void * Argument = NULL;

    UNICODE_STRING uniWin32NameString;

//------------------------- Deregister the client ----------------------------

    if (DoCardServices(CS_DEREGCLIENT, &ClientHandle, &Argument, 0, NULL)
                                                                    == SUCCESS)
        DbgPrint("NTDEMOCL: CS Client successfully deregistered\n");
    else
        DbgPrint("NTDEMOCL: Unable to deregister CS Client. "
                 "You probably did not release all client resources\n");

//--------------------------- End of Award code ------------------------------

    //
    // Create counted string version of our Win32 device name.
    //

    RtlInitUnicodeString( &uniWin32NameString, DOS_DEVICE_NAME);

    //
    // Delete the link from our device name to a name in the Win32 namespace.
    //

    IoDeleteSymbolicLink( &uniWin32NameString );

    //
    // Finally delete our device object
    //

    IoDeleteDevice( DriverObject->DeviceObject );
}


//****************************************************************************
//                           --- DriverEntry ---
//
// Purpose: This code is copied from NT DDK sample of generic driver. NT call
//          this routine during driver load
//   Input: IN PDRIVER_OBJECT  DriverObject - Pointer to the driver object
//          IN PUNICODE_STRING RegistryPath - Pointer to registry path
//  Output: NTSTATUS                        - NT status code
// Written: by Alexei A. Piatetsky 24-07-97
//****************************************************************************
NTSTATUS DriverEntry(IN PDRIVER_OBJECT  DriverObject,
                     IN PUNICODE_STRING RegistryPath)
{
//------------------------ Award added code begin ----------------------------

    RClARGS        RegisterClient;          // Card Services call structure
    CSINFO         CSInfo;                  // Card Services call structure
    void  *        pCallBackFunction = CallBackFunction;
                                            // Card Services callback routine
    void  *        fp = NULL;               // Card Services call dummy argument
    CS_HND         h  = 0;                  // Card Services call dummy argument

//------------------------- Award added code end -----------------------------

    PDEVICE_OBJECT deviceObject = NULL;
    NTSTATUS status;
    UNICODE_STRING uniNtNameString;
    UNICODE_STRING uniWin32NameString;
    //
    // Create counted string version of our device name.
    //

    RtlInitUnicodeString( &uniNtNameString, NT_DEVICE_NAME );

    //
    // Create the device object
    //

    status = IoCreateDevice(
                 DriverObject,
                 0,                     // We don't use a device extension
                 &uniNtNameString,
                 FILE_DEVICE_UNKNOWN,
                 0,                     // No standard device characteristics
                 FALSE,                 // This isn't an exclusive device
                 &deviceObject
                 );

    if ( NT_SUCCESS(status) )
    {

        //
        // Create dispatch points for create/open, close, unload.
        //

        DriverObject->MajorFunction[IRP_MJ_CREATE] = DemoOpenClose;
        DriverObject->MajorFunction[IRP_MJ_CLOSE]  = DemoOpenClose;
        DriverObject->DriverUnload                 = DemoUnload;

        //
        // Create counted string version of our Win32 device name.
        //

        RtlInitUnicodeString( &uniWin32NameString, DOS_DEVICE_NAME );

        //
        // Create a link from our device name to a name in the Win32 namespace.
        //

        status = IoCreateSymbolicLink( &uniWin32NameString, &uniNtNameString );

        if (!NT_SUCCESS(status))
        {
            IoDeleteDevice( DriverObject->DeviceObject );
        }
        else
        {
//////////////////////////////////////////////////////////////////////////////
//------------------------ Award added code begin ----------------------------
//////////////////////////////////////////////////////////////////////////////

//----------------------- Check for CS installation --------------------------

            RtlZeroMemory(&CSInfo, sizeof(CSINFO));
            if (DoCardServices(CS_GETCSINFO, &h, &fp, sizeof(CSINFO), &CSInfo)
                                                                    == SUCCESS)
            {
                // Check for valid CS signature

                if (CSInfo.sigCS == 'SC')
                {
                    DbgPrint("NTDEMOCL: Detect Card Services driver\n");
                    DbgPrint("          Driver revision:      %X.%02X\n",
                             CSInfo.wRevision / 0x100, CSInfo.wRevision % 0x100);
                    DbgPrint("          Compliance level:     %X.%02X\n",
                             CSInfo.wRelease  / 0x100, CSInfo.wRelease  % 0x100);
                    DbgPrint("          Installed Sockets:    %u\n",
                             CSInfo.wNumSockets);
                    DbgPrint("          Max functions/socket: %u\n",
                             CSInfo.wFuncsPerSkt);
                    DbgPrint("          Card Services Vendor: %s\n",
                             CSInfo.szVendor);

//-------------------------- Register CS client ------------------------------

                    // Note: This code register I/O client which receives
                    //       notifications for all events. Please modify client
                    //       attributes and event mask below in order to match
                    //       your requirements

                    RegisterClient.wAttr       = 0x1C;
                                            // Register I/O client, wants to be
                                            // notified of ALL card insertions
                    RegisterClient.wEventMask  = 0x0FFF;
                                            // Notify of all events
                    RegisterClient.dwDataOff   = 0;  // No pointer to data
                    RegisterClient.wClientData = 0;
                    RegisterClient.wCSLevel    = 0x0500;
                                            // Only works with CS level 5.00
                                            // and above

                    if (DoCardServices(CS_REGCLIENT,     &ClientHandle,
                                      &pCallBackFunction, sizeof(RClARGS),
                                      &RegisterClient) == SUCCESS)
                        DbgPrint("NTDEMOCL: CS Client registered. "
                                 "Client handle = %u\n", ClientHandle);
                    else
                        DbgPrint("NTDEMOCL: Unable to register CS client\n");
                }
                else
                    DbgPrint("NTDEMOCL: Invalid Card Services Info\n");
            }
            else
                DbgPrint("NTDEMOCL: Fail to retrieve Card Services Info\n");

        }
    }
    return status;
}