/*
 ****************************************************************************
 * Copyright (c) 1990-2000 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 File    : AsynPnp.c
 *
 * Purpose        : PNP Irp Handler routines
 *
 *----------------------------------------------------------------------
 */
#include "digiasyn.h"
#if NT5_BUILD
#include <stdio.h>
#include <initguid.h>
#include <ntddser.h>

struct PNP_IRP_NAME
{
    ULONG Minor;
    PCSTR Name;
};

const struct PNP_IRP_NAME PnpIrpTable[] = 
{
    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"
};


STATIC PCSTR GetPnpIrpName( ULONG Minor )
{
    static const CHAR Unknown[] = "Unknown";
    PCSTR Name = Unknown;
    int i;
    for ( i = 0; i < sizeof PnpIrpTable / sizeof PnpIrpTable[0]; i++ )
    {
        if ( Minor == PnpIrpTable[i].Minor )
        {
            Name = PnpIrpTable[i].Name;
            break;
        }
    }
    return Name;
}

VOID INLINE CompletePNPIrp( IN PIRP Irp, NTSTATUS Status )
{
    Irp->IoStatus.Status = Status;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );
}

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


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


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

    ASSERT( pNextLowerDriver );
    ASSERT( pIrp );
        
    KeInitializeEvent( &Event, SynchronizationEvent, FALSE );

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

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

#if 0
STATIC PWCHAR
GetInstanceString( 
        PWSTR pString,
        ULONG Length )
/*++

Description: 

        Parse a WCHAR string, returning a pointer to the device instance.  Expected format
        is "<SomeGuid>\<InstanceNumber>".

Parameters:

        pString
                String to parse

        Length
                Length in WCHARs of the string.

Return Value:

        NULL if not found, otherwise pointer to InstanceNumber string.

--*/
{
    ULONG i = Length;
    if ( pString == NULL || i == 0 )
        return NULL;

    while ( i > 0 && pString[--i] != '\\' )
    {
        ;
    }

    if ( i != 0 && i < Length-1 )
        return &pString[i+1];
    else
        return NULL;
}


STATIC NTSTATUS
GetDeviceInstance( 
        IN PDEVICE_OBJECT pPdo,
        IN OUT PWCHAR     buf,
        IN ULONG          cb )
{
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    WCHAR MyBuf[64] = {0};
    ULONG MyCb;
    Status = IoGetDeviceProperty( pPdo, DevicePropertyDriverKeyName, sizeof MyBuf, 
                                  MyBuf, &MyCb );
    if ( NT_SUCCESS( Status ) )
    {
        PWSTR p = GetInstanceString( MyBuf, MyCb/sizeof(WCHAR) );
        if ( p && wcslen(p)*sizeof(WCHAR) < cb )
        {
            wcscpy( buf, p );
            Status = STATUS_SUCCESS;
        }
    }
    return Status;
}
#else

STATIC NTSTATUS
GetDeviceInstance( 
        IN PDEVICE_OBJECT pPdo,
        IN OUT PWCHAR     buf,
        IN ULONG          cb )
{
    NTSTATUS Status = STATUS_UNSUCCESSFUL;

    static ULONG InstanceCt = 0;

    if (cb >= 9) {
        swprintf(buf,L"%08.8x",InstanceCt++);
        Status = STATUS_SUCCESS;
    }

    return Status;
}

#endif

