#include "Driver.h"
// Предварительные объявления функций:
NTSTATUS DeviceControlRoutine( IN PDEVICE_OBJECT fdo, IN PIRP Irp );
VOID     UnloadRoutine(IN PDRIVER_OBJECT DriverObject);
NTSTATUS ReadWrite_IRPhandler( IN PDEVICE_OBJECT fdo, IN PIRP Irp );
NTSTATUS Create_File_IRPprocessing(IN PDEVICE_OBJECT fdo, IN PIRP Irp);
NTSTATUS Close_HandleIRPprocessing(IN PDEVICE_OBJECT fdo, IN PIRP Irp);
extern "C"
NTSTATUS Close_HandleIRPprocessing(IN PDEVICE_OBJECT fdo,IN PIRP Irp) 
{
#if DBG
DbgPrint("-Primer- In Close handler."); 
#endif
return CompleteIrp(Irp,STATUS_SUCCESS,0);
}
// Хотя и нехорошо делать глобальные переменные в драйвере...
KSPIN_LOCK MySpinLock;
#pragma code_seg("INIT") // начало секции INIT
extern "C"
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, //об'єкт драйвера, містить покажчики на всі необхідні операційній системі функції, які ми повинні будемо ініціювати.
                      IN PUNICODE_STRING RegistryPath  ) //ім'я розділу в реєстрі, де зберігається інформація про даний драйвер.
{
    NTSTATUS status = STATUS_SUCCESS;
    PDEVICE_OBJECT  fdo;
    UNICODE_STRING  devName;
    #if DBG
    DbgPrint("=Example= In DriverEntry.");
    DbgPrint("=Example= RegistryPath = %ws.", RegistryPath->Buffer);
    #endif
    
    
    // DriverObject->DriverExtension->AddDevice= OurAddDeviceRoutine;
    DriverObject->DriverUnload = UnloadRoutine;
    DriverObject->MajorFunction[IRP_MJ_CREATE]= Create_File_IRPprocessing;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = Close_HandleIRPprocessing;
    DriverObject->MajorFunction[IRP_MJ_READ]  = ReadWrite_IRPhandler;
    DriverObject->MajorFunction[IRP_MJ_WRITE] = ReadWrite_IRPhandler;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]= DeviceControlRoutine;
    //========================================================
    
    RtlInitUnicodeString( &devName, L"\\Device\\EXAMPLE" );
    // Создаем наш Functional Device Object (FDO) и получаем
    // указатель на созданный FDO в нашей переменной fdo.    
    
    status = IoCreateDevice(DriverObject,     
                            sizeof(EXAMPLE_DEVICE_EXTENSION),
                            &devName, 
                            FILE_DEVICE_UNKNOWN, 
                            0,
                            FALSE, 
                            &fdo);
    if(!NT_SUCCESS(status)) return status;
    
    PEXAMPLE_DEVICE_EXTENSION dx = (PEXAMPLE_DEVICE_EXTENSION)fdo->DeviceExtension;
    dx->fdo = fdo;  
    
    #if DBG
    DbgPrint("=Example= FDO %X, DevExt=%X.",fdo,dx);
    #endif
    //=======================================
    
    UNICODE_STRING symLinkName;   
    #define    SYM_LINK_NAME    L"\\DosDevices\\Example"
    RtlInitUnicodeString(      
                          &symLinkName, 
                          SYM_LINK_NAME ); 
    dx->ustrSymLinkName = symLinkName;
    
    
    
    status = IoCreateSymbolicLink( &symLinkName, &devName );
    if (!NT_SUCCESS(status))
    { // при неудаче √ удалить Device Object и вернуть управление
        IoDeleteDevice( fdo );
        return status;
        } 
        KeInitializeSpinLock(&MySpinLock);
      
        #if DBG
        DbgPrint("=Example= DriverEntry successfully completed.");
        #endif
        return status;
}
#pragma code_seg() // end INIT section
extern "C"
NTSTATUS CompleteIrp( PIRP Irp, NTSTATUS status, ULONG info)
{
    Irp->IoStatus.Status = status;
    Irp->IoStatus.Information = info;//Параметр info, если он не равен нулю, чаще всего содержит число байт, переданных клиенту (полученных от клиента) драйвера
    IoCompleteRequest(Irp,IO_NO_INCREMENT);
    return status;
}
extern "C"
NTSTATUS ReadWrite_IRPhandler( IN PDEVICE_OBJECT fdo, IN PIRP Irp )
{
    ULONG BytesTxd = 0;
    NTSTATUS status = STATUS_SUCCESS; //Завершение с кодом status
    // Задаем печать отладочных сообщений √ если сборка отладочная
    #if DBG
    DbgPrint("-Example- in ReadWrite_IRPhandler.");
    #endif
    return CompleteIrp(Irp,status,BytesTxd);
}
extern "C"
NTSTATUS Create_File_IRPprocessing(IN PDEVICE_OBJECT fdo,IN PIRP Irp)
{
    PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
    // Задаем печать отладочных сообщений - если сборка отладочная
    #if DBG
    DbgPrint("-Example- Create File is %ws",
        &(IrpStack->FileObject->FileName.Buffer));
    #endif
return CompleteIrp(Irp,STATUS_SUCCESS,0); // Успешное завершение
}
extern "C"
NTSTATUS DeviceControlRoutine( IN PDEVICE_OBJECT fdo, IN PIRP Irp )
{
    NTSTATUS status = STATUS_SUCCESS;
    ULONG BytesTxd =0; 
    PIO_STACK_LOCATION IrpStack=IoGetCurrentIrpStackLocation(Irp);
    PEXAMPLE_DEVICE_EXTENSION dx =
                 (PEXAMPLE_DEVICE_EXTENSION)fdo->DeviceExtension;
    //-------------------------------
    
    ULONG ControlCode =
    IrpStack->Parameters.DeviceIoControl.IoControlCode;
    ULONG method = ControlCode & 0x03;
    KIRQL irql,
    currentIrql = KeGetCurrentIrql();
    #if DBG
    DbgPrint("-Example- In DeviceControlRoutine (fdo= %X)\n",fdo);
    DbgPrint("-Example- DeviceIoControl: IOCTL %x.", ControlCode );
    if(currentIrql==PASSIVE_LEVEL)
        DbgPrint("-Example- PASSIVE_LEVEL (val=%d)",currentIrql);
    #endif
     
    KeAcquireSpinLock(&MySpinLock,&irql);
     
    switch( ControlCode) {
    #ifndef SMALL_VERSION
    case IOCTL_PRINT_DEBUG_MESS:
    {      
        #if DBG
        DbgPrint("-Example- IOCTL_PRINT_DEBUG_MESS.");
        #endif
        break;
    }
    case IOCTL_CHANGE_IRQL:
    {
        #if DBG
         
         
        DbgPrint("-Example- IOCTL_CHANGE_IRQL.");
        KIRQL dl = DISPATCH_LEVEL,  
        oldIrql,
        newIrql=25;  
         
        KeRaiseIrql(newIrql,&oldIrql);
        newIrql=KeGetCurrentIrql();  
        DbgPrint("-Example- DISPATCH_LEVEL value =%d",dl);
        DbgPrint("-Example- IRQLs are old=%d new=%d",
                        oldIrql,newIrql);
        KeLowerIrql(oldIrql);  
        #endif
        break;
    }
    #endif  
    case IOCTL_MAKE_SYSTEM_CRASH:
    {
        int errDetected=0;
        char x = (char)0xFF;
        #if    DBG   
        DbgPrint("-Example- IOCTL_MAKE_SYSTEM_CRASH.");
        #endif
         
        __try {
        x = *(char*)0x0L;  
             
        }
        __except(EXCEPTION_EXECUTE_HANDLER)
        {    
             
             
            errDetected=1;
        };
        #if DBG
        DbgPrint("-Example- Value of x is %X.",x);
        if(errDetected)
            DbgPrint("-Example- Except detected in Example driver.");
        #endif
        break;
    }
    #ifndef SMALL_VERSION
    case IOCTL_TOUCH_PORT_378H:
    {    
        unsigned short ECRegister = 0x378+0x402;
        #if DBG
        DbgPrint("-Example- IOCTL_TOUCH_PORT_378H.");
        #endif
         
         
         
        _asm  {
            mov dx,ECRegister ;
            xor al,al    ;
            out dx,al    ;    Установить EPP mode 000
            mov al,095h  ;    Биты 7:5 = 100
            out dx,al    ;    Установить EPP mode 100
        }
           
        //  
        break;
    }
    case IOCTL_SEND_BYTE_TO_USER:
    {    
         
        ULONG InputLength =  
            IrpStack->Parameters.DeviceIoControl.InputBufferLength;
         
        ULONG OutputLength =
        IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
        #if DBG
        DbgPrint("-Example- Buffer outlength %d",OutputLength);
        #endif
        if( OutputLength<1 )
        { 
            status = STATUS_INVALID_PARAMETER;
            break;
        }
        UCHAR *buff;  
        if(method==METHOD_BUFFERED)
        {
            buff = (PUCHAR)Irp->AssociatedIrp.SystemBuffer;
            #if DBG
            DbgPrint("-Example- Method : BUFFERED.");
            #endif
        }
        else
            if (method==METHOD_NEITHER)
            {
                buff=(unsigned char*)Irp->UserBuffer;
                #if DBG
                DbgPrint("-Example- Method : NEITHER.");
                #endif
            }
            else
            {
                #if DBG
                DbgPrint("-Example- Method : unsupported.");
                #endif
                status = STATUS_INVALID_DEVICE_REQUEST;
                break;
            }
        #if DBG
        DbgPrint("-Example- Buffer address is %08X",buff);
        #endif
        *buff=33;      
        BytesTxd = 1;  
        break;
    }
    #endif  
     
    default: status = STATUS_INVALID_DEVICE_REQUEST;
    }
     
    KeReleaseSpinLock(&MySpinLock,irql);
    #if DBG
    DbgPrint("-Example- DeviceIoControl: %d bytes written.", (int)BytesTxd);
    #endif
return CompleteIrp(Irp,status,BytesTxd);  
}
 
#pragma code_seg("PAGE")
 
extern "C"
VOID UnloadRoutine(IN PDRIVER_OBJECT pDriverObject)
{
    PDEVICE_OBJECT    pNextDevObj;
    int i;
     
    #if DBG
    DbgPrint("-Example- In Unload Routine.");
    #endif
    //==========================================================
     
    pNextDevObj = pDriverObject->DeviceObject;
    for(i=0; pNextDevObj!=NULL; i++)
    {
        PEXAMPLE_DEVICE_EXTENSION dx =
                (PEXAMPLE_DEVICE_EXTENSION)pNextDevObj->DeviceExtension;
         
        UNICODE_STRING *pLinkName = & (dx->ustrSymLinkName);
          
        pNextDevObj = pNextDevObj->NextDevice;
        #if DBG
        DbgPrint("-Example- Deleted device (%d) : pointer to FDO = %X.",
                            i,dx->fdo);
        DbgPrint("-Example- Deleted symlink = %ws.", pLinkName->Buffer);
        #endif
        IoDeleteSymbolicLink(pLinkName);
        IoDeleteDevice( dx->fdo);
    }
}
#pragma code_seg()