mirror of
https://github.com/reactos/reactos.git
synced 2025-05-30 14:39:46 +00:00
- Export KsNullDriverUnload
- Stop the audio stream at PASSIVE_LEVEL - Refactor sysaudio code to make it less complex and remove code which is not used - Add a few comments svn path=/trunk/; revision=40238
This commit is contained in:
parent
bf2b5adf8e
commit
5a37c1fdad
9 changed files with 310 additions and 240 deletions
|
@ -98,7 +98,7 @@
|
|||
@ stdcall KsFreeObjectCreateItem(ptr ptr)
|
||||
@ stdcall KsFreeObjectCreateItemsByContext(ptr ptr)
|
||||
@ stdcall KsLoadResource(ptr long ptr long ptr ptr)
|
||||
; KsNullDriverUnload@4
|
||||
@ stdcall KsNullDriverUnload(ptr)
|
||||
@ stdcall KsPinDataIntersectionEx(ptr ptr ptr long ptr long ptr ptr)
|
||||
@ stdcall KsQueryDevicePnpObject(ptr)
|
||||
@ stdcall KsRecalculateStackDepth(ptr long)
|
||||
|
|
|
@ -165,7 +165,7 @@ KsLoadResource(
|
|||
/*
|
||||
@unimplemented
|
||||
*/
|
||||
VOID
|
||||
KSDDKAPI VOID NTAPI
|
||||
KsNullDriverUnload(
|
||||
IN PDRIVER_OBJECT DriverObject)
|
||||
{
|
||||
|
|
|
@ -16,6 +16,7 @@ typedef struct
|
|||
KSSTATE State;
|
||||
PKSDATAFORMAT Format;
|
||||
KSPIN_CONNECT * ConnectDetails;
|
||||
KDPC Dpc;
|
||||
|
||||
PVOID CommonBuffer;
|
||||
ULONG CommonBufferSize;
|
||||
|
@ -183,6 +184,42 @@ UpdateCommonBufferOverlap(
|
|||
UpdateCommonBuffer(This, Position);
|
||||
}
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
StopStreamWorkerRoutine(
|
||||
IN PDEVICE_OBJECT DeviceObject,
|
||||
IN PVOID Context)
|
||||
{
|
||||
IPortPinWaveCyclicImpl * This = (IPortPinWaveCyclicImpl*)Context;
|
||||
|
||||
DPRINT1("Stopping %u Irql %u\n", This->IrpQueue->lpVtbl->NumMappings(This->IrpQueue), KeGetCurrentIrql());
|
||||
|
||||
This->Stream->lpVtbl->SetState(This->Stream, KSSTATE_STOP);
|
||||
This->State = KSSTATE_STOP;
|
||||
}
|
||||
|
||||
VOID
|
||||
NTAPI
|
||||
StopStreamRoutine(
|
||||
IN PKDPC Dpc,
|
||||
IN PVOID DeferredContext,
|
||||
IN PVOID SystemArgument1,
|
||||
IN PVOID SystemArgument2)
|
||||
{
|
||||
IPortPinWaveCyclicImpl * This = (IPortPinWaveCyclicImpl*)DeferredContext;
|
||||
PIO_WORKITEM WorkItem;
|
||||
|
||||
if (This->IrpQueue->lpVtbl->NumMappings(This->IrpQueue))
|
||||
return;
|
||||
|
||||
WorkItem = IoAllocateWorkItem(GetDeviceObject(This->Port));
|
||||
if (!WorkItem)
|
||||
return;
|
||||
|
||||
IoQueueWorkItem(WorkItem, StopStreamWorkerRoutine, DelayedWorkQueue, (PVOID)This);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static
|
||||
VOID
|
||||
|
@ -194,32 +231,18 @@ IServiceSink_fnRequestService(
|
|||
NTSTATUS Status;
|
||||
PUCHAR Buffer;
|
||||
ULONG BufferSize;
|
||||
|
||||
IPortPinWaveCyclicImpl * This = (IPortPinWaveCyclicImpl*)CONTAINING_RECORD(iface, IPortPinWaveCyclicImpl, lpVtblServiceSink);
|
||||
|
||||
Status = This->IrpQueue->lpVtbl->GetMapping(This->IrpQueue, &Buffer, &BufferSize);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
if (!This->IrpQueue->lpVtbl->CancelBuffers(This->IrpQueue))
|
||||
{
|
||||
/* there is an active dpc pending
|
||||
* wait untill this dpc is done, in order to complete the remaining irps
|
||||
*/
|
||||
return;
|
||||
}
|
||||
DPRINT1("Stopping %u\n", This->IrpQueue->lpVtbl->NumMappings(This->IrpQueue));
|
||||
|
||||
This->Stream->lpVtbl->SetState(This->Stream, KSSTATE_STOP);
|
||||
This->State = KSSTATE_STOP;
|
||||
KeInsertQueueDpc(&This->Dpc, NULL, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (KeGetCurrentIrql() == DISPATCH_LEVEL)
|
||||
return;
|
||||
|
||||
|
||||
Status = This->Stream->lpVtbl->GetPosition(This->Stream, &Position);
|
||||
DPRINT("Position %u BufferSize %u ActiveIrpOffset %u\n", Position, This->CommonBufferSize, BufferSize);
|
||||
DPRINT1("Position %u Buffer %p BufferSize %u ActiveIrpOffset %u\n", Position, Buffer, This->CommonBufferSize, BufferSize);
|
||||
|
||||
if (Position < This->CommonBufferOffset)
|
||||
{
|
||||
|
@ -836,6 +859,7 @@ IPortPinWaveCyclic_fnInit(
|
|||
This->KsPinDescriptor = KsPinDescriptor;
|
||||
This->ConnectDetails = ConnectDetails;
|
||||
This->Miniport = GetWaveCyclicMiniport(Port);
|
||||
KeInitializeDpc(&This->Dpc, StopStreamRoutine, (PVOID)This);
|
||||
|
||||
DeviceObject = GetDeviceObject(Port);
|
||||
|
||||
|
@ -904,7 +928,7 @@ IPortPinWaveCyclic_fnInit(
|
|||
This->CommonBuffer = This->DmaChannel->lpVtbl->SystemAddress(This->DmaChannel);
|
||||
This->Capture = Capture;
|
||||
|
||||
//Status = This->Stream->lpVtbl->SetNotificationFreq(This->Stream, 10, &This->FrameSize);
|
||||
Status = This->Stream->lpVtbl->SetNotificationFreq(This->Stream, 10, &This->FrameSize);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -67,8 +67,6 @@ SysAudioOpenVirtualDevice(
|
|||
IN ULONG DeviceNumber,
|
||||
PSYSAUDIODEVEXT DeviceExtension)
|
||||
{
|
||||
PSYSAUDIO_CLIENT_HANDELS Index;
|
||||
ULONG Count;
|
||||
PSYSAUDIO_CLIENT ClientInfo;
|
||||
PKSAUDIO_DEVICE_ENTRY Entry;
|
||||
PKSOBJECT_CREATE_ITEM CreateItem;
|
||||
|
@ -83,66 +81,26 @@ SysAudioOpenVirtualDevice(
|
|||
return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
|
||||
}
|
||||
|
||||
/* get client context */
|
||||
ClientInfo = (PSYSAUDIO_CLIENT)CreateItem->Context;
|
||||
|
||||
/* sanity check */
|
||||
ASSERT(ClientInfo);
|
||||
|
||||
/* check for valid device index */
|
||||
if (DeviceNumber >= ClientInfo->NumDevices)
|
||||
{
|
||||
/* invalid device index */
|
||||
return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
|
||||
}
|
||||
|
||||
/* get device context */
|
||||
Entry = GetListEntry(&DeviceExtension->KsAudioDeviceList, DeviceNumber);
|
||||
ASSERT(Entry != NULL);
|
||||
|
||||
/* get client context */
|
||||
ClientInfo = (PSYSAUDIO_CLIENT)CreateItem->Context;
|
||||
/* does the client already use a device */
|
||||
if (!ClientInfo->NumDevices)
|
||||
{
|
||||
/* first device to be openend */
|
||||
ClientInfo->Devs = ExAllocatePool(NonPagedPool, sizeof(SYSAUDIO_CLIENT_HANDELS));
|
||||
if (!ClientInfo->Devs)
|
||||
{
|
||||
/* no memory */
|
||||
return SetIrpIoStatus(Irp, STATUS_NO_MEMORY, 0);
|
||||
}
|
||||
|
||||
ClientInfo->NumDevices = 1;
|
||||
ClientInfo->Devs[0].DeviceId = DeviceNumber;
|
||||
ClientInfo->Devs[0].ClientHandles = NULL;
|
||||
ClientInfo->Devs[0].ClientHandlesCount = 0;
|
||||
/* increase usage count */
|
||||
Entry->NumberOfClients++;
|
||||
return SetIrpIoStatus(Irp, STATUS_SUCCESS, 0);
|
||||
}
|
||||
|
||||
/* check if device has already been openend */
|
||||
for(Count = 0; Count < ClientInfo->NumDevices; Count++)
|
||||
{
|
||||
if (ClientInfo->Devs[Count].DeviceId == DeviceNumber)
|
||||
{
|
||||
/* device has already been opened */
|
||||
return SetIrpIoStatus(Irp, STATUS_SUCCESS, 0);
|
||||
}
|
||||
}
|
||||
/* new device to be openend */
|
||||
Index = ExAllocatePool(NonPagedPool, sizeof(SYSAUDIO_CLIENT_HANDELS) * (ClientInfo->NumDevices + 1));
|
||||
if (!Index)
|
||||
{
|
||||
/* no memory */
|
||||
return SetIrpIoStatus(Irp, STATUS_NO_MEMORY, 0);
|
||||
}
|
||||
|
||||
if (ClientInfo->NumDevices)
|
||||
{
|
||||
/* copy device count array */
|
||||
RtlMoveMemory(Index, ClientInfo->Devs, ClientInfo->NumDevices * sizeof(SYSAUDIO_CLIENT_HANDELS));
|
||||
}
|
||||
|
||||
Index[ClientInfo->NumDevices].DeviceId = DeviceNumber;
|
||||
Index[ClientInfo->NumDevices].ClientHandlesCount = 0;
|
||||
Index[ClientInfo->NumDevices].ClientHandles = NULL;
|
||||
|
||||
/* increase usage count */
|
||||
Entry->NumberOfClients++;
|
||||
|
||||
ExFreePool(ClientInfo->Devs);
|
||||
|
||||
ClientInfo->Devs = Index;
|
||||
ClientInfo->NumDevices++;
|
||||
return SetIrpIoStatus(Irp, STATUS_SUCCESS, 0);
|
||||
}
|
||||
|
||||
|
@ -362,7 +320,7 @@ CreatePinWorkerRoutine(
|
|||
|
||||
if (WorkerContext->Entry->Pins[WorkerContext->PinConnect->PinId].MaxPinInstanceCount == 1)
|
||||
{
|
||||
/* store the pin handle there is the pin can only be instantiated once*/
|
||||
/* store the pin handle there if the pin can only be instantiated once*/
|
||||
WorkerContext->Entry->Pins[WorkerContext->PinConnect->PinId].PinHandle = RealPinHandle;
|
||||
}
|
||||
|
||||
|
@ -955,8 +913,7 @@ SysAudioHandleProperty(
|
|||
PSYSAUDIODEVEXT DeviceExtension;
|
||||
PKSAUDIO_DEVICE_ENTRY Entry;
|
||||
PSYSAUDIO_INSTANCE_INFO InstanceInfo;
|
||||
PSYSAUDIO_CLIENT ClientInfo;
|
||||
ULONG Count, BytesReturned;
|
||||
ULONG BytesReturned;
|
||||
PKSOBJECT_CREATE_ITEM CreateItem;
|
||||
UNICODE_STRING GuidString;
|
||||
|
||||
|
@ -1034,7 +991,7 @@ SysAudioHandleProperty(
|
|||
if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
|
||||
{
|
||||
/* too small buffer */
|
||||
return SetIrpIoStatus(Irp, STATUS_BUFFER_TOO_SMALL, sizeof(KSCOMPONENTID));
|
||||
return SetIrpIoStatus(Irp, STATUS_BUFFER_TOO_SMALL, sizeof(ULONG));
|
||||
}
|
||||
|
||||
if (Property->Flags & KSPROPERTY_TYPE_SET)
|
||||
|
@ -1042,23 +999,6 @@ SysAudioHandleProperty(
|
|||
Index = (PULONG)Irp->UserBuffer;
|
||||
return SysAudioOpenVirtualDevice(Irp, *Index, DeviceExtension);
|
||||
}
|
||||
else if (Property->Flags & KSPROPERTY_TYPE_GET)
|
||||
{
|
||||
Index = (PULONG)Irp->UserBuffer;
|
||||
/* get client context */
|
||||
ClientInfo = (PSYSAUDIO_CLIENT)CreateItem->Context;
|
||||
ASSERT(ClientInfo);
|
||||
/* does the client already use a device */
|
||||
if (!ClientInfo->NumDevices)
|
||||
{
|
||||
/* no device open */
|
||||
return SetIrpIoStatus(Irp, STATUS_UNSUCCESSFUL, 0);
|
||||
}
|
||||
/* store last opened device number */
|
||||
*Index = ClientInfo->Devs[ClientInfo->NumDevices-1].DeviceId;
|
||||
/* found no device with that device index open */
|
||||
return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(ULONG));
|
||||
}
|
||||
}
|
||||
else if (Property->Id == KSPROPERTY_SYSAUDIO_INSTANCE_INFO)
|
||||
{
|
||||
|
@ -1075,28 +1015,6 @@ SysAudioHandleProperty(
|
|||
{
|
||||
return SysAudioOpenVirtualDevice(Irp, InstanceInfo->DeviceNumber, DeviceExtension);
|
||||
}
|
||||
else if (Property->Flags & KSPROPERTY_TYPE_GET)
|
||||
{
|
||||
/* get client context */
|
||||
ClientInfo = (PSYSAUDIO_CLIENT)CreateItem->Context;
|
||||
ASSERT(ClientInfo);
|
||||
/* does the client already use a device */
|
||||
if (!ClientInfo->NumDevices)
|
||||
{
|
||||
/* no device open */
|
||||
return SetIrpIoStatus(Irp, STATUS_UNSUCCESSFUL, 0);
|
||||
}
|
||||
for(Count = 0; Count < ClientInfo->NumDevices; Count++)
|
||||
{
|
||||
if (ClientInfo->Devs[Count].DeviceId == InstanceInfo->DeviceNumber)
|
||||
{
|
||||
/* specified device is open */
|
||||
return SetIrpIoStatus(Irp, STATUS_SUCCESS, 0);
|
||||
}
|
||||
}
|
||||
/* found no device with that device index open */
|
||||
return SetIrpIoStatus(Irp, STATUS_UNSUCCESSFUL, 0);
|
||||
}
|
||||
}
|
||||
else if (Property->Id == (ULONG)-1)
|
||||
{
|
||||
|
|
|
@ -178,7 +178,7 @@ DeviceInterfaceChangeCallback(
|
|||
DeviceEntry = ExAllocatePool(NonPagedPool, sizeof(KSAUDIO_DEVICE_ENTRY));
|
||||
if (!DeviceEntry)
|
||||
{
|
||||
DPRINT1("No Mem\n");
|
||||
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
|
@ -188,14 +188,12 @@ DeviceInterfaceChangeCallback(
|
|||
DeviceEntry->DeviceName.Buffer = ExAllocatePool(NonPagedPool, DeviceEntry->DeviceName.MaximumLength);
|
||||
if (!DeviceEntry->DeviceName.Buffer)
|
||||
{
|
||||
DPRINT1("No Mem\n");
|
||||
ExFreePool(DeviceEntry);
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(RtlAppendUnicodeToString(&DeviceEntry->DeviceName, L"\\??\\")))
|
||||
{
|
||||
DPRINT1("No Mem\n");
|
||||
ExFreePool(DeviceEntry->DeviceName.Buffer);
|
||||
ExFreePool(DeviceEntry);
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
|
@ -203,14 +201,11 @@ DeviceInterfaceChangeCallback(
|
|||
|
||||
if (!NT_SUCCESS(RtlAppendUnicodeStringToString(&DeviceEntry->DeviceName, Event->SymbolicLinkName)))
|
||||
{
|
||||
DPRINT1("No Mem\n");
|
||||
ExFreePool(DeviceEntry->DeviceName.Buffer);
|
||||
ExFreePool(DeviceEntry);
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
DPRINT1("Sym %wZ\n", &DeviceEntry->DeviceName);
|
||||
|
||||
Status = OpenDevice(&DeviceEntry->DeviceName, &DeviceEntry->Handle, &DeviceEntry->FileObject);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
|
@ -231,24 +226,12 @@ DeviceInterfaceChangeCallback(
|
|||
|
||||
return Status;
|
||||
}
|
||||
else if (IsEqualGUIDAligned(&Event->Event,
|
||||
&GUID_DEVICE_INTERFACE_REMOVAL))
|
||||
{
|
||||
DPRINT1("Remove interface to audio device!\n");
|
||||
///FIXME
|
||||
///
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
UNICODE_STRING EventName, InterfaceGuid;
|
||||
|
||||
RtlStringFromGUID(&Event->Event, &EventName);
|
||||
RtlStringFromGUID(&Event->InterfaceClassGuid, &InterfaceGuid);
|
||||
DPRINT1("Unknown event: Event %wZ GUID %wZ\n", &EventName, &InterfaceGuid);
|
||||
DPRINT1("Remove interface to audio device!\n");
|
||||
UNIMPLEMENTED
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
|
|
|
@ -24,17 +24,13 @@ Dispatch_fnDeviceIoControl(
|
|||
{
|
||||
PIO_STACK_LOCATION IoStack;
|
||||
|
||||
//DPRINT("Dispatch_fnDeviceIoControl called DeviceObject %p Irp %p\n", DeviceObject);
|
||||
|
||||
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
if (IoStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_KS_PROPERTY)
|
||||
{
|
||||
return SysAudioHandleProperty(DeviceObject, Irp);
|
||||
}
|
||||
|
||||
DPRINT1("Dispatch_fnDeviceIoControl Unhandeled %x\n", IoStack->Parameters.DeviceIoControl.IoControlCode);
|
||||
DbgBreakPoint();
|
||||
|
||||
/* unsupported request */
|
||||
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
||||
Irp->IoStatus.Information = 0;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
|
@ -47,8 +43,7 @@ Dispatch_fnRead(
|
|||
PDEVICE_OBJECT DeviceObject,
|
||||
PIRP Irp)
|
||||
{
|
||||
DPRINT1("Dispatch_fnRead called DeviceObject %p Irp %p\n", DeviceObject);
|
||||
|
||||
/* unsupported request */
|
||||
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
||||
Irp->IoStatus.Information = 0;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
|
@ -61,8 +56,7 @@ Dispatch_fnWrite(
|
|||
PDEVICE_OBJECT DeviceObject,
|
||||
PIRP Irp)
|
||||
{
|
||||
DPRINT1("Dispatch_fnWrite called DeviceObject %p Irp %p\n", DeviceObject);
|
||||
|
||||
/* unsupported request */
|
||||
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
||||
Irp->IoStatus.Information = 0;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
|
@ -75,9 +69,6 @@ Dispatch_fnFlush(
|
|||
PDEVICE_OBJECT DeviceObject,
|
||||
PIRP Irp)
|
||||
{
|
||||
DPRINT1("Dispatch_fnFlush called DeviceObject %p Irp %p\n", DeviceObject);
|
||||
//FIXME
|
||||
// cleanup resources
|
||||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
Irp->IoStatus.Information = 0;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
|
@ -127,8 +118,9 @@ Dispatch_fnClose(
|
|||
}
|
||||
else
|
||||
{
|
||||
/* this is pin which can only be instantiated once
|
||||
* so we just need to release the reference count on that pin */
|
||||
/* this is a pin which can only be instantiated once
|
||||
* so we just need to release the reference count on that pin
|
||||
*/
|
||||
Entry->Pins[Client->Devs[Index].ClientHandles[SubIndex].PinId].References--;
|
||||
|
||||
DispatchContext = (PDISPATCH_CONTEXT)Client->Devs[Index].ClientHandles[SubIndex].DispatchContext;
|
||||
|
@ -274,6 +266,8 @@ DispatchCreateSysAudio(
|
|||
PKSOBJECT_CREATE_ITEM CreateItem;
|
||||
PIO_STACK_LOCATION IoStatus;
|
||||
LPWSTR Buffer;
|
||||
PSYSAUDIODEVEXT DeviceExtension;
|
||||
ULONG Index;
|
||||
|
||||
static LPWSTR KS_NAME_PIN = L"{146F1A80-4791-11D0-A5D6-28DB04C10000}";
|
||||
|
||||
|
@ -300,16 +294,55 @@ DispatchCreateSysAudio(
|
|||
/* allocate create item */
|
||||
CreateItem = ExAllocatePool(NonPagedPool, sizeof(KSOBJECT_CREATE_ITEM));
|
||||
if (!CreateItem)
|
||||
{
|
||||
Irp->IoStatus.Information = 0;
|
||||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
Client = ExAllocatePool(NonPagedPool, sizeof(SYSAUDIO_CLIENT));
|
||||
if (!Client)
|
||||
{
|
||||
ExFreePool(CreateItem);
|
||||
|
||||
Irp->IoStatus.Information = 0;
|
||||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
/* initialize client struct */
|
||||
RtlZeroMemory(Client, sizeof(SYSAUDIO_CLIENT));
|
||||
|
||||
/* get device extension */
|
||||
DeviceExtension = (PSYSAUDIODEVEXT) DeviceObject->DeviceExtension;
|
||||
|
||||
Client->NumDevices = DeviceExtension->NumberOfKsAudioDevices;
|
||||
/* has sysaudio found any devices */
|
||||
if (Client->NumDevices)
|
||||
{
|
||||
Client->Devs = ExAllocatePool(NonPagedPool, sizeof(SYSAUDIO_CLIENT_HANDELS) * Client->NumDevices);
|
||||
if (!Client->Devs)
|
||||
{
|
||||
ExFreePool(CreateItem);
|
||||
ExFreePool(Client);
|
||||
Irp->IoStatus.Information = 0;
|
||||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no devices yet available */
|
||||
Client->Devs = NULL;
|
||||
}
|
||||
|
||||
/* Initialize devs array */
|
||||
for(Index = 0; Index < Client->NumDevices; Index++)
|
||||
{
|
||||
Client->Devs[Index].DeviceId = Index;
|
||||
Client->Devs[Index].ClientHandles = NULL;
|
||||
Client->Devs[Index].ClientHandlesCount = 0;
|
||||
}
|
||||
|
||||
/* zero create struct */
|
||||
RtlZeroMemory(CreateItem, sizeof(KSOBJECT_CREATE_ITEM));
|
||||
|
|
|
@ -46,27 +46,33 @@ SysAudio_Pnp(
|
|||
UNICODE_STRING SymlinkName = RTL_CONSTANT_STRING(L"\\DosDevices\\sysaudio");
|
||||
SYSAUDIODEVEXT *DeviceExtension;
|
||||
|
||||
/* Get current irp stack */
|
||||
IrpStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
|
||||
DPRINT("SysAudio_Pnp called for func %x\n", IrpStack->MinorFunction);
|
||||
|
||||
/* Fetch the device extension */
|
||||
DeviceExtension = (SYSAUDIODEVEXT*)DeviceObject->DeviceExtension;
|
||||
ASSERT(DeviceExtension);
|
||||
|
||||
if (IrpStack->MinorFunction == IRP_MN_REMOVE_DEVICE)
|
||||
{
|
||||
/* Unregister the echo cancel hook */
|
||||
if (DeviceExtension->EchoCancelNotificationEntry)
|
||||
IoUnregisterPlugPlayNotification(DeviceExtension->EchoCancelNotificationEntry);
|
||||
|
||||
/* Unregister the ks audio hook */
|
||||
if (DeviceExtension->KsAudioNotificationEntry)
|
||||
IoUnregisterPlugPlayNotification(DeviceExtension->KsAudioNotificationEntry);
|
||||
|
||||
/* Destroy our symbolic link */
|
||||
IoDeleteSymbolicLink(&SymlinkName);
|
||||
}
|
||||
else if (IrpStack->MinorFunction == IRP_MN_QUERY_PNP_DEVICE_STATE)
|
||||
{
|
||||
/* Sysaudio can not be disabled */
|
||||
Irp->IoStatus.Information |= PNP_DEVICE_NOT_DISABLEABLE;
|
||||
}
|
||||
|
||||
/* Perform default pnp actions */
|
||||
return KsDefaultDispatchPnp(DeviceObject, Irp);
|
||||
}
|
||||
|
||||
|
@ -79,13 +85,12 @@ SysAudio_InstallDevice(
|
|||
UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\sysaudio");
|
||||
UNICODE_STRING SymlinkName = RTL_CONSTANT_STRING(L"\\DosDevices\\sysaudio");
|
||||
PDEVICE_OBJECT DeviceObject;
|
||||
//PDEVICE_OBJECT NextDeviceObject;
|
||||
SYSAUDIODEVEXT *DeviceExtension;
|
||||
|
||||
|
||||
DPRINT1("SysAudio_InstallDevice called\n");
|
||||
|
||||
/* create the device */
|
||||
/* Create the device */
|
||||
Status = IoCreateDevice(DriverObject,
|
||||
sizeof(SYSAUDIODEVEXT),
|
||||
&DeviceName,
|
||||
|
@ -94,19 +99,19 @@ SysAudio_InstallDevice(
|
|||
FALSE,
|
||||
&DeviceObject);
|
||||
|
||||
/* check for success */
|
||||
/* Check for success */
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DPRINT("Failed to create \\Device\\sysaudio !\n");
|
||||
return Status;
|
||||
}
|
||||
|
||||
/* register device interfaces */
|
||||
/* Register device interfaces */
|
||||
Status = SysAudioRegisterDeviceInterfaces(DeviceObject);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
/* failed to register
|
||||
* create a hack interface
|
||||
/* Failed to register
|
||||
* Create a hack interface
|
||||
*/
|
||||
Status = IoCreateSymbolicLink(&SymlinkName, &DeviceName);
|
||||
if (!NT_SUCCESS(Status))
|
||||
|
@ -116,15 +121,18 @@ SysAudio_InstallDevice(
|
|||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
/* Acquire device extension */
|
||||
DeviceExtension = (SYSAUDIODEVEXT*)DeviceObject->DeviceExtension;
|
||||
/* initialize device extension */
|
||||
/* Initialize device extension */
|
||||
RtlZeroMemory(DeviceExtension, sizeof(SYSAUDIODEVEXT));
|
||||
|
||||
/* Initialize the mutex */
|
||||
KeInitializeMutex(&DeviceExtension->Mutex, 0);
|
||||
//DeviceExtension->PhysicalDeviceObject = PhysicalDeviceObject;
|
||||
|
||||
/* Initialize the ks audio device list */
|
||||
InitializeListHead(&DeviceExtension->KsAudioDeviceList);
|
||||
|
||||
/* Allocate kernel streaming device header */
|
||||
Status = SysAudioAllocateDeviceHeader(DeviceExtension);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
|
@ -132,6 +140,7 @@ SysAudio_InstallDevice(
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Register device notification hooks */
|
||||
Status = SysAudioRegisterNotifications(DriverObject,
|
||||
DeviceObject);
|
||||
if (!NT_SUCCESS(Status))
|
||||
|
@ -140,6 +149,7 @@ SysAudio_InstallDevice(
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Load kmixer */
|
||||
Status = SysAudioOpenKMixer(DeviceExtension);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
|
@ -152,7 +162,7 @@ SysAudio_InstallDevice(
|
|||
/* clear initializing flag */
|
||||
DeviceObject->Flags &= ~ DO_DEVICE_INITIALIZING;
|
||||
|
||||
DPRINT("Device SysAudio_InstallDevice result %x\n", Status);
|
||||
/* Done */
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
cleanup:
|
||||
|
@ -168,22 +178,30 @@ cleanup:
|
|||
return Status;
|
||||
}
|
||||
|
||||
NTSTATUS NTAPI
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
DriverEntry(
|
||||
IN PDRIVER_OBJECT DriverObject,
|
||||
IN PUNICODE_STRING RegistryPath)
|
||||
{
|
||||
DPRINT1("System audio graph builder (sysaudio) started\n");
|
||||
|
||||
/* Let ks handle these */
|
||||
KsSetMajorFunctionHandler(DriverObject, IRP_MJ_CREATE);
|
||||
KsSetMajorFunctionHandler(DriverObject, IRP_MJ_CLOSE);
|
||||
KsSetMajorFunctionHandler(DriverObject, IRP_MJ_WRITE);
|
||||
KsSetMajorFunctionHandler(DriverObject, IRP_MJ_DEVICE_CONTROL);
|
||||
|
||||
/* Let ks handle these */
|
||||
DriverObject->MajorFunction[IRP_MJ_POWER] = KsDefaultDispatchPower;
|
||||
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = KsDefaultForwardIrp;
|
||||
DriverObject->MajorFunction[IRP_MJ_PNP] = SysAudio_Pnp;
|
||||
DriverObject->DriverUnload = SysAudio_Unload;
|
||||
|
||||
/* Use provided ks unload function */
|
||||
DriverObject->DriverUnload = KsNullDriverUnload;
|
||||
|
||||
/* Sysaudio needs to do work on pnp, so handle it */
|
||||
DriverObject->MajorFunction[IRP_MJ_PNP] = SysAudio_Pnp;
|
||||
|
||||
/* Call our initialization function */
|
||||
return SysAudio_InstallDevice(DriverObject);
|
||||
}
|
||||
|
|
|
@ -27,13 +27,19 @@ Pin_fnDeviceIoControl(
|
|||
ULONG BytesReturned;
|
||||
PIO_STACK_LOCATION IoStack;
|
||||
|
||||
DPRINT1("Pin_fnDeviceIoControl called DeviceObject %p Irp %p\n", DeviceObject);
|
||||
DPRINT("Pin_fnDeviceIoControl called DeviceObject %p Irp %p\n", DeviceObject);
|
||||
|
||||
/* Get current stack location */
|
||||
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
|
||||
/* The dispatch context is stored in the FsContext2 member */
|
||||
Context = (PDISPATCH_CONTEXT)IoStack->FileObject->FsContext2;
|
||||
ASSERT(Context);
|
||||
|
||||
/* Sanity check */
|
||||
ASSERT(Context);
|
||||
ASSERT(Context->FileObject != NULL);
|
||||
|
||||
/* Re-dispatch the request to the real target pin */
|
||||
Status = KsSynchronousIoControlDevice(Context->FileObject, KernelMode, IoStack->Parameters.DeviceIoControl.IoControlCode,
|
||||
IoStack->Parameters.DeviceIoControl.Type3InputBuffer,
|
||||
IoStack->Parameters.DeviceIoControl.InputBufferLength,
|
||||
|
@ -41,11 +47,12 @@ Pin_fnDeviceIoControl(
|
|||
IoStack->Parameters.DeviceIoControl.OutputBufferLength,
|
||||
&BytesReturned);
|
||||
|
||||
DPRINT1("Status %x\n", Status);
|
||||
|
||||
/* Save status and information */
|
||||
Irp->IoStatus.Information = BytesReturned;
|
||||
Irp->IoStatus.Status = Status;
|
||||
/* Complete the irp */
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
/* Done */
|
||||
return Status;
|
||||
}
|
||||
|
||||
|
@ -55,27 +62,43 @@ Pin_fnRead(
|
|||
PDEVICE_OBJECT DeviceObject,
|
||||
PIRP Irp)
|
||||
{
|
||||
DPRINT1("Pin_fnRead called DeviceObject %p Irp %p\n", DeviceObject);
|
||||
PDISPATCH_CONTEXT Context;
|
||||
PIO_STACK_LOCATION IoStack;
|
||||
ULONG BytesReturned;
|
||||
NTSTATUS Status;
|
||||
|
||||
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
||||
/* Get current stack location */
|
||||
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
|
||||
/* The dispatch context is stored in the FsContext2 member */
|
||||
Context = (PDISPATCH_CONTEXT)IoStack->FileObject->FsContext2;
|
||||
|
||||
/* Sanity check */
|
||||
ASSERT(Context);
|
||||
ASSERT(Context->FileObject != NULL);
|
||||
|
||||
/* Re-dispatch the request to the real target pin */
|
||||
Status = KsSynchronousIoControlDevice(Context->FileObject, KernelMode, IOCTL_KS_READ_STREAM,
|
||||
MmGetMdlVirtualAddress(Irp->MdlAddress),
|
||||
IoStack->Parameters.Read.Length,
|
||||
NULL,
|
||||
0,
|
||||
&BytesReturned);
|
||||
|
||||
if (Context->hMixerPin && Context->MixerFileObject)
|
||||
{
|
||||
// FIXME
|
||||
// call kmixer to convert stream
|
||||
UNIMPLEMENTED
|
||||
}
|
||||
|
||||
/* Save status and information */
|
||||
Irp->IoStatus.Status = Status;
|
||||
Irp->IoStatus.Information = 0;
|
||||
/* Complete the irp */
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
PinWriteCompletionRoutine(
|
||||
IN PDEVICE_OBJECT DeviceObject,
|
||||
IN PIRP Irp,
|
||||
IN PVOID Context)
|
||||
{
|
||||
PIRP CIrp = (PIRP)Context;
|
||||
|
||||
CIrp->IoStatus.Status = STATUS_SUCCESS;
|
||||
CIrp->IoStatus.Information = 0;
|
||||
IoCompleteRequest(CIrp, IO_NO_INCREMENT);
|
||||
return STATUS_SUCCESS;
|
||||
/* Done */
|
||||
return Status;
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
|
@ -89,23 +112,38 @@ Pin_fnWrite(
|
|||
ULONG BytesReturned;
|
||||
NTSTATUS Status;
|
||||
|
||||
DPRINT1("Pin_fnWrite called DeviceObject %p Irp %p\n", DeviceObject);
|
||||
|
||||
/* Get current stack location */
|
||||
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
|
||||
/* The dispatch context is stored in the FsContext2 member */
|
||||
Context = (PDISPATCH_CONTEXT)IoStack->FileObject->FsContext2;
|
||||
ASSERT(Context);
|
||||
|
||||
/* Sanity check */
|
||||
ASSERT(Context);
|
||||
ASSERT(Context->FileObject != NULL);
|
||||
|
||||
if (Context->hMixerPin && Context->MixerFileObject)
|
||||
{
|
||||
// FIXME
|
||||
// call kmixer to convert stream
|
||||
UNIMPLEMENTED
|
||||
}
|
||||
|
||||
|
||||
/* Re-dispatch the request to the real target pin */
|
||||
Status = KsSynchronousIoControlDevice(Context->FileObject, KernelMode, IOCTL_KS_WRITE_STREAM,
|
||||
MmGetMdlVirtualAddress(Irp->MdlAddress),
|
||||
IoStack->Parameters.Write.Length,
|
||||
IoStack->Parameters.Read.Length,
|
||||
NULL,
|
||||
0,
|
||||
&BytesReturned);
|
||||
|
||||
Irp->IoStatus.Information = BytesReturned;
|
||||
/* Save status and information */
|
||||
Irp->IoStatus.Status = Status;
|
||||
Irp->IoStatus.Information = 0;
|
||||
/* Complete the irp */
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
/* Done */
|
||||
return Status;
|
||||
}
|
||||
|
||||
|
@ -115,12 +153,59 @@ Pin_fnFlush(
|
|||
PDEVICE_OBJECT DeviceObject,
|
||||
PIRP Irp)
|
||||
{
|
||||
DPRINT1("Pin_fnFlush called DeviceObject %p Irp %p\n", DeviceObject);
|
||||
PDISPATCH_CONTEXT Context;
|
||||
PIO_STACK_LOCATION IoStack;
|
||||
PDEVICE_OBJECT PinDeviceObject;
|
||||
PIRP PinIrp;
|
||||
IO_STATUS_BLOCK IoStatus;
|
||||
KEVENT Event;
|
||||
NTSTATUS Status = STATUS_UNSUCCESSFUL;
|
||||
|
||||
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
||||
/* Get current stack location */
|
||||
IoStack = IoGetCurrentIrpStackLocation(Irp);
|
||||
|
||||
/* The dispatch context is stored in the FsContext2 member */
|
||||
Context = (PDISPATCH_CONTEXT)IoStack->FileObject->FsContext2;
|
||||
|
||||
/* Sanity check */
|
||||
ASSERT(Context);
|
||||
ASSERT(Context->FileObject != NULL);
|
||||
|
||||
/* Get Pin's device object */
|
||||
PinDeviceObject = IoGetRelatedDeviceObject(Context->FileObject);
|
||||
|
||||
/* Initialize notification event */
|
||||
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
||||
|
||||
/* build target irp */
|
||||
PinIrp = IoBuildSynchronousFsdRequest(IRP_MJ_FLUSH_BUFFERS, PinDeviceObject, NULL, 0, NULL, &Event, &IoStatus);
|
||||
if (PinIrp)
|
||||
{
|
||||
|
||||
/* Get the next stack location */
|
||||
IoStack = IoGetNextIrpStackLocation(PinIrp);
|
||||
/* The file object must be present in the irp as it contains the KSOBJECT_HEADER */
|
||||
IoStack->FileObject = Context->FileObject;
|
||||
|
||||
/* call the driver */
|
||||
Status = IoCallDriver(PinDeviceObject, PinIrp);
|
||||
/* Has request already completed ? */
|
||||
if (Status == STATUS_PENDING)
|
||||
{
|
||||
/* Wait untill the request has completed */
|
||||
KeWaitForSingleObject(&Event, UserRequest, KernelMode, FALSE, NULL);
|
||||
/* Update status */
|
||||
Status = IoStatus.Status;
|
||||
}
|
||||
}
|
||||
|
||||
/* store status */
|
||||
Irp->IoStatus.Status = Status;
|
||||
Irp->IoStatus.Information = 0;
|
||||
/* Complete the irp */
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
/* Done */
|
||||
return Status;
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
|
|
|
@ -3,84 +3,93 @@
|
|||
|
||||
typedef struct
|
||||
{
|
||||
BOOL bHandle;
|
||||
ULONG PinId;
|
||||
HANDLE hPin;
|
||||
HANDLE hMixer;
|
||||
PVOID DispatchContext;
|
||||
BOOL bHandle; // indicates if an audio pin can be instantated more than once
|
||||
ULONG PinId; // specifies the pin id
|
||||
HANDLE hPin; // handle to audio irp pin
|
||||
HANDLE hMixer; // handle to mixer pin
|
||||
PVOID DispatchContext; // pointer to dispatch context
|
||||
}SYSAUDIO_PIN_HANDLE, *PSYSAUDIO_PIN_HANDLE;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ULONG DeviceId;
|
||||
ULONG ClientHandlesCount;
|
||||
PSYSAUDIO_PIN_HANDLE ClientHandles;
|
||||
|
||||
ULONG DeviceId; //specifies the device id
|
||||
ULONG ClientHandlesCount; // number of client handles
|
||||
PSYSAUDIO_PIN_HANDLE ClientHandles; // array of client handles
|
||||
}SYSAUDIO_CLIENT_HANDELS, *PSYSAUDIO_CLIENT_HANDELS;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ULONG NumDevices;
|
||||
PSYSAUDIO_CLIENT_HANDELS Devs;
|
||||
ULONG NumDevices; // number of devices in Devs array
|
||||
PSYSAUDIO_CLIENT_HANDELS Devs; // array of client handles
|
||||
|
||||
}SYSAUDIO_CLIENT, *PSYSAUDIO_CLIENT;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ULONG MaxPinInstanceCount;
|
||||
HANDLE PinHandle;
|
||||
ULONG References;
|
||||
KSPIN_DATAFLOW DataFlow;
|
||||
KSPIN_COMMUNICATION Communication;
|
||||
ULONG MaxPinInstanceCount; // maximum times a audio irp pin can be instantiated
|
||||
HANDLE PinHandle; // handle to audio irp pin
|
||||
ULONG References; // number of clients having a reference to this audio irp pin
|
||||
KSPIN_DATAFLOW DataFlow; // specifies data flow
|
||||
KSPIN_COMMUNICATION Communication; // pin type
|
||||
}PIN_INFO;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
LIST_ENTRY Entry;
|
||||
HANDLE Handle;
|
||||
PFILE_OBJECT FileObject;
|
||||
UNICODE_STRING DeviceName;
|
||||
ULONG NumberOfClients;
|
||||
LIST_ENTRY Entry; // device entry for KsAudioDeviceList
|
||||
HANDLE Handle; // handle to audio device
|
||||
PFILE_OBJECT FileObject; // file object for audio device
|
||||
UNICODE_STRING DeviceName; // symbolic link of audio device
|
||||
ULONG NumberOfClients; // number of clients referenced audio device
|
||||
|
||||
ULONG NumberOfPins;
|
||||
PIN_INFO * Pins;
|
||||
ULONG NumberOfPins; // number of pins of audio device
|
||||
PIN_INFO * Pins; // array of PIN_INFO
|
||||
|
||||
ULONG NumWaveOutPin;
|
||||
ULONG NumWaveInPin;
|
||||
ULONG NumWaveOutPin; // number of wave out pins
|
||||
ULONG NumWaveInPin; // number of wave in pins
|
||||
|
||||
}KSAUDIO_DEVICE_ENTRY, *PKSAUDIO_DEVICE_ENTRY;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
KSDEVICE_HEADER KsDeviceHeader;
|
||||
PDEVICE_OBJECT PhysicalDeviceObject;
|
||||
PDEVICE_OBJECT NextDeviceObject;
|
||||
ULONG NumberOfKsAudioDevices;
|
||||
KSDEVICE_HEADER KsDeviceHeader; // ks streaming header - must always be first item in device extension
|
||||
PDEVICE_OBJECT PhysicalDeviceObject; // pdo
|
||||
PDEVICE_OBJECT NextDeviceObject; // lower device object
|
||||
ULONG NumberOfKsAudioDevices; // number of audio devices
|
||||
|
||||
LIST_ENTRY KsAudioDeviceList;
|
||||
PVOID KsAudioNotificationEntry;
|
||||
PVOID EchoCancelNotificationEntry;
|
||||
KMUTEX Mutex;
|
||||
LIST_ENTRY KsAudioDeviceList; // audio device list
|
||||
PVOID KsAudioNotificationEntry; // ks audio notification hook
|
||||
PVOID EchoCancelNotificationEntry; // ks echo cancel notification hook
|
||||
KMUTEX Mutex; // audio device list mutex
|
||||
|
||||
PFILE_OBJECT KMixerFileObject;
|
||||
HANDLE KMixerHandle;
|
||||
PFILE_OBJECT KMixerFileObject; // mixer file object
|
||||
HANDLE KMixerHandle; // mixer file handle
|
||||
|
||||
}SYSAUDIODEVEXT, *PSYSAUDIODEVEXT;
|
||||
|
||||
// struct DISPATCH_CONTEXT
|
||||
//
|
||||
// This structure is used to dispatch read / write / device io requests
|
||||
// It is stored in the file object FsContext2 member
|
||||
// Note: FsContext member is reserved for ks object header
|
||||
|
||||
typedef struct
|
||||
{
|
||||
HANDLE Handle;
|
||||
PFILE_OBJECT FileObject;
|
||||
ULONG PinId;
|
||||
PKSAUDIO_DEVICE_ENTRY AudioEntry;
|
||||
HANDLE Handle; // audio irp pin handle
|
||||
PFILE_OBJECT FileObject; // audio irp pin file object
|
||||
ULONG PinId; // pin id of device
|
||||
PKSAUDIO_DEVICE_ENTRY AudioEntry; // pointer to audio device entry
|
||||
|
||||
HANDLE hMixerPin;
|
||||
PFILE_OBJECT MixerFileObject;
|
||||
HANDLE hMixerPin; // handle to mixer pin
|
||||
PFILE_OBJECT MixerFileObject; // mixer file object
|
||||
}DISPATCH_CONTEXT, *PDISPATCH_CONTEXT;
|
||||
|
||||
// struct PIN_WORKER_CONTEXT
|
||||
//
|
||||
// This structure holds all information required
|
||||
// to create audio irp pin, mixer pin and virtual sysaudio pin
|
||||
//
|
||||
typedef struct
|
||||
{
|
||||
PIRP Irp;
|
||||
|
|
Loading…
Reference in a new issue