/*++
*******************************************************************************
* 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 Name:

   dxbdevice.c

Abstract:

   This module contains required NT kernelmode driver dispatch routines, including
   Create, Close, IoControl and Cleanup.

Author:

   Michael Zarns

--*/
#include "digidxb.h"
#include <stdio.h>
#include "sradxb.h"
#if NT5_BUILD
#include <initguid.h>
#include <devguid.h>
#endif

ULONG gInstanceCt = 0;

NTSTATUS
DxbDeviceCreate(
    IN PDEVICE_OBJECT pDevice,
    IN PIRP           pIrp )
/*++

Routine Description:

   This routine is called to open the device associated with DeviceObject.
   We will always allow the driver to be opened.

Arguments:

   pDevice
        Device object being opened.

    pIrp
        The open request.

Return Value:

   STATUS_SUCCESS

--*/
{
    PDXB_DEVICE_EXTENSION const pExt = pDevice->DeviceExtension;
    NTSTATUS Status;
    LARGE_INTEGER Time = {0};
    UNREFERENCED_PARAMETER( pDevice );
#if DBG
    KeQuerySystemTime( &Time );
#endif

    // Fail create if spoofing!
    Status = pExt->fSpoof ? STATUS_UNSUCCESSFUL : STATUS_SUCCESS;

#if NT5_BUILD
    // Fail create if adapter isn't started
    if ( NT_SUCCESS( Status ) )
        Status = pExt->PnpState == DXBPNP_STARTED ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
#endif

   SRAPRINT2(DIGIDEVICE, SRA_MED_LEVEL, 
             ( "DXB!DxbCreate: time 0x%08x%08x, device %S\n",
             Time.HighPart, Time.LowPart, 
             ((PDXB_DEVICE_EXTENSION)(pDevice->DeviceExtension))->Name.Buffer ) );

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


NTSTATUS
DxbDeviceClose(
    IN PDEVICE_OBJECT pDevice,
    IN PIRP           pIrp )
/*++

Routine Description:

   This routine is called to close the device.

Arguments:

    pDevice
        Device object being closed.

    pIrp
        The close request.

Return Value:

   STATUS_SUCCESS

--*/
{
    LARGE_INTEGER Time = {0};
    UNREFERENCED_PARAMETER( pDevice );

#if DBG
    KeQuerySystemTime( &Time );
#endif

   SRAPRINT2(DIGIDEVICE, SRA_MED_LEVEL,
             ( "DXB!DeviceClose: time 0x%08x%08x, device %S\n",
             Time.HighPart, Time.LowPart,
             ((PDXB_DEVICE_EXTENSION)(pDevice->DeviceExtension))->Name.Buffer ) );

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


NTSTATUS
DxbDeviceIoControl(
    IN PDEVICE_OBJECT pDevice,
    IN PIRP           pIrp )
/*++

Description:

    The primary dispatch routine for IRP_MJ_DEVICE_CONTROL requests.

Parameters:

    Standard NT Dispatch routine parameters.

Return Value:

    STATUS_SUCCESS if request completed successfully.

--*/
{
    PDXB_DEVICE_EXTENSION pExt = pDevice->DeviceExtension;
    PIO_STACK_LOCATION pIos = IoGetCurrentIrpStackLocation( pIrp );
    ULONG Ioctl = pIos->Parameters.DeviceIoControl.IoControlCode;
    NTSTATUS Status;

    LARGE_INTEGER Time = {0};

#if DBG
    KeQuerySystemTime( &Time );
#endif

    SRAPRINT2(DIGIDEVICE, SRA_MED_LEVEL,
              ( "DXB!DeviceIoControl: Enter, time 0x%08x%08x, device %S, pIrp 0x%08x, Ioctl %s\n", 
                Time.HighPart, Time.LowPart,
                pExt->Name.Buffer, pIrp,
                Ioctl == IOCTL_DS_OPEN   ? "IOCTL_DS_OPEN" :
                Ioctl == IOCTL_DS_CLOSE  ? "IOCTL_DS_CLOSE" :
                Ioctl == IOCTL_DS_PUTMSG ? "IOCTL_DS_PUTMSG" :
                Ioctl == IOCTL_DS_GETMSG ? "IOCTL_DS_GETMSG" :
                Ioctl == IOCTL_DS_IOCTL  ? "IOCTL_DS_IOCTL" :
                Ioctl == IOCTL_DS_LINK   ? "IOCTL_DS_LINK" :
                Ioctl == IOCTL_DS_PURGE  ? "IOCTL_DS_PURGE" :
                Ioctl == IOCTL_DS_READ   ? "IOCTL_DS_READ" :
                Ioctl == IOCTL_DS_WRITE  ? "IOCTL_DS_WRITE" :
                Ioctl == IOCTL_DS_POP    ? "IOCTL_DS_POP" :
                Ioctl == IOCTL_DS_PUSH   ? "IOCTL_DS_PUSH" :
                Ioctl == IOCTL_DS_UNLINK ? "IOCTL_DS_UNLINK" : 
                Ioctl == IOCTL_KME_READ  ? "IOCTL_KME_READ" :
                Ioctl == IOCTL_KME_WRITE ? "IOCTL_KME_WRITE" : "Unknown" ) );
    
    // First things first - if this is not a close and our 'adapter missing' 
    // flag is set then fail this IRP straightaway.
    if (Ioctl != IOCTL_DS_CLOSE && 
        (pExt->bAdapterFailed || pExt->bShutdown))
    {
        Status = STATUS_IO_DEVICE_ERROR;
        dxbIoCompleteRequest( pIrp, Status, 0 );
        return Status;
    }

    switch ( Ioctl )
    {
    case IOCTL_DS_OPEN:
        Status = dxbStreamOpen( pExt, pIrp, pIos );
        break;

    case IOCTL_DS_CLOSE:
        Status = dxbStreamClose( pExt, pIrp, pIos );
        break;

    case IOCTL_DS_PUTMSG:
        Status = dxbStreamPutMsg( pExt, pIrp, pIos );
        break;

    case IOCTL_DS_GETMSG:
        Status = dxbStreamGetMsg( pExt, pIrp, pIos );
        break;

    case IOCTL_DS_IOCTL:
        Status = dxbStreamIoctl( pExt, pIrp, pIos );
        break;

    case IOCTL_DS_LINK:
        Status = dxbStreamLink( pExt, pIrp, pIos );
        break;

    case IOCTL_DS_PURGE:
        Status = dxbStreamPurge( pExt, pIrp, pIos );
        break;

    case IOCTL_KME_READ:
        Status = DxbKmeRead( pExt, pIrp );
        break;

    case IOCTL_KME_WRITE:
        Status = DxbKmeWrite( pExt, pIrp );
        break;

    case IOCTL_KME_MAP:
        Status = DxbKmeMap( pExt, pIrp );
        break;

    case IOCTL_KME_UNMAP:
        Status = DxbKmeUnmap( pIrp );
        break;

    case IOCTL_DS_READ:
    case IOCTL_DS_WRITE:
    case IOCTL_DS_POP:
    case IOCTL_DS_PUSH:
    case IOCTL_DS_UNLINK:
    default:
        SRAPRINT2(DIGIERROR, SRA_MED_LEVEL,
                  ( "DXB!DispatchIoctl: Rejecting unsupported DSAPI request: %s!\n",
                    Ioctl == IOCTL_DS_READ   ? "IOCTL_DS_READ"   :
                    Ioctl == IOCTL_DS_WRITE  ? "IOCTL_DS_WRITE"  :
                    Ioctl == IOCTL_DS_POP    ? "IOCTL_DS_POP"    :
                    Ioctl == IOCTL_DS_POP    ? "IOCTL_DS_PUSH"   :
                    Ioctl == IOCTL_DS_UNLINK ? "IOCTL_DS_UNLINK" :
                    "Unknown" ) );

        Status = STATUS_UNSUCCESSFUL;
        dxbIoCompleteRequest( pIrp, Status, 0 );
        break;
    }

#if DBG
    KeQuerySystemTime( &Time );
#endif
    SRAPRINT2(DIGIDEVICE, SRA_MED_LEVEL,
              ( "DXB!DeviceIoControl: Leave, time 0x%08x%08x, device %S, pIrp 0x%08x, Status 0x%x\n", 
                Time.HighPart, Time.LowPart, pExt->Name.Buffer, pIrp, Status ) );

    return Status;
}


STATIC NTSTATUS
dxbCloseComplete(
    PDEVICE_OBJECT pDeviceObject,
    PIRP           pIrp,
    PVOID          Context )
/*++

Description:

    This is the completion routine for DxbClose.

Parameters:

    pDeviceObject

    pIrp

    Context
        Not used.

Return Value:

    STATUS_MORE_PROCESSING_REQUIRED

--*/
{
    UNREFERENCED_PARAMETER( pDeviceObject );
    UNREFERENCED_PARAMETER( Context );
    ASSERT( pIrp );

    ASSERT( pIrp->UserIosb );
    *pIrp->UserIosb = pIrp->IoStatus;

    ASSERT( pIrp->UserEvent );
    (void)KeSetEvent( pIrp->UserEvent, IO_NO_INCREMENT, FALSE );
    IoFreeIrp( pIrp );
    return( STATUS_MORE_PROCESSING_REQUIRED );
}


STATIC NTSTATUS
dxbClose(
    PDXB_STREAM_OBJECT pStream )
/*++

Description:

    This routine performs a synchronous IOCTL_DS_CLOSE request.  Used internally at
    IRP_MJ_CLEANUP time to close orphan streams.

Parameters:

    pStream
        Stream to be closed.

Return Value:

    STATUS_SUCCESS, or some error

--*/
{
    DS_CLOSE Close;
    PIRP pIrp;
    KEVENT Event;
    IO_STATUS_BLOCK IoStatus;
    NTSTATUS Status;
    PIO_STACK_LOCATION pIos;

    ASSERT( pStream && pStream->Type == DXB_TYPE_STREAM_OBJECT );
    RtlZeroMemory( &Close, sizeof( Close ) );
    Close.hStream = (HANDLE)pStream->Channel;

    ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );

    KeInitializeEvent( &Event, NotificationEvent, FALSE );
    RtlZeroMemory( &IoStatus, sizeof( IoStatus ) );

    pIrp = IoAllocateIrp( pStream->pExt->pDeviceObject->StackSize, FALSE );
    if ( !pIrp )
        return( STATUS_INSUFFICIENT_RESOURCES );

    pIrp->RequestorMode = KernelMode;
    pIrp->AssociatedIrp.SystemBuffer = &Close;
    pIrp->UserIosb = &IoStatus;
    pIrp->UserEvent = &Event;

    pIos = IoGetNextIrpStackLocation( pIrp );
    pIos->MajorFunction = IRP_MJ_DEVICE_CONTROL;
    pIos->Parameters.DeviceIoControl.IoControlCode = (ULONG)IOCTL_DS_CLOSE;
    pIos->Parameters.DeviceIoControl.InputBufferLength = sizeof( Close );
    IoSetCompletionRoutine( pIrp, dxbCloseComplete, NULL, TRUE, TRUE, TRUE );

    Status = IoCallDriver( pStream->pExt->pDeviceObject, pIrp );
    if ( Status == STATUS_PENDING )
    {
        (void)KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL );
        Status = IoStatus.Status;
    }
    return Status;
}


