/*++
*******************************************************************************
* Copyright (c) 1990-99 Digi International Inc., All Rights Reserved          *
*                                                                             *
* This software contains proprietary and confidential information of Digi     *
* International Inc.  By accepting transfer of this copy, Recipient agrees    *
* to retain this software in confidence, to prevent disclosure to others,     *
* and to make no use of this software other than that for which it was        *
* delivered.  This is an unpublished copyrighted work of Digi International   *
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly   *
* prohibited.                                                                 *
*                                                                             *
* Restricted Rights Legend                                                    *
*                                                                             *
* Use, duplication, or disclosure by the Government is subject to             *
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in         *
* Technical Data and Computer Software clause at DFARS 252.227-7031 or        *
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -          *
* Restricted Rights at 48 CFR 52.227-19, as applicable.                       *
*                                                                             *
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343          *
*                                                                             *
*******************************************************************************

Module Name:

   dxbpnp.c

Abstract:

   NT 5.0 PNP Irp handler routines.

Author:

   Michael Zarns

--*/
#include "digidxb.h"
#include <stdio.h>
#include "sraproto.h"

#if NT5_BUILD
typedef struct ADAPTER_OPTION
{
    PCWSTR             Name;            // Adapter Option name string
    PDXB_ADAPTER_START StartRoutine;    // Adapter Startup routine

} ADAPTER_OPTION, *PADAPTER_OPTION;


// Table of adapter startup routines
static ADAPTER_OPTION Options[] = 
{
    { DXB_REGVAL_OPTION_ENUM_SYNCPORT, dxbEpcStartAdapterSyncport },
    { DXB_REGVAL_OPTION_ENUM_PRIME,    dxbEpcStartAdapterPrime    },
    { DXB_REGVAL_OPTION_ENUM_CLYDE,    dxbClydeStartNonCsuAdapter },
    { DXB_REGVAL_OPTION_ENUM_HOUSTON,  dxbHlcStartAdapterDense    },
    { DXB_REGVAL_OPTION_ENUM_LONDON,   dxbHlcStartAdapterDense    },
    { DXB_REGVAL_OPTION_ENUM_CHASKA,   dxbHlcStartAdapterDense    },
    { DXB_REGVAL_OPTION_ENUM_ACCELEPORT_2000,   dxbRapStartAdapterTigger    }
};

#define OptionCount     ( sizeof Options / sizeof Options[0] )


struct PNP_IRP_NAME
{
    ULONG Minor;
    PCSTR Name;
};

STATIC const struct DXB_MINOR_TABLE PnpIrps[] = 
{
    IRP_MN_START_DEVICE,                    "IRP_MN_START_DEVICE",
    IRP_MN_QUERY_STOP_DEVICE,               "IRP_MN_QUERY_STOP_DEVICE",
    IRP_MN_STOP_DEVICE,                         "IRP_MN_STOP_DEVICE",
    IRP_MN_CANCEL_STOP_DEVICE,          "IRP_MN_CANCEL_STOP_DEVICE",
    IRP_MN_QUERY_REMOVE_DEVICE,             "IRP_MN_QUERY_REMOVE_DEVICE",
    IRP_MN_REMOVE_DEVICE,                   "IRP_MN_REMOVE_DEVICE",
    IRP_MN_CANCEL_REMOVE_DEVICE,            "IRP_MN_CANCEL_REMOVE_DEVICE",
    IRP_MN_SURPRISE_REMOVAL,                "IRP_MN_SURPRISE_REMOVAL",
    IRP_MN_QUERY_CAPABILITIES,          "IRP_MN_QUERY_CAPABILITIES",
    IRP_MN_QUERY_PNP_DEVICE_STATE,      "IRP_MN_QUERY_PNP_DEVICE_STATE",
    IRP_MN_FILTER_RESOURCE_REQUIREMENTS,"IRP_MN_FILTER_RESOURCE_REQUIREMENTS",
    IRP_MN_DEVICE_USAGE_NOTIFICATION,   "IRP_MN_DEVICE_USAGE_NOTIFICATION",
    IRP_MN_QUERY_DEVICE_RELATIONS,      "IRP_MN_QUERY_DEVICE_RELATIONS",
    IRP_MN_QUERY_RESOURCES,                 "IRP_MN_QUERY_RESOURCES",
    IRP_MN_QUERY_RESOURCE_REQUIREMENTS, "IRP_MN_QUERY_RESOURCE_REQUIREMENTS",
    IRP_MN_QUERY_ID,                            "IRP_MN_QUERY_ID",
    IRP_MN_QUERY_DEVICE_TEXT,               "IRP_MN_QUERY_DEVICE_TEXT",
    IRP_MN_QUERY_BUS_INFORMATION,       "IRP_MN_QUERY_BUS_INFORMATION",
    IRP_MN_QUERY_INTERFACE,                 "IRP_MN_QUERY_INTERFACE",
    IRP_MN_READ_CONFIG,                         "IRP_MN_READ_CONFIG",
    IRP_MN_WRITE_CONFIG,                    "IRP_MN_WRITE_CONFIG",
    IRP_MN_EJECT,                               "IRP_MN_EJECT",
    IRP_MN_SET_LOCK,                            "IRP_MN_SET_LOCK",
    IRP_MN_QUERY_LEGACY_BUS_INFORMATION,"IRP_MN_QUERY_LEGACY_BUS_INFORMATION"
};

PCSTR 
DxbGetPnpIrpName( ULONG Minor )
{
    return DxbGetIrpName( Minor, PnpIrps, sizeof PnpIrps / sizeof PnpIrps[0] );
}


