xref: /reactos/drivers/storage/port/storport/fdo.c (revision 7ed1883c)
1b2c6c07dSEricKohl /*
2b2c6c07dSEricKohl  * PROJECT:     ReactOS Storport Driver
3b2c6c07dSEricKohl  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4b2c6c07dSEricKohl  * PURPOSE:     Storport FDO code
5b2c6c07dSEricKohl  * COPYRIGHT:   Copyright 2017 Eric Kohl (eric.kohl@reactos.org)
6b2c6c07dSEricKohl  */
7b2c6c07dSEricKohl 
8b2c6c07dSEricKohl /* INCLUDES *******************************************************************/
9b2c6c07dSEricKohl 
10b2c6c07dSEricKohl #include "precomp.h"
11b2c6c07dSEricKohl 
12b2c6c07dSEricKohl #define NDEBUG
13b2c6c07dSEricKohl #include <debug.h>
14b2c6c07dSEricKohl 
15b2c6c07dSEricKohl 
16b2c6c07dSEricKohl /* FUNCTIONS ******************************************************************/
17b2c6c07dSEricKohl 
18b2c6c07dSEricKohl static
195cfc1e31SEric Kohl BOOLEAN
205cfc1e31SEric Kohl NTAPI
PortFdoInterruptRoutine(_In_ PKINTERRUPT Interrupt,_In_ PVOID ServiceContext)215cfc1e31SEric Kohl PortFdoInterruptRoutine(
225cfc1e31SEric Kohl     _In_ PKINTERRUPT Interrupt,
235cfc1e31SEric Kohl     _In_ PVOID ServiceContext)
245cfc1e31SEric Kohl {
255cfc1e31SEric Kohl     PFDO_DEVICE_EXTENSION DeviceExtension;
265cfc1e31SEric Kohl 
275cfc1e31SEric Kohl     DPRINT1("PortFdoInterruptRoutine(%p %p)\n",
285cfc1e31SEric Kohl             Interrupt, ServiceContext);
295cfc1e31SEric Kohl 
305cfc1e31SEric Kohl     DeviceExtension = (PFDO_DEVICE_EXTENSION)ServiceContext;
315cfc1e31SEric Kohl 
325cfc1e31SEric Kohl     return MiniportHwInterrupt(&DeviceExtension->Miniport);
335cfc1e31SEric Kohl }
345cfc1e31SEric Kohl 
355cfc1e31SEric Kohl 
365cfc1e31SEric Kohl static
375cfc1e31SEric Kohl NTSTATUS
PortFdoConnectInterrupt(_In_ PFDO_DEVICE_EXTENSION DeviceExtension)385cfc1e31SEric Kohl PortFdoConnectInterrupt(
395cfc1e31SEric Kohl     _In_ PFDO_DEVICE_EXTENSION DeviceExtension)
405cfc1e31SEric Kohl {
415cfc1e31SEric Kohl     ULONG Vector;
425cfc1e31SEric Kohl     KIRQL Irql;
435cfc1e31SEric Kohl     KINTERRUPT_MODE InterruptMode;
445cfc1e31SEric Kohl     BOOLEAN ShareVector;
455cfc1e31SEric Kohl     KAFFINITY Affinity;
465cfc1e31SEric Kohl     NTSTATUS Status;
475cfc1e31SEric Kohl 
485cfc1e31SEric Kohl     DPRINT1("PortFdoConnectInterrupt(%p)\n",
495cfc1e31SEric Kohl             DeviceExtension);
505cfc1e31SEric Kohl 
515cfc1e31SEric Kohl     /* No resources, no interrupt. Done! */
525cfc1e31SEric Kohl     if (DeviceExtension->AllocatedResources == NULL ||
535cfc1e31SEric Kohl         DeviceExtension->TranslatedResources == NULL)
545cfc1e31SEric Kohl     {
555cfc1e31SEric Kohl         DPRINT1("Checkpoint\n");
565cfc1e31SEric Kohl         return STATUS_SUCCESS;
575cfc1e31SEric Kohl     }
585cfc1e31SEric Kohl 
595cfc1e31SEric Kohl     /* Get the interrupt data from the resource list */
605cfc1e31SEric Kohl     Status = GetResourceListInterrupt(DeviceExtension,
615cfc1e31SEric Kohl                                       &Vector,
625cfc1e31SEric Kohl                                       &Irql,
635cfc1e31SEric Kohl                                       &InterruptMode,
645cfc1e31SEric Kohl                                       &ShareVector,
655cfc1e31SEric Kohl                                       &Affinity);
665cfc1e31SEric Kohl     if (!NT_SUCCESS(Status))
675cfc1e31SEric Kohl     {
685cfc1e31SEric Kohl         DPRINT1("GetResourceListInterrupt() failed (Status 0x%08lx)\n", Status);
695cfc1e31SEric Kohl         return Status;
705cfc1e31SEric Kohl     }
715cfc1e31SEric Kohl 
725cfc1e31SEric Kohl     DPRINT1("Vector: %lu\n", Vector);
735cfc1e31SEric Kohl     DPRINT1("Irql: %lu\n", Irql);
745cfc1e31SEric Kohl 
755cfc1e31SEric Kohl     DPRINT1("Affinity: 0x%08lx\n", Affinity);
765cfc1e31SEric Kohl 
775cfc1e31SEric Kohl     /* Connect the interrupt */
785cfc1e31SEric Kohl     Status = IoConnectInterrupt(&DeviceExtension->Interrupt,
795cfc1e31SEric Kohl                                 PortFdoInterruptRoutine,
805cfc1e31SEric Kohl                                 DeviceExtension,
815cfc1e31SEric Kohl                                 NULL,
825cfc1e31SEric Kohl                                 Vector,
835cfc1e31SEric Kohl                                 Irql,
845cfc1e31SEric Kohl                                 Irql,
855cfc1e31SEric Kohl                                 InterruptMode,
865cfc1e31SEric Kohl                                 ShareVector,
875cfc1e31SEric Kohl                                 Affinity,
885cfc1e31SEric Kohl                                 FALSE);
895cfc1e31SEric Kohl     if (NT_SUCCESS(Status))
905cfc1e31SEric Kohl     {
915cfc1e31SEric Kohl         DeviceExtension->InterruptIrql = Irql;
925cfc1e31SEric Kohl     }
935cfc1e31SEric Kohl     else
945cfc1e31SEric Kohl     {
955cfc1e31SEric Kohl         DPRINT1("IoConnectInterrupt() failed (Status 0x%08lx)\n", Status);
965cfc1e31SEric Kohl     }
975cfc1e31SEric Kohl 
985cfc1e31SEric Kohl     return Status;
995cfc1e31SEric Kohl }
1005cfc1e31SEric Kohl 
1015cfc1e31SEric Kohl 
1025cfc1e31SEric Kohl static
103b2c6c07dSEricKohl NTSTATUS
PortFdoStartMiniport(_In_ PFDO_DEVICE_EXTENSION DeviceExtension)1048dea67f8SEric Kohl PortFdoStartMiniport(
1058dea67f8SEric Kohl     _In_ PFDO_DEVICE_EXTENSION DeviceExtension)
1068dea67f8SEric Kohl {
1078dea67f8SEric Kohl     PHW_INITIALIZATION_DATA InitData;
1088dea67f8SEric Kohl     INTERFACE_TYPE InterfaceType;
1098dea67f8SEric Kohl     NTSTATUS Status;
1108dea67f8SEric Kohl 
1118dea67f8SEric Kohl     DPRINT1("PortFdoStartDevice(%p)\n", DeviceExtension);
1128dea67f8SEric Kohl 
1138dea67f8SEric Kohl     /* Get the interface type of the lower device */
1148dea67f8SEric Kohl     InterfaceType = GetBusInterface(DeviceExtension->LowerDevice);
1158dea67f8SEric Kohl     if (InterfaceType == InterfaceTypeUndefined)
1168dea67f8SEric Kohl         return STATUS_NO_SUCH_DEVICE;
1178dea67f8SEric Kohl 
1188dea67f8SEric Kohl     /* Get the driver init data for the given interface type */
1198dea67f8SEric Kohl     InitData = PortGetDriverInitData(DeviceExtension->DriverExtension,
1208dea67f8SEric Kohl                                      InterfaceType);
1218dea67f8SEric Kohl     if (InitData == NULL)
1228dea67f8SEric Kohl         return STATUS_NO_SUCH_DEVICE;
1238dea67f8SEric Kohl 
1248dea67f8SEric Kohl     /* Initialize the miniport */
12542cb5353SEric Kohl     Status = MiniportInitialize(&DeviceExtension->Miniport,
1268dea67f8SEric Kohl                                 DeviceExtension,
1278dea67f8SEric Kohl                                 InitData);
12842cb5353SEric Kohl     if (!NT_SUCCESS(Status))
12942cb5353SEric Kohl     {
13042cb5353SEric Kohl         DPRINT1("MiniportInitialize() failed (Status 0x%08lx)\n", Status);
13142cb5353SEric Kohl         return Status;
13242cb5353SEric Kohl     }
1338dea67f8SEric Kohl 
1348dea67f8SEric Kohl     /* Call the miniports FindAdapter function */
1358dea67f8SEric Kohl     Status = MiniportFindAdapter(&DeviceExtension->Miniport);
1368dea67f8SEric Kohl     if (!NT_SUCCESS(Status))
1378dea67f8SEric Kohl     {
1388dea67f8SEric Kohl         DPRINT1("MiniportFindAdapter() failed (Status 0x%08lx)\n", Status);
1398dea67f8SEric Kohl         return Status;
1408dea67f8SEric Kohl     }
1418dea67f8SEric Kohl 
14258bb4b31SEric Kohl     /* Connect the configured interrupt */
1435cfc1e31SEric Kohl     Status = PortFdoConnectInterrupt(DeviceExtension);
14458bb4b31SEric Kohl     if (!NT_SUCCESS(Status))
14558bb4b31SEric Kohl     {
14658bb4b31SEric Kohl         DPRINT1("PortFdoConnectInterrupt() failed (Status 0x%08lx)\n", Status);
14758bb4b31SEric Kohl         return Status;
14858bb4b31SEric Kohl     }
1495cfc1e31SEric Kohl 
1508dea67f8SEric Kohl     /* Call the miniports HwInitialize function */
1518dea67f8SEric Kohl     Status = MiniportHwInitialize(&DeviceExtension->Miniport);
1528dea67f8SEric Kohl     if (!NT_SUCCESS(Status))
1538dea67f8SEric Kohl     {
1548dea67f8SEric Kohl         DPRINT1("MiniportHwInitialize() failed (Status 0x%08lx)\n", Status);
1558dea67f8SEric Kohl         return Status;
1568dea67f8SEric Kohl     }
1578dea67f8SEric Kohl 
158cc95d3ecSEric Kohl     /* Call the HwPassiveInitRoutine function, if available */
159cc95d3ecSEric Kohl     if (DeviceExtension->HwPassiveInitRoutine != NULL)
160cc95d3ecSEric Kohl     {
161cc95d3ecSEric Kohl         DPRINT1("Calling HwPassiveInitRoutine()\n");
162cc95d3ecSEric Kohl         if (!DeviceExtension->HwPassiveInitRoutine(&DeviceExtension->Miniport.MiniportExtension->HwDeviceExtension))
163cc95d3ecSEric Kohl         {
164cc95d3ecSEric Kohl             DPRINT1("HwPassiveInitRoutine() failed\n");
165cc95d3ecSEric Kohl             return STATUS_UNSUCCESSFUL;
166cc95d3ecSEric Kohl         }
167cc95d3ecSEric Kohl     }
168cc95d3ecSEric Kohl 
1698dea67f8SEric Kohl     return STATUS_SUCCESS;
1708dea67f8SEric Kohl }
1718dea67f8SEric Kohl 
1728dea67f8SEric Kohl 
1738dea67f8SEric Kohl static
1748dea67f8SEric Kohl NTSTATUS
175b2c6c07dSEricKohl NTAPI
PortFdoStartDevice(_In_ PFDO_DEVICE_EXTENSION DeviceExtension,_In_ PIRP Irp)176b2c6c07dSEricKohl PortFdoStartDevice(
177b2c6c07dSEricKohl     _In_ PFDO_DEVICE_EXTENSION DeviceExtension,
178b2c6c07dSEricKohl     _In_ PIRP Irp)
179b2c6c07dSEricKohl {
1803f5aeb93SEric Kohl     PIO_STACK_LOCATION Stack;
1818dea67f8SEric Kohl     NTSTATUS Status;
1828dea67f8SEric Kohl 
183b2c6c07dSEricKohl     DPRINT1("PortFdoStartDevice(%p %p)\n",
184b2c6c07dSEricKohl             DeviceExtension, Irp);
185b2c6c07dSEricKohl 
186b2c6c07dSEricKohl     ASSERT(DeviceExtension->ExtensionType == FdoExtension);
187b2c6c07dSEricKohl 
1883f5aeb93SEric Kohl     /* Get the current stack location */
1893f5aeb93SEric Kohl     Stack = IoGetCurrentIrpStackLocation(Irp);
1903f5aeb93SEric Kohl 
1918dea67f8SEric Kohl     /* Start the lower device if the FDO is in 'stopped' state */
1928dea67f8SEric Kohl     if (DeviceExtension->PnpState == dsStopped)
1938dea67f8SEric Kohl     {
194*7ed1883cSVictor Perevertkin         if (IoForwardIrpSynchronously(DeviceExtension->LowerDevice, Irp))
195*7ed1883cSVictor Perevertkin         {
196*7ed1883cSVictor Perevertkin             Status = Irp->IoStatus.Status;
197*7ed1883cSVictor Perevertkin         }
198*7ed1883cSVictor Perevertkin         else
199*7ed1883cSVictor Perevertkin         {
200*7ed1883cSVictor Perevertkin             Status = STATUS_UNSUCCESSFUL;
201*7ed1883cSVictor Perevertkin         }
202*7ed1883cSVictor Perevertkin 
2038dea67f8SEric Kohl         if (!NT_SUCCESS(Status))
2048dea67f8SEric Kohl         {
205*7ed1883cSVictor Perevertkin             DPRINT1("Lower device failed the IRP (Status 0x%08lx)\n", Status);
2068dea67f8SEric Kohl             return Status;
2078dea67f8SEric Kohl         }
2088dea67f8SEric Kohl     }
2098dea67f8SEric Kohl 
2108dea67f8SEric Kohl     /* Change to the 'started' state */
2118dea67f8SEric Kohl     DeviceExtension->PnpState = dsStarted;
2128dea67f8SEric Kohl 
2133f5aeb93SEric Kohl     /* Copy the raw and translated resource lists into the device extension */
2143f5aeb93SEric Kohl     if (Stack->Parameters.StartDevice.AllocatedResources != NULL &&
2153f5aeb93SEric Kohl         Stack->Parameters.StartDevice.AllocatedResourcesTranslated != NULL)
2163f5aeb93SEric Kohl     {
2173f5aeb93SEric Kohl         DeviceExtension->AllocatedResources = CopyResourceList(NonPagedPool,
2183f5aeb93SEric Kohl                                                                Stack->Parameters.StartDevice.AllocatedResources);
2193f5aeb93SEric Kohl         if (DeviceExtension->AllocatedResources == NULL)
2203f5aeb93SEric Kohl             return STATUS_NO_MEMORY;
2213f5aeb93SEric Kohl 
2223f5aeb93SEric Kohl         DeviceExtension->TranslatedResources = CopyResourceList(NonPagedPool,
2233f5aeb93SEric Kohl                                                                 Stack->Parameters.StartDevice.AllocatedResourcesTranslated);
2243f5aeb93SEric Kohl         if (DeviceExtension->TranslatedResources == NULL)
2253f5aeb93SEric Kohl             return STATUS_NO_MEMORY;
2263f5aeb93SEric Kohl     }
2273f5aeb93SEric Kohl 
228b21019e3SEric Kohl     /* Get the bus interface of the lower (bus) device */
229b21019e3SEric Kohl     Status = QueryBusInterface(DeviceExtension->LowerDevice,
230b21019e3SEric Kohl                                (PGUID)&GUID_BUS_INTERFACE_STANDARD,
231b21019e3SEric Kohl                                sizeof(BUS_INTERFACE_STANDARD),
232b21019e3SEric Kohl                                1,
233b21019e3SEric Kohl                                &DeviceExtension->BusInterface,
234b21019e3SEric Kohl                                NULL);
235b21019e3SEric Kohl     DPRINT1("Status: 0x%08lx\n", Status);
236b21019e3SEric Kohl     if (NT_SUCCESS(Status))
237b21019e3SEric Kohl     {
238b21019e3SEric Kohl         DPRINT1("Context: %p\n", DeviceExtension->BusInterface.Context);
239b21019e3SEric Kohl         DeviceExtension->BusInitialized = TRUE;
240b21019e3SEric Kohl     }
241b21019e3SEric Kohl 
2428dea67f8SEric Kohl     /* Start the miniport (FindAdapter & Initialize) */
2438dea67f8SEric Kohl     Status = PortFdoStartMiniport(DeviceExtension);
2448dea67f8SEric Kohl     if (!NT_SUCCESS(Status))
2458dea67f8SEric Kohl     {
2468dea67f8SEric Kohl         DPRINT1("FdoStartMiniport() failed (Status 0x%08lx)\n", Status);
2478dea67f8SEric Kohl         DeviceExtension->PnpState = dsStopped;
2488dea67f8SEric Kohl     }
2498dea67f8SEric Kohl 
2508dea67f8SEric Kohl     return Status;
251b2c6c07dSEricKohl }
252b2c6c07dSEricKohl 
253b2c6c07dSEricKohl 
254545692e8SEric Kohl static
255545692e8SEric Kohl NTSTATUS
PortSendInquiry(_In_ PPDO_DEVICE_EXTENSION PdoExtension)256be88574fSEric Kohl PortSendInquiry(
257545692e8SEric Kohl     _In_ PPDO_DEVICE_EXTENSION PdoExtension)
258f7c01906SEric Kohl {
259545692e8SEric Kohl     IO_STATUS_BLOCK IoStatusBlock;
260545692e8SEric Kohl     PIO_STACK_LOCATION IrpStack;
261545692e8SEric Kohl     KEVENT Event;
262545692e8SEric Kohl //    KIRQL Irql;
263545692e8SEric Kohl     PIRP Irp;
264545692e8SEric Kohl     NTSTATUS Status;
265545692e8SEric Kohl     PSENSE_DATA SenseBuffer;
266be88574fSEric Kohl     BOOLEAN KeepTrying = TRUE;
267be88574fSEric Kohl     ULONG RetryCount = 0;
268f7c01906SEric Kohl     SCSI_REQUEST_BLOCK Srb;
269f7c01906SEric Kohl     PCDB Cdb;
270545692e8SEric Kohl //    PSCSI_PORT_LUN_EXTENSION LunExtension;
271545692e8SEric Kohl //    PFDO_DEVICE_EXTENSION DeviceExtension;
272f7c01906SEric Kohl 
273545692e8SEric Kohl     DPRINT("PortSendInquiry(%p)\n", PdoExtension);
274f7c01906SEric Kohl 
275545692e8SEric Kohl     if (PdoExtension->InquiryBuffer == NULL)
276545692e8SEric Kohl     {
277545692e8SEric Kohl         PdoExtension->InquiryBuffer = ExAllocatePoolWithTag(NonPagedPool, INQUIRYDATABUFFERSIZE, TAG_INQUIRY_DATA);
278545692e8SEric Kohl         if (PdoExtension->InquiryBuffer == NULL)
279f7c01906SEric Kohl             return STATUS_INSUFFICIENT_RESOURCES;
280545692e8SEric Kohl     }
281f7c01906SEric Kohl 
282f7c01906SEric Kohl     SenseBuffer = ExAllocatePoolWithTag(NonPagedPool, SENSE_BUFFER_SIZE, TAG_SENSE_DATA);
283f7c01906SEric Kohl     if (SenseBuffer == NULL)
284f7c01906SEric Kohl     {
285f7c01906SEric Kohl         return STATUS_INSUFFICIENT_RESOURCES;
286f7c01906SEric Kohl     }
287f7c01906SEric Kohl 
288be88574fSEric Kohl     while (KeepTrying)
289f7c01906SEric Kohl     {
290545692e8SEric Kohl         /* Initialize event for waiting */
291545692e8SEric Kohl         KeInitializeEvent(&Event,
292545692e8SEric Kohl                           NotificationEvent,
293545692e8SEric Kohl                           FALSE);
294545692e8SEric Kohl 
295545692e8SEric Kohl         /* Create an IRP */
296545692e8SEric Kohl         Irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_IN,
297545692e8SEric Kohl                                             PdoExtension->Device,
298545692e8SEric Kohl                                             NULL,
299545692e8SEric Kohl                                             0,
300545692e8SEric Kohl                                             PdoExtension->InquiryBuffer,
301545692e8SEric Kohl                                             INQUIRYDATABUFFERSIZE,
302545692e8SEric Kohl                                             TRUE,
303545692e8SEric Kohl                                             &Event,
304545692e8SEric Kohl                                             &IoStatusBlock);
305545692e8SEric Kohl         if (Irp == NULL)
306545692e8SEric Kohl         {
307545692e8SEric Kohl             DPRINT("IoBuildDeviceIoControlRequest() failed\n");
308545692e8SEric Kohl 
309545692e8SEric Kohl             /* Quit the loop */
310545692e8SEric Kohl             Status = STATUS_INSUFFICIENT_RESOURCES;
311545692e8SEric Kohl             KeepTrying = FALSE;
312545692e8SEric Kohl             continue;
313545692e8SEric Kohl         }
314545692e8SEric Kohl 
315f7c01906SEric Kohl         /* Prepare SRB */
316f7c01906SEric Kohl         RtlZeroMemory(&Srb, sizeof(SCSI_REQUEST_BLOCK));
317f7c01906SEric Kohl 
318f7c01906SEric Kohl         Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
319545692e8SEric Kohl         Srb.OriginalRequest = Irp;
320545692e8SEric Kohl         Srb.PathId = PdoExtension->Bus;
321545692e8SEric Kohl         Srb.TargetId = PdoExtension->Target;
322545692e8SEric Kohl         Srb.Lun = PdoExtension->Lun;
323f7c01906SEric Kohl         Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
324f7c01906SEric Kohl         Srb.SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
325f7c01906SEric Kohl         Srb.TimeOutValue = 4;
326f7c01906SEric Kohl         Srb.CdbLength = 6;
327f7c01906SEric Kohl 
328f7c01906SEric Kohl         Srb.SenseInfoBuffer = SenseBuffer;
329f7c01906SEric Kohl         Srb.SenseInfoBufferLength = SENSE_BUFFER_SIZE;
330f7c01906SEric Kohl 
331545692e8SEric Kohl         Srb.DataBuffer = PdoExtension->InquiryBuffer;
332f7c01906SEric Kohl         Srb.DataTransferLength = INQUIRYDATABUFFERSIZE;
333f7c01906SEric Kohl 
334545692e8SEric Kohl         /* Attach Srb to the Irp */
335545692e8SEric Kohl         IrpStack = IoGetNextIrpStackLocation(Irp);
336545692e8SEric Kohl         IrpStack->Parameters.Scsi.Srb = &Srb;
337f7c01906SEric Kohl 
338f7c01906SEric Kohl         /* Fill in CDB */
339f7c01906SEric Kohl         Cdb = (PCDB)Srb.Cdb;
340545692e8SEric Kohl         Cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY;
341545692e8SEric Kohl         Cdb->CDB6INQUIRY.LogicalUnitNumber = PdoExtension->Lun;
342545692e8SEric Kohl         Cdb->CDB6INQUIRY.AllocationLength = INQUIRYDATABUFFERSIZE;
343f7c01906SEric Kohl 
344545692e8SEric Kohl         /* Call the driver */
345545692e8SEric Kohl         Status = IoCallDriver(PdoExtension->Device, Irp);
346545692e8SEric Kohl 
347545692e8SEric Kohl         /* Wait for it to complete */
348545692e8SEric Kohl         if (Status == STATUS_PENDING)
349be88574fSEric Kohl         {
350545692e8SEric Kohl             DPRINT1("PortSendInquiry(): Waiting for the driver to process request...\n");
351545692e8SEric Kohl             KeWaitForSingleObject(&Event,
352545692e8SEric Kohl                                   Executive,
353545692e8SEric Kohl                                   KernelMode,
354545692e8SEric Kohl                                   FALSE,
355545692e8SEric Kohl                                   NULL);
356545692e8SEric Kohl             Status = IoStatusBlock.Status;
357be88574fSEric Kohl         }
358f7c01906SEric Kohl 
359545692e8SEric Kohl         DPRINT("PortSendInquiry(): Request processed by driver, status = 0x%08X\n", Status);
360545692e8SEric Kohl 
361f7c01906SEric Kohl         if (SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_SUCCESS)
362f7c01906SEric Kohl         {
363be88574fSEric Kohl             DPRINT("Found a device!\n");
364be88574fSEric Kohl 
365f7c01906SEric Kohl             /* Quit the loop */
366f7c01906SEric Kohl             Status = STATUS_SUCCESS;
367be88574fSEric Kohl             KeepTrying = FALSE;
368be88574fSEric Kohl             continue;
369f7c01906SEric Kohl         }
370f7c01906SEric Kohl 
371f7c01906SEric Kohl         DPRINT("Inquiry SRB failed with SrbStatus 0x%08X\n", Srb.SrbStatus);
372f7c01906SEric Kohl 
373545692e8SEric Kohl         /* Check if the queue is frozen */
374545692e8SEric Kohl         if (Srb.SrbStatus & SRB_STATUS_QUEUE_FROZEN)
375545692e8SEric Kohl         {
376545692e8SEric Kohl             /* Something weird happened, deal with it (unfreeze the queue) */
377545692e8SEric Kohl             KeepTrying = FALSE;
378545692e8SEric Kohl 
379545692e8SEric Kohl             DPRINT("SpiSendInquiry(): the queue is frozen at TargetId %d\n", Srb.TargetId);
380545692e8SEric Kohl 
381545692e8SEric Kohl //            LunExtension = SpiGetLunExtension(DeviceExtension,
382545692e8SEric Kohl //                                              LunInfo->PathId,
383545692e8SEric Kohl //                                              LunInfo->TargetId,
384545692e8SEric Kohl //                                              LunInfo->Lun);
385545692e8SEric Kohl 
386545692e8SEric Kohl             /* Clear frozen flag */
387545692e8SEric Kohl //            LunExtension->Flags &= ~LUNEX_FROZEN_QUEUE;
388545692e8SEric Kohl 
389545692e8SEric Kohl             /* Acquire the spinlock */
390545692e8SEric Kohl //            KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql);
391545692e8SEric Kohl 
392545692e8SEric Kohl             /* Process the request */
393545692e8SEric Kohl //            SpiGetNextRequestFromLun(DeviceObject->DeviceExtension, LunExtension);
394545692e8SEric Kohl 
395545692e8SEric Kohl             /* SpiGetNextRequestFromLun() releases the spinlock,
396545692e8SEric Kohl                 so we just lower irql back to what it was before */
397545692e8SEric Kohl //            KeLowerIrql(Irql);
398545692e8SEric Kohl         }
399545692e8SEric Kohl 
400545692e8SEric Kohl         /* Check if data overrun happened */
401545692e8SEric Kohl         if (SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN)
402545692e8SEric Kohl         {
403545692e8SEric Kohl             DPRINT("Data overrun at TargetId %d\n", PdoExtension->Target);
404545692e8SEric Kohl 
405545692e8SEric Kohl             /* Quit the loop */
406545692e8SEric Kohl             Status = STATUS_SUCCESS;
407545692e8SEric Kohl             KeepTrying = FALSE;
408545692e8SEric Kohl         }
409545692e8SEric Kohl         else if ((Srb.SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
410545692e8SEric Kohl                  SenseBuffer->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST)
411545692e8SEric Kohl         {
412545692e8SEric Kohl             /* LUN is not valid, but some device responds there.
413545692e8SEric Kohl                 Mark it as invalid anyway */
414545692e8SEric Kohl 
415545692e8SEric Kohl             /* Quit the loop */
416545692e8SEric Kohl             Status = STATUS_INVALID_DEVICE_REQUEST;
417545692e8SEric Kohl             KeepTrying = FALSE;
418545692e8SEric Kohl         }
419545692e8SEric Kohl         else
420545692e8SEric Kohl         {
421f7c01906SEric Kohl             /* Retry a couple of times if no timeout happened */
422f7c01906SEric Kohl             if ((RetryCount < 2) &&
423f7c01906SEric Kohl                 (SRB_STATUS(Srb.SrbStatus) != SRB_STATUS_NO_DEVICE) &&
424f7c01906SEric Kohl                 (SRB_STATUS(Srb.SrbStatus) != SRB_STATUS_SELECTION_TIMEOUT))
425f7c01906SEric Kohl             {
426f7c01906SEric Kohl                 RetryCount++;
427f7c01906SEric Kohl                 KeepTrying = TRUE;
428f7c01906SEric Kohl             }
429f7c01906SEric Kohl             else
430f7c01906SEric Kohl             {
431f7c01906SEric Kohl                 /* That's all, quit the loop */
432f7c01906SEric Kohl                 KeepTrying = FALSE;
433f7c01906SEric Kohl 
434f7c01906SEric Kohl                 /* Set status according to SRB status */
435f7c01906SEric Kohl                 if (SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_BAD_FUNCTION ||
436f7c01906SEric Kohl                     SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_BAD_SRB_BLOCK_LENGTH)
437f7c01906SEric Kohl                 {
438f7c01906SEric Kohl                     Status = STATUS_INVALID_DEVICE_REQUEST;
439f7c01906SEric Kohl                 }
440f7c01906SEric Kohl                 else
441f7c01906SEric Kohl                 {
442f7c01906SEric Kohl                     Status = STATUS_IO_DEVICE_ERROR;
443f7c01906SEric Kohl                 }
444f7c01906SEric Kohl             }
445f7c01906SEric Kohl         }
446545692e8SEric Kohl     }
447f7c01906SEric Kohl 
448545692e8SEric Kohl     /* Free the sense buffer */
449f7c01906SEric Kohl     ExFreePoolWithTag(SenseBuffer, TAG_SENSE_DATA);
450f7c01906SEric Kohl 
451545692e8SEric Kohl     DPRINT("PortSendInquiry() done with Status 0x%08X\n", Status);
452f7c01906SEric Kohl 
453f7c01906SEric Kohl     return Status;
454f7c01906SEric Kohl }
455f7c01906SEric Kohl 
456f7c01906SEric Kohl 
457545692e8SEric Kohl 
458f7c01906SEric Kohl static
459f7c01906SEric Kohl NTSTATUS
PortFdoScanBus(_In_ PFDO_DEVICE_EXTENSION DeviceExtension)460f7c01906SEric Kohl PortFdoScanBus(
461f7c01906SEric Kohl     _In_ PFDO_DEVICE_EXTENSION DeviceExtension)
462f7c01906SEric Kohl {
463545692e8SEric Kohl     PPDO_DEVICE_EXTENSION PdoExtension;
464545692e8SEric Kohl     ULONG Bus, Target; //, Lun;
465f7c01906SEric Kohl     NTSTATUS Status;
466f7c01906SEric Kohl 
467545692e8SEric Kohl     DPRINT("PortFdoScanBus(%p)\n", DeviceExtension);
468f7c01906SEric Kohl 
469545692e8SEric Kohl     DPRINT("NumberOfBuses: %lu\n", DeviceExtension->Miniport.PortConfig.NumberOfBuses);
470545692e8SEric Kohl     DPRINT("MaximumNumberOfTargets: %lu\n", DeviceExtension->Miniport.PortConfig.MaximumNumberOfTargets);
471545692e8SEric Kohl     DPRINT("MaximumNumberOfLogicalUnits: %lu\n", DeviceExtension->Miniport.PortConfig.MaximumNumberOfLogicalUnits);
472f7c01906SEric Kohl 
473f7c01906SEric Kohl     /* Scan all buses */
474f7c01906SEric Kohl     for (Bus = 0; Bus < DeviceExtension->Miniport.PortConfig.NumberOfBuses; Bus++)
475f7c01906SEric Kohl     {
476545692e8SEric Kohl         DPRINT("Scanning bus %ld\n", Bus);
477f7c01906SEric Kohl 
478f7c01906SEric Kohl         /* Scan all targets */
479f7c01906SEric Kohl         for (Target = 0; Target < DeviceExtension->Miniport.PortConfig.MaximumNumberOfTargets; Target++)
480f7c01906SEric Kohl         {
481545692e8SEric Kohl             DPRINT("  Scanning target %ld:%ld\n", Bus, Target);
482f7c01906SEric Kohl 
483545692e8SEric Kohl             DPRINT("    Scanning logical unit %ld:%ld:%ld\n", Bus, Target, 0);
484545692e8SEric Kohl             Status = PortCreatePdo(DeviceExtension, Bus, Target, 0, &PdoExtension);
485545692e8SEric Kohl             if (NT_SUCCESS(Status))
486f7c01906SEric Kohl             {
487545692e8SEric Kohl                 /* Scan LUN 0 */
488545692e8SEric Kohl                 Status = PortSendInquiry(PdoExtension);
489545692e8SEric Kohl                 DPRINT("PortSendInquiry returned 0x%08lx\n", Status);
490545692e8SEric Kohl                 if (!NT_SUCCESS(Status))
491545692e8SEric Kohl                 {
492545692e8SEric Kohl                     PortDeletePdo(PdoExtension);
493545692e8SEric Kohl                 }
494545692e8SEric Kohl                 else
495545692e8SEric Kohl                 {
496545692e8SEric Kohl                     DPRINT("VendorId: %.8s\n", PdoExtension->InquiryBuffer->VendorId);
497545692e8SEric Kohl                     DPRINT("ProductId: %.16s\n", PdoExtension->InquiryBuffer->ProductId);
498545692e8SEric Kohl                     DPRINT("ProductRevisionLevel: %.4s\n", PdoExtension->InquiryBuffer->ProductRevisionLevel);
499545692e8SEric Kohl                     DPRINT("VendorSpecific: %.20s\n", PdoExtension->InquiryBuffer->VendorSpecific);
500545692e8SEric Kohl                 }
501545692e8SEric Kohl             }
502f7c01906SEric Kohl 
503545692e8SEric Kohl #if 0
504545692e8SEric Kohl             /* Scan all logical units */
505545692e8SEric Kohl             for (Lun = 1; Lun < DeviceExtension->Miniport.PortConfig.MaximumNumberOfLogicalUnits; Lun++)
506545692e8SEric Kohl             {
507545692e8SEric Kohl                 DPRINT("    Scanning logical unit %ld:%ld:%ld\n", Bus, Target, Lun);
508be88574fSEric Kohl                 Status = PortSendInquiry(DeviceExtension->Device, Bus, Target, Lun);
509545692e8SEric Kohl                 DPRINT("PortSendInquiry returned 0x%08lx\n", Status);
510be88574fSEric Kohl                 if (!NT_SUCCESS(Status))
511be88574fSEric Kohl                     break;
512f7c01906SEric Kohl             }
513545692e8SEric Kohl #endif
514f7c01906SEric Kohl         }
515f7c01906SEric Kohl     }
516f7c01906SEric Kohl 
517545692e8SEric Kohl     DPRINT("PortFdoScanBus() done!\n");
518f7c01906SEric Kohl 
519f7c01906SEric Kohl     return STATUS_SUCCESS;
520f7c01906SEric Kohl }
521f7c01906SEric Kohl 
522f7c01906SEric Kohl 
523b2c6c07dSEricKohl static
524b2c6c07dSEricKohl NTSTATUS
PortFdoQueryBusRelations(_In_ PFDO_DEVICE_EXTENSION DeviceExtension,_Out_ PULONG_PTR Information)525b2c6c07dSEricKohl PortFdoQueryBusRelations(
526b2c6c07dSEricKohl     _In_ PFDO_DEVICE_EXTENSION DeviceExtension,
527b2c6c07dSEricKohl     _Out_ PULONG_PTR Information)
528b2c6c07dSEricKohl {
529b2c6c07dSEricKohl     NTSTATUS Status = STATUS_SUCCESS;;
530b2c6c07dSEricKohl 
531b2c6c07dSEricKohl     DPRINT1("PortFdoQueryBusRelations(%p %p)\n",
532b2c6c07dSEricKohl             DeviceExtension, Information);
533b2c6c07dSEricKohl 
534f7c01906SEric Kohl     Status = PortFdoScanBus(DeviceExtension);
535f7c01906SEric Kohl 
536545692e8SEric Kohl     DPRINT1("Units found: %lu\n", DeviceExtension->PdoCount);
537be88574fSEric Kohl 
538b2c6c07dSEricKohl     *Information = 0;
539b2c6c07dSEricKohl 
540b2c6c07dSEricKohl     return Status;
541b2c6c07dSEricKohl }
542b2c6c07dSEricKohl 
543b2c6c07dSEricKohl 
544dd0027baSEric Kohl static
545dd0027baSEric Kohl NTSTATUS
PortFdoFilterRequirements(PFDO_DEVICE_EXTENSION DeviceExtension,PIRP Irp)546dd0027baSEric Kohl PortFdoFilterRequirements(
547dd0027baSEric Kohl     PFDO_DEVICE_EXTENSION DeviceExtension,
548dd0027baSEric Kohl     PIRP Irp)
549dd0027baSEric Kohl {
550dd0027baSEric Kohl     PIO_RESOURCE_REQUIREMENTS_LIST RequirementsList;
551dd0027baSEric Kohl 
552dd0027baSEric Kohl     DPRINT1("PortFdoFilterRequirements(%p %p)\n", DeviceExtension, Irp);
553dd0027baSEric Kohl 
554dd0027baSEric Kohl     /* Get the bus number and the slot number */
555dd0027baSEric Kohl     RequirementsList =(PIO_RESOURCE_REQUIREMENTS_LIST)Irp->IoStatus.Information;
556dd0027baSEric Kohl     if (RequirementsList != NULL)
557dd0027baSEric Kohl     {
558dd0027baSEric Kohl         DeviceExtension->BusNumber = RequirementsList->BusNumber;
559dd0027baSEric Kohl         DeviceExtension->SlotNumber = RequirementsList->SlotNumber;
560dd0027baSEric Kohl     }
561dd0027baSEric Kohl 
562dd0027baSEric Kohl     return STATUS_SUCCESS;
563dd0027baSEric Kohl }
564dd0027baSEric Kohl 
565dd0027baSEric Kohl 
566b2c6c07dSEricKohl NTSTATUS
567b2c6c07dSEricKohl NTAPI
PortFdoScsi(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp)568f7c01906SEric Kohl PortFdoScsi(
569f7c01906SEric Kohl     _In_ PDEVICE_OBJECT DeviceObject,
570f7c01906SEric Kohl     _In_ PIRP Irp)
571f7c01906SEric Kohl {
572f7c01906SEric Kohl     PFDO_DEVICE_EXTENSION DeviceExtension;
573f7c01906SEric Kohl //    PIO_STACK_LOCATION Stack;
574f7c01906SEric Kohl     ULONG_PTR Information = 0;
575f7c01906SEric Kohl     NTSTATUS Status = STATUS_NOT_SUPPORTED;
576f7c01906SEric Kohl 
577545692e8SEric Kohl     DPRINT("PortFdoScsi(%p %p)\n", DeviceObject, Irp);
578f7c01906SEric Kohl 
579f7c01906SEric Kohl     DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
580f7c01906SEric Kohl     ASSERT(DeviceExtension);
581f7c01906SEric Kohl     ASSERT(DeviceExtension->ExtensionType == FdoExtension);
582f7c01906SEric Kohl 
583f7c01906SEric Kohl //    Stack = IoGetCurrentIrpStackLocation(Irp);
584f7c01906SEric Kohl 
585f7c01906SEric Kohl 
586f7c01906SEric Kohl     Irp->IoStatus.Information = Information;
587f7c01906SEric Kohl     Irp->IoStatus.Status = Status;
588f7c01906SEric Kohl     IoCompleteRequest(Irp, IO_NO_INCREMENT);
589f7c01906SEric Kohl 
590f7c01906SEric Kohl     return Status;
591f7c01906SEric Kohl }
592f7c01906SEric Kohl 
593f7c01906SEric Kohl 
594f7c01906SEric Kohl NTSTATUS
595f7c01906SEric Kohl NTAPI
PortFdoPnp(_In_ PDEVICE_OBJECT DeviceObject,_In_ PIRP Irp)596b2c6c07dSEricKohl PortFdoPnp(
597b2c6c07dSEricKohl     _In_ PDEVICE_OBJECT DeviceObject,
598b2c6c07dSEricKohl     _In_ PIRP Irp)
599b2c6c07dSEricKohl {
600b2c6c07dSEricKohl     PFDO_DEVICE_EXTENSION DeviceExtension;
601b2c6c07dSEricKohl     PIO_STACK_LOCATION Stack;
602b2c6c07dSEricKohl     ULONG_PTR Information = 0;
603b2c6c07dSEricKohl     NTSTATUS Status = STATUS_NOT_SUPPORTED;
604b2c6c07dSEricKohl 
605b2c6c07dSEricKohl     DPRINT1("PortFdoPnp(%p %p)\n",
606b2c6c07dSEricKohl             DeviceObject, Irp);
607b2c6c07dSEricKohl 
608b2c6c07dSEricKohl     DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
609b2c6c07dSEricKohl     ASSERT(DeviceExtension);
610b2c6c07dSEricKohl     ASSERT(DeviceExtension->ExtensionType == FdoExtension);
611b2c6c07dSEricKohl 
612b2c6c07dSEricKohl     Stack = IoGetCurrentIrpStackLocation(Irp);
613b2c6c07dSEricKohl 
614b2c6c07dSEricKohl     switch (Stack->MinorFunction)
615b2c6c07dSEricKohl     {
616b2c6c07dSEricKohl         case IRP_MN_START_DEVICE: /* 0x00 */
617b2c6c07dSEricKohl             DPRINT1("IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
618b2c6c07dSEricKohl             Status = PortFdoStartDevice(DeviceExtension, Irp);
619b2c6c07dSEricKohl             break;
620b2c6c07dSEricKohl 
621b2c6c07dSEricKohl         case IRP_MN_QUERY_REMOVE_DEVICE: /* 0x01 */
622b2c6c07dSEricKohl             DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_REMOVE_DEVICE\n");
623b2c6c07dSEricKohl             break;
624b2c6c07dSEricKohl 
625b2c6c07dSEricKohl         case IRP_MN_REMOVE_DEVICE: /* 0x02 */
626b2c6c07dSEricKohl             DPRINT1("IRP_MJ_PNP / IRP_MN_REMOVE_DEVICE\n");
627b2c6c07dSEricKohl             break;
628b2c6c07dSEricKohl 
629b2c6c07dSEricKohl         case IRP_MN_CANCEL_REMOVE_DEVICE: /* 0x03 */
630b2c6c07dSEricKohl             DPRINT1("IRP_MJ_PNP / IRP_MN_CANCEL_REMOVE_DEVICE\n");
631b2c6c07dSEricKohl             break;
632b2c6c07dSEricKohl 
633b2c6c07dSEricKohl         case IRP_MN_STOP_DEVICE: /* 0x04 */
634b2c6c07dSEricKohl             DPRINT1("IRP_MJ_PNP / IRP_MN_STOP_DEVICE\n");
635b2c6c07dSEricKohl             break;
636b2c6c07dSEricKohl 
637b2c6c07dSEricKohl         case IRP_MN_QUERY_STOP_DEVICE: /* 0x05 */
638b2c6c07dSEricKohl             DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_STOP_DEVICE\n");
639b2c6c07dSEricKohl             break;
640b2c6c07dSEricKohl 
641b2c6c07dSEricKohl         case IRP_MN_CANCEL_STOP_DEVICE: /* 0x06 */
642b2c6c07dSEricKohl             DPRINT1("IRP_MJ_PNP / IRP_MN_CANCEL_STOP_DEVICE\n");
643b2c6c07dSEricKohl             break;
644b2c6c07dSEricKohl 
645b2c6c07dSEricKohl         case IRP_MN_QUERY_DEVICE_RELATIONS: /* 0x07 */
646b2c6c07dSEricKohl             DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS\n");
647b2c6c07dSEricKohl             switch (Stack->Parameters.QueryDeviceRelations.Type)
648b2c6c07dSEricKohl             {
649b2c6c07dSEricKohl                 case BusRelations:
650b2c6c07dSEricKohl                     DPRINT1("    IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n");
651b2c6c07dSEricKohl                     Status = PortFdoQueryBusRelations(DeviceExtension, &Information);
652b2c6c07dSEricKohl                     break;
653b2c6c07dSEricKohl 
654b2c6c07dSEricKohl                 case RemovalRelations:
655b2c6c07dSEricKohl                     DPRINT1("    IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n");
656b2c6c07dSEricKohl                     return ForwardIrpAndForget(DeviceExtension->LowerDevice, Irp);
657b2c6c07dSEricKohl 
658b2c6c07dSEricKohl                 default:
659b2c6c07dSEricKohl                     DPRINT1("    IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / Unknown type 0x%lx\n",
660b2c6c07dSEricKohl                             Stack->Parameters.QueryDeviceRelations.Type);
661b2c6c07dSEricKohl                     return ForwardIrpAndForget(DeviceExtension->LowerDevice, Irp);
662b2c6c07dSEricKohl             }
663b2c6c07dSEricKohl             break;
664b2c6c07dSEricKohl 
665b2c6c07dSEricKohl         case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: /* 0x0d */
666b2c6c07dSEricKohl             DPRINT1("IRP_MJ_PNP / IRP_MN_FILTER_RESOURCE_REQUIREMENTS\n");
667dd0027baSEric Kohl             PortFdoFilterRequirements(DeviceExtension, Irp);
668b2c6c07dSEricKohl             return ForwardIrpAndForget(DeviceExtension->LowerDevice, Irp);
669b2c6c07dSEricKohl 
670b2c6c07dSEricKohl         case IRP_MN_QUERY_PNP_DEVICE_STATE: /* 0x14 */
671b2c6c07dSEricKohl             DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_PNP_DEVICE_STATE\n");
672b2c6c07dSEricKohl             break;
673b2c6c07dSEricKohl 
674b2c6c07dSEricKohl         case IRP_MN_DEVICE_USAGE_NOTIFICATION: /* 0x16 */
675b2c6c07dSEricKohl             DPRINT1("IRP_MJ_PNP / IRP_MN_DEVICE_USAGE_NOTIFICATION\n");
676b2c6c07dSEricKohl             break;
677b2c6c07dSEricKohl 
678b2c6c07dSEricKohl         case IRP_MN_SURPRISE_REMOVAL: /* 0x17 */
679b2c6c07dSEricKohl             DPRINT1("IRP_MJ_PNP / IRP_MN_SURPRISE_REMOVAL\n");
680b2c6c07dSEricKohl             break;
681b2c6c07dSEricKohl 
682b2c6c07dSEricKohl         default:
683b2c6c07dSEricKohl             DPRINT1("IRP_MJ_PNP / Unknown IOCTL 0x%lx\n", Stack->MinorFunction);
684b2c6c07dSEricKohl             return ForwardIrpAndForget(DeviceExtension->LowerDevice, Irp);
685b2c6c07dSEricKohl     }
686b2c6c07dSEricKohl 
687b2c6c07dSEricKohl     Irp->IoStatus.Information = Information;
688b2c6c07dSEricKohl     Irp->IoStatus.Status = Status;
689b2c6c07dSEricKohl     IoCompleteRequest(Irp, IO_NO_INCREMENT);
690b2c6c07dSEricKohl 
691b2c6c07dSEricKohl     return Status;
692b2c6c07dSEricKohl }
693b2c6c07dSEricKohl 
694b2c6c07dSEricKohl /* EOF */
695