Partial implementation of WDMAUD.DRV - device capability querying fails
[reactos.git] / reactos / lib / wdmaud / kernel.c
1 /*
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS Multimedia
5 * FILE: lib/wdmaud/kernel.c
6 * PURPOSE: WDM Audio Support - Kernel Mode Interface
7 * PROGRAMMER: Andrew Greenwood
8 * UPDATE HISTORY:
9 * Nov 18, 2005: Created
10 */
11
12 #define INITGUID /* FIXME */
13
14 #include <windows.h>
15 #include <setupapi.h>
16 #include "wdmaud.h"
17
18 /* HACK ALERT - This goes in ksmedia.h */
19 DEFINE_GUID(KSCATEGORY_WDMAUD,
20 0x3e227e76L, 0x690d, 0x11d2, 0x81, 0x61, 0x00, 0x00, 0xf8, 0x77, 0x5b, 0xf1);
21
22 /* This stores the handle of the kernel device */
23 static HANDLE kernel_device_handle = NULL;
24
25 //static WCHAR*
26
27
28 /*
29 TODO: There's a variant of this that uses critical sections...
30 */
31
32 MMRESULT CallKernelDevice(
33 PWDMAUD_DEVICE_INFO device,
34 DWORD ioctl_code,
35 DWORD param1,
36 DWORD param2)
37 {
38 OVERLAPPED overlap;
39 MMRESULT result = MMSYSERR_ERROR;
40 DWORD name_len = 0;
41 DWORD bytes_returned = 0;
42 BOOL using_critical_section = FALSE;
43
44 ASSERT(kernel_device_handle);
45 ASSERT(device);
46
47 DPRINT("Creating event\n");
48 overlap.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
49
50 if ( ! overlap.hEvent )
51 {
52 DPRINT1("CreateEvent failed - error %d\n", (int)GetLastError());
53 result = MMSYSERR_NOMEM;
54 goto cleanup;
55 }
56
57 DPRINT("Sizeof wchar == %d\n", (int) sizeof(WCHAR));
58 name_len = lstrlen(device->path) * sizeof(WCHAR); /* ok ? */
59
60 /* These seem to carry optional structures */
61 device->ioctl_param1 = param1;
62 device->ioctl_param2 = param2;
63
64 /* Enter critical section if wave/midi device, and if required */
65 if ( ( ! IsMixerDeviceType(device->type) ) &&
66 ( ! IsAuxDeviceType(device->type) ) &&
67 ( device->with_critical_section ) )
68 {
69 /* this seems to crash under some conditions */
70 ASSERT(device->state);
71 using_critical_section = TRUE;
72 EnterCriticalSection(device->state->queue_critical_section);
73 }
74
75 DPRINT("Calling DeviceIoControl with IOCTL %x\n", (int) ioctl_code);
76
77 if ( ! DeviceIoControl(kernel_device_handle,
78 ioctl_code,
79 device,
80 name_len + sizeof(WDMAUD_DEVICE_INFO),
81 device,
82 sizeof(WDMAUD_DEVICE_INFO),
83 &bytes_returned,
84 &overlap) )
85 {
86 DWORD error = GetLastError();
87
88 if (error != ERROR_IO_PENDING)
89 {
90 DPRINT1("FAILED in CallKernelDevice (error %d)\n", (int) error);
91
92 DUMP_WDMAUD_DEVICE_INFO(device);
93
94 result = TranslateWinError(error);
95 goto cleanup;
96 }
97
98 DPRINT("Waiting for overlap I/O event\n");
99
100 /* Wait for the IO to be complete */
101 WaitForSingleObject(overlap.hEvent, INFINITE);
102 }
103
104 result = MMSYSERR_NOERROR;
105 DPRINT("CallKernelDevice succeeded :)\n");
106
107 DUMP_WDMAUD_DEVICE_INFO(device);
108
109 cleanup :
110 {
111 /* Leave the critical section */
112 if ( using_critical_section )
113 LeaveCriticalSection(device->state->queue_critical_section);
114
115 if ( overlap.hEvent )
116 CloseHandle(overlap.hEvent);
117
118 return result;
119 }
120 }
121
122
123 static BOOL ChangeKernelDeviceState(BOOL enable)
124 {
125 PWDMAUD_DEVICE_INFO device = NULL;
126 DWORD ioctl_code;
127 MMRESULT call_result;
128
129 ioctl_code = enable ? IOCTL_WDMAUD_HELLO : IOCTL_WDMAUD_GOODBYE;
130
131 device = CreateDeviceData(WDMAUD_AUX, L"");
132
133 if ( ! device )
134 {
135 DPRINT1("Couldn't create a new device instance structure\n");
136 return FALSE;
137 }
138
139 DPRINT("Setting device type and disabling critical section\n");
140
141 device->type = WDMAUD_AUX;
142 device->with_critical_section = FALSE;
143
144 DPRINT("Calling kernel device\n");
145
146 call_result = CallKernelDevice(device, ioctl_code, 0, 0);
147
148 DeleteDeviceData(device);
149
150 if ( call_result != MMSYSERR_NOERROR )
151 {
152 DPRINT1("Kernel device doesn't like us! (error %d)\n", (int) GetLastError());
153 return FALSE;
154 }
155 else
156 {
157 return TRUE;
158 }
159 }
160
161
162 BOOL EnableKernelInterface()
163 {
164 /* SetupAPI variables/structures for querying device data */
165 SP_DEVICE_INTERFACE_DATA interface_data;
166 PSP_DEVICE_INTERFACE_DETAIL_DATA detail = NULL;
167 DWORD detail_size = 0;
168 HANDLE heap = NULL;
169 HDEVINFO dev_info;
170
171 /* Set to TRUE right at the end to define cleanup behaviour */
172 BOOL success = FALSE;
173
174 /* Don't want to be called more than once */
175 ASSERT(kernel_device_handle == NULL);
176
177 dev_info = SetupDiGetClassDevs(&KSCATEGORY_WDMAUD,
178 NULL,
179 NULL,
180 DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
181
182 if ( ( ! dev_info ) || ( dev_info == INVALID_HANDLE_VALUE ) )
183 {
184 DPRINT1("SetupDiGetClassDevs failed\n");
185 goto cleanup;
186 }
187
188 interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
189
190 if ( ! SetupDiEnumDeviceInterfaces(dev_info,
191 NULL,
192 &KSCATEGORY_WDMAUD,
193 0,
194 &interface_data) )
195 {
196 DPRINT1("SetupDiEnumDeviceInterfaces failed\n");
197 goto cleanup;
198 }
199
200 /*
201 We need to find out the size of the interface detail, before we can
202 actually retrieve the detail. This is a bit backwards, as the function
203 will return a status of success if the interface is invalid, but we
204 need it to fail with ERROR_INSUFFICIENT_BUFFER so we can be told how
205 much memory we need to allocate.
206 */
207
208 if ( SetupDiGetDeviceInterfaceDetail(dev_info,
209 &interface_data,
210 NULL,
211 0,
212 &detail_size,
213 NULL) )
214 {
215 DPRINT1("SetupDiGetDeviceInterfaceDetail shouldn't succeed!\n");
216 goto cleanup;
217 }
218
219 /*
220 Now we make sure the error was the one we expected. If not, bail out.
221 */
222
223 if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
224 {
225 DPRINT1("SetupDiGetDeviceInterfaceDetail returned wrong error code\n");
226 goto cleanup;
227 }
228
229 heap = GetProcessHeap();
230
231 if ( ! heap )
232 {
233 DPRINT1("Unable to get the process heap (error %d)\n",
234 (int)GetLastError());
235 goto cleanup;
236 }
237
238 detail = (PSP_DEVICE_INTERFACE_DETAIL_DATA) HeapAlloc(heap,
239 HEAP_ZERO_MEMORY,
240 detail_size);
241
242 if ( ! detail )
243 {
244 DPRINT1("Unable to allocate memory for the detail buffer (error %d)\n",
245 (int)GetLastError());
246 goto cleanup;
247 }
248
249 detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
250
251 if ( ! SetupDiGetDeviceInterfaceDetail(dev_info,
252 &interface_data,
253 detail,
254 detail_size,
255 0,
256 NULL) )
257 {
258 DPRINT1("SetupDiGetDeviceInterfaceDetail failed\n");
259 goto cleanup;
260 }
261
262 DPRINT("Device path: %S\n", detail->DevicePath);
263
264 /* FIXME - params! */
265 kernel_device_handle = CreateFile(detail->DevicePath,
266 0xC0000000,
267 0,
268 0,
269 3,
270 0x40000080,
271 0);
272
273 DPRINT("kernel_device_handle == 0x%x\n", (int) kernel_device_handle);
274
275 if ( ! kernel_device_handle )
276 {
277 DPRINT1("Unable to open kernel device (error %d)\n",
278 (int) GetLastError());
279 goto cleanup;
280 }
281
282 /* Now we say hello to wdmaud.sys */
283 if ( ! ChangeKernelDeviceState(TRUE) )
284 {
285 DPRINT1("Couldn't enable the kernel device\n");
286 goto cleanup;
287 }
288
289 success = TRUE;
290
291 cleanup :
292 {
293 DPRINT("Cleanup - success == %d\n", (int) success);
294
295 if ( ! success )
296 {
297 DPRINT("Failing\n");
298
299 if ( kernel_device_handle )
300 CloseHandle(kernel_device_handle);
301 }
302
303 if ( heap )
304 {
305 if ( detail )
306 HeapFree(heap, 0, detail);
307 }
308 }
309
310 return success;
311 }
312
313 /*
314 Nothing here should fail, but if it does, we just give up and ASSERT(). If
315 we don't, we could be left in a limbo-state (eg: device open but disabled.)
316 */
317
318 BOOL DisableKernelInterface()
319 {
320 return ChangeKernelDeviceState(FALSE);
321
322 #if 0 /* OLD CODE */
323 PWDMAUD_DEVICE_INFO device = NULL;
324
325 ASSERT(kernel_device_handle);
326
327 /* Say goodbyte to wdmaud.sys */
328 device = CreateDeviceData(WDMAUD_AUX, L"");
329
330 ASSERT(device);
331
332 DPRINT("Setting device type and disabling critical section\n");
333
334 device->type = WDMAUD_AUX;
335 device->with_critical_section = FALSE;
336
337 DPRINT("Calling kernel device\n");
338
339 ASSERT( CallKernelDevice(device, IOCTL_WDMAUD_GOODBYE, 0, 0) == MMSYSERR_NOERROR );
340 ASSERT( CloseHandle(kernel_device_handle) );
341
342 DPRINT("Kernel interface now disabled\n");
343
344 kernel_device_handle = NULL;
345
346 DeleteDeviceData(device);
347 #endif
348 }
349
350
351 /*
352 The use of this should be avoided...
353 */
354
355 HANDLE GetKernelInterface()
356 {
357 return kernel_device_handle;
358 }