NTSTATUS
DxbDeviceCleanup(
    IN PDEVICE_OBJECT pDevice,
    IN PIRP           pIrp )
/*++

Routine Description:

    A File object (i.e. handle returned from CreateFile) is being closed.  Shutdown
    all streams opened on this File Object.

Arguments:

    pDevice
        Device Object

    pIrp
        The cleanup request

Return Value:


--*/
{
    PDXB_DEVICE_EXTENSION pExt = pDevice->DeviceExtension;
    PLIST_ENTRY pHead, pEntry;
    NTSTATUS Status;
    LARGE_INTEGER Time = {0};

#if DBG
    KeQuerySystemTime( &Time );
#endif

    SRAPRINT2(DIGIDEVICE, SRA_MED_LEVEL,
              ( "DXB!DeviceCleanup: time 0x%08x%08x, device %S\n",
                Time.HighPart, Time.LowPart,
                ((PDXB_DEVICE_EXTENSION)(pDevice->DeviceExtension))->Name.Buffer ) );

    DxbAdapterLock( pExt );
    pHead = &pExt->StreamList;
    pEntry = pHead->Flink;
    while ( pEntry != pHead )
    {
        PDXB_STREAM_OBJECT pStream;
        pStream = CONTAINING_RECORD( pEntry, DXB_STREAM_OBJECT, Entries.StreamList );

        ASSERT( pStream->Type == DXB_TYPE_STREAM_OBJECT );
        if ( pStream->OriginalFileObject == pIrp->Tail.Overlay.OriginalFileObject )
        {
            // This stream was created by the owner of the FileObject being closed.
            // Close the stream since it's creator/owner is going away.
            DxbAdapterUnlock( pExt );
            SRAPRINT2(DIGIERROR, SRA_MED_LEVEL, 
                      ( "*** DxbCleanup: closing orphan Stream 0x%x ***\n", pStream ) );

            Status = dxbClose( pStream );   // Don't use DsClose!  Not a dsapi lib hStream.
            if ( NT_ERROR(Status) )
            {
                // Informational only - don't care if it fails!
                SRAPRINT2(DIGIERROR, SRA_MED_LEVEL,
                          ( "***DxbCleanup: DsClose error, Status 0x%x ***\n", Status) );
            }

            DxbAdapterLock( pExt );

            // Must reset to head of list, because any number of Streams may have
            // been closed since between the last release and this acquire of spinlock.
            pEntry = pHead;
        }
        // Get next entry
        pEntry = pEntry->Flink;
    }

    DxbAdapterUnlock( pExt );
    dxbIoCompleteRequest( pIrp, STATUS_SUCCESS, 0 );

    SRAPRINT2(DIGIDEVICE, SRA_MED_LEVEL, 
              ( "DXB!DeviceCleanup: Leave, device %S\n",
                ((PDXB_DEVICE_EXTENSION)(pDevice->DeviceExtension))->Name.Buffer ) );

    return STATUS_SUCCESS;
}


