[WDMAUD] Close mixers on cleanup. Should fix CORE-10735 definitely (#21)
[reactos.git] / drivers / wdm / audio / legacy / wdmaud / entry.c
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 }