STATIC NTSTATUS
DxbPnpIrpComplete( 
    IN PDEVICE_OBJECT pDevice,
    IN PIRP           pIrp,
    IN PKEVENT        pEvent )
{
    KeSetEvent( pEvent, IO_NO_INCREMENT, FALSE );
    return STATUS_MORE_PROCESSING_REQUIRED;
}


STATIC NTSTATUS
DxbPnpForwardIrp( 
    IN PDEVICE_OBJECT pNextLowerDriver,
    IN PIRP           pIrp )
{
    ASSERT( pNextLowerDriver );
    ASSERT( pIrp );
    IoSkipCurrentIrpStackLocation( pIrp );
    return IoCallDriver( pNextLowerDriver, pIrp );
}



STATIC NTSTATUS
DxbPnpForwardIrpWait( 
    IN PDEVICE_OBJECT pNextLowerDriver,
    IN PIRP           pIrp )
{
    KEVENT Event;
    NTSTATUS Status;

    KeInitializeEvent( &Event, SynchronizationEvent, FALSE );

    IoCopyCurrentIrpStackLocationToNext( pIrp );
    IoSetCompletionRoutine( pIrp, DxbPnpIrpComplete, &Event, TRUE, TRUE, TRUE );

    Status = IoCallDriver( pNextLowerDriver, pIrp );
    if ( Status == STATUS_PENDING )
    {
        DigiDump( DIGIPNPLOUD, ( "Dxb!PnpForwardIrp: waiting for Irp 0x%x completion...", pIrp ) );
        KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL );
        Status = pIrp->IoStatus.Status;
        DigiDump( DIGIPNPLOUD, ( "Dxb!PnpForwardIrp: Irp completed   0x%x, status 0x%x", pIrp, Status ) );
    }
    return Status;
}


STATIC VOID
DumpResourceList( 
    IN PCM_RESOURCE_LIST pResourceList )
{
    if ( pResourceList )
    {
        PCM_FULL_RESOURCE_DESCRIPTOR pFull = pResourceList->List;
        USHORT frd;

        DigiDump( DIGIPNPLOUD, ( "ResourceList:\n"
                                 "   Count %d\n", pResourceList->Count ) );

        if ( pFull )
        {
            for ( frd = 0; frd < pResourceList->Count; frd++ )
            {
                PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartial;
                ULONG prd;

                DigiDump( DIGIPNPLOUD, ( "   List[%d]\n"
                                         "      InterfaceType 0x%x\n"
                                         "      BusNumber 0x%x\n"
                                         "      PartialResourceList\n"
                                         "         Version 0x%x\n"
                                         "         Revision 0x%x\n"
                                         "         Count 0x%x\n",
                                         frd, pFull->InterfaceType, pFull->BusNumber,
                                         pFull->PartialResourceList.Version,
                                         pFull->PartialResourceList.Revision,
                                         pFull->PartialResourceList.Count ) );
                
                // Walk through all partial descriptors
                pPartial = pFull->PartialResourceList.PartialDescriptors;
                for ( prd = 0; prd < pFull->PartialResourceList.Count; prd++ )
                {
                    DigiDump( DIGIPNPLOUD, ( "         PartialDescriptor[%d]\n"
                                             "            Type 0x%x\n"
                                             "            ShareDisposition 0x%x\n"
                                             "            Flags 0x%x\n",
                                             prd, pPartial->Type,
                                             pPartial->ShareDisposition,
                                             pPartial->Flags ) );
                    switch ( pPartial->Type )
                    {
                        case CmResourceTypePort:
                            DigiDump( DIGIPNPLOUD, ( "            Port\n"
                                                     "               Start  0x%x%x\n"
                                                     "               Length 0x%x\n",
                                                     pPartial->u.Port.Start.HighPart,
                                                     pPartial->u.Port.Start.LowPart,
                                                     pPartial->u.Port.Length ) );
                            break;

                        case CmResourceTypeMemory:
                            DigiDump( DIGIPNPLOUD, ( "            Memory\n"
                                                     "               Start  0x%x%x\n"
                                                     "               Length 0x%x\n",
                                                     pPartial->u.Memory.Start.HighPart,
                                                     pPartial->u.Memory.Start.LowPart,
                                                     pPartial->u.Memory.Length ) );
                            break;

                        case CmResourceTypeInterrupt:
                            DigiDump( DIGIPNPLOUD, ( "            Interrupt\n"
                                                      "               Level    0x%x\n"
                                                      "               Vector   0x%x\n"
                                                      "               Affinity 0x%x\n",
                                                      pPartial->u.Interrupt.Level,
                                                      pPartial->u.Interrupt.Vector,
                                                      pPartial->u.Interrupt.Affinity ) );
                            break;

                        case CmResourceTypeDevicePrivate:
                            DigiDump( DIGIPNPLOUD, ( "            DevicePrivate\n"
                                                      "               Data[0] 0x%x\n"
                                                      "               Data[1] 0x%x\n"
                                                      "               Data[2] 0x%x\n",
                                                      pPartial->u.DevicePrivate.Data[0],
                                                      pPartial->u.DevicePrivate.Data[1],
                                                      pPartial->u.DevicePrivate.Data[2] ) );
                            break;

                        default:
                            DigiDump( DIGIPNPLOUD, ( "            Unknown type!\n" ) );
                            break;
                    }
                    pPartial++;
                }
                // Next FULL_RESOURCE_DESCRIPTOR starts immediately after.
                pFull = (PVOID) pPartial;
            }
        }
    }
}