NTSTATUS
DxbDeviceShutdown(
    IN PDEVICE_OBJECT pDevice,
    IN PIRP           pIrp )
{
    PDXB_DEVICE_EXTENSION pExt = pDevice->DeviceExtension;
    ASSERT( pExt->Type == DXB_TYPE_DEVICE_EXT );
    SRAPRINT2(DIGIWARN, SRA_MED_LEVEL, 
              ( "DXB!DeviceShutdown: device %S\n", pExt->Name.Buffer ) );

    pExt->bShutdown = TRUE;

    // Cancel outstanding IO
    DxbCancelAdapterIo( pExt, STATUS_IO_DEVICE_ERROR, TRUE/* all */);

    {
        // Delay to make sure DPC has seen the shutdown
        LARGE_INTEGER Delay;
        Delay.QuadPart = 100 * ONE_MILLISECOND;
        ASSERT( KeGetCurrentIrql() < DISPATCH_LEVEL );
        KeDelayExecutionThread( KernelMode, FALSE, &Delay );
    }

    pExt->AssertReset( pExt );
    dxbIoCompleteRequest( pIrp, STATUS_SUCCESS, 0 );
    return STATUS_SUCCESS;
}


VOID
dxbDeleteDevice(
    PDXB_DEVICE_EXTENSION pExt )
