cee9acbdfdd4970d97490698532b4a35a3468df8
[reactos.git] / reactos / drivers / wdm / audio / sysaudio / deviface.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel Streaming
4 * FILE: drivers/wdm/audio/sysaudio/deviface.c
5 * PURPOSE: System Audio graph builder
6 * PROGRAMMER: Johannes Anderwald
7 */
8
9 #include "sysaudio.h"
10
11 const GUID GUID_DEVICE_INTERFACE_ARRIVAL = {0xCB3A4004L, 0x46F0, 0x11D0, {0xB0, 0x8F, 0x00, 0x60, 0x97, 0x13, 0x05, 0x3F}};
12 const GUID GUID_DEVICE_INTERFACE_REMOVAL = {0xCB3A4005L, 0x46F0, 0x11D0, {0xB0, 0x8F, 0x00, 0x60, 0x97, 0x13, 0x05, 0x3F}};
13 const GUID KS_CATEGORY_AUDIO = {0x6994AD04L, 0x93EF, 0x11D0, {0xA3, 0xCC, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
14 const GUID KS_CATEGORY_TOPOLOGY = {0xDDA54A40, 0x1E4C, 0x11D1, {0xA0, 0x50, 0x40, 0x57, 0x05, 0xC1, 0x00, 0x00}};
15 const GUID DMOCATEGORY_ACOUSTIC_ECHO_CANCEL = {0xBF963D80L, 0xC559, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
16
17 #define IOCTL_KS_OBJECT_CLASS CTL_CODE(FILE_DEVICE_KS, 0x7, METHOD_NEITHER, FILE_ANY_ACCESS)
18
19 NTSTATUS
20 BuildPinDescriptor(
21 IN PKSAUDIO_DEVICE_ENTRY DeviceEntry,
22 IN ULONG Count)
23 {
24 ULONG Index;
25 KSP_PIN PinRequest;
26 KSPIN_DATAFLOW DataFlow;
27 KSPIN_COMMUNICATION Communication;
28 ULONG NumWaveOutPin, NumWaveInPin;
29 NTSTATUS Status;
30 ULONG BytesReturned;
31
32 NumWaveInPin = 0;
33 NumWaveOutPin = 0;
34 for(Index = 0; Index < Count; Index++)
35 {
36 /* retrieve data flow */
37 PinRequest.PinId = Index;
38 PinRequest.Property.Set = KSPROPSETID_Pin;
39 PinRequest.Property.Flags = KSPROPERTY_TYPE_GET;
40
41 /* get dataflow direction */
42 PinRequest.Property.Id = KSPROPERTY_PIN_DATAFLOW;
43 Status = KsSynchronousIoControlDevice(DeviceEntry->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&PinRequest, sizeof(KSP_PIN), (PVOID)&DataFlow, sizeof(KSPIN_DATAFLOW), &BytesReturned);
44 if (NT_SUCCESS(Status))
45 {
46 DeviceEntry->PinDescriptors[Index].DataFlow = DataFlow;
47 }
48
49 /* get irp flow direction */
50 PinRequest.Property.Id = KSPROPERTY_PIN_COMMUNICATION;
51 Status = KsSynchronousIoControlDevice(DeviceEntry->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&PinRequest, sizeof(KSP_PIN), (PVOID)&Communication, sizeof(KSPIN_COMMUNICATION), &BytesReturned);
52 if (NT_SUCCESS(Status))
53 {
54 DeviceEntry->PinDescriptors[Index].Communication = Communication;
55 }
56
57 if (Communication == KSPIN_COMMUNICATION_SINK && DataFlow == KSPIN_DATAFLOW_IN)
58 NumWaveOutPin++;
59
60 if (Communication == KSPIN_COMMUNICATION_SINK && DataFlow == KSPIN_DATAFLOW_OUT)
61 NumWaveInPin++;
62
63 /* FIXME query for interface, dataformat etc */
64 }
65
66 DPRINT("Num Pins %u Num WaveIn Pins %u Name WaveOut Pins %u\n", DeviceEntry->PinDescriptorsCount, NumWaveInPin, NumWaveOutPin);
67 return STATUS_SUCCESS;
68 }
69
70 VOID
71 QueryFilterRoutine(
72 IN PKSAUDIO_DEVICE_ENTRY DeviceEntry)
73 {
74 KSPROPERTY PropertyRequest;
75 ULONG Count;
76 NTSTATUS Status;
77 ULONG BytesReturned;
78
79 DPRINT("Querying filter...\n");
80
81 PropertyRequest.Set = KSPROPSETID_Pin;
82 PropertyRequest.Flags = KSPROPERTY_TYPE_GET;
83 PropertyRequest.Id = KSPROPERTY_PIN_CTYPES;
84
85 /* query for num of pins */
86 Status = KsSynchronousIoControlDevice(DeviceEntry->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&PropertyRequest, sizeof(KSPROPERTY), (PVOID)&Count, sizeof(ULONG), &BytesReturned);
87 if (!NT_SUCCESS(Status))
88 {
89 DPRINT1("Failed to query number of pins Status %x\n", Status);
90 return;
91 }
92
93 if (!Count)
94 {
95 DPRINT1("Filter has no pins!\n");
96 return;
97 }
98
99 /* allocate pin descriptor array */
100 DeviceEntry->PinDescriptors = ExAllocatePool(NonPagedPool, Count * sizeof(KSPIN_DESCRIPTOR));
101 if (!DeviceEntry->PinDescriptors)
102 {
103 /* no memory */
104 return;
105 }
106
107 /* zero array pin descriptor array */
108 RtlZeroMemory(DeviceEntry->PinDescriptors, Count * sizeof(KSPIN_DESCRIPTOR));
109
110 /* build the device descriptor */
111 Status = BuildPinDescriptor(DeviceEntry, Count);
112 if (!NT_SUCCESS(Status))
113 return;
114
115
116 /* allocate pin array */
117 DeviceEntry->Pins = ExAllocatePool(NonPagedPool, Count * sizeof(PIN_INFO));
118 if (!DeviceEntry->Pins)
119 {
120 /* no memory */
121 DPRINT1("Failed to allocate memory Pins %u Block %x\n", Count, Count * sizeof(PIN_INFO));
122 return;
123 }
124
125 /* clear array */
126 RtlZeroMemory(DeviceEntry->Pins, sizeof(PIN_INFO) * Count);
127 DeviceEntry->PinDescriptorsCount = Count;
128
129 }
130
131 VOID
132 NTAPI
133 FilterPinWorkerRoutine(
134 IN PDEVICE_OBJECT DeviceObject,
135 IN PVOID Context)
136 {
137 PKSAUDIO_DEVICE_ENTRY DeviceEntry;
138 PFILTER_WORKER_CONTEXT Ctx = (PFILTER_WORKER_CONTEXT)Context;
139
140 DeviceEntry = Ctx->DeviceEntry;
141
142 QueryFilterRoutine(DeviceEntry);
143
144 /* free work item */
145 IoFreeWorkItem(Ctx->WorkItem);
146 /* free work item context */
147 ExFreePool(Ctx);
148 return;
149
150 }
151
152 NTSTATUS
153 OpenDevice(
154 IN PUNICODE_STRING DeviceName,
155 IN PHANDLE HandleOut,
156 IN PFILE_OBJECT * FileObjectOut)
157 {
158 NTSTATUS Status;
159 HANDLE NodeHandle;
160 PFILE_OBJECT FileObject;
161 OBJECT_ATTRIBUTES ObjectAttributes;
162 IO_STATUS_BLOCK IoStatusBlock;
163
164 InitializeObjectAttributes(&ObjectAttributes, DeviceName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
165
166 Status = ZwCreateFile(&NodeHandle,
167 GENERIC_READ | GENERIC_WRITE,
168 &ObjectAttributes,
169 &IoStatusBlock,
170 NULL,
171 0,
172 0,
173 FILE_OPEN,
174 FILE_SYNCHRONOUS_IO_NONALERT,
175 NULL,
176 0);
177
178
179 if (!NT_SUCCESS(Status))
180 {
181 DPRINT("ZwCreateFile failed with %x %S\n", Status, DeviceName->Buffer);
182 return Status;
183 }
184
185 Status = ObReferenceObjectByHandle(NodeHandle, GENERIC_READ | GENERIC_WRITE, IoFileObjectType, KernelMode, (PVOID*)&FileObject, NULL);
186 if (!NT_SUCCESS(Status))
187 {
188 ZwClose(NodeHandle);
189 DPRINT("ObReferenceObjectByHandle failed with %x\n", Status);
190 return Status;
191 }
192
193 *HandleOut = NodeHandle;
194 *FileObjectOut = FileObject;
195 return Status;
196 }
197
198 NTSTATUS
199 InsertAudioDevice(
200 IN PDEVICE_OBJECT DeviceObject,
201 IN PUNICODE_STRING DeviceName,
202 IN LPWSTR ReferenceString)
203 {
204 NTSTATUS Status = STATUS_SUCCESS;
205 PFILTER_WORKER_CONTEXT Ctx = NULL;
206 PIO_WORKITEM WorkItem = NULL;
207 PSYSAUDIODEVEXT DeviceExtension;
208 PKSAUDIO_DEVICE_ENTRY DeviceEntry = NULL;
209
210 /* a new device has arrived */
211 DeviceEntry = ExAllocatePool(NonPagedPool, sizeof(KSAUDIO_DEVICE_ENTRY));
212 if (!DeviceEntry)
213 {
214 /* no memory */
215 return STATUS_INSUFFICIENT_RESOURCES;
216 }
217
218 /* initialize audio device entry */
219 RtlZeroMemory(DeviceEntry, sizeof(KSAUDIO_DEVICE_ENTRY));
220
221 /* allocate filter ctx */
222 Ctx = ExAllocatePool(NonPagedPool, sizeof(FILTER_WORKER_CONTEXT));
223 if (!Ctx)
224 {
225 /* no memory */
226 Status = STATUS_INSUFFICIENT_RESOURCES;
227 goto cleanup;
228 }
229
230 /* allocate work item */
231 WorkItem = IoAllocateWorkItem(DeviceObject);
232 if (!WorkItem)
233 {
234 /* no memory */
235 Status = STATUS_INSUFFICIENT_RESOURCES;
236 goto cleanup;
237 }
238
239 /* set device name */
240 DeviceEntry->DeviceName.Length = 0;
241 DeviceEntry->DeviceName.MaximumLength = DeviceName->MaximumLength + 10 * sizeof(WCHAR);
242
243 /* hack for bug 4566 */
244 if (ReferenceString)
245 {
246 DeviceEntry->DeviceName.MaximumLength += (wcslen(ReferenceString) + 2) * sizeof(WCHAR);
247 }
248
249 DeviceEntry->DeviceName.Buffer = ExAllocatePool(NonPagedPool, DeviceEntry->DeviceName.MaximumLength);
250
251 if (!DeviceEntry->DeviceName.Buffer)
252 {
253 Status = STATUS_INSUFFICIENT_RESOURCES;
254 goto cleanup;
255 }
256
257 RtlAppendUnicodeToString(&DeviceEntry->DeviceName, L"\\??\\");
258 RtlAppendUnicodeStringToString(&DeviceEntry->DeviceName, DeviceName);
259
260 if (ReferenceString)
261 {
262 RtlAppendUnicodeToString(&DeviceEntry->DeviceName, L"\\");
263 RtlAppendUnicodeToString(&DeviceEntry->DeviceName, ReferenceString);
264 }
265
266 Status = OpenDevice(&DeviceEntry->DeviceName, &DeviceEntry->Handle, &DeviceEntry->FileObject);
267
268 if (!NT_SUCCESS(Status))
269 {
270 goto cleanup;
271 }
272
273 Ctx->DeviceEntry = DeviceEntry;
274 Ctx->WorkItem = WorkItem;
275
276 /* fetch device extension */
277 DeviceExtension = (PSYSAUDIODEVEXT)DeviceObject->DeviceExtension;
278 /* insert new audio device */
279 ExInterlockedInsertTailList(&DeviceExtension->KsAudioDeviceList, &DeviceEntry->Entry, &DeviceExtension->Lock);
280 InterlockedIncrement((PLONG)&DeviceExtension->NumberOfKsAudioDevices);
281
282 DPRINT("Successfully opened audio device %u Device %S\n", DeviceExtension->NumberOfKsAudioDevices, DeviceEntry->DeviceName.Buffer);
283 IoQueueWorkItem(WorkItem, FilterPinWorkerRoutine, DelayedWorkQueue, (PVOID)Ctx);
284 return Status;
285
286 cleanup:
287 if (Ctx)
288 ExFreePool(Ctx);
289
290 if (WorkItem)
291 IoFreeWorkItem(WorkItem);
292
293 if (DeviceEntry)
294 {
295 if (DeviceEntry->DeviceName.Buffer)
296 ExFreePool(DeviceEntry->DeviceName.Buffer);
297
298 ExFreePool(DeviceEntry);
299 }
300
301 return Status;
302
303 }
304
305
306 NTSTATUS
307 NTAPI
308 DeviceInterfaceChangeCallback(
309 IN PVOID NotificationStructure,
310 IN PVOID Context)
311 {
312 DEVICE_INTERFACE_CHANGE_NOTIFICATION * Event;
313 NTSTATUS Status = STATUS_SUCCESS;
314 PSYSAUDIODEVEXT DeviceExtension;
315 UNICODE_STRING DeviceName;
316 HANDLE Handle;
317 PFILE_OBJECT FileObject;
318 LPWSTR ReferenceString;
319 ULONG BytesReturned;
320
321
322 PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)Context;
323
324 DeviceExtension = (PSYSAUDIODEVEXT)DeviceObject->DeviceExtension;
325
326 Event = (DEVICE_INTERFACE_CHANGE_NOTIFICATION*)NotificationStructure;
327
328 if (IsEqualGUIDAligned(&Event->Event,
329 &GUID_DEVICE_INTERFACE_ARRIVAL))
330 {
331 /*<HACK>
332 * 1) Open the filter w/o reference string
333 * 2) Retrieve reference strings with our private IOCTL_KS_OBJECT_CLASS
334 * 3) Append these reference strings to symbolic link we got
335 * * see bug 4566
336 */
337
338 DeviceName.Length = 0;
339 DeviceName.MaximumLength = Event->SymbolicLinkName->Length + 10 * sizeof(WCHAR);
340
341 DeviceName.Buffer = ExAllocatePool(NonPagedPool, DeviceName.MaximumLength);
342
343 if (!DeviceName.Buffer)
344 {
345 return STATUS_INSUFFICIENT_RESOURCES;
346 }
347
348 RtlAppendUnicodeToString(&DeviceName, L"\\??\\");
349 RtlAppendUnicodeStringToString(&DeviceName, Event->SymbolicLinkName);
350
351
352 Status = OpenDevice(&DeviceName, &Handle, &FileObject);
353 if (!NT_SUCCESS(Status))
354 {
355 ExFreePool(DeviceName.Buffer);
356 return Status;
357 }
358
359 Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_OBJECT_CLASS, NULL, 0, &ReferenceString, sizeof(LPWSTR), &BytesReturned);
360 if (!NT_SUCCESS(Status))
361 {
362 DPRINT1("failed Status %x\n", Status);
363
364 ExFreePool(DeviceName.Buffer);
365 ObDereferenceObject(FileObject);
366 ZwClose(Handle);
367 return Status;
368 }
369
370 while(*ReferenceString)
371 {
372 Status = InsertAudioDevice(DeviceObject, Event->SymbolicLinkName, ReferenceString);
373 ReferenceString += wcslen(ReferenceString) + 1;
374 }
375 //ExFreePool(ReferenceString);
376 ObDereferenceObject(FileObject);
377 ZwClose(Handle);
378 ExFreePool(DeviceName.Buffer);
379 return Status;
380 }
381 else
382 {
383 DPRINT("Remove interface to audio device!\n");
384 UNIMPLEMENTED
385 return STATUS_SUCCESS;
386 }
387
388
389 }
390
391 NTSTATUS
392 SysAudioRegisterNotifications(
393 IN PDRIVER_OBJECT DriverObject,
394 IN PDEVICE_OBJECT DeviceObject)
395 {
396 NTSTATUS Status;
397 PSYSAUDIODEVEXT DeviceExtension;
398
399 DeviceExtension = (PSYSAUDIODEVEXT)DeviceObject->DeviceExtension;
400
401 Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
402 PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
403 (PVOID)&KS_CATEGORY_AUDIO,
404 DriverObject,
405 DeviceInterfaceChangeCallback,
406 (PVOID)DeviceObject,
407 (PVOID*)&DeviceExtension->KsAudioNotificationEntry);
408
409 if (!NT_SUCCESS(Status))
410 {
411 DPRINT("IoRegisterPlugPlayNotification failed with %x\n", Status);
412 }
413
414 Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
415 PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
416 (PVOID)&DMOCATEGORY_ACOUSTIC_ECHO_CANCEL,
417 DriverObject,
418 DeviceInterfaceChangeCallback,
419 (PVOID)DeviceObject,
420 (PVOID*)&DeviceExtension->EchoCancelNotificationEntry);
421
422 if (!NT_SUCCESS(Status))
423 {
424 /* ignore failure for now */
425 DPRINT("IoRegisterPlugPlayNotification failed for DMOCATEGORY_ACOUSTIC_ECHO_CANCEL\n", Status);
426 }
427
428 return STATUS_SUCCESS;
429 }
430
431
432
433 NTSTATUS
434 SysAudioRegisterDeviceInterfaces(
435 IN PDEVICE_OBJECT DeviceObject)
436 {
437 NTSTATUS Status;
438 UNICODE_STRING SymbolicLink;
439
440 Status = IoRegisterDeviceInterface(DeviceObject, &KSCATEGORY_PREFERRED_MIDIOUT_DEVICE, NULL, &SymbolicLink);
441 if (NT_SUCCESS(Status))
442 {
443 IoSetDeviceInterfaceState(&SymbolicLink, TRUE);
444 RtlFreeUnicodeString(&SymbolicLink);
445 }
446 else
447 {
448 DPRINT("Failed to register KSCATEGORY_PREFERRED_MIDIOUT_DEVICE interface Status %x\n", Status);
449 return Status;
450 }
451
452 Status = IoRegisterDeviceInterface(DeviceObject, &KSCATEGORY_PREFERRED_WAVEIN_DEVICE, NULL, &SymbolicLink);
453 if (NT_SUCCESS(Status))
454 {
455 IoSetDeviceInterfaceState(&SymbolicLink, TRUE);
456 RtlFreeUnicodeString(&SymbolicLink);
457 }
458 else
459 {
460 DPRINT("Failed to register KSCATEGORY_PREFERRED_WAVEIN_DEVICE interface Status %x\n", Status);
461 return Status;
462 }
463
464 Status = IoRegisterDeviceInterface(DeviceObject, &KSCATEGORY_PREFERRED_WAVEOUT_DEVICE, NULL, &SymbolicLink);
465 if (NT_SUCCESS(Status))
466 {
467 IoSetDeviceInterfaceState(&SymbolicLink, TRUE);
468 RtlFreeUnicodeString(&SymbolicLink);
469 }
470 else
471 {
472 DPRINT("Failed to register KSCATEGORY_PREFERRED_WAVEOUT_DEVICE interface Status %x\n", Status);
473 }
474
475 Status = IoRegisterDeviceInterface(DeviceObject, &KSCATEGORY_SYSAUDIO, NULL, &SymbolicLink);
476 if (NT_SUCCESS(Status))
477 {
478 IoSetDeviceInterfaceState(&SymbolicLink, TRUE);
479 RtlFreeUnicodeString(&SymbolicLink);
480 }
481 else
482 {
483 DPRINT("Failed to register KSCATEGORY_SYSAUDIO interface Status %x\n", Status);
484 }
485
486 return Status;
487 }
488