NTSTATUS
DxbPnpStartDevice(
    PDXB_DEVICE_EXTENSION pExt, 
    PIRP                  pIrp, 
    PIO_STACK_LOCATION    pIos )
/*++

Description:

    Handle an IRP_MN_START_DEVICE Irp.  The PnP Manager sends this IRP after it has 
    assigned resources, if any, to the device. The device may have been recently 
    enumerated and is being started for the first time, or the device may be 
    restarting after being stopped for resource reconfiguration. 

    The PnP Manager sends this IRP at IRQL PASSIVE_LEVEL in the context of a system 
    thread.

Parameters:

    pExt
        Device extension for our FDO which describes the adapter.
    
    pIrp
        Request.  May be NULL, if called from within IRP_MN_SET_POWER to come out of
        hibernation.

    pIos
        Parameters passed along with Irp. May be NULL, if called from within 
        IRP_MN_SET_POWER to come out of hibernation.

Return Value:

    STATUS_SUCCESS, or appropriate error status.

--*/
{
    NTSTATUS Status;
    HANDLE hkDriver = NULL;
    HANDLE hkParameters = NULL;
    DIGI_RESOURCE_LIST Resources;
    UNICODE_STRING Option = {0};
    BOOLEAN fCalledInternally = pIrp == NULL ? TRUE : FALSE;

    SRAPRINT( DIGIPNP, ( "Dxb!PnpStartDevice: pExt 0x%x, PnpState 0x%x\n", pExt, pExt->PnpState ) );    

    if ( pExt->PnpState != DXBPNP_ADDED )
    {
        SRAPRINT( DIGIPNP, ( "Dxb!StartDevice: *** Bad PnpState 0x%x! ***\n", pExt->PnpState ) );
        goto Exit;
    }
    if ( !fCalledInternally )
    {
        Status = DxbPnpForwardIrpWait( pExt->pNextDeviceObject, pIrp );

        if ( NT_ERROR( Status ) )
            goto Exit;

        SRAPRINT( DIGIPNPLOUD, ( "AllocatedResources\n" ) );
        DumpResourceList( pIos->Parameters.StartDevice.AllocatedResources );
        SRAPRINT( DIGIPNPLOUD, ( "AllocatedResourcesTranslated\n" ) );
        DumpResourceList( pIos->Parameters.StartDevice.AllocatedResourcesTranslated );

        Status = DigiAssignPnpResources( pIos->Parameters.StartDevice.AllocatedResources,
                                         pIos->Parameters.StartDevice.AllocatedResourcesTranslated,
                                         &Resources );
        if ( NT_ERROR( Status ) )
        {
            SRAPRINT( DIGIPNP, ( "Dxb!PnpStartDevice: Error parsing resources lists, status 0x%x!\n", Status ) );
            goto Exit;
        }

        pExt->Hardware.BusType = Resources.BusType;
        pExt->PowerResources = Resources;   // set backup copy of resources
    }
    else
    {
        Resources = pExt->PowerResources;   // use backup copy of resources
    }

    pExt->bShutdown = FALSE;




    // Open adapter registry key, i.e. CurrentControlSet\Class\{...}\0001.
    Status = IoOpenDeviceRegistryKey( pExt->pPdo, PLUGPLAY_REGKEY_DRIVER, MAXIMUM_ALLOWED, &hkDriver );
    if ( NT_ERROR( Status ) )
    {
        SRAPRINT( DIGIPNP, ( "Dxb!PnpStartDevice: Error opening registry key, status 0x%x!\n", Status ) );
        goto Exit;
    }

    if ( NT_SUCCESS( Status ) )
        Status = DigiRegOpenSubkey( &hkParameters, hkDriver, DXB_REGKEY_PARAMETERS );

    if ( !NT_SUCCESS ( Status ) )
    {
        SRAPRINT( DIGIPNP, ( "Dxb!PnpStartDevice: Error opening Parameters registry subkey, status 0x%x!\n", Status ) );
    }

    if ( NT_SUCCESS( Status ) )
    {
        // First time a card comes up, the INF sets Spoof to TRUE, telling us to
        // spoof startup.  Skip configuration and clear the spoof flag so next time
        // we come up completely.
        ULONG Spoof;
        DigiRegQueryDword( hkParameters, DXB_REGVAL_SPOOF, &Spoof, DigiRegFlagOptional, FALSE );
        DigiRegWriteDword( hkParameters, DXB_REGVAL_SPOOF, 0 );
        pExt->fSpoof = Spoof != FALSE;
        if ( Spoof )
            goto EnumerateChildren;
    }       

    if ( NT_SUCCESS( Status ) )
        Status = DigiRegQuerySz( hkParameters, DXB_REGVAL_OPTION, &Option, DigiRegFlagRequired, NULL );

    if ( !NT_SUCCESS ( Status ) )
    {
        SRAPRINT( DIGIPNP, ( "Dxb!PnpStartDevice: Error opening registry key DigiRegFlagRequired, status 0x%x!\n", Status ) );
    }

    if ( NT_SUCCESS( Status ) )
    {
        // Find and call the adapter startup routine based on the Option found.
        ULONG i;
        Status = STATUS_UNSUCCESSFUL;

        for ( i = 0; i < OptionCount; i++ )
        {
            UNICODE_STRING OptionName;
            RtlInitUnicodeString( &OptionName, Options[i].Name );
            if ( RtlEqualUnicodeString( &Option, &OptionName, TRUE ) )
            {
                Status = Options[i].StartRoutine( pExt, &OptionName, hkParameters, &Resources );

                break;
            }
        }
        if ( i == OptionCount )
        {
            SRAPRINT( DIGIERROR, ( "Dxb!StartDevice: Option %S not found!\n", Option.Buffer ) );
        }
    }

    // Create Symbolic links
    if ( NT_SUCCESS( Status ) )
    {
            WCHAR buf[16];
            UNICODE_STRING UniqueName       = {0};
            UNICODE_STRING UniqueDeviceName = {0};
            UNICODE_STRING DeviceName       = {0};
            UNICODE_STRING SymbolicName     = {0};

            Status = DigiStrDeviceName( &DeviceName, pExt->Name.Buffer );
            if ( !NT_SUCCESS ( Status ) )
            {
                SRAPRINT( DIGIPNP, ( "Dxb!PnpStartDevice: Error creating device name, status 0x%x!\n", Status ) );
                goto Exit;
            }
            if ( NT_SUCCESS( Status ) )
                Status = DigiStrSymbolicName( &SymbolicName, pExt->Name.Buffer );
            if ( !NT_SUCCESS ( Status ) )
            {
                SRAPRINT( DIGIPNP, ( "Dxb!PnpStartDevice: Error creating symbolic name, status 0x%x!\n", Status ) );
                goto Exit;
            }
            
            if (NT_SUCCESS(Status))
                Status = DxbDeviceUniqueName(pExt->UniqueId, &UniqueName);

            if ( !NT_SUCCESS ( Status ) )
            {
                SRAPRINT( DIGIPNP, ( "Dxb!PnpStartDevice: Error parsing unique device name, status 0x%x!\n", Status ) );
                goto Exit;
            }
            

            if (NT_SUCCESS(Status))
                Status = DigiStrDeviceName( &UniqueDeviceName, UniqueName.Buffer);
            
            if ( !NT_SUCCESS ( Status ) )
            {
                SRAPRINT( DIGIPNP, ( "Dxb!PnpStartDevice: Error creating unique device name, status 0x%x!\n", Status ) );
            }
            

            // Now, make sure symbolic link to FDO does not exist then create it to point
            // to current FDO
            if (NT_SUCCESS(Status))
            {
                IoDeleteSymbolicLink( &DeviceName );

                Status = IoCreateSymbolicLink( &DeviceName, &UniqueDeviceName );
            }

            if ( !NT_SUCCESS ( Status ) )
            {
                SRAPRINT( DIGIPNP, ( "Dxb!PnpStartDevice: Error creating symbolic link, status 0x%x!\n", Status ) );
            }
            
            // Finally, create DOSDevices symbolic link - may already exist so ignore errors
            if ( NT_SUCCESS( Status ) )
            {
                (void)IoCreateSymbolicLink( &SymbolicName, &DeviceName );
            }

            if (SymbolicName.Buffer)
                DigiStrFreeUnicodeString( &SymbolicName );
            if (DeviceName.Buffer)
                DigiStrFreeUnicodeString( &DeviceName );
            if (UniqueName.Buffer)
                DigiStrFreeUnicodeString( &UniqueName );
            if (UniqueDeviceName.Buffer)
                DigiStrFreeUnicodeString( &UniqueDeviceName );
    }


    if ( NT_SUCCESS( Status ) )
    {
        InsertTailList( &gDeviceExtList, &pExt->ExtListEntry );
        pExt->PnpState = DXBPNP_STARTED;
        IoRegisterShutdownNotification( pExt->pDeviceObject );
    }

EnumerateChildren:

    if ( NT_SUCCESS( Status ) )
        (void)DxbChildCreateChildList( pExt, hkParameters );

Exit:
    if ( Option.Buffer )
        DigiRegFreeString( &Option );

    if ( hkParameters )
        DigiRegCloseKey( hkParameters );

    if ( hkDriver )
        ZwClose( hkDriver );

    if ( !fCalledInternally )
        dxbIoCompleteRequest( pIrp, Status, 0 );
            
    if ( !NT_SUCCESS( Status ) || pExt->PnpState != DXBPNP_STARTED)
    {
        SRAPRINT( DBGLVL_ALWAYS, ( "Dxb!PnpStartDevice: Errors encountered (0x%x), calling StopAdapter routine\n", Status ) );
        SRAPRINT( DBGLVL_ALWAYS, ( "Dxb!PnpStartDevice: pExt is 0x%x, PnpState 0x%x\n", pExt, pExt->PnpState) );

        if (pExt->StopAdapter)
        {
            pExt->StopAdapter( pExt );
        }
        SRAPRINT( DBGLVL_ALWAYS, ( "Dxb!PnpStartDevice: back from StopAdapter\n") );
    }

    return Status;
}