/*++

Routine Description:

   Release any resources which we have allocated for the DeviceObject and delete the
    device object.

Arguments:

   pExt - the device extension of the device to delete.

Return Value:

   None.

--*/
{
    SRAPRINT2(DIGIDEVICE, SRA_MED_LEVEL, 
              ( "DXB!dxbDeleteDevice, device %S\n", pExt->Name.Buffer ) );

    // Delete SymbolicLink
    {
        UNICODE_STRING SymbolicName;
        if ( NT_SUCCESS( DigiStrSymbolicName( &SymbolicName, pExt->Name.Buffer ) ) )
        {
            (void)IoDeleteSymbolicLink( &SymbolicName );
            DigiStrFreeUnicodeString( &SymbolicName );
        }
    }

//#if NT5_BUILD
    if ( pExt->pNextDeviceObject )
        IoDetachDevice( pExt->pNextDeviceObject );
//#endif

    IoDeleteDevice( pExt->pDeviceObject );
}


//#if NT5_BUILD
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;
}


STATIC NTSTATUS
GetDeviceClass( 
    IN PDEVICE_OBJECT pPdo,
    IN OUT PWCHAR     buf,
    IN ULONG          cb )


{
    // Use the GUID of the adapter device class to determine whether this adapter
    // was installed as a MultiPortSerial adapter or Net adapter.
    // 
    // If MultiPortSerial, the string "Mps" will be used when defining the name
    // of the device object - this will guarantee no name conflicts between
    // Digi adapters of each class installed in the same system.
#define MULTIPORTSERIAL L"Mps"
   
    NTSTATUS Status;   
    ULONG       MyCb;

    Status = IoGetDeviceProperty( pPdo, DevicePropertyClassGuid, 0, NULL, &MyCb );

    if ( Status == STATUS_BUFFER_TOO_SMALL && MyCb != 0) 
    {
        PWCHAR  pGuidStr;

        pGuidStr = DigiAllocMem( NonPagedPool, MyCb );                  
        if ( NULL != pGuidStr ) 
        {
            UNICODE_STRING  GuidUString;
            GUID                Guid;

            Status = IoGetDeviceProperty( pPdo, DevicePropertyClassGuid, MyCb, pGuidStr, &MyCb );
            if (NT_SUCCESS( Status ))
                Status = DigiStrAllocUnicodeString( &GuidUString, pGuidStr, NULL, NULL );
            if (NT_SUCCESS( Status ))
                Status = RtlGUIDFromString( &GuidUString, &Guid );            
            if (NT_SUCCESS( Status )) 
            { 
                if ( IsEqualGUID( &Guid, (PVOID)&GUID_DEVCLASS_MULTIPORTSERIAL ))
                {
                    if ( cb < sizeof MULTIPORTSERIAL )
                        Status = STATUS_BUFFER_TOO_SMALL;

                    else
                        wcscpy( buf, MULTIPORTSERIAL );                     
                }               
            }               
            DigiFreeMem( pGuidStr );
            if ( GuidUString.Buffer )
                DigiStrFreeUnicodeString( &GuidUString );
        }
    }                     
   return Status;   
}      
//#endif

