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 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 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 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 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 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 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 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 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