STATIC NTSTATUS 
AsynPnpStartDevice( IN PASYN_PORT_EXT pPortExt, PIRP pIrp )
{
    NTSTATUS Status;
    HANDLE hkDriver = NULL;
    UNICODE_STRING RegPath = {0};
    UNICODE_STRING FullRedirectPath = {0};

    // Forward the IRP to the bus driver and wait for it to come back
    // up before continuing.
    //
    Status = AsynPnpForwardIrpWait( pPortExt->pNextDeviceObject, pIrp );
    DigiDump( DIGIPNP, ( "AsynPnpStartDevice: AsynPnpForwardIrpWait returned status 0x%x\n", Status ) );
    if ( NT_ERROR( Status ) )
        goto Exit;
      
    Status = IoOpenDeviceRegistryKey( pPortExt->pPdo, PLUGPLAY_REGKEY_DRIVER, MAXIMUM_ALLOWED, &hkDriver );
    if ( NT_ERROR( Status ) )
        goto Exit;

    {
        HANDLE hkParameters;
        Status = DigiRegOpenSubkey( &hkParameters, hkDriver, DXB_REGKEY_PARAMETERS );
        if ( NT_SUCCESS( Status ) )
        {
            // The DLL will write this based on adapter type, it will be located at the redirect path
            Status = DigiRegQuerySz( hkParameters, DXB_REGVAL_REGPATH, &RegPath, DigiRegFlagRequired, NULL );
            (void)DigiRegCloseKey( hkParameters );
        }            
        ZwClose( hkDriver );
    }
   
    if ( NT_ERROR( Status ) )
        goto ErrorExit;
   
#define REDIRECTPATH_ROOT L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\"
    Status = DigiStrAllocUnicodeString( &FullRedirectPath, REDIRECTPATH_ROOT, RegPath.Buffer, NULL );
    if ( NT_ERROR( Status ) )
        goto ErrorExit;
           
    {
        HANDLE hKey;
        Status = DigiRegOpenSubkey( &hKey, NULL, FullRedirectPath.Buffer );
        if ( NT_SUCCESS( Status ))
        {   
            Status = AsynInitPort( pPortExt, hKey );      
            DigiDump( DIGIPNP, ( "AsynPnpStartDevice: AsynInitPort returned status 0x%x\n", Status ) );
            DigiRegCloseKey( hKey );
        }   
    }            
   
    if (NT_ERROR(Status))
        goto ErrorExit;
      
    //
    // Try to open the DStreams Library
    //
    Status = DsOpenDevice( &pPortExt->DxbDevice, &pPortExt->DsHandle);
    DigiDump( DIGIPNP, ( "AsynPnpStartDevice: DsOpenDevice returned status 0x%x\n", Status ) );
    if ( NT_ERROR( Status ) )
    {
        AsynLogDsOpenDeviceError( Status, &pPortExt->DxbDevice );
        goto ErrorExit;
    }
   
    // Make the device visible via a device association.
    Status = IoRegisterDeviceInterface(pPortExt->pPdo, (LPGUID)&GUID_CLASS_COMPORT, NULL, 
                                       &pPortExt->DeviceInterfaceSymbolicName);
    if ( NT_ERROR( Status ) ) 
    {
        DigiDump( DIGIPNP|DIGIERROR, ("AsynPnpStartDevice: Couldn't register class association ------- for port %wZ\n",
                                      &pPortExt->PortName));
        pPortExt->DeviceInterfaceSymbolicName.Buffer = NULL;
        goto ErrorExit;
    }
   
    // Now set the symbolic link for the association
    Status = IoSetDeviceInterfaceState( &pPortExt->DeviceInterfaceSymbolicName, TRUE);

    if ( NT_ERROR( Status ) )
    {
        DigiDump( DIGIPNP|DIGIERROR, ("AsynPnpStartDevice: Couldn't enable class association ------- for port %wZ\n",
                                      &pPortExt->PortName));
        goto ErrorExit;                                       
    }
   
    pPortExt->PnpState = PNPSTATE_OK;
    pPortExt->DeviceStateFlags = 0;
     
    DigiDump( DIGIPNP, ("ASYN!AsynPnpStartDevice successful: PortName = %S PortNum =%d \n", pPortExt->PortName.Buffer,pPortExt->iSdxbPort));
    goto Exit;

 ErrorExit:   
    if ( pPortExt->DeviceInterfaceSymbolicName.Buffer )
    {
        ExFreePool( pPortExt->DeviceInterfaceSymbolicName.Buffer );   
        pPortExt->DeviceInterfaceSymbolicName.Buffer = NULL;
    }   
            
    if ( pPortExt->DsHandle != NULL )
    {
        ( void )DsCloseDevice( pPortExt->DsHandle );  
        pPortExt->DsHandle = NULL;
    }      

    AsynFreePortResources( pPortExt );      
   
 Exit:
    CompletePNPIrp( pIrp, Status );
    if ( FullRedirectPath.Buffer )  
        DigiStrFreeUnicodeString( &FullRedirectPath );
    if ( RegPath.Buffer )
        DigiStrFreeUnicodeString( &RegPath );      
    return Status;
}