NTSTATUS
DxbPnpQueryStopDevice(
    PDXB_DEVICE_EXTENSION pExt, 
    PIRP                  pIrp, 
    PIO_STACK_LOCATION    pIos )
/*++

Description:

    Handle an IRP_MN_QUERY_STOP_DEVICE Irp.  The PnP Manager sends this IRP to query 
    whether a device can be stopped for resource reconfiguration.

    The PnP Manager sends this IRP at IRQL PASSIVE_LEVEL in the context of a system 
    thread.

Parameters:

    pExt
        Device extension for our FDO which describes the adapter.
    
    pIrp
        Request.

    pIos
        Parameters passed along with Irp.

Return Value:

    STATUS_SUCCESS, or appropriate error status.

--*/
{
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
        
    SRAPRINT( DIGIPNP, ( "Dxb!QueryStopDevice: pExt 0x%x, PnpState 0x%x\n", pExt, pExt->PnpState ) );
    // TODO: Check if we can stop the device...
#if 0
    DigiAcquireSpinLock( &pExt->DxbLock );
    if ( IsListEmpty( &pExt->StreamList ) )
    {
#endif
        Status = STATUS_SUCCESS;
        pExt->fHoldNewRequests = TRUE;
#if 0
    }
    DigiReleaseSpinLock( &pExt->DxbLock );
#endif
    if ( NT_SUCCESS( Status ) )
    {
        // We can stop it, continue
        pIrp->IoStatus.Status = Status;
        Status = DxbPnpForwardIrp( pExt->pNextDeviceObject, pIrp );
    }
    else
        // We can't stop it, reject the request now
        dxbIoCompleteRequest( pIrp, Status, 0 );

    return Status;
}



