
/** @file events.c
 *
 * Podpurne funkce pro praci se zaznamy reprezentujici  jednotlive 
 * udalosti.
 */

#include <ntddk.h>
#include "driver.h"
#include "lists.h"
#include "events.h"

// Spojovy seznam vsech udalosti, ktere si jeste "nevyzvedla" aplikace
static PLIST Events = NULL;


/** Alokuje novy zaznam pro udalost.
 *
 * Pamet je alokovana z nestrankovaneho fondu, protoze pristup do seznamu
 * udalosti se synchronizuje pres spinlock.
 *
 * Funkce vraci adresu nove alokovaneho zaznamu udalosti. Nepodari-li se
 * alokovat dostatecne mnozstvi pameti, je vracena hodnota NULL.
 *
 * @param typ Ceho se udalost tyka (procesu, vlakna, souboru PE).
 * @param velikost Pocet bajtu, ktery se ma pro zaznam vyhradit.
 */
PEVENT EventAlloc(IN ULONG Type, IN ULONG Size)
{
  LARGE_INTEGER SystemTime;
  PEVENT Event = NULL;
  KdPrint(("events.c: EventAlloc(Type=%u; Size=%u)\n", Type, Size));

  // pokusime se alokovat pamet pro novy zaznam
  Size = Size + sizeof(EVENT);
  Event = ExAllocatePoolWithTag(NonPagedPool, Size, POOL_TAG);
  if (Event != NULL) {
    RtlZeroMemory(Event, Size);
    // Inicializujeme polozku LIST_ENTRY a typu a velikosti udalosti
    InitializeListHead(&Event->ListEntry);
    Event->Size = Size;
    Event->Type = Type;
    KeQuerySystemTime(&SystemTime);
	ExSystemTimeToLocalTime(&SystemTime, &Event->Time);
  }

  KdPrint(("events.c: EventAlloc(-):0x%p\n", Event));
  return Event;
}


/** Uvolni pamet alokovanou pro udalost.
 *
 * @param udalost Zaznam udalosti, jehoz pamet ma byt uvolnena.
 */
VOID EventFree(PEVENT Event)
{
  KdPrint(("events.c: EventFree(Event=0x%p)\n", Event));

  // Uvolnovany zaznam udalosti by se nemel nachazet v zadnem spojovem 
  // seznamu
  ExFreePoolWithTag(Event, POOL_TAG);

  KdPrint(("events.c: EventFree(-)\n"));
  return;
}


/** Zaradi udalost na konec seznamu udalosti.
 *
 * @param udalost Udalost, ktera ma byt zarazena do seznamu.
 */
VOID EventAddToList(PEVENT Event)
{
  KdPrint(("events.c: EventAddToList(Event=0x%p)\n", Event));

  lListTailInsert(Events, &Event->ListEntry);

  KdPrint(("events.c: EventAddToList(-)\n"));
  return;
}


VOID EventAddToListLock(PEVENT Event)
{
  KLOCK_QUEUE_HANDLE QHandle;
  KdPrint(("events.c: EventAddToListLock(Event=0x%p)\n", Event));

  EventListLock(&QHandle);
  EventAddToList(Event);
  EventListUnlock(&QHandle);

  KdPrint(("events.c: EventAddToListLock(-)\n"));
  return;
}

/** Vrati udalost do seznamu udalosti.
 *
 * Vraceni do seznamu znamena vlozeni na prvni misto seznamu, takze pri dalsim
 * pozadavku aplikace bude tato udalost znovu vybrana.
 *
 * @param udalost Zaznam udalosti, ktery ma byt vracen do seznamu.
 */
VOID EventInsertFirst(PEVENT Event)
{
  KdPrint(("events.c: EventInsertFirstLock(Event=0x%p)\n", Event));

  lListHeadInsert(Events, &Event->ListEntry);

  KdPrint(("events.c: EventInsertFirstLock(-)\n"));
  return;
}

VOID EventInsertFirstLock(PEVENT Event)
{
  KLOCK_QUEUE_HANDLE QHandle;
  KdPrint(("events.c: EventInsertFirst(Event=0x%p)\n", Event));

  EventListLock(&QHandle);
  EventInsertFirst(Event);
  EventListUnlock(&QHandle);

  KdPrint(("events.c: EventInsertFirst(-)\n"));
  return;
}

/** Odebere prvni udalost ze seznamu.
 *
 * Vrati adresu prvniho zaznamu ze seznamu. Tento zaznam je odebran
 * ze seznamu udalosti.
 */
PEVENT EventGetFirst(VOID)
{
  PEVENT Event = NULL;
  KdPrint(("events.c: EventGetFirst()\n"));

  Event = (PEVENT)lListHeadRemove(Events);

  KdPrint(("events.c: EventGetFirst(-):0x%p\n", Event));
  return Event;
}


PEVENT EventGetFirstLock(VOID)
{
  PEVENT Event = NULL;
  KLOCK_QUEUE_HANDLE QHandle;
  KdPrint(("events.c: EventGetFirstLock()\n"));

  EventListLock(&QHandle);
  Event = EventGetFirst();
  EventListUnlock(&QHandle);

  KdPrint(("events.c: EventGetFirstLock(-):0x%p\n", Event));
  return Event;
}


VOID EventListLock(PKLOCK_QUEUE_HANDLE QHandle)
{
  lListLock(Events, QHandle);

  return;
}

VOID EventListUnlock(PKLOCK_QUEUE_HANDLE QHandle)
{
  lListUnlock(Events, QHandle);

  return;
}


/** Zpetne volana funkce, ktera se zavola na kazdou udalost, ktera
 * zustala v seznamu udalosti pri jeho ruseni.
 *
 * @param pudalost Polozka seznamu, na kterou je rutina zavolana.
 */
VOID _EventDestroy(PVOID pEvent)
{
  KdPrint(("events.c: _EventDestroy(pEvent=0x%p)\n", pEvent));

  EventFree((PEVENT)pEvent);

  KdPrint(("events.c: _EventDestroy(-)\n"));
  return;
}


/** Inicializuje seznam udalosti.
 *
 */
NTSTATUS EventsInit(VOID)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("events.c: EventsInit()\n"));

  Events = lListCreate(NonPagedPool, _EventDestroy);
  if (Events != NULL)
    Status = STATUS_SUCCESS;
  else Status = STATUS_INSUFFICIENT_RESOURCES;

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


/** Uvolni prostredky pouzite seznamem udalosti.
 *
 * Tato rutina je volana behem uvolnovani ovladace z pameti.
 */
VOID EventsFinit(VOID)
{
  KdPrint(("events.c: EventsFinit()\n"));

  lListDestroyLock(Events);
  Events = NULL;

  KdPrint(("events.c: EventsFinit(-)\n"));
  return;
}
