752c5ac79237935dd60afb5ee7886e2c4089b00c
[reactos.git] / reactos / ntoskrnl / io / device.c
1 /* $Id: device.c,v 1.48 2002/09/08 10:23:24 chorns Exp $
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/io/device.c
6 * PURPOSE: Manage devices
7 * PROGRAMMER: David Welch (welch@cwcom.net)
8 * UPDATE HISTORY:
9 * 15/05/98: Created
10 */
11
12 /* INCLUDES ****************************************************************/
13
14 #include <ddk/ntddk.h>
15 #include <internal/io.h>
16 #include <internal/po.h>
17 #include <internal/ldr.h>
18 #include <internal/id.h>
19 #include <internal/pool.h>
20 #include <internal/registry.h>
21
22 #include <roscfg.h>
23
24 #define NDEBUG
25 #include <internal/debug.h>
26
27 /* GLOBALS *******************************************************************/
28
29 #define TAG_DRIVER TAG('D', 'R', 'V', 'R')
30 #define TAG_DRIVER_EXTENSION TAG('D', 'R', 'V', 'E')
31 #define TAG_DEVICE_EXTENSION TAG('D', 'E', 'X', 'T')
32
33 #define DRIVER_REGISTRY_KEY_BASENAME L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"
34
35 /* FUNCTIONS ***************************************************************/
36
37 NTSTATUS STDCALL
38 IoAttachDeviceByPointer(IN PDEVICE_OBJECT SourceDevice,
39 IN PDEVICE_OBJECT TargetDevice)
40 {
41 PDEVICE_OBJECT AttachedDevice;
42
43 DPRINT("IoAttachDeviceByPointer(SourceDevice %x, TargetDevice %x)\n",
44 SourceDevice,
45 TargetDevice);
46
47 AttachedDevice = IoAttachDeviceToDeviceStack (SourceDevice,
48 TargetDevice);
49 if (AttachedDevice == NULL)
50 return STATUS_NO_SUCH_DEVICE;
51
52 return STATUS_SUCCESS;
53 }
54
55
56 VOID STDCALL
57 IoDeleteDevice(PDEVICE_OBJECT DeviceObject)
58 {
59 PDEVICE_OBJECT Previous;
60
61 if (DeviceObject->Flags & DO_SHUTDOWN_REGISTERED)
62 IoUnregisterShutdownNotification(DeviceObject);
63
64 /* remove the timer if it exists */
65 if (DeviceObject->Timer)
66 {
67 IoStopTimer(DeviceObject);
68 ExFreePool(DeviceObject->Timer);
69 }
70
71 /* free device extension */
72 if (DeviceObject->DeviceObjectExtension)
73 ExFreePool (DeviceObject->DeviceObjectExtension);
74
75 /* remove device from driver device list */
76 Previous = DeviceObject->DriverObject->DeviceObject;
77 if (Previous == DeviceObject)
78 {
79 DeviceObject->DriverObject->DeviceObject = DeviceObject->NextDevice;
80 }
81 else
82 {
83 while (Previous->NextDevice != DeviceObject)
84 Previous = Previous->NextDevice;
85 Previous->NextDevice = DeviceObject->NextDevice;
86 }
87
88 ObDereferenceObject (DeviceObject);
89 }
90
91
92 PDEVICE_OBJECT
93 STDCALL
94 IoGetRelatedDeviceObject (
95 IN PFILE_OBJECT FileObject
96 )
97 {
98 return (FileObject->DeviceObject);
99 }
100
101
102 NTSTATUS
103 STDCALL
104 IoGetDeviceObjectPointer (
105 IN PUNICODE_STRING ObjectName,
106 IN ACCESS_MASK DesiredAccess,
107 OUT PFILE_OBJECT * FileObject,
108 OUT PDEVICE_OBJECT * DeviceObject)
109 {
110 OBJECT_ATTRIBUTES ObjectAttributes;
111 IO_STATUS_BLOCK StatusBlock;
112 PFILE_OBJECT LocalFileObject;
113 HANDLE FileHandle;
114 NTSTATUS Status;
115
116 DPRINT("IoGetDeviceObjectPointer(ObjectName %wZ, DesiredAccess %x, FileObject %p DeviceObject %p)\n",
117 ObjectName,
118 DesiredAccess,
119 FileObject,
120 DeviceObject);
121
122 InitializeObjectAttributes (&ObjectAttributes,
123 ObjectName,
124 0,
125 NULL,
126 NULL);
127
128 Status = NtOpenFile (&FileHandle,
129 DesiredAccess,
130 &ObjectAttributes,
131 &StatusBlock,
132 0,
133 FILE_NON_DIRECTORY_FILE);
134 if (!NT_SUCCESS(Status))
135 return Status;
136
137 Status = ObReferenceObjectByHandle (FileHandle,
138 0,
139 IoFileObjectType,
140 KernelMode,
141 (PVOID*)&LocalFileObject,
142 NULL);
143 if (NT_SUCCESS(Status))
144 {
145 *DeviceObject = IoGetRelatedDeviceObject (LocalFileObject);
146 *FileObject = LocalFileObject;
147 }
148 NtClose (FileHandle);
149
150 return Status;
151 }
152
153
154 VOID
155 STDCALL
156 IoDetachDevice(PDEVICE_OBJECT TargetDevice)
157 {
158 UNIMPLEMENTED;
159 }
160
161
162 PDEVICE_OBJECT
163 STDCALL
164 IoGetAttachedDevice(PDEVICE_OBJECT DeviceObject)
165 {
166 PDEVICE_OBJECT Current = DeviceObject;
167
168 // DPRINT("IoGetAttachedDevice(DeviceObject %x)\n",DeviceObject);
169
170 while (Current->AttachedDevice!=NULL)
171 {
172 Current = Current->AttachedDevice;
173 // DPRINT("Current %x\n",Current);
174 }
175
176 // DPRINT("IoGetAttachedDevice() = %x\n",DeviceObject);
177 return(Current);
178 }
179
180 PDEVICE_OBJECT
181 STDCALL
182 IoGetAttachedDeviceReference(PDEVICE_OBJECT DeviceObject)
183 {
184 PDEVICE_OBJECT Current = DeviceObject;
185
186 while (Current->AttachedDevice!=NULL)
187 {
188 Current = Current->AttachedDevice;
189 }
190
191 ObReferenceObject(Current);
192 return(Current);
193 }
194
195 PDEVICE_OBJECT STDCALL
196 IoAttachDeviceToDeviceStack(PDEVICE_OBJECT SourceDevice,
197 PDEVICE_OBJECT TargetDevice)
198 {
199 PDEVICE_OBJECT AttachedDevice;
200
201 DPRINT("IoAttachDeviceToDeviceStack(SourceDevice %x, TargetDevice %x)\n",
202 SourceDevice,TargetDevice);
203
204 AttachedDevice = IoGetAttachedDevice(TargetDevice);
205 AttachedDevice->AttachedDevice = SourceDevice;
206 SourceDevice->AttachedDevice = NULL;
207 SourceDevice->StackSize = AttachedDevice->StackSize + 1;
208 SourceDevice->Vpb = AttachedDevice->Vpb;
209 return(AttachedDevice);
210 }
211
212 VOID STDCALL
213 IoRegisterDriverReinitialization(PDRIVER_OBJECT DriverObject,
214 PDRIVER_REINITIALIZE ReinitRoutine,
215 PVOID Context)
216 {
217 UNIMPLEMENTED;
218 }
219
220 NTSTATUS STDCALL
221 IopDefaultDispatchFunction(PDEVICE_OBJECT DeviceObject,
222 PIRP Irp)
223 {
224 Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED;
225 Irp->IoStatus.Information = 0;
226
227 IoCompleteRequest(Irp, IO_NO_INCREMENT);
228 return(STATUS_NOT_IMPLEMENTED);
229 }
230
231
232 NTSTATUS
233 IopCreateDriverObject(PDRIVER_OBJECT *DriverObject,
234 PUNICODE_STRING ServiceName,
235 BOOLEAN FileSystem)
236 {
237 PDRIVER_OBJECT Object;
238 HANDLE DriverHandle = 0;
239 ULONG i;
240 WCHAR NameBuffer[MAX_PATH];
241 UNICODE_STRING DriverName;
242 OBJECT_ATTRIBUTES ObjectAttributes;
243 NTSTATUS Status;
244
245 DPRINT("IopCreateDriverObject(%p '%wZ' %x)\n", DriverObject, ServiceName, FileSystem);
246
247 *DriverObject = NULL;
248
249 /* Create ModuleName string */
250 if (ServiceName != NULL)
251 {
252 if (FileSystem == TRUE)
253 wcscpy(NameBuffer, FILESYSTEM_ROOT_NAME);
254 else
255 wcscpy(NameBuffer, DRIVER_ROOT_NAME);
256 wcscat(NameBuffer, ServiceName->Buffer);
257
258 RtlInitUnicodeString(&DriverName,
259 NameBuffer);
260 DPRINT("Driver name: '%wZ'\n", &DriverName);
261 }
262
263 /* Initialize ObjectAttributes for ModuleObject */
264 InitializeObjectAttributes(&ObjectAttributes,
265 (ServiceName != NULL)? &DriverName : NULL,
266 OBJ_PERMANENT,
267 NULL,
268 NULL);
269
270 /* Create module object */
271 Status = ObCreateObject(&DriverHandle,
272 STANDARD_RIGHTS_REQUIRED,
273 &ObjectAttributes,
274 IoDriverObjectType,
275 (PVOID*)&Object);
276 if (!NT_SUCCESS(Status))
277 {
278 return(Status);
279 }
280
281 NtClose(DriverHandle);
282
283 /* Create driver extension */
284 Object->DriverExtension = (PDRIVER_EXTENSION)
285 ExAllocatePoolWithTag(NonPagedPool,
286 sizeof(DRIVER_EXTENSION),
287 TAG_DRIVER_EXTENSION);
288 if (Object->DriverExtension == NULL)
289 {
290 ExFreePool(Object);
291 return(STATUS_INSUFFICIENT_RESOURCES);
292 }
293
294 RtlZeroMemory(Object->DriverExtension, sizeof(DRIVER_EXTENSION));
295
296 Object->Type = InternalDriverType;
297
298 for (i=0; i<=IRP_MJ_MAXIMUM_FUNCTION; i++)
299 {
300 Object->MajorFunction[i] = (PDRIVER_DISPATCH) IopDefaultDispatchFunction;
301 }
302
303 *DriverObject = Object;
304
305 return STATUS_SUCCESS;
306 }
307
308 NTSTATUS
309 IopAttachFilterDrivers(PDEVICE_NODE DeviceNode,
310 BOOLEAN Lower)
311 {
312 return STATUS_SUCCESS;
313 }
314
315 NTSTATUS
316 IopInitializeDevice(PDEVICE_NODE DeviceNode,
317 BOOLEAN BootDriversOnly)
318 {
319 IO_STATUS_BLOCK IoStatusBlock;
320 PDRIVER_OBJECT DriverObject;
321 IO_STACK_LOCATION Stack;
322 PDEVICE_OBJECT Fdo;
323 NTSTATUS Status;
324
325 DriverObject = DeviceNode->DriverObject;
326
327 if (DriverObject->DriverExtension->AddDevice)
328 {
329 /* This is a Plug and Play driver */
330 DPRINT("Plug and Play driver found\n");
331
332 assert(DeviceNode->Pdo);
333
334 DPRINT("Calling driver AddDevice entrypoint at %08lx\n",
335 DriverObject->DriverExtension->AddDevice);
336 Status = DriverObject->DriverExtension->AddDevice(
337 DriverObject, DeviceNode->Pdo);
338 if (!NT_SUCCESS(Status))
339 {
340 return(Status);
341 }
342
343 IopDeviceNodeSetFlag(DeviceNode, DNF_ADDED);
344
345 DPRINT("Sending IRP_MN_START_DEVICE to driver\n");
346
347 Fdo = IoGetAttachedDeviceReference(DeviceNode->Pdo);
348
349 if (Fdo == DeviceNode->Pdo)
350 {
351 /* FIXME: What do we do? Unload the driver or just disable the device? */
352 DbgPrint("An FDO was not attached\n");
353 KeBugCheck(0);
354 }
355
356 /* FIXME: Put some resources in the IRP for the device */
357 Stack.Parameters.StartDevice.AllocatedResources = NULL;
358 Stack.Parameters.StartDevice.AllocatedResourcesTranslated = NULL;
359
360 Status = IopInitiatePnpIrp(
361 Fdo,
362 &IoStatusBlock,
363 IRP_MN_START_DEVICE,
364 &Stack);
365 if (!NT_SUCCESS(Status))
366 {
367 DPRINT("IopInitiatePnpIrp() failed\n");
368 ObDereferenceObject(Fdo);
369 return(Status);
370 }
371
372 if (Fdo->DeviceType == FILE_DEVICE_BUS_EXTENDER)
373 {
374 DPRINT("Bus extender found\n");
375
376 Status = IopInterrogateBusExtender(
377 DeviceNode, Fdo, BootDriversOnly);
378 if (!NT_SUCCESS(Status))
379 {
380 ObDereferenceObject(Fdo);
381 return(Status);
382 }
383 }
384 else if (Fdo->DeviceType == FILE_DEVICE_ACPI)
385 {
386 #ifdef ACPI
387 static BOOLEAN SystemPowerDeviceNodeCreated = FALSE;
388
389 /* There can be only one system power device */
390 if (!SystemPowerDeviceNodeCreated)
391 {
392 PopSystemPowerDeviceNode = DeviceNode;
393 SystemPowerDeviceNodeCreated = TRUE;
394 }
395 #endif /* ACPI */
396 }
397 ObDereferenceObject(Fdo);
398 }
399
400 return STATUS_SUCCESS;
401 }
402
403 NTSTATUS
404 IopInitializeService(
405 PDEVICE_NODE DeviceNode,
406 PUNICODE_STRING ImagePath)
407 {
408 PMODULE_OBJECT ModuleObject;
409 NTSTATUS Status;
410
411 ModuleObject = LdrGetModuleObject(&DeviceNode->ServiceName);
412 if (ModuleObject == NULL)
413 {
414 /* The module is currently not loaded, so load it now */
415
416 Status = LdrLoadModule(ImagePath, &ModuleObject);
417 if (!NT_SUCCESS(Status))
418 {
419 /* FIXME: Log the error */
420 CPRINT("Driver load failed\n");
421 return(Status);
422 }
423
424 Status = IopInitializeDriver(ModuleObject->EntryPoint, DeviceNode, FALSE);
425 if (!NT_SUCCESS(Status))
426 {
427 LdrUnloadModule(ModuleObject);
428
429 /* FIXME: Log the error */
430 CPRINT("A driver failed to initialize\n");
431 return(Status);
432 }
433 }
434
435 Status = IopInitializeDevice(DeviceNode, TRUE);
436
437 return(Status);
438 }
439
440 NTSTATUS
441 IopInitializeDeviceNodeService(PDEVICE_NODE DeviceNode)
442 {
443 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
444 UNICODE_STRING ImagePath;
445 HANDLE KeyHandle;
446 NTSTATUS Status;
447
448 Status = RtlpGetRegistryHandle(
449 RTL_REGISTRY_SERVICES,
450 DeviceNode->ServiceName.Buffer,
451 TRUE,
452 &KeyHandle);
453 if (!NT_SUCCESS(Status))
454 {
455 DPRINT("RtlpGetRegistryHandle() failed (Status %x)\n", Status);
456 return(Status);
457 }
458
459 RtlZeroMemory(QueryTable, sizeof(QueryTable));
460
461 RtlInitUnicodeString(&ImagePath, NULL);
462
463 QueryTable[0].Name = L"ImagePath";
464 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
465 QueryTable[0].EntryContext = &ImagePath;
466
467 Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
468 (PWSTR)KeyHandle,
469 QueryTable,
470 NULL,
471 NULL);
472 NtClose(KeyHandle);
473
474 DPRINT("RtlQueryRegistryValues() returned status %x\n", Status);
475
476 if (NT_SUCCESS(Status))
477 {
478 DPRINT("Got ImagePath %S\n", ImagePath.Buffer);
479
480 Status = IopInitializeService(DeviceNode, &ImagePath);
481
482 RtlFreeUnicodeString(&ImagePath);
483 }
484
485 return(Status);
486 }
487
488 NTSTATUS
489 IopInitializeDriver(PDRIVER_INITIALIZE DriverEntry,
490 PDEVICE_NODE DeviceNode,
491 BOOLEAN FileSystemDriver)
492 /*
493 * FUNCTION: Called to initalize a loaded driver
494 * ARGUMENTS:
495 * DriverEntry = Pointer to driver entry routine
496 * DeviceNode = Pointer to device node
497 */
498 {
499 WCHAR RegistryKeyBuffer[MAX_PATH];
500 PDRIVER_OBJECT DriverObject;
501 UNICODE_STRING RegistryKey;
502 NTSTATUS Status;
503
504 DPRINT("IopInitializeDriver(DriverEntry %08lx, DeviceNode %08lx)\n",
505 DriverEntry, DeviceNode);
506
507 Status = IopCreateDriverObject(&DriverObject,
508 &DeviceNode->ServiceName,
509 FileSystemDriver);
510 if (!NT_SUCCESS(Status))
511 {
512 return(Status);
513 }
514
515 DeviceNode->DriverObject = DriverObject;
516
517 if (DeviceNode->ServiceName.Buffer)
518 {
519 wcscpy(RegistryKeyBuffer, DRIVER_REGISTRY_KEY_BASENAME);
520 wcscat(RegistryKeyBuffer, DeviceNode->ServiceName.Buffer);
521 RtlInitUnicodeString(&RegistryKey, RegistryKeyBuffer);
522 }
523 else
524 {
525 RtlInitUnicodeString(&RegistryKey, NULL);
526 }
527
528 DPRINT("RegistryKey: %wZ\n", &RegistryKey);
529 DPRINT("Calling driver entrypoint at %08lx\n", DriverEntry);
530
531 Status = DriverEntry(DriverObject, &RegistryKey);
532 if (!NT_SUCCESS(Status))
533 {
534 DeviceNode->DriverObject = NULL;
535 ExFreePool(DriverObject->DriverExtension);
536 ObMakeTemporaryObject(DriverObject);
537 ObDereferenceObject(DriverObject);
538 return(Status);
539 }
540
541 Status = IopInitializeDevice(DeviceNode, TRUE);
542
543 return(Status);
544 }
545
546
547 NTSTATUS STDCALL
548 IoAttachDevice(PDEVICE_OBJECT SourceDevice,
549 PUNICODE_STRING TargetDevice,
550 PDEVICE_OBJECT* AttachedDevice)
551 /*
552 * FUNCTION: Layers a device over the highest device in a device stack
553 * ARGUMENTS:
554 * SourceDevice = Device to attached
555 * TargetDevice = Name of the target device
556 * AttachedDevice (OUT) = Caller storage for the device attached to
557 */
558 {
559 UNIMPLEMENTED;
560 }
561
562
563 NTSTATUS STDCALL
564 IopCreateDevice(PVOID ObjectBody,
565 PVOID Parent,
566 PWSTR RemainingPath,
567 POBJECT_ATTRIBUTES ObjectAttributes)
568 {
569
570 DPRINT("IopCreateDevice(ObjectBody %x, Parent %x, RemainingPath %S)\n",
571 ObjectBody, Parent, RemainingPath);
572
573 if (RemainingPath != NULL && wcschr(RemainingPath+1, '\\') != NULL)
574 {
575 return(STATUS_UNSUCCESSFUL);
576 }
577
578 return(STATUS_SUCCESS);
579 }
580
581
582 NTSTATUS STDCALL
583 IoCreateDevice(PDRIVER_OBJECT DriverObject,
584 ULONG DeviceExtensionSize,
585 PUNICODE_STRING DeviceName,
586 DEVICE_TYPE DeviceType,
587 ULONG DeviceCharacteristics,
588 BOOLEAN Exclusive,
589 PDEVICE_OBJECT* DeviceObject)
590 /*
591 * FUNCTION: Allocates memory for and intializes a device object for use for
592 * a driver
593 * ARGUMENTS:
594 * DriverObject : Driver object passed by iomgr when the driver was
595 * loaded
596 * DeviceExtensionSize : Number of bytes for the device extension
597 * DeviceName : Unicode name of device
598 * DeviceType : Device type
599 * DeviceCharacteristics : Bit mask of device characteristics
600 * Exclusive : True if only one thread can access the device at a
601 * time
602 * RETURNS:
603 * Success or failure
604 * DeviceObject : Contains a pointer to allocated device object
605 * if the call succeeded
606 * NOTES: See the DDK documentation for more information
607 */
608 {
609 PDEVICE_OBJECT CreatedDeviceObject;
610 OBJECT_ATTRIBUTES ObjectAttributes;
611 HANDLE DeviceHandle;
612 NTSTATUS Status;
613
614 assert_irql(PASSIVE_LEVEL);
615
616 if (DeviceName != NULL)
617 {
618 DPRINT("IoCreateDevice(DriverObject %x, DeviceName %S)\n",DriverObject,
619 DeviceName->Buffer);
620 }
621 else
622 {
623 DPRINT("IoCreateDevice(DriverObject %x)\n",DriverObject);
624 }
625
626 if (DeviceName != NULL)
627 {
628 InitializeObjectAttributes(&ObjectAttributes,DeviceName,0,NULL,NULL);
629 Status = ObCreateObject(&DeviceHandle,
630 0,
631 &ObjectAttributes,
632 IoDeviceObjectType,
633 (PVOID*)&CreatedDeviceObject);
634 }
635 else
636 {
637 Status = ObCreateObject(&DeviceHandle,
638 0,
639 NULL,
640 IoDeviceObjectType,
641 (PVOID*)&CreatedDeviceObject);
642 }
643
644 *DeviceObject = NULL;
645
646 if (!NT_SUCCESS(Status))
647 {
648 return(Status);
649 }
650
651 if (DriverObject->DeviceObject == NULL)
652 {
653 DriverObject->DeviceObject = CreatedDeviceObject;
654 CreatedDeviceObject->NextDevice = NULL;
655 }
656 else
657 {
658 CreatedDeviceObject->NextDevice = DriverObject->DeviceObject;
659 DriverObject->DeviceObject = CreatedDeviceObject;
660 }
661
662 CreatedDeviceObject->Type = DeviceType;
663 CreatedDeviceObject->DriverObject = DriverObject;
664 CreatedDeviceObject->CurrentIrp = NULL;
665 CreatedDeviceObject->Flags = 0;
666
667 CreatedDeviceObject->DeviceExtension =
668 ExAllocatePoolWithTag(NonPagedPool, DeviceExtensionSize,
669 TAG_DEVICE_EXTENSION);
670 if (DeviceExtensionSize > 0 && CreatedDeviceObject->DeviceExtension == NULL)
671 {
672 ExFreePool(CreatedDeviceObject);
673 return(STATUS_INSUFFICIENT_RESOURCES);
674 }
675
676 if (DeviceExtensionSize > 0)
677 {
678 RtlZeroMemory(CreatedDeviceObject->DeviceExtension,
679 DeviceExtensionSize);
680 }
681
682 CreatedDeviceObject->AttachedDevice = NULL;
683 CreatedDeviceObject->DeviceType = DeviceType;
684 CreatedDeviceObject->StackSize = 1;
685 CreatedDeviceObject->AlignmentRequirement = 1;
686 KeInitializeDeviceQueue(&CreatedDeviceObject->DeviceQueue);
687
688 KeInitializeEvent(&CreatedDeviceObject->DeviceLock,
689 SynchronizationEvent,
690 TRUE);
691
692 /* FIXME: Do we need to add network drives too?! */
693 if (CreatedDeviceObject->DeviceType == FILE_DEVICE_DISK ||
694 CreatedDeviceObject->DeviceType == FILE_DEVICE_CD_ROM ||
695 CreatedDeviceObject->DeviceType == FILE_DEVICE_TAPE)
696 {
697 IoAttachVpb(CreatedDeviceObject);
698 }
699
700 *DeviceObject = CreatedDeviceObject;
701
702 return(STATUS_SUCCESS);
703 }
704
705
706 NTSTATUS
707 STDCALL
708 IoOpenDeviceInstanceKey (
709 DWORD Unknown0,
710 DWORD Unknown1,
711 DWORD Unknown2,
712 DWORD Unknown3,
713 DWORD Unknown4
714 )
715 {
716 UNIMPLEMENTED;
717 return(STATUS_NOT_IMPLEMENTED);
718 }
719
720
721 DWORD
722 STDCALL
723 IoQueryDeviceEnumInfo (
724 DWORD Unknown0,
725 DWORD Unknown1
726 )
727 {
728 UNIMPLEMENTED;
729 return 0;
730 }
731
732
733 /* EOF */