NTSTATUS
DxbPnpStopDevice(
    PDXB_DEVICE_EXTENSION pExt, 
    PIRP                  pIrp, 
    PIO_STACK_LOCATION    pIos )
/*++

Description:

    Handle an IRP_MN_STOP_DEVICE Irp.  The PnP Manager sends this IRP to stop a device
    so it can reconfigure its hardware resources. The PnP Manager only sends this IRP 
    if a prior IRP_MN_QUERY_STOP_DEVICE completed successfully.

    The PnP Manager sends this IRP at IRQL PASSIVE_LEVEL in the context of a system 
    thread.

Parameters:

    pExt
        Device extension for our FDO which describes the adapter.
    
    pIrp
        Request.  May be NULL, if called from within IRP_MN_SET_POWER to hibernate.

    pIos
        Parameters passed along with Irp.  May be NULL, if called from within 
        IRP_MN_SET_POWER to hibernate.

Return Value:

    This routine must return STATUS_SUCCESS.

--*/
{
    BOOLEAN fCalledInternally = pIrp == NULL ? TRUE : FALSE;
    NTSTATUS Status = STATUS_SUCCESS;
    PDEVICE_OBJECT const pNext = pExt->pNextDeviceObject;

    SRAPRINT( DIGIPNP, ( "Dxb!PnpStopDevice: pExt 0x%x, PnpState 0x%x\n", pExt, pExt->PnpState ) );
    
    if (pExt->PnpState == DXBPNP_STARTED)
    {
        pExt->PnpState = DXBPNP_ADDED;

        if (!fCalledInternally)
        {
            // Child PDO's are created on a Start Device - we have to remove them on a Stop Device   
            if (ChildrenRemoved(pExt))
            {
#if 0
// XXX Not required I think - PnP already understands children should be
// XXX handled!
                // If any child devices were found - notify pnp system of the removal.
                IoInvalidateDeviceRelations (pExt->pPdo, BusRelations);
                DigiDump( DIGIINIT, ("Dxb!StopDevice: InvalidateDeviceRelations\n"));
#endif
            }  
        }  
        RemoveEntryList( &pExt->ExtListEntry );
        pExt->StopAdapter( pExt );
        IoUnregisterShutdownNotification( pExt->pDeviceObject );
    }
    else
    {
        SRAPRINT( DIGIWARN, ( "Dxb!StopDevice: *** Bad PnpState 0x%x! ***\n", pExt->PnpState ) );
    }

    if (!fCalledInternally)
    {
        Status = DxbPnpForwardIrp(pNext, pIrp );
    }

    return Status;
}


NTSTATUS
DxbPnpCancelStopDevice(
    PDXB_DEVICE_EXTENSION pExt, 
    PIRP                  pIrp, 
    PIO_STACK_LOCATION    pIos )
/*++

Description:

    Handle an IRP_MN_CANCEL_STOP_DEVICE Irp.  The PnP Manager uses this IRP to inform 
    the drivers for a device that the device will not be stopped for resource 
    reconfiguration.
    
    The PnP Manager sends this IRP at IRQL PASSIVE_LEVEL in the context of a system 
    thread.

Parameters:

    pExt
        Device extension for our FDO which describes the adapter.
    
    pIrp
        Request.

    pIos
        Parameters passed along with Irp.

Return Value:

    STATUS_SUCCESS, or appropriate error status.

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    
    SRAPRINT( DIGIPNP, ( "Dxb!PnpcancelStopDevice: pExt 0x%x, PnpState 0x%x\n", pExt, pExt->PnpState ) );
    
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    Status = DxbPnpForwardIrpWait( pExt->pNextDeviceObject, pIrp );
    if ( NT_SUCCESS( Status ) )
    {
        // TODO: return to running status
        pExt->fHoldNewRequests = FALSE;
    }

    dxbIoCompleteRequest( pIrp, Status, 0 );
    return Status;
}


NTSTATUS
DxbPnpQueryRemoveDevice(
    PDXB_DEVICE_EXTENSION pExt, 
    PIRP                  pIrp, 
    PIO_STACK_LOCATION    pIos )
/*++

Description:

    Handle an IRP_MN_QUERY_REMOVE_DEVICE Irp.  The PnP Manager sends this IRP to 
    inform drivers that a device is about to be removed from the system and to query 
    whether the device can be removed without disrupting the system.

    The PnP Manager sends this IRP at IRQL PASSIVE_LEVEL in the context of a system 
    thread.

Parameters:

    pExt
        Device extension for our FDO which describes the adapter.
    
    pIrp
        Request.

    pIos
        Parameters passed along with Irp.

Return Value:

    STATUS_SUCCESS, or appropriate error status.

--*/
{
    NTSTATUS Status;
    SRAPRINT( DIGIPNP, ( "Dxb!PnpQueryRemoveDevice: pExt 0x%x, PnpState 0x%x\n", pExt, pExt->PnpState ) );

    // TODO: Check if we can remove the device...
    Status = STATUS_SUCCESS;

    if ( NT_SUCCESS( Status ) )
    {
        // We can Remove it, continue
        pIrp->IoStatus.Status = Status;
        pExt->fHoldNewRequests = TRUE;
        Status = DxbPnpForwardIrp( pExt->pNextDeviceObject, pIrp );
    }
    else
        // We can't Remove it, reject the request now
        dxbIoCompleteRequest( pIrp, Status, 0 );

    return Status;
}


