xref: /reactos/drivers/parallel/parport/fdo.c (revision 84344399)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         Parallel Port Function Driver
4  * FILE:            drivers/parallel/parport/fdo.c
5  * PURPOSE:         FDO functions
6  */
7 
8 #include "parport.h"
9 
10 /* FUNCTIONS ****************************************************************/
11 
12 NTSTATUS
13 NTAPI
14 AddDeviceInternal(IN PDRIVER_OBJECT DriverObject,
15                   IN PDEVICE_OBJECT Pdo,
16                   IN PULONG pLptPortNumber OPTIONAL,
17                   OUT PDEVICE_OBJECT* pFdo OPTIONAL)
18 {
19     PFDO_DEVICE_EXTENSION DeviceExtension = NULL;
20     PDEVICE_OBJECT Fdo = NULL;
21     WCHAR DeviceNameBuffer[32];
22     UNICODE_STRING DeviceName;
23     NTSTATUS Status;
24 
25     DPRINT("AddDeviceInternal()\n");
26 
27     ASSERT(DriverObject);
28     ASSERT(Pdo);
29 
30     /* Create new device object */
31     swprintf(DeviceNameBuffer,
32              L"\\Device\\ParallelPort%lu",
33              IoGetConfigurationInformation()->ParallelCount);
34     RtlInitUnicodeString(&DeviceName,
35                          DeviceNameBuffer);
36 
37     Status = IoCreateDevice(DriverObject,
38                             sizeof(FDO_DEVICE_EXTENSION),
39                             &DeviceName,
40                             FILE_DEVICE_PARALLEL_PORT,
41                             FILE_DEVICE_SECURE_OPEN,
42                             FALSE,
43                             &Fdo);
44     if (!NT_SUCCESS(Status))
45     {
46         DPRINT1("IoCreateDevice() failed (Status 0x%08lx)\n", Status);
47         Fdo = NULL;
48         goto done;
49     }
50 
51     DeviceExtension = (PFDO_DEVICE_EXTENSION)Fdo->DeviceExtension;
52     RtlZeroMemory(DeviceExtension,
53                   sizeof(FDO_DEVICE_EXTENSION));
54 
55     DeviceExtension->Common.IsFDO = TRUE;
56     DeviceExtension->Common.PnpState = dsStopped;
57 
58     DeviceExtension->PortNumber = IoGetConfigurationInformation()->ParallelCount++;
59     DeviceExtension->Pdo = Pdo;
60 
61     Status = IoAttachDeviceToDeviceStackSafe(Fdo,
62                                              Pdo,
63                                              &DeviceExtension->LowerDevice);
64     if (!NT_SUCCESS(Status))
65     {
66         DPRINT1("IoAttachDeviceToDeviceStackSafe() failed (Status 0x%08lx)\n", Status);
67         goto done;
68     }
69 
70     if (DeviceExtension->LowerDevice->Flags & DO_POWER_PAGABLE)
71         Fdo->Flags |= DO_POWER_PAGABLE;
72 
73     if (DeviceExtension->LowerDevice->Flags & DO_BUFFERED_IO)
74         Fdo->Flags |= DO_BUFFERED_IO;
75 
76     if (DeviceExtension->LowerDevice->Flags & DO_DIRECT_IO)
77         Fdo->Flags |= DO_DIRECT_IO;
78 
79     /* Choose default strategy */
80     if ((Fdo->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO)) == 0)
81         Fdo->Flags |= DO_BUFFERED_IO;
82 
83     Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
84 
85     if (pFdo)
86     {
87         *pFdo = Fdo;
88     }
89 
90     return STATUS_SUCCESS;
91 
92 done:
93     if (Fdo)
94     {
95         IoDeleteDevice(Fdo);
96     }
97 
98     return Status;
99 }
100 
101 
102 NTSTATUS
103 NTAPI
104 FdoStartDevice(IN PDEVICE_OBJECT DeviceObject,
105                IN PCM_RESOURCE_LIST ResourceList,
106                IN PCM_RESOURCE_LIST ResourceListTranslated)
107 {
108     PFDO_DEVICE_EXTENSION DeviceExtension;
109     ULONG i;
110 //    ULONG Vector = 0;
111 //    KIRQL Dirql = 0;
112 //    KAFFINITY Affinity = 0;
113 //    KINTERRUPT_MODE InterruptMode = Latched;
114 //    BOOLEAN ShareInterrupt = TRUE;
115 
116     DPRINT("FdoStartDevice ()\n");
117 
118     DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
119 
120     ASSERT(DeviceExtension);
121     ASSERT(DeviceExtension->Common.IsFDO == TRUE);
122 
123     if (!ResourceList)
124     {
125         DPRINT1("No allocated resources sent to driver\n");
126         return STATUS_INSUFFICIENT_RESOURCES;
127     }
128 
129     if (ResourceList->Count != 1)
130     {
131         DPRINT1("Wrong number of allocated resources sent to driver\n");
132         return STATUS_INSUFFICIENT_RESOURCES;
133     }
134 
135     if ((ResourceList->List[0].PartialResourceList.Version != 1) ||
136         (ResourceList->List[0].PartialResourceList.Revision != 1) ||
137         (ResourceListTranslated->List[0].PartialResourceList.Version != 1) ||
138         (ResourceListTranslated->List[0].PartialResourceList.Revision != 1))
139     {
140         DPRINT1("Revision mismatch: %u.%u != 1.1 or %u.%u != 1.1\n",
141                 ResourceList->List[0].PartialResourceList.Version,
142                 ResourceList->List[0].PartialResourceList.Revision,
143                 ResourceListTranslated->List[0].PartialResourceList.Version,
144                 ResourceListTranslated->List[0].PartialResourceList.Revision);
145         return STATUS_REVISION_MISMATCH;
146     }
147 
148     DeviceExtension->BaseAddress = 0;
149 
150     for (i = 0; i < ResourceList->List[0].PartialResourceList.Count; i++)
151     {
152         PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor = &ResourceList->List[0].PartialResourceList.PartialDescriptors[i];
153         PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptorTranslated = &ResourceListTranslated->List[0].PartialResourceList.PartialDescriptors[i];
154 
155         switch (PartialDescriptor->Type)
156         {
157             case CmResourceTypePort:
158                 DPRINT("Port: BaseAddress 0x%lx  Length %lu\n",
159                        PartialDescriptor->u.Port.Start.u.LowPart,
160                        PartialDescriptor->u.Port.Length);
161 
162                 if (DeviceExtension->BaseAddress == 0)
163                 {
164                     if (PartialDescriptor->u.Port.Length < 3)
165                         return STATUS_INSUFFICIENT_RESOURCES;
166 
167                     DeviceExtension->BaseAddress = PartialDescriptor->u.Port.Start.u.LowPart;
168                 }
169                 break;
170 
171             case CmResourceTypeInterrupt:
172                 DPRINT("Interrupt: Level %lu  Vector %lu\n",
173                        PartialDescriptorTranslated->u.Interrupt.Level,
174                        PartialDescriptorTranslated->u.Interrupt.Vector);
175 
176 //                Dirql = (KIRQL)PartialDescriptorTranslated->u.Interrupt.Level;
177 //                Vector = PartialDescriptorTranslated->u.Interrupt.Vector;
178 //                Affinity = PartialDescriptorTranslated->u.Interrupt.Affinity;
179 
180 //                if (PartialDescriptorTranslated->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
181 //                    InterruptMode = Latched;
182 //                else
183 //                    InterruptMode = LevelSensitive;
184 
185 //                ShareInterrupt = (PartialDescriptorTranslated->ShareDisposition == CmResourceShareShared);
186                 break;
187 
188             default:
189                 DPRINT1("Other resource: \n");
190                 break;
191         }
192     }
193 
194     DPRINT("New LPT port: Base 0x%lx\n",
195            DeviceExtension->BaseAddress);
196 
197     if (!DeviceExtension->BaseAddress)
198         return STATUS_INSUFFICIENT_RESOURCES;
199 
200 #if 0
201     if (!Dirql)
202         return STATUS_INSUFFICIENT_RESOURCES;
203 #endif
204 
205     DeviceExtension->Common.PnpState = dsStarted;
206 
207 
208     /* We don't really care if the call succeeded or not... */
209 
210     return STATUS_SUCCESS;
211 }
212 
213 
214 static
215 NTSTATUS
216 FdoCreateRawParallelPdo(
217     IN PDEVICE_OBJECT DeviceObject)
218 {
219     PFDO_DEVICE_EXTENSION FdoDeviceExtension;
220     PPDO_DEVICE_EXTENSION PdoDeviceExtension = NULL;
221     PDEVICE_OBJECT Pdo = NULL;
222     WCHAR DeviceNameBuffer[32];
223     WCHAR LinkNameBuffer[32];
224     WCHAR LptPortBuffer[32];
225     UNICODE_STRING DeviceName;
226     UNICODE_STRING LinkName;
227     UNICODE_STRING LptPort;
228     OBJECT_ATTRIBUTES ObjectAttributes;
229     UNICODE_STRING KeyName;
230     HANDLE KeyHandle;
231     NTSTATUS Status;
232 
233     DPRINT("FdoCreateRawParallelPdo()\n");
234 
235     FdoDeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
236 
237     /* Create new device object */
238     swprintf(DeviceNameBuffer,
239              L"\\Device\\Parallel%lu",
240              FdoDeviceExtension->PortNumber);
241     RtlInitUnicodeString(&DeviceName,
242                          DeviceNameBuffer);
243 
244     Status = IoCreateDevice(DeviceObject->DriverObject,
245                             sizeof(PDO_DEVICE_EXTENSION),
246                             &DeviceName,
247                             FILE_DEVICE_CONTROLLER,
248                             0,
249                             FALSE,
250                             &Pdo);
251     if (!NT_SUCCESS(Status))
252     {
253         DPRINT1("IoCreateDevice() failed with status 0x%08x\n", Status);
254         goto done;
255     }
256 
257     Pdo->Flags |= DO_BUS_ENUMERATED_DEVICE;
258     Pdo->Flags |= DO_POWER_PAGABLE;
259 
260     PdoDeviceExtension = (PPDO_DEVICE_EXTENSION)Pdo->DeviceExtension;
261     RtlZeroMemory(PdoDeviceExtension, sizeof(PDO_DEVICE_EXTENSION));
262 
263     PdoDeviceExtension->Common.IsFDO = FALSE;
264     PdoDeviceExtension->Common.PnpState = dsStopped;
265 
266     Pdo->StackSize = DeviceObject->StackSize + 1;
267 
268     FdoDeviceExtension->AttachedRawPdo = Pdo;
269     PdoDeviceExtension->AttachedFdo = DeviceObject;
270 
271     PdoDeviceExtension->PortNumber = FdoDeviceExtension->PortNumber;
272     PdoDeviceExtension->LptPort = PdoDeviceExtension->PortNumber + 1;
273 
274 
275     /* Create link \DosDevices\LPTX -> \Device\ParallelY */
276     swprintf(LinkNameBuffer, L"\\DosDevices\\LPT%lu", PdoDeviceExtension->LptPort);
277     RtlInitUnicodeString(&LinkName, LinkNameBuffer);
278     Status = IoCreateSymbolicLink(&LinkName,
279                                   &DeviceName);
280     if (!NT_SUCCESS(Status))
281     {
282         DPRINT1("IoCreateSymbolicLink() failed with status 0x%08x\n", Status);
283         goto done;
284     }
285 
286     swprintf(LptPortBuffer, L"LPT%lu", PdoDeviceExtension->LptPort);
287     RtlInitUnicodeString(&LptPort, LptPortBuffer);
288 
289     /* Write an entry value under HKLM\HARDWARE\DeviceMap\PARALLEL PORTS. */
290     /* This step is not mandatory, so do not exit in case of error. */
291     RtlInitUnicodeString(&KeyName,
292                          L"\\Registry\\Machine\\HARDWARE\\DeviceMap\\PARALLEL PORTS");
293     InitializeObjectAttributes(&ObjectAttributes,
294                                &KeyName,
295                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
296                                NULL,
297                                NULL);
298 
299     Status = ZwCreateKey(&KeyHandle,
300                          KEY_SET_VALUE,
301                          &ObjectAttributes,
302                          0,
303                          NULL,
304                          REG_OPTION_VOLATILE,
305                          NULL);
306     if (NT_SUCCESS(Status))
307     {
308         /* Key = \Device\Parallelx, Value = LPTx */
309         ZwSetValueKey(KeyHandle,
310                       &DeviceName,
311                       0,
312                       REG_SZ,
313                       LptPortBuffer,
314                       LptPort.Length + sizeof(WCHAR));
315         ZwClose(KeyHandle);
316     }
317 
318     Pdo->Flags |= DO_BUFFERED_IO;
319     Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
320 
321 done:
322     if (!NT_SUCCESS(Status))
323     {
324         if (Pdo)
325         {
326             ASSERT(PdoDeviceExtension);
327             IoDeleteDevice(Pdo);
328         }
329     }
330 
331     return Status;
332 }
333 
334 
335 static
336 NTSTATUS
337 FdoQueryBusRelations(
338     IN PDEVICE_OBJECT DeviceObject,
339     IN PIRP Irp,
340     PIO_STACK_LOCATION IrpSp)
341 {
342     PFDO_DEVICE_EXTENSION DeviceExtension;
343     PDEVICE_RELATIONS DeviceRelations;
344     ULONG Size;
345     ULONG i;
346     ULONG PdoCount = 0;
347     NTSTATUS Status;
348 
349     UNREFERENCED_PARAMETER(IrpSp);
350 
351     DPRINT("FdoQueryBusRelations()\n");
352 
353     DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
354     ASSERT(DeviceExtension->Common.IsFDO);
355 
356     /* TODO: Enumerate parallel devices and create their PDOs */
357 
358     Status = FdoCreateRawParallelPdo(DeviceObject);
359     if (!NT_SUCCESS(Status))
360         return Status;
361 
362     PdoCount++;
363 
364     /* Allocate a buffer for the device relations */
365     Size = sizeof(DEVICE_RELATIONS) + sizeof(PDEVICE_OBJECT) * (PdoCount - 1);
366     DeviceRelations = ExAllocatePoolWithTag(PagedPool, Size, PARPORT_TAG);
367     if (DeviceRelations == NULL)
368         return STATUS_INSUFFICIENT_RESOURCES;
369 
370     /* Fill the buffer */
371     i = 0;
372     ObReferenceObject(DeviceExtension->AttachedRawPdo);
373     DeviceRelations->Objects[i] = DeviceExtension->AttachedRawPdo;
374     DeviceRelations->Count = 1;
375 
376     Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations;
377 
378     DPRINT("Done\n");
379 
380     return STATUS_SUCCESS;
381 }
382 
383 
384 /* PUBLIC FUNCTIONS *********************************************************/
385 
386 NTSTATUS
387 NTAPI
388 AddDevice(IN PDRIVER_OBJECT DriverObject,
389           IN PDEVICE_OBJECT Pdo)
390 {
391     DPRINT("AddDevice(%p %p)\n", DriverObject, Pdo);
392 
393     /* Serial.sys is a legacy driver. AddDevice is called once
394      * with a NULL Pdo just after the driver initialization.
395      * Detect this case and return success.
396      */
397     if (Pdo == NULL)
398         return STATUS_SUCCESS;
399 
400     /* We have here a PDO not null. It represents a real serial
401      * port. So call the internal AddDevice function.
402      */
403     return AddDeviceInternal(DriverObject, Pdo, NULL, NULL);
404 }
405 
406 
407 NTSTATUS
408 NTAPI
409 FdoCreate(IN PDEVICE_OBJECT DeviceObject,
410           IN PIRP Irp)
411 {
412     PFDO_DEVICE_EXTENSION DeviceExtension;
413     PIO_STACK_LOCATION Stack;
414     NTSTATUS Status = STATUS_SUCCESS;
415 
416     DPRINT("FdoCreate()\n");
417 
418     Stack = IoGetCurrentIrpStackLocation(Irp);
419     DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
420 
421     if (Stack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
422     {
423         DPRINT1("Not a directory\n");
424         Status = STATUS_NOT_A_DIRECTORY;
425         goto done;
426     }
427 
428     DPRINT("Open parallel port %lu: successful\n", DeviceExtension->PortNumber);
429     DeviceExtension->OpenCount++;
430 
431 done:
432     Irp->IoStatus.Status = Status;
433     Irp->IoStatus.Information = 0;
434     IoCompleteRequest(Irp, IO_NO_INCREMENT);
435 
436     return Status;
437 }
438 
439 
440 NTSTATUS
441 NTAPI
442 FdoClose(IN PDEVICE_OBJECT DeviceObject,
443          IN PIRP Irp)
444 {
445     PFDO_DEVICE_EXTENSION pDeviceExtension;
446 
447     DPRINT("FdoClose()\n");
448 
449     pDeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
450     pDeviceExtension->OpenCount--;
451 
452     Irp->IoStatus.Information = 0;
453     Irp->IoStatus.Status = STATUS_SUCCESS;
454     IoCompleteRequest(Irp, IO_NO_INCREMENT);
455 
456     return STATUS_SUCCESS;
457 }
458 
459 
460 NTSTATUS
461 NTAPI
462 FdoCleanup(IN PDEVICE_OBJECT DeviceObject,
463            IN PIRP Irp)
464 {
465     DPRINT("FdoCleanup()\n");
466 
467     Irp->IoStatus.Information = 0;
468     Irp->IoStatus.Status = STATUS_SUCCESS;
469     IoCompleteRequest(Irp, IO_NO_INCREMENT);
470 
471     return STATUS_SUCCESS;
472 }
473 
474 
475 NTSTATUS
476 NTAPI
477 FdoRead(IN PDEVICE_OBJECT DeviceObject,
478         IN PIRP Irp)
479 {
480     DPRINT("FdoRead()\n");
481 
482     Irp->IoStatus.Information = 0;
483     Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
484     IoCompleteRequest(Irp, IO_NO_INCREMENT);
485     return STATUS_NOT_SUPPORTED;
486 }
487 
488 
489 NTSTATUS
490 NTAPI
491 FdoWrite(IN PDEVICE_OBJECT DeviceObject,
492          IN PIRP Irp)
493 {
494     DPRINT("FdoWrite()\n");
495 
496     Irp->IoStatus.Information = 0;
497     Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
498     IoCompleteRequest(Irp, IO_NO_INCREMENT);
499     return STATUS_NOT_SUPPORTED;
500 }
501 
502 
503 NTSTATUS
504 NTAPI
505 FdoPnp(IN PDEVICE_OBJECT DeviceObject,
506        IN PIRP Irp)
507 {
508     PFDO_DEVICE_EXTENSION FdoExtension;
509     ULONG MinorFunction;
510     PIO_STACK_LOCATION Stack;
511     ULONG_PTR Information = 0;
512     NTSTATUS Status;
513 
514     DPRINT("FdoPnp()\n");
515 
516     Stack = IoGetCurrentIrpStackLocation(Irp);
517     MinorFunction = Stack->MinorFunction;
518 
519     switch (MinorFunction)
520     {
521         /* FIXME: do all these minor functions
522         IRP_MN_QUERY_REMOVE_DEVICE 0x1
523         IRP_MN_REMOVE_DEVICE 0x2
524         {
525             TRACE_(SERIAL, "IRP_MJ_PNP / IRP_MN_REMOVE_DEVICE\n");
526             IoAcquireRemoveLock
527             IoReleaseRemoveLockAndWait
528             pass request to DeviceExtension-LowerDriver
529             disable interface
530             IoDeleteDevice(Fdo) and/or IoDetachDevice
531             break;
532         }
533         IRP_MN_CANCEL_REMOVE_DEVICE 0x3
534         IRP_MN_STOP_DEVICE 0x4
535         IRP_MN_QUERY_STOP_DEVICE 0x5
536         IRP_MN_CANCEL_STOP_DEVICE 0x6
537         IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations (optional) 0x7
538         IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations (optional) 0x7
539         IRP_MN_QUERY_INTERFACE (optional) 0x8
540         IRP_MN_QUERY_CAPABILITIES (optional) 0x9
541         IRP_MN_FILTER_RESOURCE_REQUIREMENTS (optional) 0xd
542         IRP_MN_QUERY_PNP_DEVICE_STATE (optional) 0x14
543         IRP_MN_DEVICE_USAGE_NOTIFICATION (required or optional) 0x16
544         IRP_MN_SURPRISE_REMOVAL 0x17
545         */
546         case IRP_MN_START_DEVICE: /* 0x0 */
547             DPRINT("IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
548 
549             /* Call lower driver */
550             FdoExtension = DeviceObject->DeviceExtension;
551             Status = STATUS_UNSUCCESSFUL;
552 
553             ASSERT(FdoExtension->Common.PnpState == dsStopped);
554 
555             if (IoForwardIrpSynchronously(FdoExtension->LowerDevice, Irp))
556             {
557                 Status = Irp->IoStatus.Status;
558                 if (NT_SUCCESS(Status))
559                 {
560                     Status = FdoStartDevice(DeviceObject,
561                         Stack->Parameters.StartDevice.AllocatedResources,
562                         Stack->Parameters.StartDevice.AllocatedResourcesTranslated);
563                 }
564             }
565             break;
566 
567         case IRP_MN_QUERY_DEVICE_RELATIONS: /* (optional) 0x7 */
568             switch (Stack->Parameters.QueryDeviceRelations.Type)
569             {
570                 case BusRelations:
571                     DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n");
572                     Status = FdoQueryBusRelations(DeviceObject, Irp, Stack);
573                     Irp->IoStatus.Status = Status;
574                     IoCompleteRequest(Irp, IO_NO_INCREMENT);
575                     return Status;
576 
577                 case RemovalRelations:
578                     DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n");
579                     return ForwardIrpAndForget(DeviceObject, Irp);
580 
581                 default:
582                     DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / Unknown type 0x%lx\n",
583                         Stack->Parameters.QueryDeviceRelations.Type);
584                     return ForwardIrpAndForget(DeviceObject, Irp);
585             }
586             break;
587 
588         case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: /* (optional) 0xd */
589             DPRINT("IRP_MJ_PNP / IRP_MN_FILTER_RESOURCE_REQUIREMENTS\n");
590             return ForwardIrpAndForget(DeviceObject, Irp);
591 
592         default:
593             DPRINT("Unknown minor function 0x%x\n", MinorFunction);
594             return ForwardIrpAndForget(DeviceObject, Irp);
595     }
596 
597     Irp->IoStatus.Information = Information;
598     Irp->IoStatus.Status = Status;
599     IoCompleteRequest(Irp, IO_NO_INCREMENT);
600 
601     return Status;
602 }
603 
604 
605 NTSTATUS
606 NTAPI
607 FdoPower(IN PDEVICE_OBJECT DeviceObject,
608          IN PIRP Irp)
609 {
610     PDEVICE_OBJECT LowerDevice;
611 
612     DPRINT("FdoPower()\n");
613 
614     LowerDevice = ((PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->LowerDevice;
615     PoStartNextPowerIrp(Irp);
616     IoSkipCurrentIrpStackLocation(Irp);
617     return PoCallDriver(LowerDevice, Irp);
618 }
619 
620 /* EOF */
621