xref: /reactos/drivers/wdm/audio/legacy/wdmaud/entry.c (revision 53221834)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Kernel Streaming
4  * FILE:            drivers/wdm/audio/legacy/wdmaud/main.c
5  * PURPOSE:         System Audio graph builder
6  * PROGRAMMER:      Andrew Greenwood
7  *                  Johannes Anderwald
8  */
9 
10 #include "wdmaud.h"
11 
12 #define NDEBUG
13 #include <debug.h>
14 
15 const GUID KSCATEGORY_SYSAUDIO = {0xA7C7A5B1L, 0x5AF3, 0x11D1, {0x9C, 0xED, 0x00, 0xA0, 0x24, 0xBF, 0x04, 0x07}};
16 const GUID KSCATEGORY_WDMAUD   = {0x3E227E76L, 0x690D, 0x11D2, {0x81, 0x61, 0x00, 0x00, 0xF8, 0x77, 0x5B, 0xF1}};
17 
18 IO_WORKITEM_ROUTINE WdmAudInitWorkerRoutine;
19 IO_TIMER_ROUTINE WdmAudTimerRoutine;
20 
21 VOID
22 NTAPI
23 WdmAudInitWorkerRoutine(
24     IN PDEVICE_OBJECT DeviceObject,
25     IN PVOID Context)
26 {
27     NTSTATUS Status;
28     PWDMAUD_DEVICE_EXTENSION DeviceExtension;
29     ULONG DeviceCount;
30 
31     /* get device extension */
32     DeviceExtension = (PWDMAUD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
33 
34 
35     if (DeviceExtension->FileObject == NULL)
36     {
37         /* find available sysaudio devices */
38         Status = WdmAudOpenSysAudioDevices(DeviceObject, DeviceExtension);
39         if (!NT_SUCCESS(Status))
40         {
41             DPRINT1("WdmAudOpenSysAudioDevices failed with %x\n", Status);
42             return;
43         }
44     }
45 
46 
47     /* get device count */
48     DeviceCount = GetSysAudioDeviceCount(DeviceObject);
49 
50     DPRINT("WdmAudInitWorkerRoutine SysAudioDeviceCount %ld\n", DeviceCount);
51 
52     /* was a device added / removed */
53     if (DeviceCount != DeviceExtension->SysAudioDeviceCount)
54     {
55         /* init mmixer library */
56         Status = WdmAudMixerInitialize(DeviceObject);
57         DPRINT("WdmAudMixerInitialize Status %x WaveIn %lu WaveOut %lu Mixer %lu\n", Status, WdmAudGetWaveInDeviceCount(), WdmAudGetWaveOutDeviceCount(), WdmAudGetMixerDeviceCount());
58 
59         /* store sysaudio device count */
60         DeviceExtension->SysAudioDeviceCount = DeviceCount;
61     }
62 
63     /* signal completion */
64     KeSetEvent(&DeviceExtension->InitializationCompletionEvent, IO_NO_INCREMENT, FALSE);
65 
66     /* reset work item status indicator */
67     InterlockedDecrement((volatile long *)&DeviceExtension->WorkItemActive);
68 }
69 
70 VOID
71 NTAPI
72 WdmAudTimerRoutine(
73     IN PDEVICE_OBJECT DeviceObject,
74     IN PVOID Context)
75 {
76     PWDMAUD_DEVICE_EXTENSION DeviceExtension;
77 
78     /* get device extension */
79     DeviceExtension = (PWDMAUD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
80 
81     if (InterlockedCompareExchange((volatile long *)&DeviceExtension->WorkItemActive, 1, 0) == 0)
82     {
83         /* queue work item */
84         IoQueueWorkItem(DeviceExtension->WorkItem, WdmAudInitWorkerRoutine, DelayedWorkQueue, (PVOID)DeviceExtension);
85     }
86 }
87 
88 NTSTATUS
89 NTAPI
90 WdmaudAddDevice(
91     IN PDRIVER_OBJECT DriverObject,
92     IN PDEVICE_OBJECT PhysicalDeviceObject)
93 {
94     PDEVICE_OBJECT DeviceObject;
95     NTSTATUS Status;
96     PWDMAUD_DEVICE_EXTENSION DeviceExtension;
97 
98     DPRINT("WdmaudAddDevice called\n");
99 
100     Status = IoCreateDevice(DriverObject,
101                             sizeof(WDMAUD_DEVICE_EXTENSION),
102                             NULL,
103                             FILE_DEVICE_KS,
104                             0,
105                             FALSE,
106                             &DeviceObject);
107 
108     if (!NT_SUCCESS(Status))
109     {
110         DPRINT1("IoCreateDevice failed with %x\n", Status);
111         return Status;
112     }
113 
114     /* get device extension */
115     DeviceExtension = (PWDMAUD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
116     RtlZeroMemory(DeviceExtension, sizeof(WDMAUD_DEVICE_EXTENSION));
117 
118     /* allocate work item */
119     DeviceExtension->WorkItem = IoAllocateWorkItem(DeviceObject);
120     if (!DeviceExtension->WorkItem)
121     {
122         /* failed to allocate work item */
123         IoDeleteDevice(DeviceObject);
124         return STATUS_INSUFFICIENT_RESOURCES;
125     }
126 
127     /* register device interfaces */
128     Status = WdmAudRegisterDeviceInterface(PhysicalDeviceObject, DeviceExtension);
129     if (!NT_SUCCESS(Status))
130     {
131         DPRINT1("WdmRegisterDeviceInterface failed with %x\n", Status);
132         IoDeleteDevice(DeviceObject);
133         return Status;
134     }
135 
136     /* initialize sysaudio device list */
137     InitializeListHead(&DeviceExtension->SysAudioDeviceList);
138 
139     /* initialize client context device list */
140     InitializeListHead(&DeviceExtension->WdmAudClientList);
141 
142     /* initialize spinlock */
143     KeInitializeSpinLock(&DeviceExtension->Lock);
144 
145     /* initialization completion event */
146     KeInitializeEvent(&DeviceExtension->InitializationCompletionEvent, NotificationEvent, FALSE);
147 
148     /* initialize timer */
149     IoInitializeTimer(DeviceObject, WdmAudTimerRoutine, (PVOID)WdmAudTimerRoutine);
150 
151     /* allocate ks device header */
152     Status = KsAllocateDeviceHeader(&DeviceExtension->DeviceHeader, 0, NULL);
153     if (!NT_SUCCESS(Status))
154     {
155         DPRINT1("KsAllocateDeviceHeader failed with %x\n", Status);
156         IoDeleteDevice(DeviceObject);
157         return Status;
158     }
159 
160     /* attach to device stack */
161     DeviceExtension->NextDeviceObject = IoAttachDeviceToDeviceStack(DeviceObject, PhysicalDeviceObject);
162     KsSetDevicePnpAndBaseObject(DeviceExtension->DeviceHeader, DeviceExtension->NextDeviceObject, DeviceObject);
163 
164 
165     /* start the timer */
166     IoStartTimer(DeviceObject);
167 
168     DeviceObject->Flags |= DO_DIRECT_IO | DO_POWER_PAGABLE;
169     DeviceObject->Flags &= ~ DO_DEVICE_INITIALIZING;
170 
171     return STATUS_SUCCESS;
172 }
173 
174 VOID
175 NTAPI
176 WdmAudUnload(
177     IN PDRIVER_OBJECT driver)
178 {
179     DPRINT("WdmAudUnload called\n");
180 }
181 
182 NTSTATUS
183 NTAPI
184 WdmAudPnp(
185     IN  PDEVICE_OBJECT DeviceObject,
186     IN  PIRP Irp)
187 {
188     PIO_STACK_LOCATION IrpStack;
189 
190     DPRINT("WdmAudPnp called\n");
191 
192     IrpStack = IoGetCurrentIrpStackLocation(Irp);
193 
194     if (IrpStack->MinorFunction == IRP_MN_QUERY_PNP_DEVICE_STATE)
195     {
196         Irp->IoStatus.Information |= PNP_DEVICE_NOT_DISABLEABLE;
197         return KsDefaultDispatchPnp(DeviceObject, Irp);
198     }
199     return KsDefaultDispatchPnp(DeviceObject, Irp);
200 }
201 
202 
203 NTSTATUS
204 NTAPI
205 WdmAudCreate(
206     IN  PDEVICE_OBJECT DeviceObject,
207     IN  PIRP Irp)
208 {
209     NTSTATUS Status;
210     PIO_STACK_LOCATION IoStack;
211     PWDMAUD_CLIENT pClient;
212     PWDMAUD_DEVICE_EXTENSION DeviceExtension;
213 
214     /* get device extension */
215     DeviceExtension = (PWDMAUD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
216 
217 #if KS_IMPLEMENTED
218     Status = KsReferenceSoftwareBusObject((KSDEVICE_HEADER)DeviceObject->DeviceExtension);
219     if (!NT_SUCCESS(Status))
220     {
221         DPRINT1("KsReferenceSoftwareBusObject failed with %x\n", Status);
222         return Status;
223     }
224 #endif
225 
226     if (DeviceExtension->FileObject == NULL)
227     {
228         /* initialize */
229         WdmAudInitWorkerRoutine(DeviceObject, NULL);
230     }
231 
232 
233     Status = WdmAudOpenSysaudio(DeviceObject, &pClient);
234     if (!NT_SUCCESS(Status))
235     {
236         DPRINT1("Failed to open sysaudio!\n");
237 
238         /* complete and forget */
239         Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
240         IoCompleteRequest(Irp, IO_NO_INCREMENT);
241         /* done */
242         return STATUS_UNSUCCESSFUL;
243     }
244 
245     IoStack = IoGetCurrentIrpStackLocation(Irp);
246     ASSERT(IoStack->FileObject);
247 
248     /* store client context in file object */
249     IoStack->FileObject->FsContext = pClient;
250     Status = STATUS_SUCCESS;
251 
252     Irp->IoStatus.Status = Status;
253     Irp->IoStatus.Information = 0;
254     IoCompleteRequest(Irp, IO_NO_INCREMENT);
255 
256     return Status;
257 }
258 
259 NTSTATUS
260 NTAPI
261 WdmAudClose(
262     IN  PDEVICE_OBJECT DeviceObject,
263     IN  PIRP Irp)
264 {
265     /* nothing to do complete request */
266 #if KS_IMPLEMENTED
267     Status = KsDereferenceSoftwareBusObject(DeviceExtension->DeviceHeader);
268 
269     if (NT_SUCCESS(Status))
270     {
271         if (DeviceExtension->SysAudioNotification)
272             Status = IoUnregisterPlugPlayNotification(DeviceExtension->SysAudioNotification);
273     }
274 #endif
275 
276     Irp->IoStatus.Status = STATUS_SUCCESS;
277     Irp->IoStatus.Information = 0;
278     IoCompleteRequest(Irp, IO_NO_INCREMENT);
279 
280     /* done */
281     return STATUS_SUCCESS;
282 }
283 
284 NTSTATUS
285 NTAPI
286 WdmAudCleanup(
287     IN  PDEVICE_OBJECT DeviceObject,
288     IN  PIRP Irp)
289 {
290     PIO_STACK_LOCATION IoStack;
291     PWDMAUD_CLIENT pClient;
292     PWDMAUD_DEVICE_EXTENSION DeviceExtension;
293     ULONG Index;
294     KIRQL OldIrql;
295 
296     /* get device extension */
297     DeviceExtension = (PWDMAUD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
298 
299     /* get current irp stack location */
300     IoStack = IoGetCurrentIrpStackLocation(Irp);
301 
302     /* sanity check */
303     ASSERT(IoStack->FileObject);
304 
305     /* get client context struct */
306     pClient = (PWDMAUD_CLIENT)IoStack->FileObject->FsContext;
307 
308     /* sanity check */
309     ASSERT(pClient);
310 
311     /* acquire client context list lock */
312     KeAcquireSpinLock(&DeviceExtension->Lock, &OldIrql);
313 
314     /* remove entry */
315     RemoveEntryList(&pClient->Entry);
316 
317     /* release lock */
318     KeReleaseSpinLock(&DeviceExtension->Lock, OldIrql);
319 
320     /* check if all audio pins have been closed */
321     for (Index = 0; Index < pClient->NumPins; Index++)
322     {
323        DPRINT("Index %u Pin %p Type %x\n", Index, pClient->hPins[Index].Handle, pClient->hPins[Index].Type);
324        if (pClient->hPins[Index].Handle && pClient->hPins[Index].Type != MIXER_DEVICE_TYPE)
325        {
326            /* found an still open audio pin */
327            ZwClose(pClient->hPins[Index].Handle);
328        }
329        WdmAudCloseAllMixers(DeviceObject, pClient, Index);
330     }
331 
332     /* free pin array */
333     if (pClient->hPins)
334         FreeItem(pClient->hPins);
335 
336     /* free client context struct */
337     FreeItem(pClient);
338 
339     /* clear old client pointer */
340     IoStack->FileObject->FsContext = NULL;
341 
342     /* complete request */
343     Irp->IoStatus.Status = STATUS_SUCCESS;
344     Irp->IoStatus.Information = 0;
345     IoCompleteRequest(Irp, IO_NO_INCREMENT);
346 
347     /* done */
348     return STATUS_SUCCESS;
349 }
350 
351 NTSTATUS
352 NTAPI
353 DriverEntry(
354     IN PDRIVER_OBJECT Driver,
355     IN PUNICODE_STRING Registry_path
356 )
357 {
358     DPRINT("Wdmaud.sys loaded\n");
359 
360     Driver->DriverUnload = WdmAudUnload;
361 
362     Driver->MajorFunction[IRP_MJ_CREATE] = WdmAudCreate;
363     Driver->MajorFunction[IRP_MJ_CLOSE] = WdmAudClose;
364     Driver->MajorFunction[IRP_MJ_PNP] = WdmAudPnp;
365     Driver->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = KsDefaultForwardIrp;
366     Driver->MajorFunction[IRP_MJ_CLEANUP] = WdmAudCleanup;
367     Driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = WdmAudDeviceControl;
368     Driver->MajorFunction[IRP_MJ_WRITE] = WdmAudReadWrite;
369     Driver->MajorFunction[IRP_MJ_READ] = WdmAudReadWrite;
370     Driver->MajorFunction[IRP_MJ_POWER] = KsDefaultDispatchPower;
371     Driver->DriverExtension->AddDevice = WdmaudAddDevice;
372 
373     return STATUS_SUCCESS;
374 }
375