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