xref: /reactos/sdk/lib/drivers/sound/legacy/devname.c (revision c2c66aff)
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