1 /*
2 ReactOS Sound System
3 Device naming & creation helper routines
4
5 Author:
6 Andrew Greenwood (silverblade@reactos.org)
7
8 History:
9 25 May 2008 - Created
10 */
11
12 #include <ntddk.h>
13 #include <ntddsnd.h>
14 #include <sndnames.h>
15 #include <sndtypes.h>
16 #include <debug.h>
17
18
19 /*
20 Default device names
21
22 Just to keep things tidy, we define a structure to hold both the \\Device
23 and \\DosDevices names, and then fill this structure with the default
24 device names that can be found in NTDDSND.H
25 */
26
27 typedef struct _DEVICE_NAME_GROUP
28 {
29 PCWSTR DeviceName;
30 PCWSTR DosDeviceName;
31 } DEVICE_NAME_GROUP;
32
33 DEVICE_NAME_GROUP SoundDeviceNameBodies[6] =
34 {
35 {
36 DD_WAVE_IN_DEVICE_NAME_U,
37 DD_WAVE_IN_DOS_DEVICE_NAME_U
38 },
39 {
40 DD_WAVE_OUT_DEVICE_NAME_U,
41 DD_WAVE_OUT_DOS_DEVICE_NAME_U
42 },
43 {
44 DD_MIDI_IN_DEVICE_NAME_U,
45 DD_MIDI_IN_DOS_DEVICE_NAME_U
46 },
47 {
48 DD_MIDI_OUT_DEVICE_NAME_U,
49 DD_MIDI_OUT_DOS_DEVICE_NAME_U
50 },
51 {
52 DD_MIX_DEVICE_NAME_U,
53 DD_MIX_DOS_DEVICE_NAME_U
54 },
55 {
56 DD_AUX_DEVICE_NAME_U,
57 DD_AUX_DOS_DEVICE_NAME_U
58 }
59 };
60
61
62 /*
63 ConstructDeviceName
64
65 This takes a wide-character string containing the device name body (for
66 example, "\\Device\\WaveOut") and appends the device index, forming a
67 string like "\\Device\\WaveOut0", and so on.
68
69 The resulting device name is a unicode string.
70 */
71
72 NTSTATUS
ConstructDeviceName(IN PCWSTR Path,IN UCHAR Index,OUT PUNICODE_STRING DeviceName)73 ConstructDeviceName(
74 IN PCWSTR Path,
75 IN UCHAR Index,
76 OUT PUNICODE_STRING DeviceName)
77 {
78 UNICODE_STRING UnicodePath;
79 UNICODE_STRING UnicodeIndex;
80 WCHAR IndexStringBuffer[5];
81 USHORT Size;
82 USHORT LastCharacterIndex;
83
84 /* Check for NULL parameters */
85 if ( ( ! Path ) || ( ! DeviceName ) )
86 {
87 DPRINT("Unexpected NULL parameter");
88 return STATUS_INVALID_PARAMETER;
89 }
90
91 /* Range-check */
92 if ( Index >= SOUND_MAX_DEVICES )
93 {
94 DPRINT("Device index %d out of range", Index);
95 return STATUS_INVALID_PARAMETER;
96 }
97
98 /* Initialise the unicode path string */
99 RtlInitUnicodeString(&UnicodePath, Path);
100
101 /* Calculate the length to hold the full string */
102 Size = UnicodePath.Length +
103 sizeof(IndexStringBuffer) +
104 sizeof(UNICODE_NULL);
105
106 /* Allocate memory for DeviceName */
107 DeviceName->Buffer = ExAllocatePool(PagedPool, Size);
108 DeviceName->MaximumLength = Size;
109
110 if ( ! DeviceName->Buffer )
111 {
112 DPRINT("Couldn't allocate memory for device name string");
113 return STATUS_INSUFFICIENT_RESOURCES;
114 }
115
116 /* Copy the path */
117 RtlCopyUnicodeString(DeviceName, &UnicodePath);
118
119 /* Convert Index to string and append */
120 UnicodeIndex.Buffer = IndexStringBuffer;
121 UnicodeIndex.MaximumLength = sizeof(IndexStringBuffer);
122
123 RtlIntegerToUnicodeString((ULONG)Index, 10, &UnicodeIndex);
124 RtlAppendUnicodeStringToString(DeviceName, &UnicodeIndex);
125
126 /* Terminate the string */
127 LastCharacterIndex = DeviceName->Length / sizeof(UNICODE_NULL);
128 DeviceName->Buffer[LastCharacterIndex] = UNICODE_NULL;
129
130 return STATUS_SUCCESS;
131 }
132
133
134 /*
135 FreeUnicodeStringBuffer
136
137 A small helper routine to free a unicode string buffer, nullify the
138 buffer and reset the lengths to zero.
139 */
140
141 VOID
FreeUnicodeStringBuffer(IN PUNICODE_STRING String)142 FreeUnicodeStringBuffer(IN PUNICODE_STRING String)
143 {
144 ASSERT(String != NULL);
145 ASSERT(String->Buffer != NULL);
146
147 ExFreePool(String->Buffer);
148
149 String->Buffer = NULL;
150 String->Length = 0;
151 String->MaximumLength = 0;
152 }
153
154
155 /*
156 GetDefaultSoundDeviceNameBodies
157
158 Simply accesses the SoundDeviceNameBodies struct defined earlier and
159 fills the DeviceNameBody and DosDeviceNameBody parameters accordingly.
160
161 Basically a "safe" way to access the array and perform two assignments
162 with one call, as this will assign the name and DOS name if a valid
163 DeviceType is passed, otherwise it will fail with STATUS_INVALID_PARAMETER.
164 */
165
166 NTSTATUS
GetDefaultSoundDeviceNameBodies(IN UCHAR DeviceType,OUT PCWSTR * DeviceNameBody,OUT PCWSTR * DosDeviceNameBody)167 GetDefaultSoundDeviceNameBodies(
168 IN UCHAR DeviceType,
169 OUT PCWSTR* DeviceNameBody,
170 OUT PCWSTR* DosDeviceNameBody)
171 {
172 if ( ! IS_VALID_SOUND_DEVICE_TYPE(DeviceType) )
173 {
174 DPRINT("Invalid device type");
175 return STATUS_INVALID_PARAMETER;
176 }
177
178 if ( DeviceNameBody )
179 {
180 DPRINT("Reporting device name\n");
181 *DeviceNameBody = SoundDeviceNameBodies[DeviceType].DeviceName;
182 DPRINT("%ws\n", *DeviceNameBody);
183 }
184
185 if ( DosDeviceNameBody )
186 {
187 DPRINT("Reporting DOS device name\n");
188 *DosDeviceNameBody = SoundDeviceNameBodies[DeviceType].DosDeviceName;
189 DPRINT("%ws\n", *DosDeviceNameBody);
190 }
191
192 return STATUS_SUCCESS;
193 }
194
195
196 /*
197 ConstructSoundDeviceNames
198
199 Given two wide-character strings and a device index, convert these into
200 two unicode strings with the index appended to the end.
201
202 This is intended for converting a device name and a DOS device name at
203 the same time.
204 */
205
206 NTSTATUS
ConstructSoundDeviceNames(IN PCWSTR DeviceNameBody,IN PCWSTR DosDeviceNameBody,IN UCHAR Index,OUT PUNICODE_STRING FullDeviceName,OUT PUNICODE_STRING FullDosDeviceName)207 ConstructSoundDeviceNames(
208 IN PCWSTR DeviceNameBody,
209 IN PCWSTR DosDeviceNameBody,
210 IN UCHAR Index,
211 OUT PUNICODE_STRING FullDeviceName,
212 OUT PUNICODE_STRING FullDosDeviceName)
213 {
214 NTSTATUS Status;
215
216 /* Check for NULL parameters */
217 if ( ( ! DeviceNameBody ) || ( ! DosDeviceNameBody ) ||
218 ( ! FullDeviceName ) || ( ! FullDosDeviceName ) )
219 {
220 DPRINT("Unexpected NULL parameter");
221 return STATUS_INVALID_PARAMETER;
222 }
223
224 /* Range-check */
225 if ( Index >= SOUND_MAX_DEVICES )
226 {
227 DPRINT("Device %d exceeds maximum", Index);
228 return STATUS_INVALID_PARAMETER;
229 }
230
231 Status = ConstructDeviceName(DeviceNameBody, Index, FullDeviceName);
232
233 if ( ! NT_SUCCESS(Status) )
234 {
235 /* No need to clean up on failure here */
236 return Status;
237 }
238
239 Status = ConstructDeviceName(DosDeviceNameBody, Index, FullDosDeviceName);
240
241 if ( ! NT_SUCCESS(Status) )
242 {
243 /* We need to free the string we successfully got earlier */
244 FreeUnicodeStringBuffer(FullDeviceName);
245 return Status;
246 }
247
248 return STATUS_SUCCESS;
249 }
250
251
252 /*
253 CreateSoundDevice
254
255 Creates a device and symbolically-links a DOS device to this. Use this
256 when you want to specify alternative device names to the defaults
257 (eg: "\\Device\\MySoundDev" rather than "\\Device\\WaveOut")
258 */
259
260 NTSTATUS
CreateSoundDevice(IN PDRIVER_OBJECT DriverObject,IN PCWSTR WideDeviceName,IN PCWSTR WideDosDeviceName,IN UCHAR Index,IN ULONG ExtensionSize,OUT PDEVICE_OBJECT * DeviceObject)261 CreateSoundDevice(
262 IN PDRIVER_OBJECT DriverObject,
263 IN PCWSTR WideDeviceName,
264 IN PCWSTR WideDosDeviceName,
265 IN UCHAR Index,
266 IN ULONG ExtensionSize,
267 OUT PDEVICE_OBJECT* DeviceObject)
268 {
269 NTSTATUS Status;
270
271 UNICODE_STRING DeviceName;
272 UNICODE_STRING DosDeviceName;
273
274 /* Check for NULL parameters */
275 if ( ( ! DriverObject ) || ( ! DeviceObject ) ||
276 ( ! WideDeviceName ) || ( ! WideDosDeviceName ) )
277 {
278 DPRINT("Unexpected NULL parameter");
279 return STATUS_INVALID_PARAMETER;
280 }
281
282 /* Range-check */
283 if ( Index >= SOUND_MAX_DEVICES )
284 {
285 DPRINT("Device index %d exceeds maximum", Index);
286 return STATUS_INVALID_PARAMETER;
287 }
288
289 /* Construct the device and DOS device names */
290 Status = ConstructSoundDeviceNames(WideDeviceName,
291 WideDosDeviceName,
292 Index,
293 &DeviceName,
294 &DosDeviceName);
295
296 if ( ! NT_SUCCESS(Status) )
297 {
298 return Status;
299 }
300
301 DPRINT("Creating device %ws\n", DeviceName.Buffer);
302
303 /* Now create the device */
304 Status = IoCreateDevice(DriverObject,
305 ExtensionSize,
306 &DeviceName,
307 FILE_DEVICE_SOUND,
308 0,
309 FALSE,
310 DeviceObject);
311
312 if ( ! NT_SUCCESS(Status) )
313 {
314 /* These will have been allocated by ConstructSoundDeviceNames */
315 FreeUnicodeStringBuffer(&DeviceName);
316 FreeUnicodeStringBuffer(&DosDeviceName);
317
318 return Status;
319 }
320
321 DPRINT("Creating link %ws\n", DosDeviceName.Buffer);
322
323 /* Create a symbolic link for the DOS deviec name */
324 Status = IoCreateSymbolicLink(&DosDeviceName, &DeviceName);
325
326 if ( ! NT_SUCCESS(Status) )
327 {
328 IoDeleteDevice(*DeviceObject);
329
330 /* These will have been allocated by ConstructSoundDeviceNames */
331 FreeUnicodeStringBuffer(&DeviceName);
332 FreeUnicodeStringBuffer(&DosDeviceName);
333
334 return Status;
335 }
336
337 return STATUS_SUCCESS;
338 }
339
340
341 /*
342 CreateSoundDeviceWithDefaultName
343
344 Similar to CreateSoundDevice, except this uses the default device names
345 ("\\Device\\WaveOut" etc.) based on the DeviceType parameter.
346 */
347
348 NTSTATUS
CreateSoundDeviceWithDefaultName(IN PDRIVER_OBJECT DriverObject,IN UCHAR DeviceType,IN UCHAR Index,IN ULONG ExtensionSize,OUT PDEVICE_OBJECT * DeviceObject)349 CreateSoundDeviceWithDefaultName(
350 IN PDRIVER_OBJECT DriverObject,
351 IN UCHAR DeviceType,
352 IN UCHAR Index,
353 IN ULONG ExtensionSize,
354 OUT PDEVICE_OBJECT* DeviceObject)
355 {
356 NTSTATUS Status;
357 PCWSTR WideDeviceName = NULL;
358 PCWSTR WideDosDeviceName = NULL;
359
360 /* Check for NULL parameters */
361 if ( ( ! DriverObject ) || ( ! DeviceObject ) )
362 {
363 DPRINT("Unexpected NULL parameter");
364 return STATUS_INVALID_PARAMETER;
365 }
366
367 /* Range-check */
368 if ( Index >= SOUND_MAX_DEVICES )
369 {
370 DPRINT("Device index %d exceeds maximum", Index);
371 return STATUS_INVALID_PARAMETER;
372 }
373
374 /* Look-up the default name based on the device type */
375 Status = GetDefaultSoundDeviceNameBodies(DeviceType,
376 &WideDeviceName,
377 &WideDosDeviceName);
378
379 if ( ! NT_SUCCESS(Status) )
380 {
381 return Status;
382 }
383
384 /* Go create the device! */
385 Status = CreateSoundDevice(DriverObject,
386 WideDeviceName,
387 WideDosDeviceName,
388 Index,
389 ExtensionSize,
390 DeviceObject);
391
392 if ( ! NT_SUCCESS(Status) )
393 {
394 /* No clean-up to do */
395 return Status;
396 }
397
398 return STATUS_SUCCESS;
399 }
400
401 NTSTATUS
DestroySoundDevice(IN PDEVICE_OBJECT DeviceObject,IN PCWSTR WideDosDeviceName,IN UCHAR Index)402 DestroySoundDevice(
403 IN PDEVICE_OBJECT DeviceObject,
404 IN PCWSTR WideDosDeviceName,
405 IN UCHAR Index)
406 {
407 NTSTATUS Status;
408 UNICODE_STRING DosDeviceName;
409
410 /* Check for NULL parameters */
411 if ( ( ! WideDosDeviceName ) || ( ! DeviceObject ) )
412 {
413 DPRINT("Unexpected NULL parameter");
414 return STATUS_INVALID_PARAMETER;
415 }
416
417 /* Range-check */
418 if ( Index >= SOUND_MAX_DEVICES )
419 {
420 DPRINT("Device %d exceeds maximum", Index);
421 return STATUS_INVALID_PARAMETER;
422 }
423
424 Status = ConstructDeviceName(WideDosDeviceName, Index, &DosDeviceName);
425
426 if ( ! NT_SUCCESS(Status) )
427 {
428 return Status;
429 }
430
431 DPRINT("Deleting symlink %ws\n", DosDeviceName.Buffer);
432
433 Status = IoDeleteSymbolicLink(&DosDeviceName);
434 DPRINT("Status of symlink deletion is 0x%08x\n", Status);
435 /*
436 ASSERT(NT_SUCCESS(Status));
437 */
438
439 IoDeleteDevice(DeviceObject);
440
441 return STATUS_SUCCESS;
442 }
443
444 NTSTATUS
DestroySoundDeviceWithDefaultName(IN PDEVICE_OBJECT DeviceObject,IN UCHAR DeviceType,IN UCHAR Index)445 DestroySoundDeviceWithDefaultName(
446 IN PDEVICE_OBJECT DeviceObject,
447 IN UCHAR DeviceType,
448 IN UCHAR Index)
449 {
450 NTSTATUS Status;
451 PCWSTR WideDosDeviceName = NULL;
452
453 /* Check for NULL parameters */
454 if ( ( ! DeviceObject ) )
455 {
456 DPRINT("Unexpected NULL parameter");
457 return STATUS_INVALID_PARAMETER;
458 }
459
460 /* Range-check */
461 if ( Index >= SOUND_MAX_DEVICES )
462 {
463 DPRINT("Device index %d exceeds maximum", Index);
464 return STATUS_INVALID_PARAMETER;
465 }
466
467 /* Look-up the default name based on the device type */
468 Status = GetDefaultSoundDeviceNameBodies(DeviceType,
469 NULL,
470 &WideDosDeviceName);
471
472 if ( ! NT_SUCCESS(Status) )
473 {
474 return Status;
475 }
476
477 DPRINT("DOS device name at %p\n", WideDosDeviceName);
478
479 DPRINT("DOS device name is based on %ws\n", WideDosDeviceName);
480
481 return DestroySoundDevice(DeviceObject, WideDosDeviceName, Index);
482 }
483