#include <ntddk.h>
#include <string.h>

#define IOCTL_VIDEO_MAP_VIDEO_MEMORY \
    CTL_CODE(FILE_DEVICE_VIDEO, 0x116, METHOD_BUFFERED, FILE_ANY_ACCESS)

#define WINAPI __stdcall

typedef unsigned long DWORD;
typedef DWORD *PDWORD;
typedef unsigned short WORD;
typedef WORD *PWORD;
typedef unsigned char BYTE;
typedef BYTE *PBYTE;

typedef struct _VIDEO_MEMORY {
    PVOID  RequestedVirtualAddress;
} VIDEO_MEMORY, *PVIDEO_MEMORY;

typedef struct _VIDEO_MEMORY_INFORMATION {
    PVOID  VideoRamBase;
    ULONG  VideoRamLength;
    PVOID  FrameBufferBase;
    ULONG  FrameBufferLength;
} VIDEO_MEMORY_INFORMATION, *PVIDEO_MEMORY_INFORMATION;

extern POBJECT_TYPE IoDriverObjectType; 

extern NTKERNELAPI NTSTATUS ObReferenceObjectByName(  
    IN PUNICODE_STRING ObjectPath,  
    IN ULONG Attributes, //OBJ_CASE_INSENSITIVE 
    IN PACCESS_STATE PassedAccessState OPTIONAL,  
    IN ACCESS_MASK DesiredAccess OPTIONAL, //KernelMode 
    IN POBJECT_TYPE ObjectType OPTIONAL, 
    IN KPROCESSOR_MODE AccessMode,  
    IN OUT PVOID ParseContext OPTIONAL,  
    OUT PVOID *ObjectPtr  
);

NTSTATUS framebuf(PCWSTR name);
VOID OnUnload(IN PDRIVER_OBJECT pDriverObject);
NTSTATUS OnStubDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);

NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING RegistryPath) {
    NTSTATUS   	Status = 0;
    PCWSTR	drvName = L"\\Driver\\vmx_svga"; // nom a changer
    int 	i;
	
    pDriverObject->DriverUnload = OnUnload;

    for(i=0; i<IRP_MJ_MAXIMUM_FUNCTION; i++) pDriverObject->MajorFunction[i] = OnStubDispatch;

    framebuf(drvName); 
	
    return Status;
}

VOID OnUnload(IN PDRIVER_OBJECT pDriverObject) {
    DbgPrint("Unload...");
}

NTSTATUS OnStubDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
    Irp->IoStatus.Status = STATUS_SUCCESS;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}

NTSTATUS framebuf(PCWSTR name) {
    NTSTATUS Status = 0;
    UNICODE_STRING drvName;
    PDRIVER_OBJECT DriverObject;
    PDEVICE_OBJECT DeviceObject;
    PIRP Irp;
    KEVENT Event;
    IO_STATUS_BLOCK IoStatus;
    VIDEO_MEMORY vidMem;
    VIDEO_MEMORY_INFORMATION vidMemInfo;

    RtlZeroMemory((PVOID)&vidMemInfo, sizeof(VIDEO_MEMORY_INFORMATION));
    vidMem.RequestedVirtualAddress = 0;
    RtlInitUnicodeString(&drvName, name);

    Status = ObReferenceObjectByName(&drvName, OBJ_CASE_INSENSITIVE, NULL, 0, IoDriverObjectType, KernelMode, NULL, &DriverObject);
    if(Status != STATUS_SUCCESS) {
	DbgPrint("Le nom du driver doit être changé.\n");
	return Status;
    }

    DeviceObject = DriverObject->DeviceObject;
    while(DeviceObject->NextDevice != NULL) DeviceObject = DeviceObject->NextDevice;

    KeInitializeEvent(&Event, NotificationEvent, FALSE);
    Irp = IoBuildDeviceIoControlRequest(
        IOCTL_VIDEO_MAP_VIDEO_MEMORY,
        DeviceObject,
        &vidMem,
        sizeof(VIDEO_MEMORY),
        &vidMemInfo,
        sizeof(VIDEO_MEMORY_INFORMATION),
        FALSE,
        &Event,
        &IoStatus);
	
    if (Irp == NULL) {
	DbgPrint("Can't create Irp");
	return STATUS_INSUFFICIENT_RESOURCES;
    }

    Status = IoCallDriver(DeviceObject, Irp);

    if (Status == STATUS_PENDING) {
	KeWaitForSingleObject(
	    &Event,
            Executive,
            KernelMode,
            FALSE,
            NULL);
	Status = IoStatus.Status;
    }

    if (!NT_SUCCESS(Status)) {
	DbgPrint("IOCTL failed");
	return Status;
    } else {
	DbgPrint("! Virtual adress !\n");
	DbgPrint("video RAM:\n\t0x%x size: %ld\n",vidMemInfo.VideoRamBase, vidMemInfo.VideoRamLength);
	DbgPrint("Frame buffer:\n\t0x%x size: %ld\n",vidMemInfo.FrameBufferBase, vidMemInfo.FrameBufferLength);
    }
    return STATUS_SUCCESS;
}