2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/io/iomgr/deviface.c
5 * PURPOSE: Device interface functions
7 * PROGRAMMERS: Filip Navara (xnavara@volny.cz)
8 * Matthew Brace (ismarc@austin.rr.com)
9 * Hervé Poussineau (hpoussin@reactos.org)
12 /* INCLUDES ******************************************************************/
19 /* FUNCTIONS *****************************************************************/
21 static PWCHAR BaseKeyString
= L
"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\DeviceClasses\\";
25 OpenRegistryHandlesFromSymbolicLink(IN PUNICODE_STRING SymbolicLinkName
,
26 IN ACCESS_MASK DesiredAccess
,
27 IN OPTIONAL PHANDLE GuidKey
,
28 IN OPTIONAL PHANDLE DeviceKey
,
29 IN OPTIONAL PHANDLE InstanceKey
)
31 OBJECT_ATTRIBUTES ObjectAttributes
;
32 WCHAR PathBuffer
[MAX_PATH
];
33 UNICODE_STRING BaseKeyU
;
34 UNICODE_STRING GuidString
, SubKeyName
, ReferenceString
;
35 PWCHAR StartPosition
, EndPosition
;
37 PHANDLE GuidKeyRealP
, DeviceKeyRealP
, InstanceKeyRealP
;
38 HANDLE GuidKeyReal
, DeviceKeyReal
, InstanceKeyReal
;
41 SubKeyName
.Buffer
= NULL
;
44 GuidKeyRealP
= GuidKey
;
46 GuidKeyRealP
= &GuidKeyReal
;
48 if (DeviceKey
!= NULL
)
49 DeviceKeyRealP
= DeviceKey
;
51 DeviceKeyRealP
= &DeviceKeyReal
;
53 if (InstanceKey
!= NULL
)
54 InstanceKeyRealP
= InstanceKey
;
56 InstanceKeyRealP
= &InstanceKeyReal
;
58 *GuidKeyRealP
= INVALID_HANDLE_VALUE
;
59 *DeviceKeyRealP
= INVALID_HANDLE_VALUE
;
60 *InstanceKeyRealP
= INVALID_HANDLE_VALUE
;
62 BaseKeyU
.Buffer
= PathBuffer
;
64 BaseKeyU
.MaximumLength
= MAX_PATH
* sizeof(WCHAR
);
66 RtlAppendUnicodeToString(&BaseKeyU
, BaseKeyString
);
68 /* Open the DeviceClasses key */
69 InitializeObjectAttributes(&ObjectAttributes
,
71 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
74 Status
= ZwOpenKey(&ClassesKey
,
75 DesiredAccess
| KEY_ENUMERATE_SUB_KEYS
,
77 if (!NT_SUCCESS(Status
))
79 DPRINT1("Failed to open %wZ\n", &BaseKeyU
);
83 StartPosition
= wcschr(SymbolicLinkName
->Buffer
, L
'{');
84 EndPosition
= wcschr(SymbolicLinkName
->Buffer
, L
'}');
85 if (!StartPosition
|| !EndPosition
|| StartPosition
> EndPosition
)
87 DPRINT1("Bad symbolic link: %wZ\n", SymbolicLinkName
);
88 return STATUS_INVALID_PARAMETER_1
;
90 GuidString
.Buffer
= StartPosition
;
91 GuidString
.MaximumLength
= GuidString
.Length
= (USHORT
)((ULONG_PTR
)(EndPosition
+ 1) - (ULONG_PTR
)StartPosition
);
93 InitializeObjectAttributes(&ObjectAttributes
,
95 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
98 Status
= ZwOpenKey(GuidKeyRealP
,
99 DesiredAccess
| KEY_ENUMERATE_SUB_KEYS
,
102 if (!NT_SUCCESS(Status
))
104 DPRINT1("Failed to open %wZ%wZ (%x)\n", &BaseKeyU
, &GuidString
, Status
);
108 SubKeyName
.MaximumLength
= SymbolicLinkName
->Length
+ sizeof(WCHAR
);
109 SubKeyName
.Length
= 0;
110 SubKeyName
.Buffer
= ExAllocatePool(PagedPool
, SubKeyName
.MaximumLength
);
111 if (!SubKeyName
.Buffer
)
113 Status
= STATUS_INSUFFICIENT_RESOURCES
;
117 RtlAppendUnicodeStringToString(&SubKeyName
,
120 SubKeyName
.Buffer
[SubKeyName
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
122 SubKeyName
.Buffer
[0] = L
'#';
123 SubKeyName
.Buffer
[1] = L
'#';
124 SubKeyName
.Buffer
[2] = L
'?';
125 SubKeyName
.Buffer
[3] = L
'#';
127 ReferenceString
.Buffer
= wcsrchr(SubKeyName
.Buffer
, '\\');
128 if (ReferenceString
.Buffer
!= NULL
)
130 ReferenceString
.Buffer
[0] = L
'#';
132 SubKeyName
.Length
= (USHORT
)((ULONG_PTR
)(ReferenceString
.Buffer
) - (ULONG_PTR
)SubKeyName
.Buffer
);
133 ReferenceString
.Length
= SymbolicLinkName
->Length
- SubKeyName
.Length
;
137 RtlInitUnicodeString(&ReferenceString
, L
"#");
140 InitializeObjectAttributes(&ObjectAttributes
,
142 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
145 Status
= ZwOpenKey(DeviceKeyRealP
,
146 DesiredAccess
| KEY_ENUMERATE_SUB_KEYS
,
148 if (!NT_SUCCESS(Status
))
150 DPRINT1("Failed to open %wZ%wZ\\%wZ\n", &BaseKeyU
, &GuidString
, &SubKeyName
);
154 InitializeObjectAttributes(&ObjectAttributes
,
156 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
159 Status
= ZwOpenKey(InstanceKeyRealP
,
162 if (!NT_SUCCESS(Status
))
164 DPRINT1("Failed to open %wZ%wZ\\%wZ%\\%wZ (%x)\n", &BaseKeyU
, &GuidString
, &SubKeyName
, &ReferenceString
, Status
);
168 Status
= STATUS_SUCCESS
;
171 if (SubKeyName
.Buffer
!= NULL
)
172 ExFreePool(SubKeyName
.Buffer
);
174 if (NT_SUCCESS(Status
))
177 ZwClose(*GuidKeyRealP
);
180 ZwClose(*DeviceKeyRealP
);
183 ZwClose(*InstanceKeyRealP
);
187 if (*GuidKeyRealP
!= INVALID_HANDLE_VALUE
)
188 ZwClose(*GuidKeyRealP
);
190 if (*DeviceKeyRealP
!= INVALID_HANDLE_VALUE
)
191 ZwClose(*DeviceKeyRealP
);
193 if (*InstanceKeyRealP
!= INVALID_HANDLE_VALUE
)
194 ZwClose(*InstanceKeyRealP
);
200 * @name IoOpenDeviceInterfaceRegistryKey
203 * Provides a handle to the device's interface instance registry key.
206 * @param SymbolicLinkName
207 * Pointer to a string which identifies the device interface instance
209 * @param DesiredAccess
210 * Desired ACCESS_MASK used to access the key (like KEY_READ,
213 * @param DeviceInterfaceKey
214 * If a call has been succesfull, a handle to the registry key
215 * will be stored there
217 * @return Three different NTSTATUS values in case of errors, and STATUS_SUCCESS
218 * otherwise (see WDK for details)
220 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a system thread
225 IoOpenDeviceInterfaceRegistryKey(IN PUNICODE_STRING SymbolicLinkName
,
226 IN ACCESS_MASK DesiredAccess
,
227 OUT PHANDLE DeviceInterfaceKey
)
229 HANDLE InstanceKey
, DeviceParametersKey
;
231 OBJECT_ATTRIBUTES ObjectAttributes
;
232 UNICODE_STRING DeviceParametersU
= RTL_CONSTANT_STRING(L
"Device Parameters");
234 Status
= OpenRegistryHandlesFromSymbolicLink(SymbolicLinkName
,
239 if (!NT_SUCCESS(Status
))
242 InitializeObjectAttributes(&ObjectAttributes
,
244 OBJ_CASE_INSENSITIVE
| OBJ_OPENIF
,
247 Status
= ZwCreateKey(&DeviceParametersKey
,
252 REG_OPTION_NON_VOLATILE
,
254 ZwClose(InstanceKey
);
256 if (NT_SUCCESS(Status
))
257 *DeviceInterfaceKey
= DeviceParametersKey
;
263 * @name IoGetDeviceInterfaceAlias
266 * Returns the alias device interface of the specified device interface
267 * instance, if the alias exists.
270 * @param SymbolicLinkName
271 * Pointer to a string which identifies the device interface instance
273 * @param AliasInterfaceClassGuid
276 * @param AliasSymbolicLinkName
279 * @return Three different NTSTATUS values in case of errors, and STATUS_SUCCESS
280 * otherwise (see WDK for details)
282 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a system thread
287 IoGetDeviceInterfaceAlias(IN PUNICODE_STRING SymbolicLinkName
,
288 IN CONST GUID
*AliasInterfaceClassGuid
,
289 OUT PUNICODE_STRING AliasSymbolicLinkName
)
291 return STATUS_NOT_IMPLEMENTED
;
295 * @name IopOpenInterfaceKey
297 * Returns the alias device interface of the specified device interface
299 * @param InterfaceClassGuid
302 * @param DesiredAccess
305 * @param pInterfaceKey
308 * @return Usual NTSTATUS
314 IopOpenInterfaceKey(IN CONST GUID
*InterfaceClassGuid
,
315 IN ACCESS_MASK DesiredAccess
,
316 OUT HANDLE
*pInterfaceKey
)
318 UNICODE_STRING LocalMachine
= RTL_CONSTANT_STRING(L
"\\Registry\\Machine\\");
319 UNICODE_STRING GuidString
;
320 UNICODE_STRING KeyName
;
321 OBJECT_ATTRIBUTES ObjectAttributes
;
322 HANDLE InterfaceKey
= INVALID_HANDLE_VALUE
;
325 GuidString
.Buffer
= KeyName
.Buffer
= NULL
;
327 Status
= RtlStringFromGUID(InterfaceClassGuid
, &GuidString
);
328 if (!NT_SUCCESS(Status
))
330 DPRINT("RtlStringFromGUID() failed with status 0x%08lx\n", Status
);
335 KeyName
.MaximumLength
= LocalMachine
.Length
+ ((USHORT
)wcslen(REGSTR_PATH_DEVICE_CLASSES
) + 1) * sizeof(WCHAR
) + GuidString
.Length
;
336 KeyName
.Buffer
= ExAllocatePool(PagedPool
, KeyName
.MaximumLength
);
339 DPRINT("ExAllocatePool() failed\n");
340 Status
= STATUS_INSUFFICIENT_RESOURCES
;
344 Status
= RtlAppendUnicodeStringToString(&KeyName
, &LocalMachine
);
345 if (!NT_SUCCESS(Status
))
347 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status
);
350 Status
= RtlAppendUnicodeToString(&KeyName
, REGSTR_PATH_DEVICE_CLASSES
);
351 if (!NT_SUCCESS(Status
))
353 DPRINT("RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status
);
356 Status
= RtlAppendUnicodeToString(&KeyName
, L
"\\");
357 if (!NT_SUCCESS(Status
))
359 DPRINT("RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status
);
362 Status
= RtlAppendUnicodeStringToString(&KeyName
, &GuidString
);
363 if (!NT_SUCCESS(Status
))
365 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status
);
369 InitializeObjectAttributes(
372 OBJ_CASE_INSENSITIVE
,
379 if (!NT_SUCCESS(Status
))
381 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status
);
385 *pInterfaceKey
= InterfaceKey
;
386 Status
= STATUS_SUCCESS
;
389 if (!NT_SUCCESS(Status
))
391 if (InterfaceKey
!= INVALID_HANDLE_VALUE
)
392 ZwClose(InterfaceKey
);
394 RtlFreeUnicodeString(&GuidString
);
395 RtlFreeUnicodeString(&KeyName
);
400 * @name IoGetDeviceInterfaces
403 * Returns a list of device interfaces of a particular device interface class.
406 * @param InterfaceClassGuid
407 * Points to a class GUID specifying the device interface class
409 * @param PhysicalDeviceObject
410 * Points to an optional PDO that narrows the search to only the
411 * device interfaces of the device represented by the PDO
414 * Specifies flags that modify the search for device interfaces. The
415 * DEVICE_INTERFACE_INCLUDE_NONACTIVE flag specifies that the list of
416 * returned symbolic links should contain also disabled device
417 * interfaces in addition to the enabled ones.
419 * @param SymbolicLinkList
420 * Points to a character pointer that is filled in on successful return
421 * with a list of unicode strings identifying the device interfaces
422 * that match the search criteria. The newly allocated buffer contains
423 * a list of symbolic link names. Each unicode string in the list is
424 * null-terminated; the end of the whole list is marked by an additional
425 * NULL. The caller is responsible for freeing the buffer (ExFreePool)
426 * when it is no longer needed.
427 * If no device interfaces match the search criteria, this routine
428 * returns STATUS_SUCCESS and the string contains a single NULL
431 * @return Usual NTSTATUS
438 IoGetDeviceInterfaces(IN CONST GUID
*InterfaceClassGuid
,
439 IN PDEVICE_OBJECT PhysicalDeviceObject OPTIONAL
,
441 OUT PWSTR
*SymbolicLinkList
)
443 UNICODE_STRING Control
= RTL_CONSTANT_STRING(L
"Control");
444 UNICODE_STRING SymbolicLink
= RTL_CONSTANT_STRING(L
"SymbolicLink");
445 HANDLE InterfaceKey
= INVALID_HANDLE_VALUE
;
446 HANDLE DeviceKey
= INVALID_HANDLE_VALUE
;
447 HANDLE ReferenceKey
= INVALID_HANDLE_VALUE
;
448 HANDLE ControlKey
= INVALID_HANDLE_VALUE
;
449 PKEY_BASIC_INFORMATION DeviceBi
= NULL
;
450 PKEY_BASIC_INFORMATION ReferenceBi
= NULL
;
451 PKEY_VALUE_PARTIAL_INFORMATION bip
= NULL
;
452 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo
;
453 UNICODE_STRING KeyName
;
454 OBJECT_ATTRIBUTES ObjectAttributes
;
455 BOOLEAN FoundRightPDO
= FALSE
;
456 ULONG i
= 0, j
, Size
, NeededLength
, ActualLength
, LinkedValue
;
457 UNICODE_STRING ReturnBuffer
= { 0, 0, NULL
};
462 Status
= IopOpenInterfaceKey(InterfaceClassGuid
, KEY_ENUMERATE_SUB_KEYS
, &InterfaceKey
);
463 if (!NT_SUCCESS(Status
))
465 DPRINT("IopOpenInterfaceKey() failed with status 0x%08lx\n", Status
);
469 /* Enumerate subkeys (ie the different device objets) */
472 Status
= ZwEnumerateKey(
479 if (Status
== STATUS_NO_MORE_ENTRIES
)
483 else if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_TOO_SMALL
)
485 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status
);
489 DeviceBi
= ExAllocatePool(PagedPool
, Size
);
492 DPRINT("ExAllocatePool() failed\n");
493 Status
= STATUS_INSUFFICIENT_RESOURCES
;
496 Status
= ZwEnumerateKey(
503 if (!NT_SUCCESS(Status
))
505 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status
);
509 /* Open device key */
510 KeyName
.Length
= KeyName
.MaximumLength
= (USHORT
)DeviceBi
->NameLength
;
511 KeyName
.Buffer
= DeviceBi
->Name
;
512 InitializeObjectAttributes(
515 OBJ_CASE_INSENSITIVE
,
520 KEY_ENUMERATE_SUB_KEYS
,
522 if (!NT_SUCCESS(Status
))
524 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status
);
528 if (PhysicalDeviceObject
)
530 /* Check if we are on the right physical device object,
531 * by reading the DeviceInstance string
533 DPRINT1("PhysicalDeviceObject != NULL. Case not implemented.\n");
534 //FoundRightPDO = TRUE;
535 Status
= STATUS_NOT_IMPLEMENTED
;
539 /* Enumerate subkeys (ie the different reference strings) */
543 Status
= ZwEnumerateKey(
550 if (Status
== STATUS_NO_MORE_ENTRIES
)
554 else if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_TOO_SMALL
)
556 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status
);
560 ReferenceBi
= ExAllocatePool(PagedPool
, Size
);
563 DPRINT("ExAllocatePool() failed\n");
564 Status
= STATUS_INSUFFICIENT_RESOURCES
;
567 Status
= ZwEnumerateKey(
574 if (!NT_SUCCESS(Status
))
576 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status
);
580 KeyName
.Length
= KeyName
.MaximumLength
= (USHORT
)ReferenceBi
->NameLength
;
581 KeyName
.Buffer
= ReferenceBi
->Name
;
582 if (RtlEqualUnicodeString(&KeyName
, &Control
, TRUE
))
584 /* Skip Control subkey */
585 goto NextReferenceString
;
588 /* Open reference key */
589 InitializeObjectAttributes(
592 OBJ_CASE_INSENSITIVE
,
599 if (!NT_SUCCESS(Status
))
601 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status
);
605 if (!(Flags
& DEVICE_INTERFACE_INCLUDE_NONACTIVE
))
607 /* We have to check if the interface is enabled, by
608 * reading the Linked value in the Control subkey
610 InitializeObjectAttributes(
613 OBJ_CASE_INSENSITIVE
,
620 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
622 /* That's OK. The key doesn't exist (yet) because
623 * the interface is not activated.
625 goto NextReferenceString
;
627 else if (!NT_SUCCESS(Status
))
629 DPRINT1("ZwOpenKey() failed with status 0x%08lx\n", Status
);
633 RtlInitUnicodeString(&KeyName
, L
"Linked");
634 Status
= ZwQueryValueKey(ControlKey
,
636 KeyValuePartialInformation
,
640 if (Status
== STATUS_BUFFER_TOO_SMALL
)
642 ActualLength
= NeededLength
;
643 PartialInfo
= ExAllocatePool(NonPagedPool
, ActualLength
);
646 Status
= STATUS_INSUFFICIENT_RESOURCES
;
650 Status
= ZwQueryValueKey(ControlKey
,
652 KeyValuePartialInformation
,
656 if (!NT_SUCCESS(Status
))
658 DPRINT1("ZwQueryValueKey #2 failed (%x)\n", Status
);
659 ExFreePool(PartialInfo
);
663 if (PartialInfo
->Type
!= REG_DWORD
|| PartialInfo
->DataLength
!= sizeof(ULONG
))
665 DPRINT1("Bad registry read\n");
666 ExFreePool(PartialInfo
);
670 RtlCopyMemory(&LinkedValue
,
672 PartialInfo
->DataLength
);
674 ExFreePool(PartialInfo
);
675 if (LinkedValue
== 0)
677 /* This interface isn't active */
678 goto NextReferenceString
;
683 DPRINT1("ZwQueryValueKey #1 failed (%x)\n", Status
);
688 /* Read the SymbolicLink string and add it into SymbolicLinkList */
689 Status
= ZwQueryValueKey(
692 KeyValuePartialInformation
,
696 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_TOO_SMALL
)
698 DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status
);
701 bip
= ExAllocatePool(PagedPool
, Size
);
704 DPRINT("ExAllocatePool() failed\n");
705 Status
= STATUS_INSUFFICIENT_RESOURCES
;
708 Status
= ZwQueryValueKey(
711 KeyValuePartialInformation
,
715 if (!NT_SUCCESS(Status
))
717 DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status
);
720 else if (bip
->Type
!= REG_SZ
)
722 DPRINT("Unexpected registry type 0x%lx (expected 0x%lx)\n", bip
->Type
, REG_SZ
);
723 Status
= STATUS_UNSUCCESSFUL
;
726 else if (bip
->DataLength
< 5 * sizeof(WCHAR
))
728 DPRINT("Registry string too short (length %lu, expected %lu at least)\n", bip
->DataLength
< 5 * sizeof(WCHAR
));
729 Status
= STATUS_UNSUCCESSFUL
;
732 KeyName
.Length
= KeyName
.MaximumLength
= (USHORT
)bip
->DataLength
- 4 * sizeof(WCHAR
);
733 KeyName
.Buffer
= &((PWSTR
)bip
->Data
)[4];
735 /* Add new symbolic link to symbolic link list */
736 if (ReturnBuffer
.Length
+ KeyName
.Length
+ sizeof(WCHAR
) > ReturnBuffer
.MaximumLength
)
739 ReturnBuffer
.MaximumLength
= (USHORT
)max(ReturnBuffer
.MaximumLength
* 2,
740 (USHORT
)(ReturnBuffer
.Length
+
743 NewBuffer
= ExAllocatePool(PagedPool
, ReturnBuffer
.MaximumLength
);
746 DPRINT("ExAllocatePool() failed\n");
747 Status
= STATUS_INSUFFICIENT_RESOURCES
;
750 RtlCopyMemory(NewBuffer
, ReturnBuffer
.Buffer
, ReturnBuffer
.Length
);
751 if (ReturnBuffer
.Buffer
)
752 ExFreePool(ReturnBuffer
.Buffer
);
753 ReturnBuffer
.Buffer
= NewBuffer
;
755 DPRINT("Adding symbolic link %wZ\n", &KeyName
);
756 Status
= RtlAppendUnicodeStringToString(&ReturnBuffer
, &KeyName
);
757 if (!NT_SUCCESS(Status
))
759 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status
);
762 /* RtlAppendUnicodeStringToString added a NULL at the end of the
763 * destination string, but didn't increase the Length field.
766 ReturnBuffer
.Length
+= sizeof(WCHAR
);
769 ExFreePool(ReferenceBi
);
774 if (ReferenceKey
!= INVALID_HANDLE_VALUE
)
776 ZwClose(ReferenceKey
);
777 ReferenceKey
= INVALID_HANDLE_VALUE
;
779 if (ControlKey
!= INVALID_HANDLE_VALUE
)
782 ControlKey
= INVALID_HANDLE_VALUE
;
787 /* No need to go further, as we already have found what we searched */
791 ExFreePool(DeviceBi
);
794 DeviceKey
= INVALID_HANDLE_VALUE
;
797 /* Add final NULL to ReturnBuffer */
798 if (ReturnBuffer
.Length
>= ReturnBuffer
.MaximumLength
)
801 ReturnBuffer
.MaximumLength
+= sizeof(WCHAR
);
802 NewBuffer
= ExAllocatePool(PagedPool
, ReturnBuffer
.MaximumLength
);
805 DPRINT("ExAllocatePool() failed\n");
806 Status
= STATUS_INSUFFICIENT_RESOURCES
;
809 if (ReturnBuffer
.Buffer
)
811 RtlCopyMemory(NewBuffer
, ReturnBuffer
.Buffer
, ReturnBuffer
.Length
);
812 ExFreePool(ReturnBuffer
.Buffer
);
814 ReturnBuffer
.Buffer
= NewBuffer
;
816 ReturnBuffer
.Buffer
[ReturnBuffer
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
817 *SymbolicLinkList
= ReturnBuffer
.Buffer
;
818 Status
= STATUS_SUCCESS
;
821 if (!NT_SUCCESS(Status
) && ReturnBuffer
.Buffer
)
822 ExFreePool(ReturnBuffer
.Buffer
);
823 if (InterfaceKey
!= INVALID_HANDLE_VALUE
)
824 ZwClose(InterfaceKey
);
825 if (DeviceKey
!= INVALID_HANDLE_VALUE
)
827 if (ReferenceKey
!= INVALID_HANDLE_VALUE
)
828 ZwClose(ReferenceKey
);
829 if (ControlKey
!= INVALID_HANDLE_VALUE
)
832 ExFreePool(DeviceBi
);
834 ExFreePool(ReferenceBi
);
841 * @name IoRegisterDeviceInterface
844 * Registers a device interface class, if it has not been previously registered,
845 * and creates a new instance of the interface class, which a driver can
846 * subsequently enable for use by applications or other system components.
849 * @param PhysicalDeviceObject
850 * Points to an optional PDO that narrows the search to only the
851 * device interfaces of the device represented by the PDO
853 * @param InterfaceClassGuid
854 * Points to a class GUID specifying the device interface class
856 * @param ReferenceString
857 * Optional parameter, pointing to a unicode string. For a full
858 * description of this rather rarely used param (usually drivers
859 * pass NULL here) see WDK
861 * @param SymbolicLinkName
862 * Pointer to the resulting unicode string
864 * @return Usual NTSTATUS
866 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a
872 IoRegisterDeviceInterface(IN PDEVICE_OBJECT PhysicalDeviceObject
,
873 IN CONST GUID
*InterfaceClassGuid
,
874 IN PUNICODE_STRING ReferenceString OPTIONAL
,
875 OUT PUNICODE_STRING SymbolicLinkName
)
877 PUNICODE_STRING InstancePath
;
878 UNICODE_STRING GuidString
;
879 UNICODE_STRING SubKeyName
;
880 UNICODE_STRING InterfaceKeyName
;
881 UNICODE_STRING BaseKeyName
;
882 UCHAR PdoNameInfoBuffer
[sizeof(OBJECT_NAME_INFORMATION
) + (256 * sizeof(WCHAR
))];
883 POBJECT_NAME_INFORMATION PdoNameInfo
= (POBJECT_NAME_INFORMATION
)PdoNameInfoBuffer
;
884 UNICODE_STRING DeviceInstance
= RTL_CONSTANT_STRING(L
"DeviceInstance");
885 UNICODE_STRING SymbolicLink
= RTL_CONSTANT_STRING(L
"SymbolicLink");
890 OBJECT_ATTRIBUTES ObjectAttributes
;
892 NTSTATUS Status
, SymLinkStatus
;
893 PEXTENDED_DEVOBJ_EXTENSION DeviceObjectExtension
;
895 ASSERT_IRQL_EQUAL(PASSIVE_LEVEL
);
897 DPRINT("IoRegisterDeviceInterface(): PDO %p, RefString: %wZ\n",
898 PhysicalDeviceObject
, ReferenceString
);
900 /* Parameters must pass three border of checks */
901 DeviceObjectExtension
= (PEXTENDED_DEVOBJ_EXTENSION
)PhysicalDeviceObject
->DeviceObjectExtension
;
903 /* 1st level: Presence of a Device Node */
904 if (DeviceObjectExtension
->DeviceNode
== NULL
)
906 DPRINT("PhysicalDeviceObject 0x%p doesn't have a DeviceNode\n", PhysicalDeviceObject
);
907 return STATUS_INVALID_DEVICE_REQUEST
;
910 /* 2nd level: Presence of an non-zero length InstancePath */
911 if (DeviceObjectExtension
->DeviceNode
->InstancePath
.Length
== 0)
913 DPRINT("PhysicalDeviceObject 0x%p's DOE has zero-length InstancePath\n", PhysicalDeviceObject
);
914 return STATUS_INVALID_DEVICE_REQUEST
;
917 /* 3rd level: Optional, based on WDK documentation */
918 if (ReferenceString
!= NULL
)
920 /* Reference string must not contain path-separator symbols */
921 for (i
= 0; i
< ReferenceString
->Length
/ sizeof(WCHAR
); i
++)
923 if ((ReferenceString
->Buffer
[i
] == '\\') ||
924 (ReferenceString
->Buffer
[i
] == '/'))
925 return STATUS_INVALID_DEVICE_REQUEST
;
929 Status
= RtlStringFromGUID(InterfaceClassGuid
, &GuidString
);
930 if (!NT_SUCCESS(Status
))
932 DPRINT("RtlStringFromGUID() failed with status 0x%08lx\n", Status
);
936 /* Create Pdo name: \Device\xxxxxxxx (unnamed device) */
937 Status
= ObQueryNameString(
938 PhysicalDeviceObject
,
940 sizeof(PdoNameInfoBuffer
),
942 if (!NT_SUCCESS(Status
))
944 DPRINT("ObQueryNameString() failed with status 0x%08lx\n", Status
);
947 ASSERT(PdoNameInfo
->Name
.Length
);
949 /* Create base key name for this interface: HKLM\SYSTEM\CurrentControlSet\Control\DeviceClasses\{GUID} */
950 ASSERT(((PEXTENDED_DEVOBJ_EXTENSION
)PhysicalDeviceObject
->DeviceObjectExtension
)->DeviceNode
);
951 InstancePath
= &((PEXTENDED_DEVOBJ_EXTENSION
)PhysicalDeviceObject
->DeviceObjectExtension
)->DeviceNode
->InstancePath
;
952 BaseKeyName
.Length
= (USHORT
)wcslen(BaseKeyString
) * sizeof(WCHAR
);
953 BaseKeyName
.MaximumLength
= BaseKeyName
.Length
955 BaseKeyName
.Buffer
= ExAllocatePool(
957 BaseKeyName
.MaximumLength
);
958 if (!BaseKeyName
.Buffer
)
960 DPRINT("ExAllocatePool() failed\n");
961 return STATUS_INSUFFICIENT_RESOURCES
;
963 wcscpy(BaseKeyName
.Buffer
, BaseKeyString
);
964 RtlAppendUnicodeStringToString(&BaseKeyName
, &GuidString
);
966 /* Create BaseKeyName key in registry */
967 InitializeObjectAttributes(
970 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
| OBJ_OPENIF
,
971 NULL
, /* RootDirectory */
972 NULL
); /* SecurityDescriptor */
974 Status
= ZwCreateKey(
981 NULL
); /* Disposition */
983 if (!NT_SUCCESS(Status
))
985 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status
);
986 ExFreePool(BaseKeyName
.Buffer
);
990 /* Create key name for this interface: ##?#ACPI#PNP0501#1#{GUID} */
991 InterfaceKeyName
.Length
= 0;
992 InterfaceKeyName
.MaximumLength
=
993 4 * sizeof(WCHAR
) + /* 4 = size of ##?# */
994 InstancePath
->Length
+
995 sizeof(WCHAR
) + /* 1 = size of # */
997 InterfaceKeyName
.Buffer
= ExAllocatePool(
999 InterfaceKeyName
.MaximumLength
);
1000 if (!InterfaceKeyName
.Buffer
)
1002 DPRINT("ExAllocatePool() failed\n");
1003 return STATUS_INSUFFICIENT_RESOURCES
;
1006 RtlAppendUnicodeToString(&InterfaceKeyName
, L
"##?#");
1007 StartIndex
= InterfaceKeyName
.Length
/ sizeof(WCHAR
);
1008 RtlAppendUnicodeStringToString(&InterfaceKeyName
, InstancePath
);
1009 for (i
= 0; i
< InstancePath
->Length
/ sizeof(WCHAR
); i
++)
1011 if (InterfaceKeyName
.Buffer
[StartIndex
+ i
] == '\\')
1012 InterfaceKeyName
.Buffer
[StartIndex
+ i
] = '#';
1014 RtlAppendUnicodeToString(&InterfaceKeyName
, L
"#");
1015 RtlAppendUnicodeStringToString(&InterfaceKeyName
, &GuidString
);
1017 /* Create the interface key in registry */
1018 InitializeObjectAttributes(
1021 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
| OBJ_OPENIF
,
1023 NULL
); /* SecurityDescriptor */
1025 Status
= ZwCreateKey(
1031 REG_OPTION_VOLATILE
,
1032 NULL
); /* Disposition */
1034 if (!NT_SUCCESS(Status
))
1036 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status
);
1038 ExFreePool(BaseKeyName
.Buffer
);
1042 /* Write DeviceInstance entry. Value is InstancePath */
1043 Status
= ZwSetValueKey(
1048 InstancePath
->Buffer
,
1049 InstancePath
->Length
);
1050 if (!NT_SUCCESS(Status
))
1052 DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status
);
1053 ZwClose(InterfaceKey
);
1055 ExFreePool(InterfaceKeyName
.Buffer
);
1056 ExFreePool(BaseKeyName
.Buffer
);
1060 /* Create subkey. Name is #ReferenceString */
1061 SubKeyName
.Length
= 0;
1062 SubKeyName
.MaximumLength
= sizeof(WCHAR
);
1063 if (ReferenceString
&& ReferenceString
->Length
)
1064 SubKeyName
.MaximumLength
+= ReferenceString
->Length
;
1065 SubKeyName
.Buffer
= ExAllocatePool(
1067 SubKeyName
.MaximumLength
);
1068 if (!SubKeyName
.Buffer
)
1070 DPRINT("ExAllocatePool() failed\n");
1071 ZwClose(InterfaceKey
);
1073 ExFreePool(InterfaceKeyName
.Buffer
);
1074 ExFreePool(BaseKeyName
.Buffer
);
1075 return STATUS_INSUFFICIENT_RESOURCES
;
1077 RtlAppendUnicodeToString(&SubKeyName
, L
"#");
1078 if (ReferenceString
&& ReferenceString
->Length
)
1079 RtlAppendUnicodeStringToString(&SubKeyName
, ReferenceString
);
1081 /* Create SubKeyName key in registry */
1082 InitializeObjectAttributes(
1085 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1086 InterfaceKey
, /* RootDirectory */
1087 NULL
); /* SecurityDescriptor */
1089 Status
= ZwCreateKey(
1095 REG_OPTION_VOLATILE
,
1096 NULL
); /* Disposition */
1098 if (!NT_SUCCESS(Status
))
1100 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status
);
1101 ZwClose(InterfaceKey
);
1103 ExFreePool(InterfaceKeyName
.Buffer
);
1104 ExFreePool(BaseKeyName
.Buffer
);
1108 /* Create symbolic link name: \??\ACPI#PNP0501#1#{GUID}\ReferenceString */
1109 SymbolicLinkName
->Length
= 0;
1110 SymbolicLinkName
->MaximumLength
= SymbolicLinkName
->Length
1111 + 4 * sizeof(WCHAR
) /* 4 = size of \??\ */
1112 + InstancePath
->Length
1113 + sizeof(WCHAR
) /* 1 = size of # */
1115 + sizeof(WCHAR
); /* final NULL */
1116 if (ReferenceString
&& ReferenceString
->Length
)
1117 SymbolicLinkName
->MaximumLength
+= sizeof(WCHAR
) + ReferenceString
->Length
;
1118 SymbolicLinkName
->Buffer
= ExAllocatePool(
1120 SymbolicLinkName
->MaximumLength
);
1121 if (!SymbolicLinkName
->Buffer
)
1123 DPRINT("ExAllocatePool() failed\n");
1125 ZwClose(InterfaceKey
);
1127 ExFreePool(InterfaceKeyName
.Buffer
);
1128 ExFreePool(SubKeyName
.Buffer
);
1129 ExFreePool(BaseKeyName
.Buffer
);
1130 return STATUS_INSUFFICIENT_RESOURCES
;
1132 RtlAppendUnicodeToString(SymbolicLinkName
, L
"\\??\\");
1133 StartIndex
= SymbolicLinkName
->Length
/ sizeof(WCHAR
);
1134 RtlAppendUnicodeStringToString(SymbolicLinkName
, InstancePath
);
1135 for (i
= 0; i
< InstancePath
->Length
/ sizeof(WCHAR
); i
++)
1137 if (SymbolicLinkName
->Buffer
[StartIndex
+ i
] == '\\')
1138 SymbolicLinkName
->Buffer
[StartIndex
+ i
] = '#';
1140 RtlAppendUnicodeToString(SymbolicLinkName
, L
"#");
1141 RtlAppendUnicodeStringToString(SymbolicLinkName
, &GuidString
);
1142 SymbolicLinkName
->Buffer
[SymbolicLinkName
->Length
/sizeof(WCHAR
)] = L
'\0';
1144 /* Create symbolic link */
1145 DPRINT("IoRegisterDeviceInterface(): creating symbolic link %wZ -> %wZ\n", SymbolicLinkName
, &PdoNameInfo
->Name
);
1146 SymLinkStatus
= IoCreateSymbolicLink(SymbolicLinkName
, &PdoNameInfo
->Name
);
1148 /* If the symbolic link already exists, return an informational success status */
1149 if (SymLinkStatus
== STATUS_OBJECT_NAME_COLLISION
)
1151 /* HACK: Delete the existing symbolic link and update it to the new PDO name */
1152 IoDeleteSymbolicLink(SymbolicLinkName
);
1153 IoCreateSymbolicLink(SymbolicLinkName
, &PdoNameInfo
->Name
);
1154 SymLinkStatus
= STATUS_OBJECT_NAME_EXISTS
;
1157 if (!NT_SUCCESS(SymLinkStatus
))
1159 DPRINT1("IoCreateSymbolicLink() failed with status 0x%08lx\n", SymLinkStatus
);
1161 ZwClose(InterfaceKey
);
1163 ExFreePool(SubKeyName
.Buffer
);
1164 ExFreePool(InterfaceKeyName
.Buffer
);
1165 ExFreePool(BaseKeyName
.Buffer
);
1166 ExFreePool(SymbolicLinkName
->Buffer
);
1167 return SymLinkStatus
;
1170 if (ReferenceString
&& ReferenceString
->Length
)
1172 RtlAppendUnicodeToString(SymbolicLinkName
, L
"\\");
1173 RtlAppendUnicodeStringToString(SymbolicLinkName
, ReferenceString
);
1175 SymbolicLinkName
->Buffer
[SymbolicLinkName
->Length
/sizeof(WCHAR
)] = L
'\0';
1177 /* Write symbolic link name in registry */
1178 SymbolicLinkName
->Buffer
[1] = '\\';
1179 Status
= ZwSetValueKey(
1184 SymbolicLinkName
->Buffer
,
1185 SymbolicLinkName
->Length
);
1186 if (!NT_SUCCESS(Status
))
1188 DPRINT1("ZwSetValueKey() failed with status 0x%08lx\n", Status
);
1189 ExFreePool(SymbolicLinkName
->Buffer
);
1193 SymbolicLinkName
->Buffer
[1] = '?';
1197 ZwClose(InterfaceKey
);
1199 ExFreePool(SubKeyName
.Buffer
);
1200 ExFreePool(InterfaceKeyName
.Buffer
);
1201 ExFreePool(BaseKeyName
.Buffer
);
1203 return NT_SUCCESS(Status
) ? SymLinkStatus
: Status
;
1207 * @name IoSetDeviceInterfaceState
1210 * Enables or disables an instance of a previously registered device
1212 * Documented in WDK.
1214 * @param SymbolicLinkName
1215 * Pointer to the string identifying instance to enable or disable
1218 * TRUE = enable, FALSE = disable
1220 * @return Usual NTSTATUS
1222 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a
1228 IoSetDeviceInterfaceState(IN PUNICODE_STRING SymbolicLinkName
,
1231 PDEVICE_OBJECT PhysicalDeviceObject
;
1232 PFILE_OBJECT FileObject
;
1233 UNICODE_STRING GuidString
;
1234 UNICODE_STRING SymLink
;
1235 PWCHAR StartPosition
;
1239 HANDLE InstanceHandle
, ControlHandle
;
1240 UNICODE_STRING KeyName
;
1241 OBJECT_ATTRIBUTES ObjectAttributes
;
1245 if (SymbolicLinkName
== NULL
)
1246 return STATUS_INVALID_PARAMETER_1
;
1248 DPRINT("IoSetDeviceInterfaceState('%wZ', %d)\n", SymbolicLinkName
, Enable
);
1250 /* Symbolic link name is \??\ACPI#PNP0501#1#{GUID}\ReferenceString */
1251 /* Get GUID from SymbolicLinkName */
1252 StartPosition
= wcschr(SymbolicLinkName
->Buffer
, L
'{');
1253 EndPosition
= wcschr(SymbolicLinkName
->Buffer
, L
'}');
1254 if (!StartPosition
||!EndPosition
|| StartPosition
> EndPosition
)
1256 DPRINT1("IoSetDeviceInterfaceState() returning STATUS_INVALID_PARAMETER_1\n");
1257 return STATUS_INVALID_PARAMETER_1
;
1259 GuidString
.Buffer
= StartPosition
;
1260 GuidString
.MaximumLength
= GuidString
.Length
= (USHORT
)((ULONG_PTR
)(EndPosition
+ 1) - (ULONG_PTR
)StartPosition
);
1262 SymLink
.Buffer
= SymbolicLinkName
->Buffer
;
1263 SymLink
.MaximumLength
= SymLink
.Length
= (USHORT
)((ULONG_PTR
)(EndPosition
+ 1) - (ULONG_PTR
)SymLink
.Buffer
);
1264 DPRINT("IoSetDeviceInterfaceState('%wZ', %d)\n", SymbolicLinkName
, Enable
);
1266 Status
= OpenRegistryHandlesFromSymbolicLink(SymbolicLinkName
,
1271 if (!NT_SUCCESS(Status
))
1274 RtlInitUnicodeString(&KeyName
, L
"Control");
1275 InitializeObjectAttributes(&ObjectAttributes
,
1277 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1280 Status
= ZwCreateKey(&ControlHandle
,
1285 REG_OPTION_VOLATILE
,
1287 ZwClose(InstanceHandle
);
1288 if (!NT_SUCCESS(Status
))
1290 DPRINT1("Failed to create the Control subkey\n");
1294 LinkedValue
= (Enable
? 1 : 0);
1296 RtlInitUnicodeString(&KeyName
, L
"Linked");
1297 Status
= ZwSetValueKey(ControlHandle
,
1303 ZwClose(ControlHandle
);
1304 if (!NT_SUCCESS(Status
))
1306 DPRINT1("Failed to write the Linked value\n");
1310 /* Get pointer to the PDO */
1311 Status
= IoGetDeviceObjectPointer(
1313 0, /* DesiredAccess */
1315 &PhysicalDeviceObject
);
1316 if (!NT_SUCCESS(Status
))
1318 DPRINT1("IoGetDeviceObjectPointer() failed with status 0x%08lx\n", Status
);
1322 Status
= RtlGUIDFromString(&GuidString
, &DeviceGuid
);
1323 if (!NT_SUCCESS(Status
))
1325 DPRINT1("RtlGUIDFromString() failed with status 0x%08lx\n", Status
);
1329 EventGuid
= Enable
? &GUID_DEVICE_INTERFACE_ARRIVAL
: &GUID_DEVICE_INTERFACE_REMOVAL
;
1330 IopNotifyPlugPlayNotification(
1331 PhysicalDeviceObject
,
1332 EventCategoryDeviceInterfaceChange
,
1335 (PVOID
)SymbolicLinkName
);
1337 ObDereferenceObject(FileObject
);
1338 DPRINT("Status %x\n", Status
);
1339 return STATUS_SUCCESS
;