NTSTATUS
DxbDeviceUniqueName(
    IN  ULONG           UniqueId,
    OUT PUNICODE_STRING UniqueName
    )
{
    WCHAR buf[16];

    swprintf(buf,L"%08.8x",UniqueId);

    return DigiStrAllocUnicodeString( UniqueName, L"SraDxbDevice", buf, NULL );
}


//#if NT5_BUILD
NTSTATUS
DxbAddDevice( 
    IN PDRIVER_OBJECT pDriverObject,
    IN PDEVICE_OBJECT pPhysicalDeviceObject )
{
    PDXB_DEVICE_EXTENSION pExt;
    PDEVICE_OBJECT pFunctionalDeviceObject = NULL;
    NTSTATUS Status;
    UNICODE_STRING Unique = {0};
    UNICODE_STRING Instance = {0};
    UNICODE_STRING Name = {0};
    ULONG UniqueId = InterlockedExchangeAdd(&gInstanceCt,1);

    {
        WCHAR buf[16] = {0};
        WCHAR ClsBuf[16] = {0};


        Status = GetDeviceInstance( pPhysicalDeviceObject, buf, sizeof buf );
        if ( NT_SUCCESS( Status ) )
            Status = GetDeviceClass( pPhysicalDeviceObject, ClsBuf, sizeof ClsBuf );
        if ( NT_SUCCESS( Status ) )
            Status = DigiStrAllocUnicodeString( &Instance, buf, NULL, NULL );
        if ( NT_SUCCESS( Status ) )
            Status = DigiStrAllocUnicodeString( &Name, L"SraDxb", ClsBuf , buf );
        if ( NT_SUCCESS( Status ) )
            Status = DxbDeviceUniqueName(UniqueId,&Unique);
    }

    if ( NT_SUCCESS( Status ) )
    {
        UNICODE_STRING DeviceName = {0};
        Status = DigiStrDeviceName( &DeviceName, Unique.Buffer );
        if ( NT_SUCCESS( Status ) )
        {
            Status = IoCreateDevice( pDriverObject, sizeof *pExt, &DeviceName, FILE_DEVICE_DS, 0, FALSE, &pFunctionalDeviceObject );
            DigiStrFreeUnicodeString( &DeviceName );
        }
        if ( NT_ERROR( Status ) )
            goto Exit;
    }

    if (!pFunctionalDeviceObject)
    {
        SRAPRINT(SRA_LOW_LEVEL,("Warning: DxbAddDevice - no pFunctionalDeviceObject\n"));
        return Status;
    }

    pExt = pFunctionalDeviceObject->DeviceExtension;

    pExt->Type = DXB_TYPE_DEVICE_EXT;
    
    pExt->pPdo = pPhysicalDeviceObject;
    pExt->fHoldNewRequests = FALSE;
    InitializeListHead( &pExt->RequestList );
    InitializeListHead( &pExt->ChildListHead );

    DigiInitializeSpinLock( &pExt->DxbLock );
    pExt->pDeviceObject = pFunctionalDeviceObject;

    InitializeListHead( &pExt->StreamList );
    DigiInitializeSpinLock( &pExt->WindowLock );

    dxbQueueInitializeEventDpcTimer( pExt );
    dxbQueueInitializeCmdWorkQueue( pExt );
    dxbQueueInitializeCreditWorkQueue( pExt );

    {
        ULONG i;
        for ( i = 0; i < sizeof pExt->Channels / sizeof pExt->Channels[0]; i++ )
            DigiInitializeSpinLock( &pExt->Channels[i].DxbLock );
    }

    {
        POWER_STATE State = {0};

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

        State.DeviceState = PowerDeviceD0;
        (void)PoSetPowerState( pFunctionalDeviceObject, DevicePowerState, State );
    }

    pFunctionalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;

    // Set FDO PAGABLE flag.  This forces SET_POWER apis to be called
    // at PASSIVE_LEVEL rather than DISPATCH_LEVEL.
    pFunctionalDeviceObject->Flags |= DO_POWER_PAGABLE;

    pExt->pNextDeviceObject = IoAttachDeviceToDeviceStack( pFunctionalDeviceObject, pPhysicalDeviceObject );

    // Create a symbolic link name for this device
    pExt->Name.Buffer = pExt->NameBuffer;
    pExt->Name.MaximumLength = sizeof pExt->NameBuffer;
    pExt->Name.Length = 0;
    RtlAppendUnicodeStringToString( &pExt->Name, &Name );

    pExt->Instance.Buffer = pExt->InstanceBuffer;
    pExt->Instance.MaximumLength = sizeof pExt->InstanceBuffer;
    pExt->Instance.Length = 0;
    RtlAppendUnicodeStringToString( &pExt->Instance, &Instance );

    pExt->UniqueId = UniqueId;

    SRAPRINT( DIGIDEVICE|DIGIPNP, ( "Dxb!AddDevice: DriverObject 0x%x, PDO  0x%x\n"
                                    "               FDO 0x%x, pExt 0x%x\n"
                                    "               NextLowerDevice 0x%x\n"
                                    "               Name %S\n"
                                    "               Instance %S\n"
                                    "               UniqueId %d\n"
                                    "               Status 0x%x\n",
                                    pDriverObject, pPhysicalDeviceObject, 
                                    pFunctionalDeviceObject, pExt, 
                                    pExt->pNextDeviceObject, 
                                    pExt->Name.Buffer, 
                                    pExt->Instance.Buffer, 
                                    pExt->UniqueId,
                                    Status ) );
 Exit:

    if ( Instance.Buffer )
        DigiStrFreeUnicodeString( &Instance );

    if ( Name.Buffer )
        DigiStrFreeUnicodeString( &Name );

    if ( Unique.Buffer )
        DigiStrFreeUnicodeString( &Unique );

    return Status;
}


