[MMIXER] Cleanup mixer notifications opened by an application when it is closed.
[reactos.git] / reactos / drivers / wdm / audio / legacy / wdmaud / control.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel Streaming
4 * FILE: drivers/wdm/audio/legacy/wdmaud/deviface.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 KSPROPSETID_Sysaudio = {0xCBE3FAA0L, 0xCC75, 0x11D0, {0xB4, 0x65, 0x00, 0x00, 0x1A, 0x18, 0x18, 0xE6}};
16
17 NTSTATUS
18 WdmAudControlOpen(
19 IN PDEVICE_OBJECT DeviceObject,
20 IN PIRP Irp,
21 IN PWDMAUD_DEVICE_INFO DeviceInfo,
22 IN PWDMAUD_CLIENT ClientInfo)
23 {
24 if (DeviceInfo->DeviceType == MIXER_DEVICE_TYPE)
25 {
26 return WdmAudControlOpenMixer(DeviceObject, Irp, DeviceInfo, ClientInfo);
27 }
28
29 if (DeviceInfo->DeviceType == WAVE_OUT_DEVICE_TYPE || DeviceInfo->DeviceType == WAVE_IN_DEVICE_TYPE)
30 {
31 return WdmAudControlOpenWave(DeviceObject, Irp, DeviceInfo, ClientInfo);
32 }
33
34 if (DeviceInfo->DeviceType == MIDI_OUT_DEVICE_TYPE || DeviceInfo->DeviceType == MIDI_IN_DEVICE_TYPE)
35 {
36 return WdmAudControlOpenMidi(DeviceObject, Irp, DeviceInfo, ClientInfo);
37 }
38
39
40 return SetIrpIoStatus(Irp, STATUS_NOT_SUPPORTED, sizeof(WDMAUD_DEVICE_INFO));
41 }
42
43 NTSTATUS
44 WdmAudControlDeviceType(
45 IN PDEVICE_OBJECT DeviceObject,
46 IN PIRP Irp,
47 IN PWDMAUD_DEVICE_INFO DeviceInfo,
48 IN PWDMAUD_CLIENT ClientInfo)
49 {
50 ULONG Result = 0;
51
52 if (DeviceInfo->DeviceType == MIXER_DEVICE_TYPE)
53 {
54 Result = WdmAudGetMixerDeviceCount();
55 }
56 else if (DeviceInfo->DeviceType == WAVE_OUT_DEVICE_TYPE)
57 {
58 Result = WdmAudGetWaveOutDeviceCount();
59 }
60 else if (DeviceInfo->DeviceType == WAVE_IN_DEVICE_TYPE)
61 {
62 Result = WdmAudGetWaveInDeviceCount();
63 }
64 else if (DeviceInfo->DeviceType == MIDI_IN_DEVICE_TYPE)
65 {
66 Result = WdmAudGetMidiInDeviceCount();
67 }
68 else if (DeviceInfo->DeviceType == MIDI_OUT_DEVICE_TYPE)
69 {
70 Result = WdmAudGetMidiOutDeviceCount();
71 }
72
73
74 /* store result count */
75 DeviceInfo->DeviceCount = Result;
76
77 DPRINT("WdmAudControlDeviceType Devices %u\n", DeviceInfo->DeviceCount);
78 return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(WDMAUD_DEVICE_INFO));
79 }
80
81 NTSTATUS
82 WdmAudControlDeviceState(
83 IN PDEVICE_OBJECT DeviceObject,
84 IN PIRP Irp,
85 IN PWDMAUD_DEVICE_INFO DeviceInfo,
86 IN PWDMAUD_CLIENT ClientInfo)
87 {
88 KSPROPERTY Property;
89 KSSTATE State;
90 NTSTATUS Status;
91 ULONG BytesReturned;
92 PFILE_OBJECT FileObject;
93
94 DPRINT("WdmAudControlDeviceState\n");
95
96 Status = ObReferenceObjectByHandle(DeviceInfo->hDevice, GENERIC_READ | GENERIC_WRITE, *IoFileObjectType, KernelMode, (PVOID*)&FileObject, NULL);
97 if (!NT_SUCCESS(Status))
98 {
99 DPRINT1("Error: invalid device handle provided %p Type %x\n", DeviceInfo->hDevice, DeviceInfo->DeviceType);
100 return SetIrpIoStatus(Irp, STATUS_UNSUCCESSFUL, 0);
101 }
102
103 Property.Set = KSPROPSETID_Connection;
104 Property.Id = KSPROPERTY_CONNECTION_STATE;
105 Property.Flags = KSPROPERTY_TYPE_SET;
106
107 State = DeviceInfo->u.State;
108
109 Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSPROPERTY), (PVOID)&State, sizeof(KSSTATE), &BytesReturned);
110
111 ObDereferenceObject(FileObject);
112
113 DPRINT("WdmAudControlDeviceState Status %x\n", Status);
114 return SetIrpIoStatus(Irp, Status, sizeof(WDMAUD_DEVICE_INFO));
115 }
116
117 NTSTATUS
118 WdmAudCapabilities(
119 IN PDEVICE_OBJECT DeviceObject,
120 IN PIRP Irp,
121 IN PWDMAUD_DEVICE_INFO DeviceInfo,
122 IN PWDMAUD_CLIENT ClientInfo)
123 {
124 PWDMAUD_DEVICE_EXTENSION DeviceExtension;
125 NTSTATUS Status = STATUS_UNSUCCESSFUL;
126
127 DPRINT("WdmAudCapabilities entered\n");
128
129 DeviceExtension = (PWDMAUD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
130
131 if (DeviceInfo->DeviceType == MIXER_DEVICE_TYPE)
132 {
133 Status = WdmAudMixerCapabilities(DeviceObject, DeviceInfo, ClientInfo, DeviceExtension);
134 }
135 else if (DeviceInfo->DeviceType == WAVE_IN_DEVICE_TYPE || DeviceInfo->DeviceType == WAVE_OUT_DEVICE_TYPE)
136 {
137 Status = WdmAudWaveCapabilities(DeviceObject, DeviceInfo, ClientInfo, DeviceExtension);
138 }
139 else if (DeviceInfo->DeviceType == MIDI_IN_DEVICE_TYPE || DeviceInfo->DeviceType == MIDI_OUT_DEVICE_TYPE)
140 {
141 Status = WdmAudMidiCapabilities(DeviceObject, DeviceInfo, ClientInfo, DeviceExtension);
142 }
143
144 return SetIrpIoStatus(Irp, Status, sizeof(WDMAUD_DEVICE_INFO));
145 }
146
147 NTSTATUS
148 NTAPI
149 WdmAudIoctlClose(
150 IN PDEVICE_OBJECT DeviceObject,
151 IN PIRP Irp,
152 IN PWDMAUD_DEVICE_INFO DeviceInfo,
153 IN PWDMAUD_CLIENT ClientInfo)
154 {
155 ULONG Index;
156
157 for(Index = 0; Index < ClientInfo->NumPins; Index++)
158 {
159 if (ClientInfo->hPins[Index].Handle == DeviceInfo->hDevice && ClientInfo->hPins[Index].Type != MIXER_DEVICE_TYPE)
160 {
161 DPRINT1("Closing device %p\n", DeviceInfo->hDevice);
162 ZwClose(DeviceInfo->hDevice);
163 ClientInfo->hPins[Index].Handle = NULL;
164 SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(WDMAUD_DEVICE_INFO));
165 return STATUS_SUCCESS;
166 }
167 else if (ClientInfo->hPins[Index].Handle == DeviceInfo->hDevice && ClientInfo->hPins[Index].Type == MIXER_DEVICE_TYPE)
168 {
169 DPRINT1("Closing mixer %p\n", DeviceInfo->hDevice);
170 return WdmAudControlCloseMixer(DeviceObject, Irp, DeviceInfo, ClientInfo, Index);
171 }
172 }
173
174 SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, sizeof(WDMAUD_DEVICE_INFO));
175 return STATUS_INVALID_PARAMETER;
176 }
177
178 NTSTATUS
179 NTAPI
180 WdmAudFrameSize(
181 IN PDEVICE_OBJECT DeviceObject,
182 IN PIRP Irp,
183 IN PWDMAUD_DEVICE_INFO DeviceInfo,
184 IN PWDMAUD_CLIENT ClientInfo)
185 {
186 PFILE_OBJECT FileObject;
187 KSPROPERTY Property;
188 ULONG BytesReturned;
189 KSALLOCATOR_FRAMING Framing;
190 NTSTATUS Status;
191
192 /* Get sysaudio pin file object */
193 Status = ObReferenceObjectByHandle(DeviceInfo->hDevice, GENERIC_WRITE, *IoFileObjectType, KernelMode, (PVOID*)&FileObject, NULL);
194 if (!NT_SUCCESS(Status))
195 {
196 DPRINT1("Invalid buffer handle %p\n", DeviceInfo->hDevice);
197 return SetIrpIoStatus(Irp, Status, 0);
198 }
199
200 /* Setup get framing request */
201 Property.Id = KSPROPERTY_CONNECTION_ALLOCATORFRAMING;
202 Property.Flags = KSPROPERTY_TYPE_GET;
203 Property.Set = KSPROPSETID_Connection;
204
205 Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSPROPERTY), (PVOID)&Framing, sizeof(KSALLOCATOR_FRAMING), &BytesReturned);
206 /* Did we succeed */
207 if (NT_SUCCESS(Status))
208 {
209 /* Store framesize */
210 DeviceInfo->u.FrameSize = Framing.FrameSize;
211 }
212
213 /* Release file object */
214 ObDereferenceObject(FileObject);
215
216 return SetIrpIoStatus(Irp, Status, sizeof(WDMAUD_DEVICE_INFO));
217
218 }
219
220 NTSTATUS
221 NTAPI
222 WdmAudGetDeviceInterface(
223 IN PDEVICE_OBJECT DeviceObject,
224 IN PIRP Irp,
225 IN PWDMAUD_DEVICE_INFO DeviceInfo)
226 {
227 NTSTATUS Status;
228 LPWSTR Device;
229 ULONG Size, Length;
230
231 /* get device interface string input length */
232 Size = DeviceInfo->u.Interface.DeviceInterfaceStringSize;
233
234 /* get mixer info */
235 Status = WdmAudGetPnpNameByIndexAndType(DeviceInfo->DeviceIndex, DeviceInfo->DeviceType, &Device);
236
237 /* check for success */
238 if (!NT_SUCCESS(Status))
239 {
240 /* invalid device id */
241 return SetIrpIoStatus(Irp, Status, sizeof(WDMAUD_DEVICE_INFO));
242 }
243
244 /* calculate length */
245 Length = (wcslen(Device)+1) * sizeof(WCHAR);
246
247 if (!Size)
248 {
249 /* store device interface size */
250 DeviceInfo->u.Interface.DeviceInterfaceStringSize = Length;
251 }
252 else if (Size < Length)
253 {
254 /* buffer too small */
255 DeviceInfo->u.Interface.DeviceInterfaceStringSize = Length;
256 return SetIrpIoStatus(Irp, STATUS_BUFFER_OVERFLOW, sizeof(WDMAUD_DEVICE_INFO));
257 }
258 else
259 {
260 //FIXME SEH
261 RtlMoveMemory(DeviceInfo->u.Interface.DeviceInterfaceString, Device, Length);
262 }
263
264 FreeItem(Device);
265 return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(WDMAUD_DEVICE_INFO));
266 }
267
268 NTSTATUS
269 NTAPI
270 WdmAudResetStream(
271 IN PDEVICE_OBJECT DeviceObject,
272 IN PIRP Irp,
273 IN PWDMAUD_DEVICE_INFO DeviceInfo)
274 {
275 KSRESET ResetStream;
276 NTSTATUS Status;
277 ULONG BytesReturned;
278 PFILE_OBJECT FileObject;
279
280 DPRINT("WdmAudResetStream\n");
281
282 Status = ObReferenceObjectByHandle(DeviceInfo->hDevice, GENERIC_READ | GENERIC_WRITE, *IoFileObjectType, KernelMode, (PVOID*)&FileObject, NULL);
283 if (!NT_SUCCESS(Status))
284 {
285 DPRINT1("Error: invalid device handle provided %p Type %x\n", DeviceInfo->hDevice, DeviceInfo->DeviceType);
286 return SetIrpIoStatus(Irp, STATUS_UNSUCCESSFUL, 0);
287 }
288
289 ResetStream = DeviceInfo->u.ResetStream;
290 ASSERT(ResetStream == KSRESET_BEGIN || ResetStream == KSRESET_END);
291
292 Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_RESET_STATE, (PVOID)&ResetStream, sizeof(KSRESET), NULL, 0, &BytesReturned);
293
294 ObDereferenceObject(FileObject);
295
296 DPRINT("WdmAudResetStream Status %x\n", Status);
297 return SetIrpIoStatus(Irp, Status, sizeof(WDMAUD_DEVICE_INFO));
298 }
299
300 NTSTATUS
301 NTAPI
302 WdmAudDeviceControl(
303 IN PDEVICE_OBJECT DeviceObject,
304 IN PIRP Irp)
305 {
306 PIO_STACK_LOCATION IoStack;
307 PWDMAUD_DEVICE_INFO DeviceInfo;
308 PWDMAUD_CLIENT ClientInfo;
309
310 IoStack = IoGetCurrentIrpStackLocation(Irp);
311
312 DPRINT("WdmAudDeviceControl entered\n");
313
314 if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(WDMAUD_DEVICE_INFO))
315 {
316 /* invalid parameter */
317 DPRINT1("Input buffer too small size %u expected %u\n", IoStack->Parameters.DeviceIoControl.InputBufferLength, sizeof(WDMAUD_DEVICE_INFO));
318 return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
319 }
320
321 DeviceInfo = (PWDMAUD_DEVICE_INFO)Irp->AssociatedIrp.SystemBuffer;
322
323 if (DeviceInfo->DeviceType < MIN_SOUND_DEVICE_TYPE || DeviceInfo->DeviceType > MAX_SOUND_DEVICE_TYPE)
324 {
325 /* invalid parameter */
326 DPRINT1("Error: device type not set\n");
327 return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
328 }
329
330 if (!IoStack->FileObject || !IoStack->FileObject->FsContext)
331 {
332 /* file object parameter */
333 DPRINT1("Error: file object is not attached\n");
334 return SetIrpIoStatus(Irp, STATUS_UNSUCCESSFUL, 0);
335 }
336 ClientInfo = (PWDMAUD_CLIENT)IoStack->FileObject->FsContext;
337
338 DPRINT("WdmAudDeviceControl entered\n");
339
340 switch(IoStack->Parameters.DeviceIoControl.IoControlCode)
341 {
342 case IOCTL_OPEN_WDMAUD:
343 return WdmAudControlOpen(DeviceObject, Irp, DeviceInfo, ClientInfo);
344 case IOCTL_GETNUMDEVS_TYPE:
345 return WdmAudControlDeviceType(DeviceObject, Irp, DeviceInfo, ClientInfo);
346 case IOCTL_SETDEVICE_STATE:
347 return WdmAudControlDeviceState(DeviceObject, Irp, DeviceInfo, ClientInfo);
348 case IOCTL_GETCAPABILITIES:
349 return WdmAudCapabilities(DeviceObject, Irp, DeviceInfo, ClientInfo);
350 case IOCTL_CLOSE_WDMAUD:
351 return WdmAudIoctlClose(DeviceObject, Irp, DeviceInfo, ClientInfo);
352 case IOCTL_GETFRAMESIZE:
353 return WdmAudFrameSize(DeviceObject, Irp, DeviceInfo, ClientInfo);
354 case IOCTL_GETLINEINFO:
355 return WdmAudGetLineInfo(DeviceObject, Irp, DeviceInfo, ClientInfo);
356 case IOCTL_GETLINECONTROLS:
357 return WdmAudGetLineControls(DeviceObject, Irp, DeviceInfo, ClientInfo);
358 case IOCTL_SETCONTROLDETAILS:
359 return WdmAudSetControlDetails(DeviceObject, Irp, DeviceInfo, ClientInfo);
360 case IOCTL_GETCONTROLDETAILS:
361 return WdmAudGetControlDetails(DeviceObject, Irp, DeviceInfo, ClientInfo);
362 case IOCTL_QUERYDEVICEINTERFACESTRING:
363 return WdmAudGetDeviceInterface(DeviceObject, Irp, DeviceInfo);
364 case IOCTL_GET_MIXER_EVENT:
365 return WdmAudGetMixerEvent(DeviceObject, Irp, DeviceInfo, ClientInfo);
366 case IOCTL_RESET_STREAM:
367 return WdmAudResetStream(DeviceObject, Irp, DeviceInfo);
368 case IOCTL_GETPOS:
369 case IOCTL_GETDEVID:
370 case IOCTL_GETVOLUME:
371 case IOCTL_SETVOLUME:
372
373 DPRINT1("Unhandled %x\n", IoStack->Parameters.DeviceIoControl.IoControlCode);
374 break;
375 }
376
377 return SetIrpIoStatus(Irp, STATUS_NOT_IMPLEMENTED, 0);
378 }
379
380 NTSTATUS
381 NTAPI
382 IoCompletion (
383 PDEVICE_OBJECT DeviceObject,
384 PIRP Irp,
385 PVOID Ctx)
386 {
387 PKSSTREAM_HEADER Header;
388 PMDL Mdl, NextMdl;
389 PWDMAUD_COMPLETION_CONTEXT Context = (PWDMAUD_COMPLETION_CONTEXT)Ctx;
390
391 /* get stream header */
392 Header = (PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer;
393
394 /* sanity check */
395 ASSERT(Header);
396
397 /* time to free all allocated mdls */
398 Mdl = Irp->MdlAddress;
399
400 while(Mdl)
401 {
402 /* get next mdl */
403 NextMdl = Mdl->Next;
404
405 /* unlock pages */
406 MmUnlockPages(Mdl);
407
408 /* grab next mdl */
409 Mdl = NextMdl;
410 }
411 //IoFreeMdl(Mdl);
412 /* clear mdl list */
413 Irp->MdlAddress = Context->Mdl;
414
415
416
417 DPRINT("IoCompletion Irp %p IoStatus %lx Information %lx\n", Irp, Irp->IoStatus.Status, Irp->IoStatus.Information);
418
419 if (!NT_SUCCESS(Irp->IoStatus.Status))
420 {
421 /* failed */
422 Irp->IoStatus.Information = 0;
423 }
424
425 /* dereference file object */
426 ObDereferenceObject(Context->FileObject);
427
428 /* free context */
429 FreeItem(Context);
430
431 return STATUS_SUCCESS;
432 }
433
434 NTSTATUS
435 NTAPI
436 WdmAudReadWrite(
437 IN PDEVICE_OBJECT DeviceObject,
438 IN PIRP Irp)
439 {
440 NTSTATUS Status;
441 PWDMAUD_DEVICE_INFO DeviceInfo;
442 PFILE_OBJECT FileObject;
443 PIO_STACK_LOCATION IoStack;
444 ULONG Length;
445 PMDL Mdl;
446 BOOLEAN Read = TRUE;
447 PWDMAUD_COMPLETION_CONTEXT Context;
448
449 /* allocate completion context */
450 Context = AllocateItem(NonPagedPool, sizeof(WDMAUD_COMPLETION_CONTEXT));
451
452 if (!Context)
453 {
454 /* not enough memory */
455 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
456 IoCompleteRequest(Irp, IO_NO_INCREMENT);
457
458 /* done */
459 return STATUS_INSUFFICIENT_RESOURCES;
460 }
461
462 /* get current irp stack location */
463 IoStack = IoGetCurrentIrpStackLocation(Irp);
464
465 /* store the input buffer in UserBuffer - as KsProbeStreamIrp operates on IRP_MJ_DEVICE_CONTROL */
466 Irp->UserBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress);
467
468 /* sanity check */
469 ASSERT(Irp->UserBuffer);
470
471 /* get the length of the request length */
472 Length = IoStack->Parameters.Write.Length;
473
474 /* store outputbuffer length */
475 IoStack->Parameters.DeviceIoControl.OutputBufferLength = Length;
476
477 /* setup context */
478 Context->Length = Length;
479 Context->Function = (IoStack->MajorFunction == IRP_MJ_WRITE ? IOCTL_KS_WRITE_STREAM : IOCTL_KS_READ_STREAM);
480 Context->Mdl = Irp->MdlAddress;
481
482 /* store mdl address */
483 Mdl = Irp->MdlAddress;
484
485 /* remove mdladdress as KsProbeStreamIrp will interpret it as an already probed audio buffer */
486 Irp->MdlAddress = NULL;
487
488 if (IoStack->MajorFunction == IRP_MJ_WRITE)
489 {
490 /* probe the write stream irp */
491 Read = FALSE;
492 Status = KsProbeStreamIrp(Irp, KSPROBE_STREAMWRITE | KSPROBE_ALLOCATEMDL | KSPROBE_PROBEANDLOCK, Length);
493 }
494 else
495 {
496 /* probe the read stream irp */
497 Status = KsProbeStreamIrp(Irp, KSPROBE_STREAMREAD | KSPROBE_ALLOCATEMDL | KSPROBE_PROBEANDLOCK, Length);
498 }
499
500 if (!NT_SUCCESS(Status))
501 {
502 DPRINT1("KsProbeStreamIrp failed with Status %x Cancel %u\n", Status, Irp->Cancel);
503 Irp->MdlAddress = Mdl;
504 FreeItem(Context);
505 return SetIrpIoStatus(Irp, Status, 0);
506 }
507
508 /* get device info */
509 DeviceInfo = (PWDMAUD_DEVICE_INFO)Irp->AssociatedIrp.SystemBuffer;
510 ASSERT(DeviceInfo);
511
512 /* now get sysaudio file object */
513 Status = ObReferenceObjectByHandle(DeviceInfo->hDevice, GENERIC_WRITE, *IoFileObjectType, KernelMode, (PVOID*)&FileObject, NULL);
514 if (!NT_SUCCESS(Status))
515 {
516 DPRINT1("Invalid pin handle %p\n", DeviceInfo->hDevice);
517 Irp->MdlAddress = Mdl;
518 FreeItem(Context);
519 return SetIrpIoStatus(Irp, Status, 0);
520 }
521
522 /* store file object whose reference is released in the completion callback */
523 Context->FileObject = FileObject;
524
525 /* skip current irp stack location */
526 IoSkipCurrentIrpStackLocation(Irp);
527
528 /* get next stack location */
529 IoStack = IoGetNextIrpStackLocation(Irp);
530
531 /* prepare stack location */
532 IoStack->FileObject = FileObject;
533 IoStack->Parameters.Write.Length = Length;
534 IoStack->MajorFunction = IRP_MJ_WRITE;
535 IoStack->Parameters.DeviceIoControl.IoControlCode = (Read ? IOCTL_KS_READ_STREAM : IOCTL_KS_WRITE_STREAM);
536 IoSetCompletionRoutine(Irp, IoCompletion, (PVOID)Context, TRUE, TRUE, TRUE);
537
538 /* mark irp as pending */
539 // IoMarkIrpPending(Irp);
540 /* call the driver */
541 Status = IoCallDriver(IoGetRelatedDeviceObject(FileObject), Irp);
542 return Status;
543 }