NTSTATUS
DxbPnpRemoveDevice(
    PDXB_DEVICE_EXTENSION pExt, 
    PIRP                  pIrp, 
    PIO_STACK_LOCATION    pIos )
/*++

Description:

    Handle an IRP_MN_REMOVE_DEVICE Irp.  The PnP Manager uses this IRP to direct 
    drivers to remove a device from their device list. 

    For an orderly device removal, the PnP Manager sends an IRP_MN_QUERY_REMOVE_DEVICE
    prior to the remove IRP. In this case, the device is in the remove-pending state 
    when the remove IRP arrives. For a surprise-style device removal, the physical 
    device has already been removed so the PnP Manager sends the remove IRP without a 
    prior query-remove. A device can be in any state when it receives a remove IRP as 
    a result of a surprise-style removal.

    The PnP Manager sends this IRP at IRQL PASSIVE_LEVEL in the context of a system 
    thread.

Parameters:

    pExt
        Device extension for our FDO which describes the adapter.
    
    pIrp
        Request.

    pIos
        Parameters passed along with Irp.

Return Value:

    This routine must return STATUS_SUCCESS.

--*/
{
    PDEVICE_OBJECT const pNext = pExt->pNextDeviceObject;
    SRAPRINT( DIGIPNP, ( "Dxb!PnpRemoveDevice: pExt 0x%x, PnpState 0x%x\n", pExt, pExt->PnpState ) );

    pIrp->IoStatus.Status = STATUS_SUCCESS;

    if ( pExt->PnpState == DXBPNP_REMOVED )
    {
        DigiDump( DIGIWARN, ( "*** WARNING: Dxb!PnpRemoveDevice: Got a second Remove on this device, 0x%x!\n", pExt->pDeviceObject  ) );
    }

    if ( pExt->PnpState == DXBPNP_STARTED )
    {
        pExt->PnpState = DXBPNP_REMOVED;
        DxbChildDeleteChildList( pExt );
        RemoveEntryList( &pExt->ExtListEntry );
        pExt->StopAdapter( pExt );
        IoUnregisterShutdownNotification( pExt->pDeviceObject );
    }
#ifdef STRATUS_DEVICE
    SraMiniportHalt(pExt);
#endif
    dxbDeleteDevice( pExt );

    return DxbPnpForwardIrp( pNext, pIrp );
}


NTSTATUS
DxbPnpCancelRemoveDevice(
    PDXB_DEVICE_EXTENSION pExt, 
    PIRP                  pIrp, 
    PIO_STACK_LOCATION    pIos )
/*++

Description:

    Handle an IRP_MN_CANCEL_REMOVE_DEVICE Irp.  The PnP Manager uses this IRP to 
    inform the drivers for a device that the device will not be removed.

    The PnP Manager sends this IRP at IRQL PASSIVE_LEVEL in the context of a system 
    thread.

Parameters:

    pExt
        Device extension for our FDO which describes the adapter.
    
    pIrp
        Request.

    pIos
        Parameters passed along with Irp.

Return Value:

    STATUS_SUCCESS, or appropriate error status.

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    SRAPRINT( DIGIPNP, ( "Dxb!PnpCancelRemoveDevice: pExt 0x%x, PnpState 0x%x\n", pExt, pExt->PnpState ) );

    pIrp->IoStatus.Status = STATUS_SUCCESS;
    Status = DxbPnpForwardIrpWait( pExt->pNextDeviceObject, pIrp );
    if ( NT_SUCCESS( Status ) )
    {
        // TODO: return to running status
        pExt->fHoldNewRequests = FALSE;
    }

    dxbIoCompleteRequest( pIrp, Status, 0 );
    return Status;
}


NTSTATUS
DxbPnpSurpriseRemoval(
    PDXB_DEVICE_EXTENSION pExt, 
    PIRP                  pIrp, 
    PIO_STACK_LOCATION    pIos )
/*++

Description:

    Handle an IRP_MN_SURPRISE_REMOVAL Irp.  The PnP Manager sends this IRP to notify 
    the drivers for a device that the device has been unexpectedly removed from the 
    machine and is no longer available for I/O. 

    The PnP Manager sends this IRP before notifying user-mode components. After this 
    IRP completes, the PnP Manager sends a GUID_TARGET_DEVICE_REMOVE_COMPLETE 
    notification to components that registered for such notification.

    The PnP Manager sends this IRP at IRQL PASSIVE_LEVEL in the context of a system 
    thread.

Parameters:

    pExt
        Device extension for our FDO which describes the adapter.
    
    pIrp
        Request.

    pIos
        Parameters passed along with Irp.

Return Value:

    This routine must return STATUS_SUCCESS.

--*/
{
    PDEVICE_OBJECT const pNext = pExt->pNextDeviceObject;

    SRAPRINT( DIGIPNP, ( "Dxb!PnpSurpriseRemoval: pExt 0x%x, PnpState 0x%x\n", pExt, pExt->PnpState ) );
    
    if (pExt->pNextDeviceObject == NULL)
    {
        DigiDump (DIGIINIT, ("SurpriseRemoval: WARNING, pExt->pNextDeviceObject is NULL!\n"));
    }

    // Simulate stop
    DxbPnpStopDevice(pExt,NULL,NULL);

    // Block new requests
    pExt->fHoldNewRequests = TRUE;

#ifdef STRATUS_DEVICE
    // Shutdown Stratus processing
    SraMiniportHalt(pExt);
#endif

    // and pass on
    pIrp->IoStatus.Status = STATUS_SUCCESS;

    return DxbPnpForwardIrp( pNext, pIrp );
}