NTSTATUS
DxbDevicePnp(
    IN PDEVICE_OBJECT pDevice,
    IN PIRP           pIrp )
/*++

Description:

    The primary dispatch routine for IRP_MJ_PNP requests.

Parameters:

    Standard NT Dispatch routine parameters.

Return Value:

    STATUS_SUCCESS if request completed successfully.

--*/
{
    DXB_OBJECT_TYPE Type = *(PULONG)pDevice->DeviceExtension;
    PIO_STACK_LOCATION pIos = IoGetCurrentIrpStackLocation( pIrp );
    ULONG Minor = pIos->MinorFunction;
    NTSTATUS Status;

    SRAPRINT2(DIGIDEVICE, SRA_MED_LEVEL, 
                  ( "DXB!DevicePnp: Enter, %s, pExt 0x%08x, pIrp 0x%08x\n", 
              DxbGetPnpIrpName(Minor),
              pDevice->DeviceExtension, pIrp ) );

    // We handle the irp differently depending on the target device.  If it's our dxb
    // adapter device (FDO), use DxbPnpDispatch.  If it's a child device (PDO), use 
    // DxbChildDispatch.

    switch ( Type )
    {
        case DXB_TYPE_DEVICE_EXT:
            Status = DxbPnpDispatch( pDevice->DeviceExtension, pIrp, pIos );
            break;

        case DXB_TYPE_CHILD_DEVICE:
            Status = DxbChildDispatch( pDevice->DeviceExtension, pIrp, pIos );
            break;

        default:
            SRAPRINT2(DIGIERROR, SRA_MED_LEVEL, 
                                  ( "*** ERROR: PNP Irp received with bad device ext type, pExt 0x%x, %s (0x%x)! ***\n", pDevice->DeviceExtension, DxbGetPnpIrpName(Minor), Minor ) );
            Status = STATUS_UNSUCCESSFUL;
            dxbIoCompleteRequest( pIrp, Status, 0 );
            break;
    }

    SRAPRINT2(DIGIDEVICE, SRA_MED_LEVEL, 
                  ( "DXB!DevicePnp: Leave %s, pExt 0x%08x, pIrp 0x%08x, Status 0x%x\n", DxbGetPnpIrpName(Minor), pDevice->DeviceExtension, pIrp, Status ) );
    return Status;
}