STATIC NTSTATUS AsynPnpQueryStopDevice( IN PASYN_PORT_EXT pPortExt, PIRP pIrp )
{
    NTSTATUS Status = STATUS_SUCCESS;
   
    // IRP_MN_QUERY_STOP_DEVICE has been received - determine whether the port can 
    // be stopped. If the port is open, we reject the request.

    DigiAcquireSpinLock( &pPortExt->Lock );
    if ( pPortExt->State == IdleState )
    {
        // Yes, we can accept the stop query - update the state so an open request will be rejected
        // and forward irp to the lower bus driver
        pPortExt->PnpState |= PNPSTATE_STOPPING;
        DigiReleaseSpinLock( &pPortExt->Lock );
        Status = AsynPnpForwardIrp( pPortExt->pNextDeviceObject, pIrp );
    }
    else
    {
        // Port is busy - reject the stop query
        DigiReleaseSpinLock( &pPortExt->Lock );
        DigiDump(DIGIPNP, ("AsynQueryStopDevice: IRP_MN_QUERY_STOP_DEVICE: Rejected query for port = %d state = %x\n",
                           pPortExt->iSdxbPort,pPortExt->State));
        Status = STATUS_UNSUCCESSFUL;
        CompletePNPIrp( pIrp, Status );
    }
    return Status;   
}

NTSTATUS AsynPnpStopDevice( IN PASYN_PORT_EXT pPortExt, PIRP pIrp, BOOLEAN fPowerStateChange )
{
    NTSTATUS Status = STATUS_SUCCESS;
    LARGE_INTEGER Timeout;
    HANDLE DsHandle;
    ULONG i;
    BOOLEAN fCalledInternally = pIrp == NULL ? TRUE : FALSE;
   
    DigiDump( DIGIPNP, ( "DigiAsyn!AsynPnpStopDevice, pPortExt 0x%p, pIrp 0x%p fPowerStateChange %x CurrentIrql %x\n", pPortExt, pIrp, fPowerStateChange, KeGetCurrentIrql() ) );

    DigiAcquireSpinLock( &pPortExt->Lock );
   
    // Update the PNP State so no more requests will be accepted   
    pPortExt->PnpState |= PNPSTATE_STOPPED;
   
    if ( pPortExt->State != IdleState )
    {
        DigiDump( DIGIPNP, ( "\tpPortExt->State %x\n", pPortExt->State ) );
        // Port is open - complete the IRPs in progress and close the device
        pPortExt->State = CloseInProgressState;
        DigiReleaseSpinLock( &pPortExt->Lock );
        (void)AsynDeviceCleanup( pPortExt->pDevice, NULL );
        (void)AsynCloseSdxbPort( pPortExt );      

        // Wait a bit for port to be idle before continuing
        Timeout.QuadPart = 50 * ONE_MILLISECOND;
      
        for ( i = 0; i < 20; i++ )
        {
            KeDelayExecutionThread( KernelMode, FALSE, &Timeout );
            if ( pPortExt->State == IdleState )     // State is updated in AsynCloseComplete processing
                break;
        }               

        // Make sure all resources are freed at this point
        AsynCancelAllPendingIo(pPortExt, STATUS_CANCELLED);
        AsynClosePortImmediate(pPortExt);
    }
    else {
        DigiReleaseSpinLock( &pPortExt->Lock );
    }

    // Now, spin until port has really entered idle state
    Timeout.QuadPart = 50 * ONE_MILLISECOND;

    while (pPortExt->State != IdleState) {
        KeDelayExecutionThread( KernelMode, FALSE, &Timeout );
    }

    DigiAcquireSpinLock( &pPortExt->Lock );
   
    DsHandle = pPortExt->DsHandle;
    pPortExt->DsHandle = NULL;
    DigiReleaseSpinLock( &pPortExt->Lock );

    if ( DsHandle != NULL )
        (void)DsCloseDevice( DsHandle );

    if ( !fPowerStateChange )   
    {
        AsynFreePortResources( pPortExt );
      
        DigiDump( DIGIPNP , ("Free DeviceInterfaceSymbolicName buffer\n"));
      
        if ( pPortExt->DeviceInterfaceSymbolicName.Buffer )
        {      
            Status = IoSetDeviceInterfaceState( &pPortExt->DeviceInterfaceSymbolicName, FALSE);
            if ( pPortExt->DeviceInterfaceSymbolicName.Buffer )
            {
                ExFreePool( pPortExt->DeviceInterfaceSymbolicName.Buffer );   
                pPortExt->DeviceInterfaceSymbolicName.Buffer = NULL;
            }
        }                  
      
    }   
   
    if ( !fCalledInternally )
    {
        // Forward the IRP to the bus driver.
        Status =  AsynPnpForwardIrp( pPortExt->pNextDeviceObject, pIrp );
    }         
    DigiDump( DIGIPNP , ("Leaving AsynPnpStopDevice: CurrentIrql = %x\n",KeGetCurrentIrql()));
      
    return Status;   
}

