
/**
 * @file: driver.c
 *
 * Hlavni soubor ovladace intcount.sys. Obsahuje rutiny, ktere jadro
 * systemu vola tesne po zavedeni do pameti a tesne pred uvolnenim.
 * Dale zajistuje obsluhu pozadavku od okoli (hlavne mineno pro aplikace
 * bezici v uzivatelskem rezimu).
 */

#include <ntddk.h>
#include "ioctls.h"
#include "sync.h"
#include "idt.h"
#include "intcount.h"
#include "driver.h"

VOID DriverUnload(PDRIVER_OBJECT DriverObject);



/** Obsluhuje pozdavky IRP_MJ_CREATE a IRP_MJ_CLOSE.
 *
 * Rutina pozadavek vzdy uspesne obslouzi.
 */
NTSTATUS DriverCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
  NTSTATUS status = STATUS_UNSUCCESSFUL;
  KdPrint(("driver.c: DriverCreateClose(DeviceObject=0x%p; Irp=0x%p)\n", DeviceObject, Irp));

  IoCompleteRequest(Irp, IO_NO_INCREMENT);
  status = STATUS_SUCCESS;

  KdPrint(("driver.c: DriverCreateClose(-):0x%x\n", status));
  return status;
}


/** Obsluhuje pozadavky typu IRP_MJ_DEVICE_CONTROL.
 *
 * Ovladac podporuje pouze pozadavek IOCTL_INTCOUNT_GET_COUNTERS, ktery
 * aplikace ziska aktualni pocty vyskytu jednotlivych preruseni.
 */
NTSTATUS DriverDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
  PIO_STACK_LOCATION IrpStack;
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  ULONG OutBufferLength = 0;
  PVOID OutBuffer = NULL;
  ULONG ControlCode = 0;
  PULONG CounterArray = NULL;
  ULONG CounterArrayLength = 0;
  KdPrint(("driver.c: DriverDeviceControl(DeviceObject=0x%p; Irp=0x%p)\n", DeviceObject, Irp));

  Irp->IoStatus.Information = 0;
  Irp->IoStatus.Status = STATUS_SUCCESS;
  IrpStack = IoGetCurrentIrpStackLocation(Irp);
  OutBufferLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
  OutBuffer = Irp->AssociatedIrp.SystemBuffer;
  ControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
  switch (ControlCode) {
    case IOCTL_INTCOUNT_GET_COUNTERS:
      CounterArray = IntCountGetCounterArray();
      CounterArrayLength = IntCountGetCounterArrayLength();
      if (OutBufferLength >= CounterArrayLength) {
        RtlCopyMemory(OutBuffer, CounterArray, CounterArrayLength);
        Irp->IoStatus.Information = CounterArrayLength;
        Status = STATUS_SUCCESS;
      } else Status = STATUS_BUFFER_TOO_SMALL;
      break;

    default:
      Status = STATUS_NOT_IMPLEMENTED;
      break;
  }

  Irp->IoStatus.Status= Status;
  IoCompleteRequest(Irp,IO_NO_INCREMENT);

  KdPrint(("driver.c: DriverDeviceControl(-):0x%x\n", Status));
  return Status;
}


/** Inicializuje cast ovladace zodpovednou za komunikaci s okolim.
 *
 * Rutina nastavi adresu procedury DriverUnload a vytvori komunikacni
 * zarizeni a symbolicky odkaz na nej.
 *
 * @param DriverObject Adresa struktury DRIVER_OBJECT, ktera reprezentuje
 * ovladac intcount.sys.
 *
 * @return Funkce vraci hodnotu NTSTATUS indikujici uspech ci neuspech
 * operace.
 */
NTSTATUS DriverInit(PDRIVER_OBJECT DriverObject)
{
  UNICODE_STRING uDeviceName;
  UNICODE_STRING uSymLinkName;
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("driver.c: DriverInit(DriverObject=0x%p)\n", DriverObject));

  RtlInitUnicodeString(&uDeviceName, DEVICE_NAME);
  Status = IoCreateDevice(DriverObject,0,&uDeviceName,FILE_DEVICE_UNKNOWN,0,FALSE,&DriverObject->DeviceObject);
  if (NT_SUCCESS(Status)) {
    RtlInitUnicodeString(&uSymLinkName, SYMLINK);
    Status = IoCreateSymbolicLink(&uSymLinkName, &uDeviceName);
    if (NT_SUCCESS(Status)) {
      DriverObject->DriverUnload = DriverUnload;
      DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverCreateClose;
      DriverObject->MajorFunction[IRP_MJ_CLOSE]  = DriverCreateClose;
      DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverDeviceControl;
    } else IoDeleteDevice(DriverObject->DeviceObject);
  }

  KdPrint(("driver.c: DriverInit(-):0x%x\n", Status));
  return Status;
}


/** Odstrani objekty nutne pro komunikaci ovladace s okolim.
 *
 * Rutina odstrani symbolicky odkaz a smaze objekt komunikacniho zarizeni.
 *
 * @param DriverObject Adresa struktury DRIVER_OBJECT, ktera reprezentuje
 * ovladac intcount.sys.
 */
VOID DriverFinit(PDRIVER_OBJECT DriverObject)
{
  UNICODE_STRING uSymLinkName;
  KdPrint(("driver.c: DriverFinit(DriverObject=0x%p)\n", DriverObject));

  RtlInitUnicodeString(&uSymLinkName,SYMLINK);
  IoDeleteSymbolicLink(&uSymLinkName);
  IoDeleteDevice(DriverObject->DeviceObject);

  KdPrint(("driver.c: DriverFinit(-)\n"));
  return;
}


/** Rutina provede uklid po ovladaci intcount.sys.
 *
 * Jadro systemu tuto proceduru zavola tesne pred uvolnenim ovladace z
 * jadra.
 */
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
  LONG i;
  KdPrint(("driver.c: DriverUnload(DriverObject=0x%p)\n", DriverObject));

  DriverFinit(DriverObject);
  IntCountFinit();
  SyncFinit();

  KdPrint(("driver.c: DriverUnload(-)\n"));
  return;
}


/** Inicializuje ovladac intcount.sys.
 *
 * Jadro tuto rutinu zavola tesne po namapovani souboru ovladace do
 * pameti. Rutina zajisti vytvoreni komunikacniho zarizeni a symbolickeho
 * odkazu a inicializuje mechanismus pro pocitani vyskytu preruseni.
 */
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegPath)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("intcount.c: DriverEntry(DriverObject=0x%p; RegPath=\"%S\")\n", DriverObject, RegPath->Buffer));

  Status = SyncInit();
  if (NT_SUCCESS(Status)) {
    Status = IntCountInit();
    if (NT_SUCCESS(Status)) {
      Status = DriverInit(DriverObject);
      if (!NT_SUCCESS(Status)) {
        IntCountFinit();
        SyncFinit();
      }
    } else SyncFinit();
  }

  KdPrint(("intcount.c: DriverEntry(-):0x%x\n", Status));
  return Status;
}