NTSTATUS
DxbPnpQueryDeviceRelations(
    PDXB_DEVICE_EXTENSION pExt, 
    PIRP                  pIrp, 
    PIO_STACK_LOCATION    pIos )
/*++

Description:

    Handle an IRP_MN_QUERY_DEVICE_RELATIONS Irp.  The PnP Manager sends this IRP to 
    gather information about devices with a relationship to the specified device.

    The PnP Manager queries a device's BusRelations (child devices) when the device is
    enumerated and at other times while the device is active, such as when a driver 
    calls IoInvalidateDeviceRelations to indicate that a child device has arrived or 
    departed. 

    The PnP Manager queries a device's RemovalRelations before it removes or ejects 
    the device and it queries for EjectionRelations before it ejects a device.

    The PnP Manager queries a device's PowerRelations when a component registers for 
    power notification on the specified device or one of its descendants.

    The PnP Manager queries a device's TargetDeviceRelation when a driver or user-mode
    application registers for PnP notification of an EventCategoryTargetDeviceChange 
    on the device. The PnP Manager queries for the device that is associated with a 
    particular file object. This is the only PnP IRP that has a valid file object 
    parameter.

    The PnP Manager sends this IRP at IRQL PASSIVE_LEVEL in an arbitrary thread 
    context.

Return Value:

    STATUS_SUCCESS, or appropriate error status.

--*/
{
    NTSTATUS Status = pIrp->IoStatus.Status;
    ULONG_PTR Information = pIrp->IoStatus.Information;
    SRAPRINT( DIGIPNP, ( "Dxb!PnpQueryDeviceRelations: pExt 0x%x, QueryType %s\n", pExt,
              pIos->Parameters.QueryDeviceRelations.Type == BusRelations ? "BusRelations" :
              pIos->Parameters.QueryDeviceRelations.Type == EjectionRelations ? "EjectionRelations" :
              pIos->Parameters.QueryDeviceRelations.Type == PowerRelations ? "PowerRelations" :
              pIos->Parameters.QueryDeviceRelations.Type == RemovalRelations ? "RemovalRelations" :
              pIos->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation ? "TargetDeviceRelation" : "Unknown" ) );

    switch ( pIos->Parameters.QueryDeviceRelations.Type )
    {
        case BusRelations:

        {
            ULONG nChildren = 0;

            // Count children to determine size of struct to allocate.  If not running,
            // use default nChildren=0.
            if ( pExt->PnpState == DXBPNP_STARTED )
            {
                PLIST_ENTRY pEntry = pExt->ChildListHead.Flink;
                while ( pEntry != &pExt->ChildListHead )
                {
                    nChildren++;
                    pEntry = pEntry->Flink;
                }
            }

            // Create response.  Alloc from paged pool, PNP manager will later free it!
            {
                PDEVICE_RELATIONS pRelations = ExAllocatePoolWithTag( PagedPool, sizeof *pRelations + nChildren * sizeof(PVOID), DefaultPoolTag );
                if ( !pRelations )
                {
                    // The OS is low on memory, so fail this IRP and do not forward it to 
                    // the lower layer.

                    // NOTE: We don't need to call dxbIoCompleteRequest() as this IRP does not 
                    // have any associated buffers which need to be unlocked

                    NTSTATUS NtStatus = STATUS_INSUFFICIENT_RESOURCES;
                    
                    pIrp->IoStatus.Status      = NtStatus;
                    pIrp->IoStatus.Information = 0;                                 
                    IoCompleteRequest( pIrp, IO_NO_INCREMENT );

                    return( NtStatus );             
                }

                pRelations->Count = nChildren;
                DigiDump( DIGIPNP, ( "Dxb: returning %d %s:\n", nChildren, nChildren == 1 ? "child" : "children") );

                // Fill in array of child device object pointers
                {
                    ULONG i;
                    PLIST_ENTRY pEntry;
                    for ( i = 0, pEntry = pExt->ChildListHead.Flink; 
                          i < nChildren && pEntry != &pExt->ChildListHead; 
                          i++, pEntry = pEntry->Flink )
                    {
                        PDXB_CHILD_DEVICE pChild = CONTAINING_RECORD( pEntry, DXB_CHILD_DEVICE, ListEntry );
                        ASSERT( pChild->Type == DXB_TYPE_CHILD_DEVICE );
                        DigiDump( DIGIPNP, ( "      PDO 0x%08x, Ext 0x%08x\n", pChild->pPhysicalDeviceObject, pChild ) );
                        pRelations->Objects[i] = pChild->pPhysicalDeviceObject;
                        ObReferenceObject( pChild->pPhysicalDeviceObject ); // PNP removes the reference
                    }
                }
                if ( pIrp->IoStatus.Information != 0 )
                {
                    DigiDump( DIGIWARN, ( "Dxb!PnpQueryDeviceRelations: *** Freeing previous DEVICE_RELATIONS struct ***\n" ) );
                    ExFreePool( (PVOID)pIrp->IoStatus.Information );
                }

                pIrp->IoStatus.Information = (ULONG_PTR)pRelations;
                pIrp->IoStatus.Status = STATUS_SUCCESS;
            }
            break;
        }
        case PowerRelations:
        case RemovalRelations:
        case EjectionRelations:
        case TargetDeviceRelation:
        default:
            break;
    }
        
    return DxbPnpForwardIrp( pExt->pNextDeviceObject, pIrp );
}


