reactos/drivers/ksfilter/ks/event.c

745 lines
19 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Kernel Streaming
* FILE: drivers/ksfilter/ks/event.c
* PURPOSE: KS Event functions
* PROGRAMMER: Johannes Anderwald
*/
#include "precomp.h"
#define NDEBUG
#include <debug.h>
BOOLEAN
KspSynchronizedEventRoutine(
IN KSEVENTS_LOCKTYPE EventsFlags,
IN PVOID EventsLock,
IN PKSEVENT_SYNCHRONIZED_ROUTINE SynchronizedRoutine,
IN PKSEVENT_CTX Ctx)
{
BOOLEAN Result = FALSE;
KIRQL OldLevel;
if (EventsFlags == KSEVENTS_NONE)
{
/* no synchronization required */
Result = SynchronizedRoutine(Ctx);
}
else if (EventsFlags == KSEVENTS_SPINLOCK)
{
/* use spin lock */
KeAcquireSpinLock((PKSPIN_LOCK)EventsLock, &OldLevel);
Result = SynchronizedRoutine(Ctx);
KeReleaseSpinLock((PKSPIN_LOCK)EventsLock, OldLevel);
}
else if (EventsFlags == KSEVENTS_MUTEX)
{
/* use a mutex */
KeWaitForSingleObject(EventsLock, Executive, KernelMode, FALSE, NULL);
Result = SynchronizedRoutine(Ctx);
KeReleaseMutex((PRKMUTEX)EventsLock, FALSE);
}
else if (EventsFlags == KSEVENTS_FMUTEX)
{
/* use a fast mutex */
ExAcquireFastMutex((PFAST_MUTEX)EventsLock);
Result = SynchronizedRoutine(Ctx);
ExReleaseFastMutex((PFAST_MUTEX)EventsLock);
}
else if (EventsFlags == KSEVENTS_FMUTEXUNSAFE)
{
/* acquire fast mutex unsafe */
KeEnterCriticalRegion();
ExAcquireFastMutexUnsafe((PFAST_MUTEX)EventsLock);
Result = SynchronizedRoutine(Ctx);
ExReleaseFastMutexUnsafe((PFAST_MUTEX)EventsLock);
KeLeaveCriticalRegion();
}
else if (EventsFlags == KSEVENTS_INTERRUPT)
{
/* use interrupt for locking */
Result = KeSynchronizeExecution((PKINTERRUPT)EventsLock, (PKSYNCHRONIZE_ROUTINE)SynchronizedRoutine, (PVOID)Ctx);
}
else if (EventsFlags == KSEVENTS_ERESOURCE)
{
/* use an eresource */
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite((PERESOURCE)EventsLock, TRUE);
Result = SynchronizedRoutine(Ctx);
ExReleaseResourceLite((PERESOURCE)EventsLock);
KeLeaveCriticalRegion();
}
return Result;
}
BOOLEAN
NTAPI
SyncAddEvent(
PKSEVENT_CTX Context)
{
InsertTailList(Context->List, &Context->EventEntry->ListEntry);
return TRUE;
}
NTSTATUS
KspEnableEvent(
IN PIRP Irp,
IN ULONG EventSetsCount,
IN const KSEVENT_SET* EventSet,
IN OUT PLIST_ENTRY EventsList OPTIONAL,
IN KSEVENTS_LOCKTYPE EventsFlags OPTIONAL,
IN PVOID EventsLock OPTIONAL,
IN PFNKSALLOCATOR Allocator OPTIONAL,
IN ULONG EventItemSize OPTIONAL)
{
PIO_STACK_LOCATION IoStack;
NTSTATUS Status;
KSEVENT Event;
PKSEVENT_ITEM EventItem, FoundEventItem;
PKSEVENTDATA EventData;
const KSEVENT_SET *FoundEventSet;
PKSEVENT_ENTRY EventEntry;
ULONG Index, SubIndex, Size;
PVOID Object;
KSEVENT_CTX Ctx;
LPGUID Guid;
/* get current stack location */
IoStack = IoGetCurrentIrpStackLocation(Irp);
if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KSEVENT))
{
/* invalid parameter */
return STATUS_NOT_SUPPORTED;
}
if (Irp->RequestorMode == UserMode)
{
_SEH2_TRY
{
ProbeForRead(IoStack->Parameters.DeviceIoControl.Type3InputBuffer, sizeof(KSEVENT), sizeof(UCHAR));
ProbeForRead(Irp->UserBuffer, IoStack->Parameters.DeviceIoControl.OutputBufferLength, sizeof(UCHAR));
RtlMoveMemory(&Event, IoStack->Parameters.DeviceIoControl.Type3InputBuffer, sizeof(KSEVENT));
Status = STATUS_SUCCESS;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Exception, get the error code */
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
/* check for success */
if (!NT_SUCCESS(Status))
{
/* failed to probe parameters */
return Status;
}
}
else
{
/* copy event struct */
RtlMoveMemory(&Event, IoStack->Parameters.DeviceIoControl.Type3InputBuffer, sizeof(KSEVENT));
}
FoundEventItem = NULL;
FoundEventSet = NULL;
if (IsEqualGUIDAligned(&Event.Set, &GUID_NULL) && Event.Id == 0 && Event.Flags == KSEVENT_TYPE_SETSUPPORT)
{
// store output size
Irp->IoStatus.Information = sizeof(GUID) * EventSetsCount;
if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(GUID) * EventSetsCount)
{
// buffer too small
return STATUS_MORE_ENTRIES;
}
// get output buffer
Guid = (LPGUID)Irp->UserBuffer;
// copy property guids from property sets
for(Index = 0; Index < EventSetsCount; Index++)
{
RtlMoveMemory(&Guid[Index], EventSet[Index].Set, sizeof(GUID));
}
return STATUS_SUCCESS;
}
/* now try to find event set */
for(Index = 0; Index < EventSetsCount; Index++)
{
if (IsEqualGUIDAligned(&Event.Set, EventSet[Index].Set))
{
EventItem = (PKSEVENT_ITEM)EventSet[Index].EventItem;
/* sanity check */
ASSERT(EventSet[Index].EventsCount);
ASSERT(EventItem);
/* now find matching event id */
for(SubIndex = 0; SubIndex < EventSet[Index].EventsCount; SubIndex++)
{
if (EventItem[SubIndex].EventId == Event.Id)
{
/* found event item */
FoundEventItem = &EventItem[SubIndex];
FoundEventSet = &EventSet[Index];
break;
}
}
if (FoundEventSet)
break;
}
}
if (!FoundEventSet)
{
UNICODE_STRING GuidString;
RtlStringFromGUID(&Event.Set, &GuidString);
DPRINT("Guid %S Id %u Flags %x not found\n", GuidString.Buffer, Event.Id, Event.Flags);
RtlFreeUnicodeString(&GuidString);
return STATUS_PROPSET_NOT_FOUND;
}
if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < FoundEventItem->DataInput)
{
/* buffer too small */
DPRINT1("Got %u expected %u\n", IoStack->Parameters.DeviceIoControl.OutputBufferLength, FoundEventItem->DataInput);
return STATUS_SUCCESS;
}
if (!FoundEventItem->AddHandler && !EventsList)
{
/* no add handler and no list to add the new entry to */
return STATUS_INVALID_PARAMETER;
}
/* get event data */
EventData = Irp->UserBuffer;
/* sanity check */
ASSERT(EventData);
if (Irp->RequestorMode == UserMode)
{
if (EventData->NotificationType == KSEVENTF_SEMAPHORE_HANDLE)
{
/* get semaphore object handle */
Status = ObReferenceObjectByHandle(EventData->SemaphoreHandle.Semaphore, SEMAPHORE_MODIFY_STATE, *ExSemaphoreObjectType, Irp->RequestorMode, &Object, NULL);
if (!NT_SUCCESS(Status))
{
/* invalid semaphore handle */
return STATUS_INVALID_PARAMETER;
}
}
else if (EventData->NotificationType == KSEVENTF_EVENT_HANDLE)
{
/* get event object handle */
Status = ObReferenceObjectByHandle(EventData->EventHandle.Event, EVENT_MODIFY_STATE, *ExEventObjectType, Irp->RequestorMode, &Object, NULL);
if (!NT_SUCCESS(Status))
{
/* invalid event handle */
return STATUS_INVALID_PARAMETER;
}
}
else
{
/* user mode client can only pass an event or semaphore handle */
return STATUS_INVALID_PARAMETER;
}
}
else
{
if (EventData->NotificationType != KSEVENTF_EVENT_OBJECT &&
EventData->NotificationType != KSEVENTF_SEMAPHORE_OBJECT &&
EventData->NotificationType != KSEVENTF_DPC &&
EventData->NotificationType != KSEVENTF_WORKITEM &&
EventData->NotificationType != KSEVENTF_KSWORKITEM)
{
/* invalid type requested */
return STATUS_INVALID_PARAMETER;
}
}
/* calculate request size */
Size = sizeof(KSEVENT_ENTRY) + FoundEventItem->ExtraEntryData;
/* do we have an allocator */
if (Allocator)
{
/* allocate event entry */
Status = Allocator(Irp, Size, FALSE);
if (!NT_SUCCESS(Status))
{
/* failed */
return Status;
}
/* assume the caller put it there */
EventEntry = KSEVENT_ENTRY_IRP_STORAGE(Irp);
}
else
{
/* allocate it from nonpaged pool */
EventEntry = AllocateItem(NonPagedPool, Size);
}
if (!EventEntry)
{
/* not enough memory */
return STATUS_INSUFFICIENT_RESOURCES;
}
/* zero event entry */
RtlZeroMemory(EventEntry, Size);
/* initialize event entry */
EventEntry->EventData = EventData;
EventEntry->NotificationType = EventData->NotificationType;
EventEntry->EventItem = FoundEventItem;
EventEntry->EventSet = FoundEventSet;
EventEntry->FileObject = IoStack->FileObject;
switch(EventEntry->NotificationType)
{
case KSEVENTF_EVENT_HANDLE:
EventEntry->Object = Object;
EventEntry->Reserved = 0;
break;
case KSEVENTF_SEMAPHORE_HANDLE:
EventEntry->Object = Object;
EventEntry->SemaphoreAdjustment = EventData->SemaphoreHandle.Adjustment;
EventEntry->Reserved = 0;
break;
case KSEVENTF_EVENT_OBJECT:
EventEntry->Object = EventData->EventObject.Event;
EventEntry->Reserved = EventData->EventObject.Increment;
break;
case KSEVENTF_SEMAPHORE_OBJECT:
EventEntry->Object = EventData->SemaphoreObject.Semaphore;
EventEntry->SemaphoreAdjustment = EventData->SemaphoreObject.Adjustment;
EventEntry->Reserved = EventData->SemaphoreObject.Increment;
break;
case KSEVENTF_DPC:
EventEntry->Object = EventData->Dpc.Dpc;
EventData->Dpc.ReferenceCount = 0;
break;
case KSEVENTF_WORKITEM:
EventEntry->Object = EventData->WorkItem.WorkQueueItem;
EventEntry->BufferItem = (PKSBUFFER_ITEM)UlongToPtr(EventData->WorkItem.WorkQueueType);
break;
case KSEVENTF_KSWORKITEM:
EventEntry->Object = EventData->KsWorkItem.KsWorkerObject;
EventEntry->DpcItem = (PKSDPC_ITEM)EventData->KsWorkItem.WorkQueueItem;
break;
default:
/* should not happen */
ASSERT(0);
}
if (FoundEventItem->AddHandler)
{
/* now add the event */
Status = FoundEventItem->AddHandler(Irp, EventData, EventEntry);
if (!NT_SUCCESS(Status))
{
/* discard event entry */
KsDiscardEvent(EventEntry);
}
}
else
{
/* setup context */
Ctx.List = EventsList;
Ctx.EventEntry = EventEntry;
/* add the event */
(void)KspSynchronizedEventRoutine(EventsFlags, EventsLock, SyncAddEvent, &Ctx);
Status = STATUS_SUCCESS;
}
/* done */
return Status;
}
/*
@implemented
*/
KSDDKAPI
NTSTATUS
NTAPI
KsEnableEvent(
IN PIRP Irp,
IN ULONG EventSetsCount,
IN KSEVENT_SET* EventSet,
IN OUT PLIST_ENTRY EventsList OPTIONAL,
IN KSEVENTS_LOCKTYPE EventsFlags OPTIONAL,
IN PVOID EventsLock OPTIONAL)
{
return KspEnableEvent(Irp, EventSetsCount, EventSet, EventsList, EventsFlags, EventsLock, NULL, 0);
}
/*
@implemented
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
KSDDKAPI
NTSTATUS
NTAPI
KsEnableEventWithAllocator(
_In_ PIRP Irp,
_In_ ULONG EventSetsCount,
_In_reads_(EventSetsCount) const KSEVENT_SET* EventSet,
_Inout_opt_ PLIST_ENTRY EventsList,
_In_opt_ KSEVENTS_LOCKTYPE EventsFlags,
_In_opt_ PVOID EventsLock,
_In_opt_ PFNKSALLOCATOR Allocator,
_In_opt_ ULONG EventItemSize)
{
return KspEnableEvent(Irp, EventSetsCount, EventSet, EventsList, EventsFlags, EventsLock, Allocator, EventItemSize);
}
BOOLEAN
NTAPI
KspDisableEvent(
IN PKSEVENT_CTX Ctx)
{
PIO_STACK_LOCATION IoStack;
PKSEVENTDATA EventData;
PKSEVENT_ENTRY EventEntry;
PLIST_ENTRY Entry;
if (!Ctx || !Ctx->List || !Ctx->FileObject || !Ctx->Irp)
{
/* invalid parameter */
return FALSE;
}
/* get current irp stack location */
IoStack = IoGetCurrentIrpStackLocation(Ctx->Irp);
/* get event data */
EventData = (PKSEVENTDATA)IoStack->Parameters.DeviceIoControl.Type3InputBuffer;
/* point to first entry */
Entry = Ctx->List->Flink;
while(Entry != Ctx->List)
{
/* get event entry */
EventEntry = (PKSEVENT_ENTRY)CONTAINING_RECORD(Entry, KSEVENT_ENTRY, ListEntry);
if (EventEntry->EventData == EventData && EventEntry->FileObject == Ctx->FileObject)
{
/* found the entry */
RemoveEntryList(&EventEntry->ListEntry);
Ctx->EventEntry = EventEntry;
return TRUE;
}
/* move to next item */
Entry = Entry->Flink;
}
/* entry not found */
return TRUE;
}
/*
@implemented
*/
KSDDKAPI
NTSTATUS
NTAPI
KsDisableEvent(
IN PIRP Irp,
IN OUT PLIST_ENTRY EventsList,
IN KSEVENTS_LOCKTYPE EventsFlags,
IN PVOID EventsLock)
{
PIO_STACK_LOCATION IoStack;
KSEVENT_CTX Ctx;
/* get current irp stack location */
IoStack = IoGetCurrentIrpStackLocation(Irp);
/* is there a event entry */
if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KSEVENTDATA))
{
if (IoStack->Parameters.DeviceIoControl.InputBufferLength == 0)
{
/* caller wants to free event items */
KsFreeEventList(IoStack->FileObject, EventsList, EventsFlags, EventsLock);
return STATUS_SUCCESS;
}
/* invalid parameter */
return STATUS_INVALID_BUFFER_SIZE;
}
/* setup event ctx */
Ctx.List = EventsList;
Ctx.FileObject = IoStack->FileObject;
Ctx.Irp = Irp;
Ctx.EventEntry = NULL;
if (KspSynchronizedEventRoutine(EventsFlags, EventsLock, KspDisableEvent, &Ctx))
{
/* was the event entry found */
if (Ctx.EventEntry)
{
/* discard event */
KsDiscardEvent(Ctx.EventEntry);
return STATUS_SUCCESS;
}
/* event was not found */
return STATUS_UNSUCCESSFUL;
}
/* invalid parameters */
return STATUS_INVALID_PARAMETER;
}
/*
@implemented
*/
KSDDKAPI
VOID
NTAPI
KsDiscardEvent(
IN PKSEVENT_ENTRY EventEntry)
{
/* sanity check */
ASSERT(EventEntry->Object);
if (EventEntry->NotificationType == KSEVENTF_SEMAPHORE_HANDLE || EventEntry->NotificationType == KSEVENTF_EVENT_HANDLE)
{
/* release object */
ObDereferenceObject(EventEntry->Object);
}
/* free event entry */
FreeItem(EventEntry);
}
BOOLEAN
NTAPI
KspFreeEventList(
IN PKSEVENT_CTX Ctx)
{
PLIST_ENTRY Entry;
PKSEVENT_ENTRY EventEntry;
/* check valid input */
if (!Ctx || !Ctx->List)
return FALSE;
if (IsListEmpty(Ctx->List))
return FALSE;
/* remove first entry */
Entry = RemoveHeadList(Ctx->List);
if (!Entry)
{
/* list is empty, bye-bye */
return FALSE;
}
/* get event entry */
EventEntry = (PKSEVENT_ENTRY)CONTAINING_RECORD(Entry, KSEVENT_ENTRY, ListEntry);
/* store event entry */
Ctx->EventEntry = EventEntry;
/* return success */
return TRUE;
}
/*
@implemented
*/
KSDDKAPI
VOID
NTAPI
KsFreeEventList(
IN PFILE_OBJECT FileObject,
IN OUT PLIST_ENTRY EventsList,
IN KSEVENTS_LOCKTYPE EventsFlags,
IN PVOID EventsLock)
{
KSEVENT_CTX Ctx;
/* setup event ctx */
Ctx.List = EventsList;
Ctx.FileObject = FileObject;
Ctx.EventEntry = NULL;
while(KspSynchronizedEventRoutine(EventsFlags, EventsLock, KspFreeEventList, &Ctx))
{
if (Ctx.EventEntry)
{
KsDiscardEvent(Ctx.EventEntry);
}
}
}
/*
@implemented
*/
KSDDKAPI
NTSTATUS
NTAPI
KsGenerateEvent(
IN PKSEVENT_ENTRY EntryEvent)
{
if (EntryEvent->NotificationType == KSEVENTF_EVENT_HANDLE || EntryEvent->NotificationType == KSEVENTF_EVENT_OBJECT)
{
/* signal event */
KeSetEvent(EntryEvent->Object, EntryEvent->Reserved, FALSE);
}
else if (EntryEvent->NotificationType == KSEVENTF_SEMAPHORE_HANDLE || EntryEvent->NotificationType == KSEVENTF_SEMAPHORE_OBJECT)
{
/* release semaphore */
KeReleaseSemaphore(EntryEvent->Object, EntryEvent->Reserved, EntryEvent->SemaphoreAdjustment, FALSE);
}
else if (EntryEvent->NotificationType == KSEVENTF_DPC)
{
/* increment reference count to indicate dpc is pending */
InterlockedIncrement((PLONG)&EntryEvent->EventData->Dpc.ReferenceCount);
/* queue dpc */
KeInsertQueueDpc((PRKDPC)EntryEvent->Object, NULL, NULL);
}
else if (EntryEvent->NotificationType == KSEVENTF_WORKITEM)
{
/* queue work item */
ExQueueWorkItem((PWORK_QUEUE_ITEM)EntryEvent->Object, PtrToUlong(EntryEvent->BufferItem));
}
else if (EntryEvent->NotificationType == KSEVENTF_KSWORKITEM)
{
/* queue work item of ks worker */
return KsQueueWorkItem((PKSWORKER)EntryEvent->Object, (PWORK_QUEUE_ITEM)EntryEvent->DpcItem);
}
else
{
/* unsupported type requested */
return STATUS_INVALID_PARAMETER;
}
return STATUS_SUCCESS;
}
/*
@unimplemented
*/
KSDDKAPI
NTSTATUS
NTAPI
KsGenerateDataEvent(
IN PKSEVENT_ENTRY EventEntry,
IN ULONG DataSize,
IN PVOID Data)
{
UNIMPLEMENTED;
return STATUS_UNSUCCESSFUL;
}
/*
@unimplemented
*/
KSDDKAPI
VOID
NTAPI
KsGenerateEventList(
IN GUID* Set OPTIONAL,
IN ULONG EventId,
IN PLIST_ENTRY EventsList,
IN KSEVENTS_LOCKTYPE EventsFlags,
IN PVOID EventsLock)
{
UNIMPLEMENTED;
}
/*
@implemented
*/
KSDDKAPI
VOID
NTAPI
KsAddEvent(
IN PVOID Object,
IN PKSEVENT_ENTRY EventEntry)
{
PKSBASIC_HEADER Header = (PKSBASIC_HEADER)((ULONG_PTR)Object - sizeof(KSBASIC_HEADER));
ExInterlockedInsertTailList(&Header->EventList, &EventEntry->ListEntry, &Header->EventListLock);
}
/*
@implemented
*/
NTSTATUS
NTAPI
KsDefaultAddEventHandler(
IN PIRP Irp,
IN PKSEVENTDATA EventData,
IN OUT PKSEVENT_ENTRY EventEntry)
{
PIO_STACK_LOCATION IoStack;
PKSIOBJECT_HEADER ObjectHeader;
PKSBASIC_HEADER Header;
/* first get the io stack location */
IoStack = IoGetCurrentIrpStackLocation(Irp);
/* now get the object header */
ObjectHeader =(PKSIOBJECT_HEADER)IoStack->FileObject->FsContext2;
/* sanity check */
ASSERT(ObjectHeader->ObjectType);
/* obtain basic header */
Header = (PKSBASIC_HEADER)((ULONG_PTR)ObjectHeader->ObjectType - sizeof(KSBASIC_HEADER));
/* now insert the event entry */
ExInterlockedInsertTailList(&Header->EventList, &EventEntry->ListEntry, &Header->EventListLock);
/* done */
return STATUS_SUCCESS;
}
/*
@unimplemented
*/
KSDDKAPI
void
NTAPI
KsGenerateEvents(
IN PVOID Object,
IN const GUID* EventSet OPTIONAL,
IN ULONG EventId,
IN ULONG DataSize,
IN PVOID Data OPTIONAL,
IN PFNKSGENERATEEVENTCALLBACK CallBack OPTIONAL,
IN PVOID CallBackContext OPTIONAL)
{
UNIMPLEMENTED;
}