/*---------------------------------------------------------------------------*/
/*  Name: DxbDevicePower                                                     */
/* --------------------------------------------------------------------------*/ 
/*    In: - pFuncDeviceObject, functional device object created by device    */
/*        - Irp, I/O request                                                 */   
/*---------------------------------------------------------------------------*/
/* InOut:                                                                    */
/*---------------------------------------------------------------------------*/
/*   Out:                                                                    */
/*---------------------------------------------------------------------------*/
/* Description: Handle IRP_MJ_POWER request                                  */
/*   uses the IRP's minor function code to dispatch a handler function (like */
/*   HandleSetPower for IRP_MN_SET_POWER)                                    */
/*---------------------------------------------------------------------------*/

NTSTATUS DxbDevicePower( IN PDEVICE_OBJECT pFuncDeviceObject, IN PIRP Irp )
{
    NTSTATUS Status;
    DXB_OBJECT_TYPE Type = *(PULONG)pFuncDeviceObject->DeviceExtension;
    
    switch (Type)
    {
    case DXB_TYPE_DEVICE_EXT:
        Status = DispatchPowerFdo( pFuncDeviceObject, Irp );
        break;

    case DXB_TYPE_CHILD_DEVICE:      
        Status = DispatchPowerPdo( pFuncDeviceObject, Irp );
        break;

    default:
        Status = DispatchPowerFdo( pFuncDeviceObject, Irp );
        break;
    }
    
    return Status;
}


NTSTATUS DxbDeviceSystemControl( 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.

--*/
{
    PDXB_DEVICE_EXTENSION pExt = pDevice->DeviceExtension;
    DXB_OBJECT_TYPE Type;

    NTSTATUS Status;
   
    DigiDump( DIGIDEVICE, ( "DXB!DeviceSystemControl: Enter, pExt 0x%08x, pIrp 0x%08x\n", 
              pDevice->DeviceExtension, Irp ) );

    if (!pDevice->DeviceExtension)
    {
        DigiDump (DIGIINIT, ("DeviceSystemControl: Invalid pDevice->DeviceExtension, using SraFirstAdapter\n"));
        // try and find it through other means
        pExt = SraFirstAdapter()->Adapter;
        if (!pExt)
        {
            Status = Irp->IoStatus.Status;
            IoCompleteRequest( Irp, IO_NO_INCREMENT );
            return Status;
        }

    }

    Type = *(PULONG)pDevice->DeviceExtension;

    switch (Type)
    {
    case DXB_TYPE_DEVICE_EXT:
        IoSkipCurrentIrpStackLocation(Irp);
        Status = IoCallDriver(pExt->pNextDeviceObject, Irp);
        break;

    case DXB_TYPE_CHILD_DEVICE:
        Status = Irp->IoStatus.Status;
        IoCompleteRequest( Irp, IO_NO_INCREMENT );
        break;

    default:
        IoSkipCurrentIrpStackLocation(Irp);
        Status = IoCallDriver(pExt->pNextDeviceObject, Irp);
        break;
    }

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


//#endif  // NT5_BUILD