NTSTATUS
DxbPnpDispatch(
    PDXB_DEVICE_EXTENSION pExt, 
    PIRP                  pIrp, 
    PIO_STACK_LOCATION    pIos )
/*++

Description:

    Dispatch routine for all adapter FDO related PNP requests.

Parameters:

    pExt
        Device extension for our FDO which describes the adapter.
    
    pIrp
        Request.

    pIos
        Parameters passed along with Irp.

Return Value:

    STATUS_SUCCESS, or some error value.

--*/
{
    ULONG const Minor = pIos->MinorFunction;
    NTSTATUS Status;

    SRAPRINT( DIGIPNP, ( "Dxb!PnpDispatch: %s, pExt 0x%08x, pIrp 0x%08x\n", DxbGetPnpIrpName(Minor), pExt, pIrp ) );

    switch ( Minor )
    {
        case IRP_MN_START_DEVICE:                       Status = DxbPnpStartDevice( pExt, pIrp, pIos ); break;
        case IRP_MN_QUERY_REMOVE_DEVICE:                Status = DxbPnpQueryRemoveDevice( pExt, pIrp, pIos );   break;
        case IRP_MN_REMOVE_DEVICE:                      Status = DxbPnpRemoveDevice( pExt, pIrp, pIos );    break;
        case IRP_MN_CANCEL_REMOVE_DEVICE:           Status = DxbPnpCancelRemoveDevice( pExt, pIrp, pIos );  break;
        case IRP_MN_QUERY_STOP_DEVICE:              Status = DxbPnpQueryStopDevice( pExt, pIrp, pIos ); break;
        case IRP_MN_STOP_DEVICE:                        Status = DxbPnpStopDevice( pExt, pIrp, pIos );  break;
        case IRP_MN_CANCEL_STOP_DEVICE:             Status = DxbPnpCancelStopDevice( pExt, pIrp, pIos );    break;
        case IRP_MN_QUERY_DEVICE_RELATIONS:         Status = DxbPnpQueryDeviceRelations( pExt, pIrp, pIos );    break;
//      case IRP_MN_QUERY_INTERFACE:                    Status = DxbPnpSkipAndForwardIrp( pExt, pIrp, pIos );   break;
//      case IRP_MN_QUERY_CAPABILITIES:             Status = DxbPnpSkipAndForwardIrp( pExt, pIrp, pIos );   break;
//      case IRP_MN_QUERY_RESOURCES:                    Status = DxbPnpQueryResources( pExt, pIrp, pIos );  break;
//      case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:    Status = DxbPnpQueryResourceRequirements( pExt, pIrp, pIos );   break;
//      case IRP_MN_QUERY_DEVICE_TEXT:              Status = DxbPnpQueryDeviceText( pExt, pIrp, pIos ); break;
//      case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:   Status = DxbPnpSkipAndForwardIrp( pExt, pIrp, pIos );   break;
//      case IRP_MN_READ_CONFIG:                        Status = DxbPnpReadConfig( pExt, pIrp, pIos );  break;
//      case IRP_MN_WRITE_CONFIG:                       Status = DxbPnpWriteConfig( pExt, pIrp, pIos ); break;
//      case IRP_MN_EJECT:                              Status = DxbPnpEject( pExt, pIrp, pIos );   break;
//      case IRP_MN_SET_LOCK:                           Status = DxbPnpSetLock( pExt, pIrp, pIos ); break;
//      case IRP_MN_QUERY_ID:                           Status = DxbPnpQueryId( pExt, pIrp, pIos ); break;
//      case IRP_MN_QUERY_PNP_DEVICE_STATE:         Status = DxbPnpSkipAndForwardIrp( pExt, pIrp, pIos );   break;
//      case IRP_MN_QUERY_BUS_INFORMATION:          Status = DxbPnpSkipAndForwardIrp( pExt, pIrp, pIos );   break;
//      case IRP_MN_DEVICE_USAGE_NOTIFICATION:      Status = DxbPnpDeviceUsageNotification( pExt, pIrp, pIos ); break;
        case IRP_MN_SURPRISE_REMOVAL:                   Status = DxbPnpSurpriseRemoval( pExt, pIrp, pIos ); break;
//      case IRP_MN_QUERY_LEGACY_BUS_INFORMATION:   break;

        default:
            DigiDump( DIGIPNPLOUD|DIGIDEVICE, ( "Dxb!PnpDispatch: Passing unhandled PNP Irp %s on to PDO!\n", DxbGetPnpIrpName( Minor ) ) );
            Status = DxbPnpForwardIrp( pExt->pNextDeviceObject, pIrp );
            break;
    }

    if ( NT_ERROR( Status ) )
    {
        SRAPRINT( DIGIPNP, ( "Dxb!PnpDispatch: %s, returning failure status 0x%x\n", DxbGetPnpIrpName(Minor), Status ) );
    }
    return Status;
}


#endif // NT5_BUILD