STATIC NTSTATUS AsynPnpCancelStopDevice( IN PASYN_PORT_EXT pPortExt, PIRP pIrp )
{
    NTSTATUS Status;
    Status =  AsynPnpForwardIrpWait( pPortExt->pNextDeviceObject, pIrp );
    if ( NT_SUCCESS( Status ) )
    {
        pPortExt->PnpState = PNPSTATE_OK;
    }
    CompletePNPIrp( pIrp, Status );
    return Status;
}


STATIC NTSTATUS AsynPnpQueryRemoveDevice( IN PASYN_PORT_EXT pPortExt, PIRP pIrp )
{
    NTSTATUS Status = STATUS_SUCCESS;

    // IRP_MN_QUERY_REMOVE_DEVICE has been received, determine whether the port can be removed from the system. 
    // If the port is open, we reject the request.

	// Stratus bugfix 8 Apr 02 - allow removal at all times. This fixes windxb-115.

	Status = STATUS_SUCCESS;
    pPortExt->PnpState |= PNPSTATE_REMOVING;
    Status = AsynPnpForwardIrp( pPortExt->pNextDeviceObject, pIrp );
    return Status;   
}

STATIC NTSTATUS AsynPnpRemoveDevice( IN PASYN_PORT_EXT pPortExt, PIRP pIrp )
{
    NTSTATUS Status = STATUS_SUCCESS;

    DigiDump( DIGIPNP, ( "DigiAsyn!AsynPnpRemoveDevice, pPortExt 0xp, pIrp 0x%p\n", pPortExt, pIrp ) );
    // Make sure port has been stopped...
    if (( pPortExt->PnpState & PNPSTATE_STOPPED) != PNPSTATE_STOPPED )
    {
        ( void )AsynPnpStopDevice( pPortExt, NULL, FALSE );
    }
   
    if ( pPortExt->DeviceName.Buffer )
        DigiStrFreeUnicodeString( &pPortExt->DeviceName );

    // Forward the IRP to the bus driver
    Status = AsynPnpForwardIrp( pPortExt->pNextDeviceObject, pIrp );
    
    // Remove the device object from the device stack
    IoDetachDevice( pPortExt->pNextDeviceObject );
   
    // Remove from global list of ports
    RemoveEntryList( &pPortExt->ListEntry );

    // Free the device object
    IoDeleteDevice( pPortExt->pDevice );

    // If the last port was removed, clean-up the global device object used for dgamode tracing
    if (IsListEmpty( &gDeviceExtList ))
        AsynDeleteGlobalCardExt();
   
    return Status;
}

STATIC NTSTATUS AsynPnpCancelRemoveDevice( IN PASYN_PORT_EXT pPortExt, PIRP pIrp )
{
    NTSTATUS Status;
    Status =  AsynPnpForwardIrpWait( pPortExt->pNextDeviceObject, pIrp );
    if ( NT_SUCCESS( Status ) )
    {
        pPortExt->PnpState &= ~PNPSTATE_REMOVING;
    }
    CompletePNPIrp( pIrp, Status );
    return Status;
}


STATIC NTSTATUS AsynPnpQueryDeviceState( IN PASYN_PORT_EXT pPortExt, PIRP pIrp )
{
    NTSTATUS Status;
    if ( pPortExt->DeviceStateFlags )
    {
        pIrp->IoStatus.Information |= pPortExt->DeviceStateFlags;
        DigiDump( DIGIPNP, ("AsynPnpQueryDeviceState: DeviceStateFlags = %x\n",pPortExt->DeviceStateFlags));
    }      
    Status =  AsynPnpForwardIrp( pPortExt->pNextDeviceObject, pIrp );
    return Status;
}


