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 (i.e. the different device objects) */
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
;
733 KeyName
.Buffer
= (PWSTR
)bip
->Data
;
735 /* Fixup the prefix (from "\\?\") */
736 RtlCopyMemory(KeyName
.Buffer
, L
"\\??\\", 4 * sizeof(WCHAR
));
738 /* Add new symbolic link to symbolic link list */
739 if (ReturnBuffer
.Length
+ KeyName
.Length
+ sizeof(WCHAR
) > ReturnBuffer
.MaximumLength
)
742 ReturnBuffer
.MaximumLength
= (USHORT
)max(ReturnBuffer
.MaximumLength
* sizeof(WCHAR
),
743 (USHORT
)(ReturnBuffer
.Length
+
746 NewBuffer
= ExAllocatePool(PagedPool
, ReturnBuffer
.MaximumLength
);
749 DPRINT("ExAllocatePool() failed\n");
750 Status
= STATUS_INSUFFICIENT_RESOURCES
;
753 if (ReturnBuffer
.Buffer
)
755 RtlCopyMemory(NewBuffer
, ReturnBuffer
.Buffer
, ReturnBuffer
.Length
);
756 ExFreePool(ReturnBuffer
.Buffer
);
758 ReturnBuffer
.Buffer
= NewBuffer
;
760 DPRINT("Adding symbolic link %wZ\n", &KeyName
);
761 Status
= RtlAppendUnicodeStringToString(&ReturnBuffer
, &KeyName
);
762 if (!NT_SUCCESS(Status
))
764 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status
);
767 /* RtlAppendUnicodeStringToString added a NULL at the end of the
768 * destination string, but didn't increase the Length field.
771 ReturnBuffer
.Length
+= sizeof(WCHAR
);
774 ExFreePool(ReferenceBi
);
779 if (ReferenceKey
!= INVALID_HANDLE_VALUE
)
781 ZwClose(ReferenceKey
);
782 ReferenceKey
= INVALID_HANDLE_VALUE
;
784 if (ControlKey
!= INVALID_HANDLE_VALUE
)
787 ControlKey
= INVALID_HANDLE_VALUE
;
792 /* No need to go further, as we already have found what we searched */
796 ExFreePool(DeviceBi
);
799 DeviceKey
= INVALID_HANDLE_VALUE
;
802 /* Add final NULL to ReturnBuffer */
803 if (ReturnBuffer
.Length
>= ReturnBuffer
.MaximumLength
)
806 ReturnBuffer
.MaximumLength
+= sizeof(WCHAR
);
807 NewBuffer
= ExAllocatePool(PagedPool
, ReturnBuffer
.MaximumLength
);
810 DPRINT("ExAllocatePool() failed\n");
811 Status
= STATUS_INSUFFICIENT_RESOURCES
;
814 if (ReturnBuffer
.Buffer
)
816 RtlCopyMemory(NewBuffer
, ReturnBuffer
.Buffer
, ReturnBuffer
.Length
);
817 ExFreePool(ReturnBuffer
.Buffer
);
819 ReturnBuffer
.Buffer
= NewBuffer
;
821 ReturnBuffer
.Buffer
[ReturnBuffer
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
822 *SymbolicLinkList
= ReturnBuffer
.Buffer
;
823 Status
= STATUS_SUCCESS
;
826 if (!NT_SUCCESS(Status
) && ReturnBuffer
.Buffer
)
827 ExFreePool(ReturnBuffer
.Buffer
);
828 if (InterfaceKey
!= INVALID_HANDLE_VALUE
)
829 ZwClose(InterfaceKey
);
830 if (DeviceKey
!= INVALID_HANDLE_VALUE
)
832 if (ReferenceKey
!= INVALID_HANDLE_VALUE
)
833 ZwClose(ReferenceKey
);
834 if (ControlKey
!= INVALID_HANDLE_VALUE
)
837 ExFreePool(DeviceBi
);
839 ExFreePool(ReferenceBi
);
846 * @name IoRegisterDeviceInterface
849 * Registers a device interface class, if it has not been previously registered,
850 * and creates a new instance of the interface class, which a driver can
851 * subsequently enable for use by applications or other system components.
854 * @param PhysicalDeviceObject
855 * Points to an optional PDO that narrows the search to only the
856 * device interfaces of the device represented by the PDO
858 * @param InterfaceClassGuid
859 * Points to a class GUID specifying the device interface class
861 * @param ReferenceString
862 * Optional parameter, pointing to a unicode string. For a full
863 * description of this rather rarely used param (usually drivers
864 * pass NULL here) see WDK
866 * @param SymbolicLinkName
867 * Pointer to the resulting unicode string
869 * @return Usual NTSTATUS
871 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a
877 IoRegisterDeviceInterface(IN PDEVICE_OBJECT PhysicalDeviceObject
,
878 IN CONST GUID
*InterfaceClassGuid
,
879 IN PUNICODE_STRING ReferenceString OPTIONAL
,
880 OUT PUNICODE_STRING SymbolicLinkName
)
882 PUNICODE_STRING InstancePath
;
883 UNICODE_STRING GuidString
;
884 UNICODE_STRING SubKeyName
;
885 UNICODE_STRING InterfaceKeyName
;
886 UNICODE_STRING BaseKeyName
;
887 UCHAR PdoNameInfoBuffer
[sizeof(OBJECT_NAME_INFORMATION
) + (256 * sizeof(WCHAR
))];
888 POBJECT_NAME_INFORMATION PdoNameInfo
= (POBJECT_NAME_INFORMATION
)PdoNameInfoBuffer
;
889 UNICODE_STRING DeviceInstance
= RTL_CONSTANT_STRING(L
"DeviceInstance");
890 UNICODE_STRING SymbolicLink
= RTL_CONSTANT_STRING(L
"SymbolicLink");
895 OBJECT_ATTRIBUTES ObjectAttributes
;
897 NTSTATUS Status
, SymLinkStatus
;
898 PEXTENDED_DEVOBJ_EXTENSION DeviceObjectExtension
;
900 ASSERT_IRQL_EQUAL(PASSIVE_LEVEL
);
902 DPRINT("IoRegisterDeviceInterface(): PDO %p, RefString: %wZ\n",
903 PhysicalDeviceObject
, ReferenceString
);
905 /* Parameters must pass three border of checks */
906 DeviceObjectExtension
= (PEXTENDED_DEVOBJ_EXTENSION
)PhysicalDeviceObject
->DeviceObjectExtension
;
908 /* 1st level: Presence of a Device Node */
909 if (DeviceObjectExtension
->DeviceNode
== NULL
)
911 DPRINT("PhysicalDeviceObject 0x%p doesn't have a DeviceNode\n", PhysicalDeviceObject
);
912 return STATUS_INVALID_DEVICE_REQUEST
;
915 /* 2nd level: Presence of an non-zero length InstancePath */
916 if (DeviceObjectExtension
->DeviceNode
->InstancePath
.Length
== 0)
918 DPRINT("PhysicalDeviceObject 0x%p's DOE has zero-length InstancePath\n", PhysicalDeviceObject
);
919 return STATUS_INVALID_DEVICE_REQUEST
;
922 /* 3rd level: Optional, based on WDK documentation */
923 if (ReferenceString
!= NULL
)
925 /* Reference string must not contain path-separator symbols */
926 for (i
= 0; i
< ReferenceString
->Length
/ sizeof(WCHAR
); i
++)
928 if ((ReferenceString
->Buffer
[i
] == '\\') ||
929 (ReferenceString
->Buffer
[i
] == '/'))
930 return STATUS_INVALID_DEVICE_REQUEST
;
934 Status
= RtlStringFromGUID(InterfaceClassGuid
, &GuidString
);
935 if (!NT_SUCCESS(Status
))
937 DPRINT("RtlStringFromGUID() failed with status 0x%08lx\n", Status
);
941 /* Create Pdo name: \Device\xxxxxxxx (unnamed device) */
942 Status
= ObQueryNameString(
943 PhysicalDeviceObject
,
945 sizeof(PdoNameInfoBuffer
),
947 if (!NT_SUCCESS(Status
))
949 DPRINT("ObQueryNameString() failed with status 0x%08lx\n", Status
);
952 ASSERT(PdoNameInfo
->Name
.Length
);
954 /* Create base key name for this interface: HKLM\SYSTEM\CurrentControlSet\Control\DeviceClasses\{GUID} */
955 ASSERT(((PEXTENDED_DEVOBJ_EXTENSION
)PhysicalDeviceObject
->DeviceObjectExtension
)->DeviceNode
);
956 InstancePath
= &((PEXTENDED_DEVOBJ_EXTENSION
)PhysicalDeviceObject
->DeviceObjectExtension
)->DeviceNode
->InstancePath
;
957 BaseKeyName
.Length
= (USHORT
)wcslen(BaseKeyString
) * sizeof(WCHAR
);
958 BaseKeyName
.MaximumLength
= BaseKeyName
.Length
960 BaseKeyName
.Buffer
= ExAllocatePool(
962 BaseKeyName
.MaximumLength
);
963 if (!BaseKeyName
.Buffer
)
965 DPRINT("ExAllocatePool() failed\n");
966 return STATUS_INSUFFICIENT_RESOURCES
;
968 wcscpy(BaseKeyName
.Buffer
, BaseKeyString
);
969 RtlAppendUnicodeStringToString(&BaseKeyName
, &GuidString
);
971 /* Create BaseKeyName key in registry */
972 InitializeObjectAttributes(
975 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
| OBJ_OPENIF
,
976 NULL
, /* RootDirectory */
977 NULL
); /* SecurityDescriptor */
979 Status
= ZwCreateKey(
986 NULL
); /* Disposition */
988 if (!NT_SUCCESS(Status
))
990 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status
);
991 ExFreePool(BaseKeyName
.Buffer
);
995 /* Create key name for this interface: ##?#ACPI#PNP0501#1#{GUID} */
996 InterfaceKeyName
.Length
= 0;
997 InterfaceKeyName
.MaximumLength
=
998 4 * sizeof(WCHAR
) + /* 4 = size of ##?# */
999 InstancePath
->Length
+
1000 sizeof(WCHAR
) + /* 1 = size of # */
1002 InterfaceKeyName
.Buffer
= ExAllocatePool(
1004 InterfaceKeyName
.MaximumLength
);
1005 if (!InterfaceKeyName
.Buffer
)
1007 DPRINT("ExAllocatePool() failed\n");
1008 return STATUS_INSUFFICIENT_RESOURCES
;
1011 RtlAppendUnicodeToString(&InterfaceKeyName
, L
"##?#");
1012 StartIndex
= InterfaceKeyName
.Length
/ sizeof(WCHAR
);
1013 RtlAppendUnicodeStringToString(&InterfaceKeyName
, InstancePath
);
1014 for (i
= 0; i
< InstancePath
->Length
/ sizeof(WCHAR
); i
++)
1016 if (InterfaceKeyName
.Buffer
[StartIndex
+ i
] == '\\')
1017 InterfaceKeyName
.Buffer
[StartIndex
+ i
] = '#';
1019 RtlAppendUnicodeToString(&InterfaceKeyName
, L
"#");
1020 RtlAppendUnicodeStringToString(&InterfaceKeyName
, &GuidString
);
1022 /* Create the interface key in registry */
1023 InitializeObjectAttributes(
1026 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
| OBJ_OPENIF
,
1028 NULL
); /* SecurityDescriptor */
1030 Status
= ZwCreateKey(
1036 REG_OPTION_VOLATILE
,
1037 NULL
); /* Disposition */
1039 if (!NT_SUCCESS(Status
))
1041 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status
);
1043 ExFreePool(BaseKeyName
.Buffer
);
1047 /* Write DeviceInstance entry. Value is InstancePath */
1048 Status
= ZwSetValueKey(
1053 InstancePath
->Buffer
,
1054 InstancePath
->Length
);
1055 if (!NT_SUCCESS(Status
))
1057 DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status
);
1058 ZwClose(InterfaceKey
);
1060 ExFreePool(InterfaceKeyName
.Buffer
);
1061 ExFreePool(BaseKeyName
.Buffer
);
1065 /* Create subkey. Name is #ReferenceString */
1066 SubKeyName
.Length
= 0;
1067 SubKeyName
.MaximumLength
= sizeof(WCHAR
);
1068 if (ReferenceString
&& ReferenceString
->Length
)
1069 SubKeyName
.MaximumLength
+= ReferenceString
->Length
;
1070 SubKeyName
.Buffer
= ExAllocatePool(
1072 SubKeyName
.MaximumLength
);
1073 if (!SubKeyName
.Buffer
)
1075 DPRINT("ExAllocatePool() failed\n");
1076 ZwClose(InterfaceKey
);
1078 ExFreePool(InterfaceKeyName
.Buffer
);
1079 ExFreePool(BaseKeyName
.Buffer
);
1080 return STATUS_INSUFFICIENT_RESOURCES
;
1082 RtlAppendUnicodeToString(&SubKeyName
, L
"#");
1083 if (ReferenceString
&& ReferenceString
->Length
)
1084 RtlAppendUnicodeStringToString(&SubKeyName
, ReferenceString
);
1086 /* Create SubKeyName key in registry */
1087 InitializeObjectAttributes(
1090 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1091 InterfaceKey
, /* RootDirectory */
1092 NULL
); /* SecurityDescriptor */
1094 Status
= ZwCreateKey(
1100 REG_OPTION_VOLATILE
,
1101 NULL
); /* Disposition */
1103 if (!NT_SUCCESS(Status
))
1105 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status
);
1106 ZwClose(InterfaceKey
);
1108 ExFreePool(InterfaceKeyName
.Buffer
);
1109 ExFreePool(BaseKeyName
.Buffer
);
1113 /* Create symbolic link name: \??\ACPI#PNP0501#1#{GUID}\ReferenceString */
1114 SymbolicLinkName
->Length
= 0;
1115 SymbolicLinkName
->MaximumLength
= SymbolicLinkName
->Length
1116 + 4 * sizeof(WCHAR
) /* 4 = size of \??\ */
1117 + InstancePath
->Length
1118 + sizeof(WCHAR
) /* 1 = size of # */
1120 + sizeof(WCHAR
); /* final NULL */
1121 if (ReferenceString
&& ReferenceString
->Length
)
1122 SymbolicLinkName
->MaximumLength
+= sizeof(WCHAR
) + ReferenceString
->Length
;
1123 SymbolicLinkName
->Buffer
= ExAllocatePool(
1125 SymbolicLinkName
->MaximumLength
);
1126 if (!SymbolicLinkName
->Buffer
)
1128 DPRINT("ExAllocatePool() failed\n");
1130 ZwClose(InterfaceKey
);
1132 ExFreePool(InterfaceKeyName
.Buffer
);
1133 ExFreePool(SubKeyName
.Buffer
);
1134 ExFreePool(BaseKeyName
.Buffer
);
1135 return STATUS_INSUFFICIENT_RESOURCES
;
1137 RtlAppendUnicodeToString(SymbolicLinkName
, L
"\\??\\");
1138 StartIndex
= SymbolicLinkName
->Length
/ sizeof(WCHAR
);
1139 RtlAppendUnicodeStringToString(SymbolicLinkName
, InstancePath
);
1140 for (i
= 0; i
< InstancePath
->Length
/ sizeof(WCHAR
); i
++)
1142 if (SymbolicLinkName
->Buffer
[StartIndex
+ i
] == '\\')
1143 SymbolicLinkName
->Buffer
[StartIndex
+ i
] = '#';
1145 RtlAppendUnicodeToString(SymbolicLinkName
, L
"#");
1146 RtlAppendUnicodeStringToString(SymbolicLinkName
, &GuidString
);
1147 SymbolicLinkName
->Buffer
[SymbolicLinkName
->Length
/sizeof(WCHAR
)] = L
'\0';
1149 /* Create symbolic link */
1150 DPRINT("IoRegisterDeviceInterface(): creating symbolic link %wZ -> %wZ\n", SymbolicLinkName
, &PdoNameInfo
->Name
);
1151 SymLinkStatus
= IoCreateSymbolicLink(SymbolicLinkName
, &PdoNameInfo
->Name
);
1153 /* If the symbolic link already exists, return an informational success status */
1154 if (SymLinkStatus
== STATUS_OBJECT_NAME_COLLISION
)
1156 /* HACK: Delete the existing symbolic link and update it to the new PDO name */
1157 IoDeleteSymbolicLink(SymbolicLinkName
);
1158 IoCreateSymbolicLink(SymbolicLinkName
, &PdoNameInfo
->Name
);
1159 SymLinkStatus
= STATUS_OBJECT_NAME_EXISTS
;
1162 if (!NT_SUCCESS(SymLinkStatus
))
1164 DPRINT1("IoCreateSymbolicLink() failed with status 0x%08lx\n", SymLinkStatus
);
1166 ZwClose(InterfaceKey
);
1168 ExFreePool(SubKeyName
.Buffer
);
1169 ExFreePool(InterfaceKeyName
.Buffer
);
1170 ExFreePool(BaseKeyName
.Buffer
);
1171 ExFreePool(SymbolicLinkName
->Buffer
);
1172 return SymLinkStatus
;
1175 if (ReferenceString
&& ReferenceString
->Length
)
1177 RtlAppendUnicodeToString(SymbolicLinkName
, L
"\\");
1178 RtlAppendUnicodeStringToString(SymbolicLinkName
, ReferenceString
);
1180 SymbolicLinkName
->Buffer
[SymbolicLinkName
->Length
/sizeof(WCHAR
)] = L
'\0';
1182 /* Write symbolic link name in registry */
1183 SymbolicLinkName
->Buffer
[1] = '\\';
1184 Status
= ZwSetValueKey(
1189 SymbolicLinkName
->Buffer
,
1190 SymbolicLinkName
->Length
);
1191 if (!NT_SUCCESS(Status
))
1193 DPRINT1("ZwSetValueKey() failed with status 0x%08lx\n", Status
);
1194 ExFreePool(SymbolicLinkName
->Buffer
);
1198 SymbolicLinkName
->Buffer
[1] = '?';
1202 ZwClose(InterfaceKey
);
1204 ExFreePool(SubKeyName
.Buffer
);
1205 ExFreePool(InterfaceKeyName
.Buffer
);
1206 ExFreePool(BaseKeyName
.Buffer
);
1208 return NT_SUCCESS(Status
) ? SymLinkStatus
: Status
;
1212 * @name IoSetDeviceInterfaceState
1215 * Enables or disables an instance of a previously registered device
1217 * Documented in WDK.
1219 * @param SymbolicLinkName
1220 * Pointer to the string identifying instance to enable or disable
1223 * TRUE = enable, FALSE = disable
1225 * @return Usual NTSTATUS
1227 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a
1233 IoSetDeviceInterfaceState(IN PUNICODE_STRING SymbolicLinkName
,
1236 PDEVICE_OBJECT PhysicalDeviceObject
;
1237 PFILE_OBJECT FileObject
;
1238 UNICODE_STRING GuidString
;
1239 UNICODE_STRING SymLink
;
1240 PWCHAR StartPosition
;
1244 HANDLE InstanceHandle
, ControlHandle
;
1245 UNICODE_STRING KeyName
;
1246 OBJECT_ATTRIBUTES ObjectAttributes
;
1250 if (SymbolicLinkName
== NULL
)
1251 return STATUS_INVALID_PARAMETER_1
;
1253 DPRINT("IoSetDeviceInterfaceState('%wZ', %u)\n", SymbolicLinkName
, Enable
);
1255 /* Symbolic link name is \??\ACPI#PNP0501#1#{GUID}\ReferenceString */
1256 /* Get GUID from SymbolicLinkName */
1257 StartPosition
= wcschr(SymbolicLinkName
->Buffer
, L
'{');
1258 EndPosition
= wcschr(SymbolicLinkName
->Buffer
, L
'}');
1259 if (!StartPosition
||!EndPosition
|| StartPosition
> EndPosition
)
1261 DPRINT1("IoSetDeviceInterfaceState() returning STATUS_INVALID_PARAMETER_1\n");
1262 return STATUS_INVALID_PARAMETER_1
;
1264 GuidString
.Buffer
= StartPosition
;
1265 GuidString
.MaximumLength
= GuidString
.Length
= (USHORT
)((ULONG_PTR
)(EndPosition
+ 1) - (ULONG_PTR
)StartPosition
);
1267 SymLink
.Buffer
= SymbolicLinkName
->Buffer
;
1268 SymLink
.MaximumLength
= SymLink
.Length
= (USHORT
)((ULONG_PTR
)(EndPosition
+ 1) - (ULONG_PTR
)SymLink
.Buffer
);
1269 DPRINT("IoSetDeviceInterfaceState('%wZ', %u)\n", SymbolicLinkName
, Enable
);
1271 Status
= OpenRegistryHandlesFromSymbolicLink(SymbolicLinkName
,
1276 if (!NT_SUCCESS(Status
))
1279 RtlInitUnicodeString(&KeyName
, L
"Control");
1280 InitializeObjectAttributes(&ObjectAttributes
,
1282 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1285 Status
= ZwCreateKey(&ControlHandle
,
1290 REG_OPTION_VOLATILE
,
1292 ZwClose(InstanceHandle
);
1293 if (!NT_SUCCESS(Status
))
1295 DPRINT1("Failed to create the Control subkey\n");
1299 LinkedValue
= (Enable
? 1 : 0);
1301 RtlInitUnicodeString(&KeyName
, L
"Linked");
1302 Status
= ZwSetValueKey(ControlHandle
,
1308 ZwClose(ControlHandle
);
1309 if (!NT_SUCCESS(Status
))
1311 DPRINT1("Failed to write the Linked value\n");
1315 /* Get pointer to the PDO */
1316 Status
= IoGetDeviceObjectPointer(
1318 0, /* DesiredAccess */
1320 &PhysicalDeviceObject
);
1321 if (!NT_SUCCESS(Status
))
1323 DPRINT1("IoGetDeviceObjectPointer() failed with status 0x%08lx\n", Status
);
1327 Status
= RtlGUIDFromString(&GuidString
, &DeviceGuid
);
1328 if (!NT_SUCCESS(Status
))
1330 DPRINT1("RtlGUIDFromString() failed with status 0x%08lx\n", Status
);
1334 EventGuid
= Enable
? &GUID_DEVICE_INTERFACE_ARRIVAL
: &GUID_DEVICE_INTERFACE_REMOVAL
;
1335 IopNotifyPlugPlayNotification(
1336 PhysicalDeviceObject
,
1337 EventCategoryDeviceInterfaceChange
,
1340 (PVOID
)SymbolicLinkName
);
1342 ObDereferenceObject(FileObject
);
1343 DPRINT("Status %x\n", Status
);
1344 return STATUS_SUCCESS
;