reactos/drivers/wdm/audio/sysaudio/deviface.c
Timo Kreuzer 9ea495ba33 Create a branch for header work.
svn path=/branches/header-work/; revision=45691
2010-02-26 22:57:55 +00:00

419 lines
13 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Kernel Streaming
* FILE: drivers/wdm/audio/sysaudio/deviface.c
* PURPOSE: System Audio graph builder
* PROGRAMMER: Johannes Anderwald
*/
#include "sysaudio.h"
const GUID GUID_DEVICE_INTERFACE_ARRIVAL = {0xCB3A4004L, 0x46F0, 0x11D0, {0xB0, 0x8F, 0x00, 0x60, 0x97, 0x13, 0x05, 0x3F}};
const GUID GUID_DEVICE_INTERFACE_REMOVAL = {0xCB3A4005L, 0x46F0, 0x11D0, {0xB0, 0x8F, 0x00, 0x60, 0x97, 0x13, 0x05, 0x3F}};
const GUID KS_CATEGORY_AUDIO = {0x6994AD04L, 0x93EF, 0x11D0, {0xA3, 0xCC, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
const GUID KS_CATEGORY_TOPOLOGY = {0xDDA54A40, 0x1E4C, 0x11D1, {0xA0, 0x50, 0x40, 0x57, 0x05, 0xC1, 0x00, 0x00}};
const GUID DMOCATEGORY_ACOUSTIC_ECHO_CANCEL = {0xBF963D80L, 0xC559, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
NTSTATUS
BuildPinDescriptor(
IN PKSAUDIO_DEVICE_ENTRY DeviceEntry,
IN ULONG Count)
{
ULONG Index;
KSP_PIN PinRequest;
KSPIN_DATAFLOW DataFlow;
KSPIN_COMMUNICATION Communication;
ULONG NumWaveOutPin, NumWaveInPin;
NTSTATUS Status;
ULONG BytesReturned;
NumWaveInPin = 0;
NumWaveOutPin = 0;
for(Index = 0; Index < Count; Index++)
{
/* retrieve data flow */
PinRequest.PinId = Index;
PinRequest.Property.Set = KSPROPSETID_Pin;
PinRequest.Property.Flags = KSPROPERTY_TYPE_GET;
/* get dataflow direction */
PinRequest.Property.Id = KSPROPERTY_PIN_DATAFLOW;
Status = KsSynchronousIoControlDevice(DeviceEntry->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&PinRequest, sizeof(KSP_PIN), (PVOID)&DataFlow, sizeof(KSPIN_DATAFLOW), &BytesReturned);
if (NT_SUCCESS(Status))
{
DeviceEntry->PinDescriptors[Index].DataFlow = DataFlow;
}
/* get irp flow direction */
PinRequest.Property.Id = KSPROPERTY_PIN_COMMUNICATION;
Status = KsSynchronousIoControlDevice(DeviceEntry->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&PinRequest, sizeof(KSP_PIN), (PVOID)&Communication, sizeof(KSPIN_COMMUNICATION), &BytesReturned);
if (NT_SUCCESS(Status))
{
DeviceEntry->PinDescriptors[Index].Communication = Communication;
}
if (Communication == KSPIN_COMMUNICATION_SINK && DataFlow == KSPIN_DATAFLOW_IN)
NumWaveOutPin++;
if (Communication == KSPIN_COMMUNICATION_SINK && DataFlow == KSPIN_DATAFLOW_OUT)
NumWaveInPin++;
/* FIXME query for interface, dataformat etc */
}
DPRINT("Num Pins %u Num WaveIn Pins %u Name WaveOut Pins %u\n", DeviceEntry->PinDescriptorsCount, NumWaveInPin, NumWaveOutPin);
return STATUS_SUCCESS;
}
VOID
QueryFilterRoutine(
IN PKSAUDIO_DEVICE_ENTRY DeviceEntry)
{
KSPROPERTY PropertyRequest;
ULONG Count;
NTSTATUS Status;
ULONG BytesReturned;
DPRINT("Querying filter...\n");
PropertyRequest.Set = KSPROPSETID_Pin;
PropertyRequest.Flags = KSPROPERTY_TYPE_GET;
PropertyRequest.Id = KSPROPERTY_PIN_CTYPES;
/* query for num of pins */
Status = KsSynchronousIoControlDevice(DeviceEntry->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&PropertyRequest, sizeof(KSPROPERTY), (PVOID)&Count, sizeof(ULONG), &BytesReturned);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to query number of pins Status %x\n", Status);
return;
}
if (!Count)
{
DPRINT1("Filter has no pins!\n");
return;
}
/* allocate pin descriptor array */
DeviceEntry->PinDescriptors = ExAllocatePool(NonPagedPool, Count * sizeof(KSPIN_DESCRIPTOR));
if (!DeviceEntry->PinDescriptors)
{
/* no memory */
return;
}
/* zero array pin descriptor array */
RtlZeroMemory(DeviceEntry->PinDescriptors, Count * sizeof(KSPIN_DESCRIPTOR));
/* build the device descriptor */
Status = BuildPinDescriptor(DeviceEntry, Count);
if (!NT_SUCCESS(Status))
return;
/* allocate pin array */
DeviceEntry->Pins = ExAllocatePool(NonPagedPool, Count * sizeof(PIN_INFO));
if (!DeviceEntry->Pins)
{
/* no memory */
DPRINT1("Failed to allocate memory Pins %u Block %x\n", Count, Count * sizeof(PIN_INFO));
return;
}
/* clear array */
RtlZeroMemory(DeviceEntry->Pins, sizeof(PIN_INFO) * Count);
DeviceEntry->PinDescriptorsCount = Count;
}
VOID
NTAPI
FilterPinWorkerRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context)
{
PKSAUDIO_DEVICE_ENTRY DeviceEntry;
PFILTER_WORKER_CONTEXT Ctx = (PFILTER_WORKER_CONTEXT)Context;
DeviceEntry = Ctx->DeviceEntry;
QueryFilterRoutine(DeviceEntry);
/* free work item */
IoFreeWorkItem(Ctx->WorkItem);
/* free work item context */
ExFreePool(Ctx);
return;
}
NTSTATUS
OpenDevice(
IN PUNICODE_STRING DeviceName,
IN PHANDLE HandleOut,
IN PFILE_OBJECT * FileObjectOut)
{
NTSTATUS Status;
HANDLE NodeHandle;
PFILE_OBJECT FileObject;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
InitializeObjectAttributes(&ObjectAttributes, DeviceName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = ZwCreateFile(&NodeHandle,
GENERIC_READ | GENERIC_WRITE,
&ObjectAttributes,
&IoStatusBlock,
NULL,
0,
0,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
if (!NT_SUCCESS(Status))
{
DPRINT("ZwCreateFile failed with %x %S\n", Status, DeviceName->Buffer);
return Status;
}
Status = ObReferenceObjectByHandle(NodeHandle, GENERIC_READ | GENERIC_WRITE, IoFileObjectType, KernelMode, (PVOID*)&FileObject, NULL);
if (!NT_SUCCESS(Status))
{
ZwClose(NodeHandle);
DPRINT("ObReferenceObjectByHandle failed with %x\n", Status);
return Status;
}
*HandleOut = NodeHandle;
*FileObjectOut = FileObject;
return Status;
}
NTSTATUS
InsertAudioDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PUNICODE_STRING DeviceName)
{
NTSTATUS Status = STATUS_SUCCESS;
PFILTER_WORKER_CONTEXT Ctx = NULL;
PIO_WORKITEM WorkItem = NULL;
PSYSAUDIODEVEXT DeviceExtension;
PKSAUDIO_DEVICE_ENTRY DeviceEntry = NULL;
/* a new device has arrived */
DeviceEntry = ExAllocatePool(NonPagedPool, sizeof(KSAUDIO_DEVICE_ENTRY));
if (!DeviceEntry)
{
/* no memory */
return STATUS_INSUFFICIENT_RESOURCES;
}
/* initialize audio device entry */
RtlZeroMemory(DeviceEntry, sizeof(KSAUDIO_DEVICE_ENTRY));
/* allocate filter ctx */
Ctx = ExAllocatePool(NonPagedPool, sizeof(FILTER_WORKER_CONTEXT));
if (!Ctx)
{
/* no memory */
Status = STATUS_INSUFFICIENT_RESOURCES;
goto cleanup;
}
/* allocate work item */
WorkItem = IoAllocateWorkItem(DeviceObject);
if (!WorkItem)
{
/* no memory */
Status = STATUS_INSUFFICIENT_RESOURCES;
goto cleanup;
}
/* set device name */
DeviceEntry->DeviceName.Length = 0;
DeviceEntry->DeviceName.MaximumLength = DeviceName->MaximumLength + 10 * sizeof(WCHAR);
DeviceEntry->DeviceName.Buffer = ExAllocatePool(NonPagedPool, DeviceEntry->DeviceName.MaximumLength);
if (!DeviceEntry->DeviceName.Buffer)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto cleanup;
}
RtlAppendUnicodeToString(&DeviceEntry->DeviceName, L"\\??\\");
RtlAppendUnicodeStringToString(&DeviceEntry->DeviceName, DeviceName);
Status = OpenDevice(&DeviceEntry->DeviceName, &DeviceEntry->Handle, &DeviceEntry->FileObject);
if (!NT_SUCCESS(Status))
{
goto cleanup;
}
Ctx->DeviceEntry = DeviceEntry;
Ctx->WorkItem = WorkItem;
/* fetch device extension */
DeviceExtension = (PSYSAUDIODEVEXT)DeviceObject->DeviceExtension;
/* insert new audio device */
ExInterlockedInsertTailList(&DeviceExtension->KsAudioDeviceList, &DeviceEntry->Entry, &DeviceExtension->Lock);
InterlockedIncrement((PLONG)&DeviceExtension->NumberOfKsAudioDevices);
DPRINT("Successfully opened audio device %u Device %S\n", DeviceExtension->NumberOfKsAudioDevices, DeviceEntry->DeviceName.Buffer);
IoQueueWorkItem(WorkItem, FilterPinWorkerRoutine, DelayedWorkQueue, (PVOID)Ctx);
return Status;
cleanup:
if (Ctx)
ExFreePool(Ctx);
if (WorkItem)
IoFreeWorkItem(WorkItem);
if (DeviceEntry)
{
if (DeviceEntry->DeviceName.Buffer)
ExFreePool(DeviceEntry->DeviceName.Buffer);
ExFreePool(DeviceEntry);
}
return Status;
}
NTSTATUS
NTAPI
DeviceInterfaceChangeCallback(
IN PVOID NotificationStructure,
IN PVOID Context)
{
DEVICE_INTERFACE_CHANGE_NOTIFICATION * Event;
NTSTATUS Status = STATUS_SUCCESS;
PSYSAUDIODEVEXT DeviceExtension;
PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)Context;
DeviceExtension = (PSYSAUDIODEVEXT)DeviceObject->DeviceExtension;
Event = (DEVICE_INTERFACE_CHANGE_NOTIFICATION*)NotificationStructure;
if (IsEqualGUIDAligned(&Event->Event,
&GUID_DEVICE_INTERFACE_ARRIVAL))
{
Status = InsertAudioDevice(DeviceObject, Event->SymbolicLinkName);
return Status;
}
else
{
DPRINT("Remove interface to audio device!\n");
UNIMPLEMENTED
return STATUS_SUCCESS;
}
}
NTSTATUS
SysAudioRegisterNotifications(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT DeviceObject)
{
NTSTATUS Status;
PSYSAUDIODEVEXT DeviceExtension;
DeviceExtension = (PSYSAUDIODEVEXT)DeviceObject->DeviceExtension;
Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
(PVOID)&KS_CATEGORY_AUDIO,
DriverObject,
DeviceInterfaceChangeCallback,
(PVOID)DeviceObject,
(PVOID*)&DeviceExtension->KsAudioNotificationEntry);
if (!NT_SUCCESS(Status))
{
DPRINT("IoRegisterPlugPlayNotification failed with %x\n", Status);
}
Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
(PVOID)&DMOCATEGORY_ACOUSTIC_ECHO_CANCEL,
DriverObject,
DeviceInterfaceChangeCallback,
(PVOID)DeviceObject,
(PVOID*)&DeviceExtension->EchoCancelNotificationEntry);
if (!NT_SUCCESS(Status))
{
/* ignore failure for now */
DPRINT("IoRegisterPlugPlayNotification failed for DMOCATEGORY_ACOUSTIC_ECHO_CANCEL\n", Status);
}
return STATUS_SUCCESS;
}
NTSTATUS
SysAudioRegisterDeviceInterfaces(
IN PDEVICE_OBJECT DeviceObject)
{
NTSTATUS Status;
UNICODE_STRING SymbolicLink;
Status = IoRegisterDeviceInterface(DeviceObject, &KSCATEGORY_PREFERRED_MIDIOUT_DEVICE, NULL, &SymbolicLink);
if (NT_SUCCESS(Status))
{
IoSetDeviceInterfaceState(&SymbolicLink, TRUE);
RtlFreeUnicodeString(&SymbolicLink);
}
else
{
DPRINT("Failed to register KSCATEGORY_PREFERRED_MIDIOUT_DEVICE interface Status %x\n", Status);
return Status;
}
Status = IoRegisterDeviceInterface(DeviceObject, &KSCATEGORY_PREFERRED_WAVEIN_DEVICE, NULL, &SymbolicLink);
if (NT_SUCCESS(Status))
{
IoSetDeviceInterfaceState(&SymbolicLink, TRUE);
RtlFreeUnicodeString(&SymbolicLink);
}
else
{
DPRINT("Failed to register KSCATEGORY_PREFERRED_WAVEIN_DEVICE interface Status %x\n", Status);
return Status;
}
Status = IoRegisterDeviceInterface(DeviceObject, &KSCATEGORY_PREFERRED_WAVEOUT_DEVICE, NULL, &SymbolicLink);
if (NT_SUCCESS(Status))
{
IoSetDeviceInterfaceState(&SymbolicLink, TRUE);
RtlFreeUnicodeString(&SymbolicLink);
}
else
{
DPRINT("Failed to register KSCATEGORY_PREFERRED_WAVEOUT_DEVICE interface Status %x\n", Status);
}
Status = IoRegisterDeviceInterface(DeviceObject, &KSCATEGORY_SYSAUDIO, NULL, &SymbolicLink);
if (NT_SUCCESS(Status))
{
IoSetDeviceInterfaceState(&SymbolicLink, TRUE);
RtlFreeUnicodeString(&SymbolicLink);
}
else
{
DPRINT("Failed to register KSCATEGORY_SYSAUDIO interface Status %x\n", Status);
}
return Status;
}