merge trunk rev : 25663 and 25664 to 0.3.1 branch - update freetype, take care of...
[reactos.git] / reactos / ntoskrnl / io / iomgr / deviface.c
1 /*
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
6 *
7 * PROGRAMMERS: Filip Navara (xnavara@volny.cz)
8 * Matthew Brace (ismarc@austin.rr.com)
9 * Hervé Poussineau (hpoussin@reactos.org)
10 */
11
12 /* INCLUDES ******************************************************************/
13
14 #include <ntoskrnl.h>
15
16 #define NDEBUG
17 #include <internal/debug.h>
18
19 /* FUNCTIONS *****************************************************************/
20
21 static PWCHAR BaseKeyString = L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\DeviceClasses\\";
22
23 /*++
24 * @name IoOpenDeviceInterfaceRegistryKey
25 * @unimplemented
26 *
27 * Provides a handle to the device's interface instance registry key.
28 * Documented in WDK.
29 *
30 * @param SymbolicLinkName
31 * Pointer to a string which identifies the device interface instance
32 *
33 * @param DesiredAccess
34 * Desired ACCESS_MASK used to access the key (like KEY_READ,
35 * KEY_WRITE, etc)
36 *
37 * @param DeviceInterfaceKey
38 * If a call has been succesfull, a handle to the registry key
39 * will be stored there
40 *
41 * @return Three different NTSTATUS values in case of errors, and STATUS_SUCCESS
42 * otherwise (see WDK for details)
43 *
44 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a system thread
45 *
46 *--*/
47 NTSTATUS
48 NTAPI
49 IoOpenDeviceInterfaceRegistryKey(IN PUNICODE_STRING SymbolicLinkName,
50 IN ACCESS_MASK DesiredAccess,
51 OUT PHANDLE DeviceInterfaceKey)
52 {
53 return STATUS_NOT_IMPLEMENTED;
54 }
55
56 /*++
57 * @name IoGetDeviceInterfaceAlias
58 * @unimplemented
59 *
60 * Returns the alias device interface of the specified device interface
61 * instance, if the alias exists.
62 * Documented in WDK.
63 *
64 * @param SymbolicLinkName
65 * Pointer to a string which identifies the device interface instance
66 *
67 * @param AliasInterfaceClassGuid
68 * See WDK
69 *
70 * @param AliasSymbolicLinkName
71 * See WDK
72 *
73 * @return Three different NTSTATUS values in case of errors, and STATUS_SUCCESS
74 * otherwise (see WDK for details)
75 *
76 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a system thread
77 *
78 *--*/
79 NTSTATUS
80 NTAPI
81 IoGetDeviceInterfaceAlias(IN PUNICODE_STRING SymbolicLinkName,
82 IN CONST GUID *AliasInterfaceClassGuid,
83 OUT PUNICODE_STRING AliasSymbolicLinkName)
84 {
85 return STATUS_NOT_IMPLEMENTED;
86 }
87
88 /*++
89 * @name IopOpenInterfaceKey
90 *
91 * Returns the alias device interface of the specified device interface
92 *
93 * @param InterfaceClassGuid
94 * FILLME
95 *
96 * @param DesiredAccess
97 * FILLME
98 *
99 * @param pInterfaceKey
100 * FILLME
101 *
102 * @return Usual NTSTATUS
103 *
104 * @remarks None
105 *
106 *--*/
107 static NTSTATUS
108 IopOpenInterfaceKey(IN CONST GUID *InterfaceClassGuid,
109 IN ACCESS_MASK DesiredAccess,
110 OUT HANDLE *pInterfaceKey)
111 {
112 UNICODE_STRING LocalMachine = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\");
113 UNICODE_STRING GuidString;
114 UNICODE_STRING KeyName;
115 OBJECT_ATTRIBUTES ObjectAttributes;
116 HANDLE InterfaceKey = INVALID_HANDLE_VALUE;
117 NTSTATUS Status;
118
119 GuidString.Buffer = KeyName.Buffer = NULL;
120
121 Status = RtlStringFromGUID(InterfaceClassGuid, &GuidString);
122 if (!NT_SUCCESS(Status))
123 {
124 DPRINT("RtlStringFromGUID() failed with status 0x%08lx\n", Status);
125 goto cleanup;
126 }
127
128 KeyName.Length = 0;
129 KeyName.MaximumLength = LocalMachine.Length + (wcslen(REGSTR_PATH_DEVICE_CLASSES) + 1) * sizeof(WCHAR) + GuidString.Length;
130 KeyName.Buffer = ExAllocatePool(PagedPool, KeyName.MaximumLength);
131 if (!KeyName.Buffer)
132 {
133 DPRINT("ExAllocatePool() failed\n");
134 Status = STATUS_INSUFFICIENT_RESOURCES;
135 goto cleanup;
136 }
137
138 Status = RtlAppendUnicodeStringToString(&KeyName, &LocalMachine);
139 if (!NT_SUCCESS(Status))
140 {
141 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
142 goto cleanup;
143 }
144 Status = RtlAppendUnicodeToString(&KeyName, REGSTR_PATH_DEVICE_CLASSES);
145 if (!NT_SUCCESS(Status))
146 {
147 DPRINT("RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status);
148 goto cleanup;
149 }
150 Status = RtlAppendUnicodeToString(&KeyName, L"\\");
151 if (!NT_SUCCESS(Status))
152 {
153 DPRINT("RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status);
154 goto cleanup;
155 }
156 Status = RtlAppendUnicodeStringToString(&KeyName, &GuidString);
157 if (!NT_SUCCESS(Status))
158 {
159 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
160 goto cleanup;
161 }
162
163 InitializeObjectAttributes(
164 &ObjectAttributes,
165 &KeyName,
166 OBJ_CASE_INSENSITIVE,
167 NULL,
168 NULL);
169 Status = ZwOpenKey(
170 &InterfaceKey,
171 DesiredAccess,
172 &ObjectAttributes);
173 if (!NT_SUCCESS(Status))
174 {
175 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
176 goto cleanup;
177 }
178
179 *pInterfaceKey = InterfaceKey;
180 Status = STATUS_SUCCESS;
181
182 cleanup:
183 if (!NT_SUCCESS(Status))
184 {
185 if (InterfaceKey != INVALID_HANDLE_VALUE)
186 ZwClose(InterfaceKey);
187 }
188 RtlFreeUnicodeString(&GuidString);
189 RtlFreeUnicodeString(&KeyName);
190 return Status;
191 }
192
193 /*++
194 * @name IoGetDeviceInterfaces
195 * @implemented
196 *
197 * Returns a list of device interfaces of a particular device interface class.
198 * Documented in WDK
199 *
200 * @param InterfaceClassGuid
201 * Points to a class GUID specifying the device interface class
202 *
203 * @param PhysicalDeviceObject
204 * Points to an optional PDO that narrows the search to only the
205 * device interfaces of the device represented by the PDO
206 *
207 * @param Flags
208 * Specifies flags that modify the search for device interfaces. The
209 * DEVICE_INTERFACE_INCLUDE_NONACTIVE flag specifies that the list of
210 * returned symbolic links should contain also disabled device
211 * interfaces in addition to the enabled ones.
212 *
213 * @param SymbolicLinkList
214 * Points to a character pointer that is filled in on successful return
215 * with a list of unicode strings identifying the device interfaces
216 * that match the search criteria. The newly allocated buffer contains
217 * a list of symbolic link names. Each unicode string in the list is
218 * null-terminated; the end of the whole list is marked by an additional
219 * NULL. The caller is responsible for freeing the buffer (ExFreePool)
220 * when it is no longer needed.
221 * If no device interfaces match the search criteria, this routine
222 * returns STATUS_SUCCESS and the string contains a single NULL
223 * character.
224 *
225 * @return Usual NTSTATUS
226 *
227 * @remarks None
228 *
229 *--*/
230 NTSTATUS
231 NTAPI
232 IoGetDeviceInterfaces(IN CONST GUID *InterfaceClassGuid,
233 IN PDEVICE_OBJECT PhysicalDeviceObject OPTIONAL,
234 IN ULONG Flags,
235 OUT PWSTR *SymbolicLinkList)
236 {
237 UNICODE_STRING Control = RTL_CONSTANT_STRING(L"Control");
238 UNICODE_STRING SymbolicLink = RTL_CONSTANT_STRING(L"SymbolicLink");
239 HANDLE InterfaceKey = INVALID_HANDLE_VALUE;
240 HANDLE DeviceKey = INVALID_HANDLE_VALUE;
241 HANDLE ReferenceKey = INVALID_HANDLE_VALUE;
242 HANDLE ControlKey = INVALID_HANDLE_VALUE;
243 PKEY_BASIC_INFORMATION DeviceBi = NULL;
244 PKEY_BASIC_INFORMATION ReferenceBi = NULL;
245 PKEY_VALUE_PARTIAL_INFORMATION bip = NULL;
246 UNICODE_STRING KeyName;
247 OBJECT_ATTRIBUTES ObjectAttributes;
248 BOOLEAN FoundRightPDO = FALSE;
249 ULONG i = 0, j, Size;
250 UNICODE_STRING ReturnBuffer = {0,};
251 NTSTATUS Status;
252
253 PAGED_CODE();
254
255 Status = IopOpenInterfaceKey(InterfaceClassGuid, KEY_ENUMERATE_SUB_KEYS, &InterfaceKey);
256 if (!NT_SUCCESS(Status))
257 {
258 DPRINT("IopOpenInterfaceKey() failed with status 0x%08lx\n", Status);
259 goto cleanup;
260 }
261
262 /* Enumerate subkeys (ie the different device objets) */
263 while (TRUE)
264 {
265 Status = ZwEnumerateKey(
266 InterfaceKey,
267 i,
268 KeyBasicInformation,
269 NULL,
270 0,
271 &Size);
272 if (Status == STATUS_NO_MORE_ENTRIES)
273 {
274 break;
275 }
276 else if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL)
277 {
278 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
279 goto cleanup;
280 }
281
282 DeviceBi = ExAllocatePool(PagedPool, Size);
283 if (!DeviceBi)
284 {
285 DPRINT("ExAllocatePool() failed\n");
286 Status = STATUS_INSUFFICIENT_RESOURCES;
287 goto cleanup;
288 }
289 Status = ZwEnumerateKey(
290 InterfaceKey,
291 i++,
292 KeyBasicInformation,
293 DeviceBi,
294 Size,
295 &Size);
296 if (!NT_SUCCESS(Status))
297 {
298 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
299 goto cleanup;
300 }
301
302 /* Open device key */
303 KeyName.Length = KeyName.MaximumLength = DeviceBi->NameLength;
304 KeyName.Buffer = DeviceBi->Name;
305 InitializeObjectAttributes(
306 &ObjectAttributes,
307 &KeyName,
308 OBJ_CASE_INSENSITIVE,
309 InterfaceKey,
310 NULL);
311 Status = ZwOpenKey(
312 &DeviceKey,
313 KEY_ENUMERATE_SUB_KEYS,
314 &ObjectAttributes);
315 if (!NT_SUCCESS(Status))
316 {
317 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
318 goto cleanup;
319 }
320
321 if (PhysicalDeviceObject)
322 {
323 /* Check if we are on the right physical device object,
324 * by reading the DeviceInstance string
325 */
326 DPRINT1("PhysicalDeviceObject != NULL. Case not implemented.\n");
327 //FoundRightPDO = TRUE;
328 Status = STATUS_NOT_IMPLEMENTED;
329 goto cleanup;
330 }
331
332 /* Enumerate subkeys (ie the different reference strings) */
333 j = 0;
334 while (TRUE)
335 {
336 Status = ZwEnumerateKey(
337 DeviceKey,
338 j,
339 KeyBasicInformation,
340 NULL,
341 0,
342 &Size);
343 if (Status == STATUS_NO_MORE_ENTRIES)
344 {
345 break;
346 }
347 else if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL)
348 {
349 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
350 goto cleanup;
351 }
352
353 ReferenceBi = ExAllocatePool(PagedPool, Size);
354 if (!ReferenceBi)
355 {
356 DPRINT("ExAllocatePool() failed\n");
357 Status = STATUS_INSUFFICIENT_RESOURCES;
358 goto cleanup;
359 }
360 Status = ZwEnumerateKey(
361 DeviceKey,
362 j++,
363 KeyBasicInformation,
364 ReferenceBi,
365 Size,
366 &Size);
367 if (!NT_SUCCESS(Status))
368 {
369 DPRINT("ZwEnumerateKey() failed with status 0x%08lx\n", Status);
370 goto cleanup;
371 }
372
373 KeyName.Length = KeyName.MaximumLength = ReferenceBi->NameLength;
374 KeyName.Buffer = ReferenceBi->Name;
375 if (RtlEqualUnicodeString(&KeyName, &Control, TRUE))
376 {
377 /* Skip Control subkey */
378 goto NextReferenceString;
379 }
380
381 /* Open reference key */
382 InitializeObjectAttributes(
383 &ObjectAttributes,
384 &KeyName,
385 OBJ_CASE_INSENSITIVE,
386 DeviceKey,
387 NULL);
388 Status = ZwOpenKey(
389 &ReferenceKey,
390 KEY_QUERY_VALUE,
391 &ObjectAttributes);
392 if (!NT_SUCCESS(Status))
393 {
394 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
395 goto cleanup;
396 }
397
398 if (!(Flags & DEVICE_INTERFACE_INCLUDE_NONACTIVE))
399 {
400 /* We have to check if the interface is enabled, by
401 * reading the Linked value in the Control subkey
402 */
403 InitializeObjectAttributes(
404 &ObjectAttributes,
405 &Control,
406 OBJ_CASE_INSENSITIVE,
407 ReferenceKey,
408 NULL);
409 Status = ZwOpenKey(
410 &ControlKey,
411 KEY_QUERY_VALUE,
412 &ObjectAttributes);
413 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
414 {
415 /* That's OK. The key doesn't exist (yet) because
416 * the interface is not activated.
417 */
418 goto NextReferenceString;
419 }
420 else if (!NT_SUCCESS(Status))
421 {
422 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
423 goto cleanup;
424 }
425 /* FIXME: Read the Linked value
426 * If it doesn't exist => ERROR
427 * If it is not a REG_DWORD or Size != sizeof(ULONG) => ERROR
428 * If its value is 0, go to NextReferenceString
429 * At the moment, do as if it is active...
430 */
431 DPRINT1("Checking if device is enabled is not implemented yet!\n");
432 }
433
434 /* Read the SymbolicLink string and add it into SymbolicLinkList */
435 Status = ZwQueryValueKey(
436 ReferenceKey,
437 &SymbolicLink,
438 KeyValuePartialInformation,
439 NULL,
440 0,
441 &Size);
442 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL)
443 {
444 DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status);
445 goto cleanup;
446 }
447 bip = ExAllocatePool(PagedPool, Size);
448 if (!bip)
449 {
450 DPRINT("ExAllocatePool() failed\n");
451 Status = STATUS_INSUFFICIENT_RESOURCES;
452 goto cleanup;
453 }
454 Status = ZwQueryValueKey(
455 ReferenceKey,
456 &SymbolicLink,
457 KeyValuePartialInformation,
458 bip,
459 Size,
460 &Size);
461 if (!NT_SUCCESS(Status))
462 {
463 DPRINT("ZwQueryValueKey() failed with status 0x%08lx\n", Status);
464 goto cleanup;
465 }
466 else if (bip->Type != REG_SZ)
467 {
468 DPRINT("Unexpected registry type 0x%lx (expected 0x%lx)\n", bip->Type, REG_SZ);
469 Status = STATUS_UNSUCCESSFUL;
470 goto cleanup;
471 }
472 else if (bip->DataLength < 5 * sizeof(WCHAR))
473 {
474 DPRINT("Registry string too short (length %lu, expected %lu at least)\n", bip->DataLength < 5 * sizeof(WCHAR));
475 Status = STATUS_UNSUCCESSFUL;
476 goto cleanup;
477 }
478 KeyName.Length = KeyName.MaximumLength = bip->DataLength - 4 * sizeof(WCHAR);
479 KeyName.Buffer = &((PWSTR)bip->Data)[4];
480 if (KeyName.Length && KeyName.Buffer[KeyName.Length / sizeof(WCHAR)] == UNICODE_NULL)
481 {
482 /* Remove trailing NULL */
483 KeyName.Length -= sizeof(WCHAR);
484 }
485
486 /* Add new symbolic link to symbolic link list */
487 if (ReturnBuffer.Length + KeyName.Length + sizeof(WCHAR) > ReturnBuffer.MaximumLength)
488 {
489 PWSTR NewBuffer;
490 ReturnBuffer.MaximumLength = max(ReturnBuffer.MaximumLength * 2, ReturnBuffer.Length + KeyName.Length + 2 * sizeof(WCHAR));
491 NewBuffer = ExAllocatePool(PagedPool, ReturnBuffer.MaximumLength);
492 if (!NewBuffer)
493 {
494 DPRINT("ExAllocatePool() failed\n");
495 Status = STATUS_INSUFFICIENT_RESOURCES;
496 goto cleanup;
497 }
498 RtlCopyMemory(NewBuffer, ReturnBuffer.Buffer, ReturnBuffer.Length);
499 ExFreePool(ReturnBuffer.Buffer);
500 ReturnBuffer.Buffer = NewBuffer;
501 }
502 DPRINT("Adding symbolic link %wZ\n", &KeyName);
503 Status = RtlAppendUnicodeStringToString(&ReturnBuffer, &KeyName);
504 if (!NT_SUCCESS(Status))
505 {
506 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
507 goto cleanup;
508 }
509 /* RtlAppendUnicodeStringToString added a NULL at the end of the
510 * destination string, but didn't increase the Length field.
511 * Do it for it.
512 */
513 ReturnBuffer.Length += sizeof(WCHAR);
514
515 NextReferenceString:
516 ExFreePool(ReferenceBi);
517 ReferenceBi = NULL;
518 ExFreePool(bip);
519 bip = NULL;
520 if (ReferenceKey != INVALID_HANDLE_VALUE)
521 {
522 ZwClose(ReferenceKey);
523 ReferenceKey = INVALID_HANDLE_VALUE;
524 }
525 if (ControlKey != INVALID_HANDLE_VALUE)
526 {
527 ZwClose(ControlKey);
528 ControlKey = INVALID_HANDLE_VALUE;
529 }
530 }
531 if (FoundRightPDO)
532 {
533 /* No need to go further, as we already have found what we searched */
534 break;
535 }
536
537 ExFreePool(DeviceBi);
538 DeviceBi = NULL;
539 ZwClose(DeviceKey);
540 DeviceKey = INVALID_HANDLE_VALUE;
541 }
542
543 /* Add final NULL to ReturnBuffer */
544 if (ReturnBuffer.Length >= ReturnBuffer.MaximumLength)
545 {
546 PWSTR NewBuffer;
547 ReturnBuffer.MaximumLength += sizeof(WCHAR);
548 NewBuffer = ExAllocatePool(PagedPool, ReturnBuffer.MaximumLength);
549 if (!NewBuffer)
550 {
551 DPRINT("ExAllocatePool() failed\n");
552 Status = STATUS_INSUFFICIENT_RESOURCES;
553 goto cleanup;
554 }
555 RtlCopyMemory(NewBuffer, ReturnBuffer.Buffer, ReturnBuffer.Length);
556 ExFreePool(ReturnBuffer.Buffer);
557 ReturnBuffer.Buffer = NewBuffer;
558 }
559 ReturnBuffer.Buffer[ReturnBuffer.Length / sizeof(WCHAR)] = UNICODE_NULL;
560 *SymbolicLinkList = ReturnBuffer.Buffer;
561 Status = STATUS_SUCCESS;
562
563 cleanup:
564 if (!NT_SUCCESS(Status))
565 ExFreePool(ReturnBuffer.Buffer);
566 if (InterfaceKey != INVALID_HANDLE_VALUE)
567 ZwClose(InterfaceKey);
568 if (DeviceKey != INVALID_HANDLE_VALUE)
569 ZwClose(DeviceKey);
570 if (ReferenceKey != INVALID_HANDLE_VALUE)
571 ZwClose(ReferenceKey);
572 if (ControlKey != INVALID_HANDLE_VALUE)
573 ZwClose(ControlKey);
574 ExFreePool(DeviceBi);
575 ExFreePool(ReferenceBi);
576 ExFreePool(bip);
577 return Status;
578 }
579
580 /*++
581 * @name IoRegisterDeviceInterface
582 * @implemented
583 *
584 * Registers a device interface class, if it has not been previously registered,
585 * and creates a new instance of the interface class, which a driver can
586 * subsequently enable for use by applications or other system components.
587 * Documented in WDK.
588 *
589 * @param PhysicalDeviceObject
590 * Points to an optional PDO that narrows the search to only the
591 * device interfaces of the device represented by the PDO
592 *
593 * @param InterfaceClassGuid
594 * Points to a class GUID specifying the device interface class
595 *
596 * @param ReferenceString
597 * Optional parameter, pointing to a unicode string. For a full
598 * description of this rather rarely used param (usually drivers
599 * pass NULL here) see WDK
600 *
601 * @param SymbolicLinkName
602 * Pointer to the resulting unicode string
603 *
604 * @return Usual NTSTATUS
605 *
606 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a
607 * system thread
608 *
609 *--*/
610 NTSTATUS
611 NTAPI
612 IoRegisterDeviceInterface(IN PDEVICE_OBJECT PhysicalDeviceObject,
613 IN CONST GUID *InterfaceClassGuid,
614 IN PUNICODE_STRING ReferenceString OPTIONAL,
615 OUT PUNICODE_STRING SymbolicLinkName)
616 {
617 PUNICODE_STRING InstancePath;
618 UNICODE_STRING GuidString;
619 UNICODE_STRING SubKeyName;
620 UNICODE_STRING InterfaceKeyName;
621 UNICODE_STRING BaseKeyName;
622 UCHAR PdoNameInfoBuffer[sizeof(OBJECT_NAME_INFORMATION) + (256 * sizeof(WCHAR))];
623 POBJECT_NAME_INFORMATION PdoNameInfo = (POBJECT_NAME_INFORMATION)PdoNameInfoBuffer;
624 UNICODE_STRING DeviceInstance = RTL_CONSTANT_STRING(L"DeviceInstance");
625 UNICODE_STRING SymbolicLink = RTL_CONSTANT_STRING(L"SymbolicLink");
626 HANDLE ClassKey;
627 HANDLE InterfaceKey;
628 HANDLE SubKey;
629 ULONG StartIndex;
630 OBJECT_ATTRIBUTES ObjectAttributes;
631 ULONG i;
632 NTSTATUS Status;
633 PEXTENDED_DEVOBJ_EXTENSION DeviceObjectExtension;
634
635 ASSERT_IRQL(PASSIVE_LEVEL);
636
637 DPRINT("IoRegisterDeviceInterface(): PDO %p, RefString: %wZ\n",
638 PhysicalDeviceObject, ReferenceString);
639
640 /* Parameters must pass three border of checks */
641 DeviceObjectExtension = (PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension;
642
643 /* 1st level: Presence of a Device Node */
644 if (DeviceObjectExtension->DeviceNode == NULL)
645 {
646 DPRINT("PhysicalDeviceObject 0x%p doesn't have a DeviceNode\n", PhysicalDeviceObject);
647 return STATUS_INVALID_DEVICE_REQUEST;
648 }
649
650 /* 2nd level: Presence of an non-zero length InstancePath */
651 if (DeviceObjectExtension->DeviceNode->InstancePath.Length == 0)
652 {
653 DPRINT("PhysicalDeviceObject 0x%p's DOE has zero-length InstancePath\n", PhysicalDeviceObject);
654 return STATUS_INVALID_DEVICE_REQUEST;
655 }
656
657 /* 3rd level: Optional, based on WDK documentation */
658 if (ReferenceString != NULL)
659 {
660 /* Reference string must not contain path-separator symbols */
661 for (i = 0; i < ReferenceString->Length / sizeof(WCHAR); i++)
662 {
663 if ((ReferenceString->Buffer[i] == '\\') ||
664 (ReferenceString->Buffer[i] == '/'))
665 return STATUS_INVALID_DEVICE_REQUEST;
666 }
667 }
668
669 Status = RtlStringFromGUID(InterfaceClassGuid, &GuidString);
670 if (!NT_SUCCESS(Status))
671 {
672 DPRINT("RtlStringFromGUID() failed with status 0x%08lx\n", Status);
673 return Status;
674 }
675
676 /* Create Pdo name: \Device\xxxxxxxx (unnamed device) */
677 Status = ObQueryNameString(
678 PhysicalDeviceObject,
679 PdoNameInfo,
680 sizeof(PdoNameInfoBuffer),
681 &i);
682 if (!NT_SUCCESS(Status))
683 {
684 DPRINT("ObQueryNameString() failed with status 0x%08lx\n", Status);
685 return Status;
686 }
687 ASSERT(PdoNameInfo->Name.Length);
688
689 /* Create base key name for this interface: HKLM\SYSTEM\CurrentControlSet\Control\DeviceClasses\{GUID} */
690 ASSERT(((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode);
691 InstancePath = &((PEXTENDED_DEVOBJ_EXTENSION)PhysicalDeviceObject->DeviceObjectExtension)->DeviceNode->InstancePath;
692 BaseKeyName.Length = wcslen(BaseKeyString) * sizeof(WCHAR);
693 BaseKeyName.MaximumLength = BaseKeyName.Length
694 + GuidString.Length;
695 BaseKeyName.Buffer = ExAllocatePool(
696 PagedPool,
697 BaseKeyName.MaximumLength);
698 if (!BaseKeyName.Buffer)
699 {
700 DPRINT("ExAllocatePool() failed\n");
701 return STATUS_INSUFFICIENT_RESOURCES;
702 }
703 wcscpy(BaseKeyName.Buffer, BaseKeyString);
704 RtlAppendUnicodeStringToString(&BaseKeyName, &GuidString);
705
706 /* Create BaseKeyName key in registry */
707 InitializeObjectAttributes(
708 &ObjectAttributes,
709 &BaseKeyName,
710 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE | OBJ_OPENIF,
711 NULL, /* RootDirectory */
712 NULL); /* SecurityDescriptor */
713
714 Status = ZwCreateKey(
715 &ClassKey,
716 KEY_WRITE,
717 &ObjectAttributes,
718 0, /* TileIndex */
719 NULL, /* Class */
720 REG_OPTION_VOLATILE,
721 NULL); /* Disposition */
722
723 if (!NT_SUCCESS(Status))
724 {
725 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
726 ExFreePool(BaseKeyName.Buffer);
727 return Status;
728 }
729
730 /* Create key name for this interface: ##?#ACPI#PNP0501#1#{GUID} */
731 InterfaceKeyName.Length = 0;
732 InterfaceKeyName.MaximumLength =
733 4 * sizeof(WCHAR) + /* 4 = size of ##?# */
734 InstancePath->Length +
735 sizeof(WCHAR) + /* 1 = size of # */
736 GuidString.Length;
737 InterfaceKeyName.Buffer = ExAllocatePool(
738 PagedPool,
739 InterfaceKeyName.MaximumLength);
740 if (!InterfaceKeyName.Buffer)
741 {
742 DPRINT("ExAllocatePool() failed\n");
743 return STATUS_INSUFFICIENT_RESOURCES;
744 }
745
746 RtlAppendUnicodeToString(&InterfaceKeyName, L"##?#");
747 StartIndex = InterfaceKeyName.Length / sizeof(WCHAR);
748 RtlAppendUnicodeStringToString(&InterfaceKeyName, InstancePath);
749 for (i = 0; i < InstancePath->Length / sizeof(WCHAR); i++)
750 {
751 if (InterfaceKeyName.Buffer[StartIndex + i] == '\\')
752 InterfaceKeyName.Buffer[StartIndex + i] = '#';
753 }
754 RtlAppendUnicodeToString(&InterfaceKeyName, L"#");
755 RtlAppendUnicodeStringToString(&InterfaceKeyName, &GuidString);
756
757 /* Create the interface key in registry */
758 InitializeObjectAttributes(
759 &ObjectAttributes,
760 &InterfaceKeyName,
761 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE | OBJ_OPENIF,
762 ClassKey,
763 NULL); /* SecurityDescriptor */
764
765 Status = ZwCreateKey(
766 &InterfaceKey,
767 KEY_WRITE,
768 &ObjectAttributes,
769 0, /* TileIndex */
770 NULL, /* Class */
771 REG_OPTION_VOLATILE,
772 NULL); /* Disposition */
773
774 if (!NT_SUCCESS(Status))
775 {
776 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
777 ZwClose(ClassKey);
778 ExFreePool(BaseKeyName.Buffer);
779 return Status;
780 }
781
782 /* Write DeviceInstance entry. Value is InstancePath */
783 Status = ZwSetValueKey(
784 InterfaceKey,
785 &DeviceInstance,
786 0, /* TileIndex */
787 REG_SZ,
788 InstancePath->Buffer,
789 InstancePath->Length);
790 if (!NT_SUCCESS(Status))
791 {
792 DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status);
793 ZwClose(InterfaceKey);
794 ZwClose(ClassKey);
795 ExFreePool(InterfaceKeyName.Buffer);
796 ExFreePool(BaseKeyName.Buffer);
797 return Status;
798 }
799
800 /* Create subkey. Name is #ReferenceString */
801 SubKeyName.Length = 0;
802 SubKeyName.MaximumLength = sizeof(WCHAR);
803 if (ReferenceString && ReferenceString->Length)
804 SubKeyName.MaximumLength += ReferenceString->Length;
805 SubKeyName.Buffer = ExAllocatePool(
806 PagedPool,
807 SubKeyName.MaximumLength);
808 if (!SubKeyName.Buffer)
809 {
810 DPRINT("ExAllocatePool() failed\n");
811 ZwClose(InterfaceKey);
812 ZwClose(ClassKey);
813 ExFreePool(InterfaceKeyName.Buffer);
814 ExFreePool(BaseKeyName.Buffer);
815 return STATUS_INSUFFICIENT_RESOURCES;
816 }
817 RtlAppendUnicodeToString(&SubKeyName, L"#");
818 if (ReferenceString && ReferenceString->Length)
819 RtlAppendUnicodeStringToString(&SubKeyName, ReferenceString);
820
821 /* Create SubKeyName key in registry */
822 InitializeObjectAttributes(
823 &ObjectAttributes,
824 &SubKeyName,
825 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
826 InterfaceKey, /* RootDirectory */
827 NULL); /* SecurityDescriptor */
828
829 Status = ZwCreateKey(
830 &SubKey,
831 KEY_WRITE,
832 &ObjectAttributes,
833 0, /* TileIndex */
834 NULL, /* Class */
835 REG_OPTION_VOLATILE,
836 NULL); /* Disposition */
837
838 if (!NT_SUCCESS(Status))
839 {
840 DPRINT("ZwCreateKey() failed with status 0x%08lx\n", Status);
841 ZwClose(InterfaceKey);
842 ZwClose(ClassKey);
843 ExFreePool(InterfaceKeyName.Buffer);
844 ExFreePool(BaseKeyName.Buffer);
845 return Status;
846 }
847
848 /* Create symbolic link name: \??\ACPI#PNP0501#1#{GUID}\ReferenceString */
849 SymbolicLinkName->Length = 0;
850 SymbolicLinkName->MaximumLength = SymbolicLinkName->Length
851 + 4 * sizeof(WCHAR) /* 4 = size of \??\ */
852 + InstancePath->Length
853 + sizeof(WCHAR) /* 1 = size of # */
854 + GuidString.Length
855 + sizeof(WCHAR); /* final NULL */
856 if (ReferenceString && ReferenceString->Length)
857 SymbolicLinkName->MaximumLength += sizeof(WCHAR) + ReferenceString->Length;
858 SymbolicLinkName->Buffer = ExAllocatePool(
859 PagedPool,
860 SymbolicLinkName->MaximumLength);
861 if (!SymbolicLinkName->Buffer)
862 {
863 DPRINT("ExAllocatePool() failed\n");
864 ZwClose(SubKey);
865 ZwClose(InterfaceKey);
866 ZwClose(ClassKey);
867 ExFreePool(InterfaceKeyName.Buffer);
868 ExFreePool(SubKeyName.Buffer);
869 ExFreePool(BaseKeyName.Buffer);
870 return STATUS_INSUFFICIENT_RESOURCES;
871 }
872 RtlAppendUnicodeToString(SymbolicLinkName, L"\\??\\");
873 StartIndex = SymbolicLinkName->Length / sizeof(WCHAR);
874 RtlAppendUnicodeStringToString(SymbolicLinkName, InstancePath);
875 for (i = 0; i < InstancePath->Length / sizeof(WCHAR); i++)
876 {
877 if (SymbolicLinkName->Buffer[StartIndex + i] == '\\')
878 SymbolicLinkName->Buffer[StartIndex + i] = '#';
879 }
880 RtlAppendUnicodeToString(SymbolicLinkName, L"#");
881 RtlAppendUnicodeStringToString(SymbolicLinkName, &GuidString);
882 if (ReferenceString && ReferenceString->Length)
883 {
884 RtlAppendUnicodeToString(SymbolicLinkName, L"\\");
885 RtlAppendUnicodeStringToString(SymbolicLinkName, ReferenceString);
886 }
887 SymbolicLinkName->Buffer[SymbolicLinkName->Length/sizeof(WCHAR)] = L'\0';
888
889 /* Create symbolic link */
890 DPRINT("IoRegisterDeviceInterface(): creating symbolic link %wZ -> %wZ\n", SymbolicLinkName, &PdoNameInfo->Name);
891 Status = IoCreateSymbolicLink(SymbolicLinkName, &PdoNameInfo->Name);
892 if (!NT_SUCCESS(Status))
893 {
894 DPRINT("IoCreateSymbolicLink() failed with status 0x%08lx\n", Status);
895 ZwClose(SubKey);
896 ZwClose(InterfaceKey);
897 ZwClose(ClassKey);
898 ExFreePool(SubKeyName.Buffer);
899 ExFreePool(InterfaceKeyName.Buffer);
900 ExFreePool(BaseKeyName.Buffer);
901 ExFreePool(SymbolicLinkName->Buffer);
902 return Status;
903 }
904
905 /* Write symbolic link name in registry */
906 SymbolicLinkName->Buffer[1] = '\\';
907 Status = ZwSetValueKey(
908 SubKey,
909 &SymbolicLink,
910 0, /* TileIndex */
911 REG_SZ,
912 SymbolicLinkName->Buffer,
913 SymbolicLinkName->Length);
914 if (!NT_SUCCESS(Status))
915 {
916 DPRINT("ZwSetValueKey() failed with status 0x%08lx\n", Status);
917 ExFreePool(SymbolicLinkName->Buffer);
918 }
919 SymbolicLinkName->Buffer[1] = '?';
920
921 ZwClose(SubKey);
922 ZwClose(InterfaceKey);
923 ZwClose(ClassKey);
924 ExFreePool(SubKeyName.Buffer);
925 ExFreePool(InterfaceKeyName.Buffer);
926 ExFreePool(BaseKeyName.Buffer);
927
928 return Status;
929 }
930
931 /*++
932 * @name IoSetDeviceInterfaceState
933 * @implemented
934 *
935 * Enables or disables an instance of a previously registered device
936 * interface class.
937 * Documented in WDK.
938 *
939 * @param SymbolicLinkName
940 * Pointer to the string identifying instance to enable or disable
941 *
942 * @param Enable
943 * TRUE = enable, FALSE = disable
944 *
945 * @return Usual NTSTATUS
946 *
947 * @remarks Must be called at IRQL = PASSIVE_LEVEL in the context of a
948 * system thread
949 *
950 *--*/
951 NTSTATUS
952 NTAPI
953 IoSetDeviceInterfaceState(IN PUNICODE_STRING SymbolicLinkName,
954 IN BOOLEAN Enable)
955 {
956 PDEVICE_OBJECT PhysicalDeviceObject;
957 PFILE_OBJECT FileObject;
958 UNICODE_STRING GuidString;
959 PWCHAR StartPosition;
960 PWCHAR EndPosition;
961 NTSTATUS Status;
962 LPCGUID EventGuid;
963
964 if (SymbolicLinkName == NULL)
965 return STATUS_INVALID_PARAMETER_1;
966
967 DPRINT("IoSetDeviceInterfaceState('%wZ', %d)\n", SymbolicLinkName, Enable);
968
969 /* Symbolic link name is \??\ACPI#PNP0501#1#{GUID}\ReferenceString */
970 /* Get GUID from SymbolicLinkName */
971 StartPosition = wcschr(SymbolicLinkName->Buffer, L'{');
972 EndPosition = wcschr(SymbolicLinkName->Buffer, L'}');
973 if (!StartPosition ||!EndPosition || StartPosition > EndPosition)
974 {
975 DPRINT("IoSetDeviceInterfaceState() returning STATUS_INVALID_PARAMETER_1\n");
976 return STATUS_INVALID_PARAMETER_1;
977 }
978 GuidString.Buffer = StartPosition;
979 GuidString.MaximumLength = GuidString.Length = (ULONG_PTR)(EndPosition + 1) - (ULONG_PTR)StartPosition;
980
981 /* Get pointer to the PDO */
982 Status = IoGetDeviceObjectPointer(
983 SymbolicLinkName,
984 0, /* DesiredAccess */
985 &FileObject,
986 &PhysicalDeviceObject);
987 if (!NT_SUCCESS(Status))
988 {
989 DPRINT("IoGetDeviceObjectPointer() failed with status 0x%08lx\n", Status);
990 return Status;
991 }
992
993 EventGuid = Enable ? &GUID_DEVICE_INTERFACE_ARRIVAL : &GUID_DEVICE_INTERFACE_REMOVAL;
994 IopNotifyPlugPlayNotification(
995 PhysicalDeviceObject,
996 EventCategoryDeviceInterfaceChange,
997 EventGuid,
998 &GuidString,
999 (PVOID)SymbolicLinkName);
1000
1001 ObDereferenceObject(FileObject);
1002
1003 return STATUS_SUCCESS;
1004 }
1005
1006 /* EOF */