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 }
544