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