NTSTATUS AsynAddDevice( IN PDRIVER_OBJECT pDriverObject, IN PDEVICE_OBJECT pPhysicalDeviceObject )
{
    NTSTATUS Status = STATUS_SUCCESS;
    UNICODE_STRING DeviceName = {0};
    UNICODE_STRING Name = {0};
    PASYN_PORT_EXT pPortExt;     
    PDEVICE_OBJECT pFunctionalDeviceObject = NULL;
   
    DigiDump( DIGIINIT, ("ASYN!AddDevice:\n"));
   
    // Create a device object for the FDO
    {
        WCHAR buf[16] = {0};
        Status = GetDeviceInstance( pPhysicalDeviceObject, buf, sizeof buf );
        if ( NT_SUCCESS( Status ) )
            Status = DigiStrAllocUnicodeString( &Name, DXB_HDWID_ASYN, buf, NULL );
    }
   
    if ( NT_ERROR( Status ) )
        goto Exit;
   
    Status = DigiStrDeviceName( &DeviceName, Name.Buffer );
   
    if ( NT_SUCCESS( Status ) )
    {
        Status = IoCreateDevice( gpDriverObject, sizeof *pPortExt, &DeviceName, FILE_DEVICE_SERIAL_PORT, 0, TRUE, &pFunctionalDeviceObject);
    }
    if ( NT_ERROR( Status ) )
        goto Exit;
   
    pPortExt = pFunctionalDeviceObject->DeviceExtension;
   
    DigiStrAllocUnicodeString( &pPortExt->DeviceName, DeviceName.Buffer, NULL, NULL );
    pPortExt->Type = ASYN_TYPE_PORT_EXT;
    pPortExt->PnpState = PNPSTATE_STOPPED;
    pPortExt->pPdo = pPhysicalDeviceObject;
        
    pFunctionalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;

    // Set both PDO and FDO PAGABLE flags.  This forces SET_POWER apis to be called
    // at PASSIVE_LEVEL rather than DISPATCH_LEVEL.
    pFunctionalDeviceObject->Flags |= DO_POWER_PAGABLE|DO_BUFFERED_IO;
    pPhysicalDeviceObject->Flags   |= DO_POWER_PAGABLE;

    // Attach the device object to the device stack 
    pPortExt->pNextDeviceObject = IoAttachDeviceToDeviceStack( pFunctionalDeviceObject, pPhysicalDeviceObject );
   
    pPortExt->pDevice = pFunctionalDeviceObject;

    {
        POWER_STATE State = {0};

        pPortExt->PowerState   = PWRSTATE_ON;  // power assumed to be on
        pPortExt->SysPower     = PowerSystemWorking;
        pPortExt->DevPower     = PowerDeviceD0;
        pPortExt->WakeSysPower = PowerSystemWorking;

        State.DeviceState = PowerDeviceD0;
        (void)PoSetPowerState( pPortExt->pDevice, DevicePowerState, State );
    }
   
    // Add to global list of ports
    InsertTailList( &gDeviceExtList, &pPortExt->ListEntry );
    
    DigiDump( DIGIINIT|DIGIPNP, ( "AsynAddDevice: DriverObject 0x%p, FDO  0x%p\n"
                                  "               NextLowerDevice 0x%p\n"
                                  "               Name %S\n"
                                  "               Status 0x%x\n",
                                  gpDriverObject,  
                                  pFunctionalDeviceObject,  
                                  pPortExt->pNextDeviceObject, 
                                  Name.Buffer, 
                                  Status ) );
                                   
        
   
 Exit:           
    if ( DeviceName.Buffer )
        DigiStrFreeUnicodeString( &DeviceName );
      
    if ( Name.Buffer )  
        DigiStrFreeUnicodeString( &Name );
      
    return Status;
}

NTSTATUS AsynPnpSurpriseRemoval( IN PASYN_PORT_EXT pPortExt, PIRP pIrp)
{
    NTSTATUS Status = STATUS_SUCCESS;
   
    DigiDump( DIGIPNP, ( "DigiAsyn!AsynPnpSurpriseRemoval, pPortExt 0x%p, pIrp 0x%p CurrentIrql %x\n", pPortExt, pIrp, KeGetCurrentIrql() ) );

    // Make sure port has been stopped so no more requests will be accepted
    if (( pPortExt->PnpState & PNPSTATE_STOPPED) != PNPSTATE_STOPPED )
    {
        ( void )AsynPnpStopDevice( pPortExt, 
                                   NULL/*internal*/, 
                                   FALSE/*not power related*/ );
    }
   
    // Forward the IRP to the bus driver.
    Status =  AsynPnpForwardIrp( pPortExt->pNextDeviceObject, pIrp );
        
    DigiDump( DIGIPNP , ("Leaving AsynPnpSurpriseRemoval: CurrentIrql = %x\n",KeGetCurrentIrql()));
      
    return Status;   
}

