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
= ZwCreateKey(GuidKeyRealP
,
99 DesiredAccess
| KEY_ENUMERATE_SUB_KEYS
,
103 REG_OPTION_NON_VOLATILE
,
106 if (!NT_SUCCESS(Status
))
108 DPRINT1("Failed to open %wZ%wZ (%x)\n", &BaseKeyU
, &GuidString
, Status
);
112 SubKeyName
.MaximumLength
= SymbolicLinkName
->Length
+ sizeof(WCHAR
);
113 SubKeyName
.Length
= 0;
114 SubKeyName
.Buffer
= ExAllocatePool(PagedPool
, SubKeyName
.MaximumLength
);
115 if (!SubKeyName
.Buffer
)
117 Status
= STATUS_INSUFFICIENT_RESOURCES
;
121 RtlAppendUnicodeStringToString(&SubKeyName
,
124 SubKeyName
.Buffer
[SubKeyName
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
126 SubKeyName
.Buffer
[0] = L
'#';
127 SubKeyName
.Buffer
[1] = L
'#';
128 SubKeyName
.Buffer
[2] = L
'?';
129 SubKeyName
.Buffer
[3] = L
'#';
131 ReferenceString
.Buffer
= wcsrchr(SubKeyName
.Buffer
, '\\');
132 if (ReferenceString
.Buffer
!= NULL
)
134 ReferenceString
.Buffer
[0] = L
'#';
136 SubKeyName
.Length
= (USHORT
)((ULONG_PTR
)(ReferenceString
.Buffer
) - (ULONG_PTR
)SubKeyName
.Buffer
);
137 ReferenceString
.Length
= SymbolicLinkName
->Length
- SubKeyName
.Length
;
141 RtlInitUnicodeString(&ReferenceString
, L
"#");
144 InitializeObjectAttributes(&ObjectAttributes
,
146 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
149 Status
= ZwCreateKey(DeviceKeyRealP
,
150 DesiredAccess
| KEY_ENUMERATE_SUB_KEYS
,
154 REG_OPTION_NON_VOLATILE
,
156 if (!NT_SUCCESS(Status
))
158 DPRINT1("Failed to open %wZ%wZ\\%wZ\n", &BaseKeyU
, &GuidString
, &SubKeyName
);
162 InitializeObjectAttributes(&ObjectAttributes
,
164 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
167 Status
= ZwCreateKey(InstanceKeyRealP
,
172 REG_OPTION_NON_VOLATILE
,
174 if (!NT_SUCCESS(Status
))
176 DPRINT1("Failed to open %wZ%wZ\\%wZ%\\%wZ (%x)\n", &BaseKeyU
, &GuidString
, &SubKeyName
, &ReferenceString
, Status
);
180 Status
= STATUS_SUCCESS
;
183 if (SubKeyName
.Buffer
!= NULL
)
184 ExFreePool(SubKeyName
.Buffer
);
186 if (NT_SUCCESS(Status
))
189 ZwClose(*GuidKeyRealP
);
192 ZwClose(*DeviceKeyRealP
);
195 ZwClose(*InstanceKeyRealP
);
199 if (*GuidKeyRealP
!= INVALID_HANDLE_VALUE
)
200 ZwClose(*GuidKeyRealP
);
202 if (*DeviceKeyRealP
!= INVALID_HANDLE_VALUE
)
203 ZwClose(*DeviceKeyRealP
);
205 if (*InstanceKeyRealP
!= INVALID_HANDLE_VALUE
)
206 ZwClose(*InstanceKeyRealP
);
212 * @name IoOpenDeviceInterfaceRegistryKey
215 * Provides a handle to the device's interface instance registry key.
218 * @param SymbolicLinkName
219 * Pointer to a string which identifies the device interface instance
221 * @param DesiredAccess
222 * Desired ACCESS_MASK used to access the key (like KEY_READ,
225 * @param DeviceInterfaceKey
226 * If a call has been succesfull, a handle to the registry key
227 * will be stored there
229 * @return Three different NTSTATUS values in case of errors, and STATUS_SUCCESS
230 * otherwise (see WDK for details)
232 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a system thread
237 IoOpenDeviceInterfaceRegistryKey(IN PUNICODE_STRING SymbolicLinkName
,
238 IN ACCESS_MASK DesiredAccess
,
239 OUT PHANDLE DeviceInterfaceKey
)
241 HANDLE InstanceKey
, DeviceParametersKey
;
243 OBJECT_ATTRIBUTES ObjectAttributes
;
244 UNICODE_STRING DeviceParametersU
= RTL_CONSTANT_STRING(L
"Device Parameters");
246 Status
= OpenRegistryHandlesFromSymbolicLink(SymbolicLinkName
,
251 if (!NT_SUCCESS(Status
))
254 InitializeObjectAttributes(&ObjectAttributes
,
256 OBJ_CASE_INSENSITIVE
| OBJ_OPENIF
,
259 Status
= ZwCreateKey(&DeviceParametersKey
,
264 REG_OPTION_NON_VOLATILE
,
266 ZwClose(InstanceKey
);
268 if (NT_SUCCESS(Status
))
269 *DeviceInterfaceKey
= DeviceParametersKey
;
275 * @name IoGetDeviceInterfaceAlias
278 * Returns the alias device interface of the specified device interface
279 * instance, if the alias exists.
282 * @param SymbolicLinkName
283 * Pointer to a string which identifies the device interface instance
285 * @param AliasInterfaceClassGuid
288 * @param AliasSymbolicLinkName
291 * @return Three different NTSTATUS values in case of errors, and STATUS_SUCCESS
292 * otherwise (see WDK for details)
294 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a system thread
299 IoGetDeviceInterfaceAlias(IN PUNICODE_STRING SymbolicLinkName
,
300 IN CONST GUID
*AliasInterfaceClassGuid
,
301 OUT PUNICODE_STRING AliasSymbolicLinkName
)
303 return STATUS_NOT_IMPLEMENTED
;
307 * @name IopOpenInterfaceKey
309 * Returns the alias device interface of the specified device interface
311 * @param InterfaceClassGuid
314 * @param DesiredAccess
317 * @param pInterfaceKey
320 * @return Usual NTSTATUS
326 IopOpenInterfaceKey(IN CONST GUID
*InterfaceClassGuid
,
327 IN ACCESS_MASK DesiredAccess
,
328 OUT HANDLE
*pInterfaceKey
)
330 UNICODE_STRING LocalMachine
= RTL_CONSTANT_STRING(L
"\\Registry\\Machine\\");
331 UNICODE_STRING GuidString
;
332 UNICODE_STRING KeyName
;
333 OBJECT_ATTRIBUTES ObjectAttributes
;
334 HANDLE InterfaceKey
= INVALID_HANDLE_VALUE
;
337 GuidString
.Buffer
= KeyName
.Buffer
= NULL
;
339 Status
= RtlStringFromGUID(InterfaceClassGuid
, &GuidString
);
340 if (!NT_SUCCESS(Status
))
342 DPRINT("RtlStringFromGUID() failed with status 0x%08lx\n", Status
);
347 KeyName
.MaximumLength
= LocalMachine
.Length
+ ((USHORT
)wcslen(REGSTR_PATH_DEVICE_CLASSES
) + 1) * sizeof(WCHAR
) + GuidString
.Length
;
348 KeyName
.Buffer
= ExAllocatePool(PagedPool
, KeyName
.MaximumLength
);
351 DPRINT("ExAllocatePool() failed\n");
352 Status
= STATUS_INSUFFICIENT_RESOURCES
;
356 Status
= RtlAppendUnicodeStringToString(&KeyName
, &LocalMachine
);
357 if (!NT_SUCCESS(Status
))
359 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status
);
362 Status
= RtlAppendUnicodeToString(&KeyName
, REGSTR_PATH_DEVICE_CLASSES
);
363 if (!NT_SUCCESS(Status
))
365 DPRINT("RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status
);
368 Status
= RtlAppendUnicodeToString(&KeyName
, L
"\\");
369 if (!NT_SUCCESS(Status
))
371 DPRINT("RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status
);
374 Status
= RtlAppendUnicodeStringToString(&KeyName
, &GuidString
);
375 if (!NT_SUCCESS(Status
))
377 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status
);
381 InitializeObjectAttributes(
384 OBJ_CASE_INSENSITIVE
,
391 if (!NT_SUCCESS(Status
))
393 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status
);
397 *pInterfaceKey
= InterfaceKey
;
398 Status
= STATUS_SUCCESS
;
401 if (!NT_SUCCESS(Status
))
403 if (InterfaceKey
!= INVALID_HANDLE_VALUE
)
404 ZwClose(InterfaceKey
);
406 RtlFreeUnicodeString(&GuidString
);
407 RtlFreeUnicodeString(&KeyName
);
412 * @name IoGetDeviceInterfaces
415 * Returns a list of device interfaces of a particular device interface class.
418 * @param InterfaceClassGuid
419 * Points to a class GUID specifying the device interface class
421 * @param PhysicalDeviceObject
422 * Points to an optional PDO that narrows the search to only the
423 * device interfaces of the device represented by the PDO
426 * Specifies flags that modify the search for device interfaces. The
427 * DEVICE_INTERFACE_INCLUDE_NONACTIVE flag specifies that the list of
428 * returned symbolic links should contain also disabled device
429 * interfaces in addition to the enabled ones.
431 * @param SymbolicLinkList
432 * Points to a character pointer that is filled in on successful return
433 * with a list of unicode strings identifying the device interfaces
434 * that match the search criteria. The newly allocated buffer contains
435 * a list of symbolic link names. Each unicode string in the list is
436 * null-terminated; the end of the whole list is marked by an additional
437 * NULL. The caller is responsible for freeing the buffer (ExFreePool)
438 * when it is no longer needed.
439 * If no device interfaces match the search criteria, this routine
440 * returns STATUS_SUCCESS and the string contains a single NULL
443 * @return Usual NTSTATUS
450 IoGetDeviceInterfaces(IN CONST GUID
*InterfaceClassGuid
,
451 IN PDEVICE_OBJECT PhysicalDeviceObject OPTIONAL
,
453 OUT PWSTR
*SymbolicLinkList
)
455 UNICODE_STRING Control
= RTL_CONSTANT_STRING(L
"Control");
456 UNICODE_STRING SymbolicLink
= RTL_CONSTANT_STRING(L
"SymbolicLink");
457 HANDLE InterfaceKey
= INVALID_HANDLE_VALUE
;
458 HANDLE DeviceKey
= INVALID_HANDLE_VALUE
;
459 HANDLE ReferenceKey
= INVALID_HANDLE_VALUE
;
460 HANDLE ControlKey
= INVALID_HANDLE_VALUE
;
461 PKEY_BASIC_INFORMATION DeviceBi
= NULL
;
462 PKEY_BASIC_INFORMATION ReferenceBi
= NULL
;
463 PKEY_VALUE_PARTIAL_INFORMATION bip
= NULL
;
464 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo
;
465 UNICODE_STRING KeyName
;
466 OBJECT_ATTRIBUTES ObjectAttributes
;
467 BOOLEAN FoundRightPDO
= FALSE
;
468 ULONG i
= 0, j
, Size
, NeededLength
, ActualLength
, LinkedValue
;
469 UNICODE_STRING ReturnBuffer
= { 0, 0, NULL
};
474 Status
= IopOpenInterfaceKey(InterfaceClassGuid
, KEY_ENUMERATE_SUB_KEYS
, &InterfaceKey
);
475 if (!NT_SUCCESS(Status
))
477 DPRINT("IopOpenInterfaceKey() failed with status 0x%08lx\n", Status
);
481 /* Enumerate subkeys (i.e. the different device objects) */
484 Status
= ZwEnumerateKey(
491 if (Status
== STATUS_NO_MORE_ENTRIES
)
495 else if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_TOO_SMALL
)
497 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status
);
501 DeviceBi
= ExAllocatePool(PagedPool
, Size
);
504 DPRINT("ExAllocatePool() failed\n");
505 Status
= STATUS_INSUFFICIENT_RESOURCES
;
508 Status
= ZwEnumerateKey(
515 if (!NT_SUCCESS(Status
))
517 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status
);
521 /* Open device key */
522 KeyName
.Length
= KeyName
.MaximumLength
= (USHORT
)DeviceBi
->NameLength
;
523 KeyName
.Buffer
= DeviceBi
->Name
;
524 InitializeObjectAttributes(
527 OBJ_CASE_INSENSITIVE
,
532 KEY_ENUMERATE_SUB_KEYS
,
534 if (!NT_SUCCESS(Status
))
536 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status
);
540 if (PhysicalDeviceObject
)
542 /* Check if we are on the right physical device object,
543 * by reading the DeviceInstance string
545 DPRINT1("PhysicalDeviceObject != NULL. Case not implemented.\n");
546 //FoundRightPDO = TRUE;
547 Status
= STATUS_NOT_IMPLEMENTED
;
551 /* Enumerate subkeys (ie the different reference strings) */
555 Status
= ZwEnumerateKey(
562 if (Status
== STATUS_NO_MORE_ENTRIES
)
566 else if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_TOO_SMALL
)
568 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status
);
572 ReferenceBi
= ExAllocatePool(PagedPool
, Size
);
575 DPRINT("ExAllocatePool() failed\n");
576 Status
= STATUS_INSUFFICIENT_RESOURCES
;
579 Status
= ZwEnumerateKey(
586 if (!NT_SUCCESS(Status
))
588 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status
);
592 KeyName
.Length
= KeyName
.MaximumLength
= (USHORT
)ReferenceBi
->NameLength
;
593 KeyName
.Buffer
= ReferenceBi
->Name
;
594 if (RtlEqualUnicodeString(&KeyName
, &Control
, TRUE
))
596 /* Skip Control subkey */
597 goto NextReferenceString
;
600 /* Open reference key */
601 InitializeObjectAttributes(
604 OBJ_CASE_INSENSITIVE
,
611 if (!NT_SUCCESS(Status
))
613 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status
);
617 if (!(Flags
& DEVICE_INTERFACE_INCLUDE_NONACTIVE
))
619 /* We have to check if the interface is enabled, by
620 * reading the Linked value in the Control subkey
622 InitializeObjectAttributes(
625 OBJ_CASE_INSENSITIVE
,
632 if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
634 /* That's OK. The key doesn't exist (yet) because
635 * the interface is not activated.
637 goto NextReferenceString
;
639 else if (!NT_SUCCESS(Status
))
641 DPRINT1("ZwOpenKey() failed with status 0x%08lx\n", Status
);
645 RtlInitUnicodeString(&KeyName
, L
"Linked");
646 Status
= ZwQueryValueKey(ControlKey
,
648 KeyValuePartialInformation
,
652 if (Status
== STATUS_BUFFER_TOO_SMALL
)
654 ActualLength
= NeededLength
;
655 PartialInfo
= ExAllocatePool(NonPagedPool
, ActualLength
);
658 Status
= STATUS_INSUFFICIENT_RESOURCES
;
662 Status
= ZwQueryValueKey(ControlKey
,
664 KeyValuePartialInformation
,
668 if (!NT_SUCCESS(Status
))
670 DPRINT1("ZwQueryValueKey #2 failed (%x)\n", Status
);
671 ExFreePool(PartialInfo
);
675 if (PartialInfo
->Type
!= REG_DWORD
|| PartialInfo
->DataLength
!= sizeof(ULONG
))
677 DPRINT1("Bad registry read\n");
678 ExFreePool(PartialInfo
);
682 RtlCopyMemory(&LinkedValue
,
684 PartialInfo
->DataLength
);
686 ExFreePool(PartialInfo
);
687 if (LinkedValue
== 0)
689 /* This interface isn't active */
690 goto NextReferenceString
;
695 DPRINT1("ZwQueryValueKey #1 failed (%x)\n", Status
);
700 /* Read the SymbolicLink string and add it into SymbolicLinkList */
701 Status
= ZwQueryValueKey(
704 KeyValuePartialInformation
,
708 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_TOO_SMALL
)
710 DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status
);
713 bip
= ExAllocatePool(PagedPool
, Size
);
716 DPRINT("ExAllocatePool() failed\n");
717 Status
= STATUS_INSUFFICIENT_RESOURCES
;
720 Status
= ZwQueryValueKey(
723 KeyValuePartialInformation
,
727 if (!NT_SUCCESS(Status
))
729 DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status
);
732 else if (bip
->Type
!= REG_SZ
)
734 DPRINT("Unexpected registry type 0x%lx (expected 0x%lx)\n", bip
->Type
, REG_SZ
);
735 Status
= STATUS_UNSUCCESSFUL
;
738 else if (bip
->DataLength
< 5 * sizeof(WCHAR
))
740 DPRINT("Registry string too short (length %lu, expected %lu at least)\n", bip
->DataLength
, 5 * sizeof(WCHAR
));
741 Status
= STATUS_UNSUCCESSFUL
;
744 KeyName
.Length
= KeyName
.MaximumLength
= (USHORT
)bip
->DataLength
;
745 KeyName
.Buffer
= (PWSTR
)bip
->Data
;
747 /* Fixup the prefix (from "\\?\") */
748 RtlCopyMemory(KeyName
.Buffer
, L
"\\??\\", 4 * sizeof(WCHAR
));
750 /* Add new symbolic link to symbolic link list */
751 if (ReturnBuffer
.Length
+ KeyName
.Length
+ sizeof(WCHAR
) > ReturnBuffer
.MaximumLength
)
754 ReturnBuffer
.MaximumLength
= (USHORT
)max(2 * ReturnBuffer
.MaximumLength
,
755 (USHORT
)(ReturnBuffer
.Length
+
758 NewBuffer
= ExAllocatePool(PagedPool
, ReturnBuffer
.MaximumLength
);
761 DPRINT("ExAllocatePool() failed\n");
762 Status
= STATUS_INSUFFICIENT_RESOURCES
;
765 if (ReturnBuffer
.Buffer
)
767 RtlCopyMemory(NewBuffer
, ReturnBuffer
.Buffer
, ReturnBuffer
.Length
);
768 ExFreePool(ReturnBuffer
.Buffer
);
770 ReturnBuffer
.Buffer
= NewBuffer
;
772 DPRINT("Adding symbolic link %wZ\n", &KeyName
);
773 Status
= RtlAppendUnicodeStringToString(&ReturnBuffer
, &KeyName
);
774 if (!NT_SUCCESS(Status
))
776 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status
);
779 /* RtlAppendUnicodeStringToString added a NULL at the end of the
780 * destination string, but didn't increase the Length field.
783 ReturnBuffer
.Length
+= sizeof(WCHAR
);
786 ExFreePool(ReferenceBi
);
791 if (ReferenceKey
!= INVALID_HANDLE_VALUE
)
793 ZwClose(ReferenceKey
);
794 ReferenceKey
= INVALID_HANDLE_VALUE
;
796 if (ControlKey
!= INVALID_HANDLE_VALUE
)
799 ControlKey
= INVALID_HANDLE_VALUE
;
804 /* No need to go further, as we already have found what we searched */
808 ExFreePool(DeviceBi
);
811 DeviceKey
= INVALID_HANDLE_VALUE
;
814 /* Add final NULL to ReturnBuffer */
815 NT_ASSERT(ReturnBuffer
.Length
<= ReturnBuffer
.MaximumLength
);
816 if (ReturnBuffer
.Length
>= ReturnBuffer
.MaximumLength
)
819 ReturnBuffer
.MaximumLength
+= sizeof(WCHAR
);
820 NewBuffer
= ExAllocatePool(PagedPool
, ReturnBuffer
.MaximumLength
);
823 DPRINT("ExAllocatePool() failed\n");
824 Status
= STATUS_INSUFFICIENT_RESOURCES
;
827 if (ReturnBuffer
.Buffer
)
829 RtlCopyMemory(NewBuffer
, ReturnBuffer
.Buffer
, ReturnBuffer
.Length
);
830 ExFreePool(ReturnBuffer
.Buffer
);
832 ReturnBuffer
.Buffer
= NewBuffer
;
834 ReturnBuffer
.Buffer
[ReturnBuffer
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
835 *SymbolicLinkList
= ReturnBuffer
.Buffer
;
836 Status
= STATUS_SUCCESS
;
839 if (!NT_SUCCESS(Status
) && ReturnBuffer
.Buffer
)
840 ExFreePool(ReturnBuffer
.Buffer
);
841 if (InterfaceKey
!= INVALID_HANDLE_VALUE
)
842 ZwClose(InterfaceKey
);
843 if (DeviceKey
!= INVALID_HANDLE_VALUE
)
845 if (ReferenceKey
!= INVALID_HANDLE_VALUE
)
846 ZwClose(ReferenceKey
);
847 if (ControlKey
!= INVALID_HANDLE_VALUE
)
850 ExFreePool(DeviceBi
);
852 ExFreePool(ReferenceBi
);
859 * @name IoRegisterDeviceInterface
862 * Registers a device interface class, if it has not been previously registered,
863 * and creates a new instance of the interface class, which a driver can
864 * subsequently enable for use by applications or other system components.
867 * @param PhysicalDeviceObject
868 * Points to an optional PDO that narrows the search to only the
869 * device interfaces of the device represented by the PDO
871 * @param InterfaceClassGuid
872 * Points to a class GUID specifying the device interface class
874 * @param ReferenceString
875 * Optional parameter, pointing to a unicode string. For a full
876 * description of this rather rarely used param (usually drivers
877 * pass NULL here) see WDK
879 * @param SymbolicLinkName
880 * Pointer to the resulting unicode string
882 * @return Usual NTSTATUS
884 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a
890 IoRegisterDeviceInterface(IN PDEVICE_OBJECT PhysicalDeviceObject
,
891 IN CONST GUID
*InterfaceClassGuid
,
892 IN PUNICODE_STRING ReferenceString OPTIONAL
,
893 OUT PUNICODE_STRING SymbolicLinkName
)
895 PUNICODE_STRING InstancePath
;
896 UNICODE_STRING GuidString
;
897 UNICODE_STRING SubKeyName
;
898 UNICODE_STRING InterfaceKeyName
;
899 UNICODE_STRING BaseKeyName
;
900 UCHAR PdoNameInfoBuffer
[sizeof(OBJECT_NAME_INFORMATION
) + (256 * sizeof(WCHAR
))];
901 POBJECT_NAME_INFORMATION PdoNameInfo
= (POBJECT_NAME_INFORMATION
)PdoNameInfoBuffer
;
902 UNICODE_STRING DeviceInstance
= RTL_CONSTANT_STRING(L
"DeviceInstance");
903 UNICODE_STRING SymbolicLink
= RTL_CONSTANT_STRING(L
"SymbolicLink");
908 OBJECT_ATTRIBUTES ObjectAttributes
;
910 NTSTATUS Status
, SymLinkStatus
;
911 PEXTENDED_DEVOBJ_EXTENSION DeviceObjectExtension
;
913 ASSERT_IRQL_EQUAL(PASSIVE_LEVEL
);
915 DPRINT("IoRegisterDeviceInterface(): PDO %p, RefString: %wZ\n",
916 PhysicalDeviceObject
, ReferenceString
);
918 /* Parameters must pass three border of checks */
919 DeviceObjectExtension
= (PEXTENDED_DEVOBJ_EXTENSION
)PhysicalDeviceObject
->DeviceObjectExtension
;
921 /* 1st level: Presence of a Device Node */
922 if (DeviceObjectExtension
->DeviceNode
== NULL
)
924 DPRINT("PhysicalDeviceObject 0x%p doesn't have a DeviceNode\n", PhysicalDeviceObject
);
925 return STATUS_INVALID_DEVICE_REQUEST
;
928 /* 2nd level: Presence of an non-zero length InstancePath */
929 if (DeviceObjectExtension
->DeviceNode
->InstancePath
.Length
== 0)
931 DPRINT("PhysicalDeviceObject 0x%p's DOE has zero-length InstancePath\n", PhysicalDeviceObject
);
932 return STATUS_INVALID_DEVICE_REQUEST
;
935 /* 3rd level: Optional, based on WDK documentation */
936 if (ReferenceString
!= NULL
)
938 /* Reference string must not contain path-separator symbols */
939 for (i
= 0; i
< ReferenceString
->Length
/ sizeof(WCHAR
); i
++)
941 if ((ReferenceString
->Buffer
[i
] == '\\') ||
942 (ReferenceString
->Buffer
[i
] == '/'))
943 return STATUS_INVALID_DEVICE_REQUEST
;
947 Status
= RtlStringFromGUID(InterfaceClassGuid
, &GuidString
);
948 if (!NT_SUCCESS(Status
))
950 DPRINT("RtlStringFromGUID() failed with status 0x%08lx\n", Status
);
954 /* Create Pdo name: \Device\xxxxxxxx (unnamed device) */
955 Status
= ObQueryNameString(
956 PhysicalDeviceObject
,
958 sizeof(PdoNameInfoBuffer
),
960 if (!NT_SUCCESS(Status
))
962 DPRINT("ObQueryNameString() failed with status 0x%08lx\n", Status
);
965 ASSERT(PdoNameInfo
->Name
.Length
);
967 /* Create base key name for this interface: HKLM\SYSTEM\CurrentControlSet\Control\DeviceClasses\{GUID} */
968 ASSERT(((PEXTENDED_DEVOBJ_EXTENSION
)PhysicalDeviceObject
->DeviceObjectExtension
)->DeviceNode
);
969 InstancePath
= &((PEXTENDED_DEVOBJ_EXTENSION
)PhysicalDeviceObject
->DeviceObjectExtension
)->DeviceNode
->InstancePath
;
970 BaseKeyName
.Length
= (USHORT
)wcslen(BaseKeyString
) * sizeof(WCHAR
);
971 BaseKeyName
.MaximumLength
= BaseKeyName
.Length
973 BaseKeyName
.Buffer
= ExAllocatePool(
975 BaseKeyName
.MaximumLength
);
976 if (!BaseKeyName
.Buffer
)
978 DPRINT("ExAllocatePool() failed\n");
979 return STATUS_INSUFFICIENT_RESOURCES
;
981 wcscpy(BaseKeyName
.Buffer
, BaseKeyString
);
982 RtlAppendUnicodeStringToString(&BaseKeyName
, &GuidString
);
984 /* Create BaseKeyName key in registry */
985 InitializeObjectAttributes(
988 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
| OBJ_OPENIF
,
989 NULL
, /* RootDirectory */
990 NULL
); /* SecurityDescriptor */
992 Status
= ZwCreateKey(
999 NULL
); /* Disposition */
1001 if (!NT_SUCCESS(Status
))
1003 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status
);
1004 ExFreePool(BaseKeyName
.Buffer
);
1008 /* Create key name for this interface: ##?#ACPI#PNP0501#1#{GUID} */
1009 InterfaceKeyName
.Length
= 0;
1010 InterfaceKeyName
.MaximumLength
=
1011 4 * sizeof(WCHAR
) + /* 4 = size of ##?# */
1012 InstancePath
->Length
+
1013 sizeof(WCHAR
) + /* 1 = size of # */
1015 InterfaceKeyName
.Buffer
= ExAllocatePool(
1017 InterfaceKeyName
.MaximumLength
);
1018 if (!InterfaceKeyName
.Buffer
)
1020 DPRINT("ExAllocatePool() failed\n");
1021 return STATUS_INSUFFICIENT_RESOURCES
;
1024 RtlAppendUnicodeToString(&InterfaceKeyName
, L
"##?#");
1025 StartIndex
= InterfaceKeyName
.Length
/ sizeof(WCHAR
);
1026 RtlAppendUnicodeStringToString(&InterfaceKeyName
, InstancePath
);
1027 for (i
= 0; i
< InstancePath
->Length
/ sizeof(WCHAR
); i
++)
1029 if (InterfaceKeyName
.Buffer
[StartIndex
+ i
] == '\\')
1030 InterfaceKeyName
.Buffer
[StartIndex
+ i
] = '#';
1032 RtlAppendUnicodeToString(&InterfaceKeyName
, L
"#");
1033 RtlAppendUnicodeStringToString(&InterfaceKeyName
, &GuidString
);
1035 /* Create the interface key in registry */
1036 InitializeObjectAttributes(
1039 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
| OBJ_OPENIF
,
1041 NULL
); /* SecurityDescriptor */
1043 Status
= ZwCreateKey(
1049 REG_OPTION_VOLATILE
,
1050 NULL
); /* Disposition */
1052 if (!NT_SUCCESS(Status
))
1054 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status
);
1056 ExFreePool(BaseKeyName
.Buffer
);
1060 /* Write DeviceInstance entry. Value is InstancePath */
1061 Status
= ZwSetValueKey(
1066 InstancePath
->Buffer
,
1067 InstancePath
->Length
);
1068 if (!NT_SUCCESS(Status
))
1070 DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status
);
1071 ZwClose(InterfaceKey
);
1073 ExFreePool(InterfaceKeyName
.Buffer
);
1074 ExFreePool(BaseKeyName
.Buffer
);
1078 /* Create subkey. Name is #ReferenceString */
1079 SubKeyName
.Length
= 0;
1080 SubKeyName
.MaximumLength
= sizeof(WCHAR
);
1081 if (ReferenceString
&& ReferenceString
->Length
)
1082 SubKeyName
.MaximumLength
+= ReferenceString
->Length
;
1083 SubKeyName
.Buffer
= ExAllocatePool(
1085 SubKeyName
.MaximumLength
);
1086 if (!SubKeyName
.Buffer
)
1088 DPRINT("ExAllocatePool() failed\n");
1089 ZwClose(InterfaceKey
);
1091 ExFreePool(InterfaceKeyName
.Buffer
);
1092 ExFreePool(BaseKeyName
.Buffer
);
1093 return STATUS_INSUFFICIENT_RESOURCES
;
1095 RtlAppendUnicodeToString(&SubKeyName
, L
"#");
1096 if (ReferenceString
&& ReferenceString
->Length
)
1097 RtlAppendUnicodeStringToString(&SubKeyName
, ReferenceString
);
1099 /* Create SubKeyName key in registry */
1100 InitializeObjectAttributes(
1103 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1104 InterfaceKey
, /* RootDirectory */
1105 NULL
); /* SecurityDescriptor */
1107 Status
= ZwCreateKey(
1113 REG_OPTION_VOLATILE
,
1114 NULL
); /* Disposition */
1116 if (!NT_SUCCESS(Status
))
1118 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status
);
1119 ZwClose(InterfaceKey
);
1121 ExFreePool(InterfaceKeyName
.Buffer
);
1122 ExFreePool(BaseKeyName
.Buffer
);
1126 /* Create symbolic link name: \??\ACPI#PNP0501#1#{GUID}\ReferenceString */
1127 SymbolicLinkName
->Length
= 0;
1128 SymbolicLinkName
->MaximumLength
= SymbolicLinkName
->Length
1129 + 4 * sizeof(WCHAR
) /* 4 = size of \??\ */
1130 + InstancePath
->Length
1131 + sizeof(WCHAR
) /* 1 = size of # */
1133 + sizeof(WCHAR
); /* final NULL */
1134 if (ReferenceString
&& ReferenceString
->Length
)
1135 SymbolicLinkName
->MaximumLength
+= sizeof(WCHAR
) + ReferenceString
->Length
;
1136 SymbolicLinkName
->Buffer
= ExAllocatePool(
1138 SymbolicLinkName
->MaximumLength
);
1139 if (!SymbolicLinkName
->Buffer
)
1141 DPRINT("ExAllocatePool() failed\n");
1143 ZwClose(InterfaceKey
);
1145 ExFreePool(InterfaceKeyName
.Buffer
);
1146 ExFreePool(SubKeyName
.Buffer
);
1147 ExFreePool(BaseKeyName
.Buffer
);
1148 return STATUS_INSUFFICIENT_RESOURCES
;
1150 RtlAppendUnicodeToString(SymbolicLinkName
, L
"\\??\\");
1151 StartIndex
= SymbolicLinkName
->Length
/ sizeof(WCHAR
);
1152 RtlAppendUnicodeStringToString(SymbolicLinkName
, InstancePath
);
1153 for (i
= 0; i
< InstancePath
->Length
/ sizeof(WCHAR
); i
++)
1155 if (SymbolicLinkName
->Buffer
[StartIndex
+ i
] == '\\')
1156 SymbolicLinkName
->Buffer
[StartIndex
+ i
] = '#';
1158 RtlAppendUnicodeToString(SymbolicLinkName
, L
"#");
1159 RtlAppendUnicodeStringToString(SymbolicLinkName
, &GuidString
);
1160 SymbolicLinkName
->Buffer
[SymbolicLinkName
->Length
/sizeof(WCHAR
)] = L
'\0';
1162 /* Create symbolic link */
1163 DPRINT("IoRegisterDeviceInterface(): creating symbolic link %wZ -> %wZ\n", SymbolicLinkName
, &PdoNameInfo
->Name
);
1164 SymLinkStatus
= IoCreateSymbolicLink(SymbolicLinkName
, &PdoNameInfo
->Name
);
1166 /* If the symbolic link already exists, return an informational success status */
1167 if (SymLinkStatus
== STATUS_OBJECT_NAME_COLLISION
)
1169 /* HACK: Delete the existing symbolic link and update it to the new PDO name */
1170 IoDeleteSymbolicLink(SymbolicLinkName
);
1171 IoCreateSymbolicLink(SymbolicLinkName
, &PdoNameInfo
->Name
);
1172 SymLinkStatus
= STATUS_OBJECT_NAME_EXISTS
;
1175 if (!NT_SUCCESS(SymLinkStatus
))
1177 DPRINT1("IoCreateSymbolicLink() failed with status 0x%08lx\n", SymLinkStatus
);
1179 ZwClose(InterfaceKey
);
1181 ExFreePool(SubKeyName
.Buffer
);
1182 ExFreePool(InterfaceKeyName
.Buffer
);
1183 ExFreePool(BaseKeyName
.Buffer
);
1184 ExFreePool(SymbolicLinkName
->Buffer
);
1185 return SymLinkStatus
;
1188 if (ReferenceString
&& ReferenceString
->Length
)
1190 RtlAppendUnicodeToString(SymbolicLinkName
, L
"\\");
1191 RtlAppendUnicodeStringToString(SymbolicLinkName
, ReferenceString
);
1193 SymbolicLinkName
->Buffer
[SymbolicLinkName
->Length
/sizeof(WCHAR
)] = L
'\0';
1195 /* Write symbolic link name in registry */
1196 SymbolicLinkName
->Buffer
[1] = '\\';
1197 Status
= ZwSetValueKey(
1202 SymbolicLinkName
->Buffer
,
1203 SymbolicLinkName
->Length
);
1204 if (!NT_SUCCESS(Status
))
1206 DPRINT1("ZwSetValueKey() failed with status 0x%08lx\n", Status
);
1207 ExFreePool(SymbolicLinkName
->Buffer
);
1211 SymbolicLinkName
->Buffer
[1] = '?';
1215 ZwClose(InterfaceKey
);
1217 ExFreePool(SubKeyName
.Buffer
);
1218 ExFreePool(InterfaceKeyName
.Buffer
);
1219 ExFreePool(BaseKeyName
.Buffer
);
1221 return NT_SUCCESS(Status
) ? SymLinkStatus
: Status
;
1225 * @name IoSetDeviceInterfaceState
1228 * Enables or disables an instance of a previously registered device
1230 * Documented in WDK.
1232 * @param SymbolicLinkName
1233 * Pointer to the string identifying instance to enable or disable
1236 * TRUE = enable, FALSE = disable
1238 * @return Usual NTSTATUS
1240 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a
1246 IoSetDeviceInterfaceState(IN PUNICODE_STRING SymbolicLinkName
,
1249 PDEVICE_OBJECT PhysicalDeviceObject
;
1250 PFILE_OBJECT FileObject
;
1251 UNICODE_STRING GuidString
;
1252 PWCHAR StartPosition
;
1256 HANDLE InstanceHandle
, ControlHandle
;
1257 UNICODE_STRING KeyName
;
1258 OBJECT_ATTRIBUTES ObjectAttributes
;
1262 if (SymbolicLinkName
== NULL
)
1263 return STATUS_INVALID_PARAMETER_1
;
1265 DPRINT("IoSetDeviceInterfaceState('%wZ', %u)\n", SymbolicLinkName
, Enable
);
1267 /* Symbolic link name is \??\ACPI#PNP0501#1#{GUID}\ReferenceString */
1268 /* Get GUID from SymbolicLinkName */
1269 StartPosition
= wcsrchr(SymbolicLinkName
->Buffer
, L
'{');
1270 EndPosition
= wcsrchr(SymbolicLinkName
->Buffer
, L
'}');
1271 if (!StartPosition
||!EndPosition
|| StartPosition
> EndPosition
)
1273 DPRINT1("IoSetDeviceInterfaceState() returning STATUS_INVALID_PARAMETER_1\n");
1274 return STATUS_INVALID_PARAMETER_1
;
1276 GuidString
.Buffer
= StartPosition
;
1277 GuidString
.MaximumLength
= GuidString
.Length
= (USHORT
)((ULONG_PTR
)(EndPosition
+ 1) - (ULONG_PTR
)StartPosition
);
1279 Status
= OpenRegistryHandlesFromSymbolicLink(SymbolicLinkName
,
1284 if (!NT_SUCCESS(Status
))
1287 RtlInitUnicodeString(&KeyName
, L
"Control");
1288 InitializeObjectAttributes(&ObjectAttributes
,
1290 OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
,
1293 Status
= ZwCreateKey(&ControlHandle
,
1298 REG_OPTION_VOLATILE
,
1300 ZwClose(InstanceHandle
);
1301 if (!NT_SUCCESS(Status
))
1303 DPRINT1("Failed to create the Control subkey\n");
1307 LinkedValue
= (Enable
? 1 : 0);
1309 RtlInitUnicodeString(&KeyName
, L
"Linked");
1310 Status
= ZwSetValueKey(ControlHandle
,
1316 ZwClose(ControlHandle
);
1317 if (!NT_SUCCESS(Status
))
1319 DPRINT1("Failed to write the Linked value\n");
1323 /* Get pointer to the PDO */
1324 Status
= IoGetDeviceObjectPointer(
1326 0, /* DesiredAccess */
1328 &PhysicalDeviceObject
);
1329 if (!NT_SUCCESS(Status
))
1331 DPRINT1("IoGetDeviceObjectPointer() failed with status 0x%08lx\n", Status
);
1335 Status
= RtlGUIDFromString(&GuidString
, &DeviceGuid
);
1336 if (!NT_SUCCESS(Status
))
1338 DPRINT1("RtlGUIDFromString() failed with status 0x%08lx\n", Status
);
1342 EventGuid
= Enable
? &GUID_DEVICE_INTERFACE_ARRIVAL
: &GUID_DEVICE_INTERFACE_REMOVAL
;
1343 IopNotifyPlugPlayNotification(
1344 PhysicalDeviceObject
,
1345 EventCategoryDeviceInterfaceChange
,
1348 (PVOID
)SymbolicLinkName
);
1350 ObDereferenceObject(FileObject
);
1351 DPRINT("Status %x\n", Status
);
1352 return STATUS_SUCCESS
;