NTSTATUS AsynDevicePnp( IN PDEVICE_OBJECT pDevice, PIRP pIrp )
{
    PIO_STACK_LOCATION pIos = IoGetCurrentIrpStackLocation( pIrp );
    PASYN_PORT_EXT pPortExt = pDevice->DeviceExtension;       
    ULONG const Minor = pIos->MinorFunction;
    NTSTATUS Status;

    SRAPRINTIRPEX(DBGTYPE_IRP,DBGLVL_METRICS,("COM",pDevice,pIrp,FALSE));
        
    if ( pDevice->DeviceType == FILE_DEVICE_UNKNOWN )
    {
        Status = STATUS_UNSUCCESSFUL;
        pIrp->IoStatus.Status = Status;
        IoCompleteRequest( pIrp, IO_NO_INCREMENT );
        return Status;
    }
    DigiDump( DIGIPNP, ( "DigiAsyn!PnpDispatch: %s, pPortExt 0x%p, pIrp 0x%p\n", GetPnpIrpName(Minor), pPortExt, pIrp ) );

    switch ( Minor )
    {
    case IRP_MN_START_DEVICE:                                   
        Status = AsynPnpStartDevice( pPortExt, pIrp );                  
        break;
    case IRP_MN_QUERY_STOP_DEVICE:
        Status = AsynPnpQueryStopDevice( pPortExt, pIrp );
        break;
    case IRP_MN_STOP_DEVICE:
        Status = AsynPnpStopDevice( pPortExt, pIrp, FALSE );      
        break;
    case IRP_MN_CANCEL_STOP_DEVICE:
        Status = AsynPnpCancelStopDevice( pPortExt, pIrp );
        break;
    case IRP_MN_QUERY_REMOVE_DEVICE:                    
        Status = AsynPnpQueryRemoveDevice( pPortExt, pIrp ); 
        break;
    case IRP_MN_REMOVE_DEVICE:                                  
        Status = AsynPnpRemoveDevice( pPortExt, pIrp );
        break;
    case IRP_MN_CANCEL_REMOVE_DEVICE:
        Status = AsynPnpCancelRemoveDevice( pPortExt, pIrp );
        break;
// Stratus (BA 12Nov01)
    case IRP_MN_SURPRISE_REMOVAL:       
        Status = AsynPnpSurpriseRemoval( pPortExt, pIrp );
        break;
// End Stratus (BA 12Nov01)
        /*
          case IRP_MN_QUERY_LEGACY_BUS_INFORMATION:     
          break;
        */

    case IRP_MN_QUERY_PNP_DEVICE_STATE:                 
        Status = AsynPnpQueryDeviceState( pPortExt, pIrp );
        break;
    default:
        DigiDump( DIGIPNP, ( "DigiAsyn!PnpDispatch: Passing unhandled PNP Irp %s on to PDO!\n", GetPnpIrpName(Minor ) ) );
        Status = AsynPnpForwardIrp( pPortExt->pNextDeviceObject, pIrp );
        break;
    }

    if ( NT_ERROR( Status ) )
    {
        DigiDump( DIGIPNP, ( "AsynPnpDispatch: %s, returning failure status 0x%x\n", GetPnpIrpName(Minor), Status ) );
    }
        
    return Status;
}


NTSTATUS AsynSystemControl( IN PDEVICE_OBJECT pDevice, IN PIRP Irp )
/*++

Description:

        The primary dispatch routine for IRP_MJ_SYSTEM_CONTROL requests.

Parameters:

        Standard NT Dispatch routine parameters.

Return Value:

        STATUS_SUCCESS if request completed successfully.

--*/
{
    PASYN_PORT_EXT pPortExt = pDevice->DeviceExtension;
    NTSTATUS Status;
   
    DigiDump( DIGIPNP, ( "DigiAsyn!AsynSystemControl: Enter, pExt 0x%08x, pIrp 0x%08x\n", 
                         pDevice->DeviceExtension, Irp ) );

    SRAPRINTIRPEX(DBGTYPE_IRP,DBGLVL_METRICS,("COM",pDevice,Irp,FALSE));

    IoSkipCurrentIrpStackLocation(Irp);
    Status = IoCallDriver(pPortExt->pNextDeviceObject, Irp);

    DigiDump( DIGIPNP, ( "DigiAsyn!AsynSystemControl: Leave, pExt 0x%08x, pIrp 0x%08x, Status 0x%x\n", pDevice->DeviceExtension, Irp, Status ) );
    return Status;   
}


#endif  // NT5_BUILD



