/*++

Copyright (c) 1998  Microsoft Corporation

Module Name:

    DBCLASS.C

Abstract:

    class driver for device bay controllers

    This module implements the code to function as 
    the Device Bay controller class driver
    
Environment:

    kernel mode only

Notes:


Revision History:

    

--*/


#include <wdm.h>
#include "stdarg.h"
#include "stdio.h"

#include "dbci.h"  
#include "dbclass.h"        //private data strutures
#include "dbfilter.h"
#include "usbioctl.h"

#include "1394.h"

#include <initguid.h>  
#include <wdmguid.h> 

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, DBCLASS_SyncSubmitDrb)
#pragma alloc_text(PAGE, DBCLASS_SyncGetSubsystemDescriptor)
#pragma alloc_text(PAGE, DBCLASS_SyncGetBayDescriptor)
#pragma alloc_text(PAGE, DBCLASS_SyncGetAllBayDescriptors)
#pragma alloc_text(PAGE, DBCLASS_SyncBayFeatureRequest)
#pragma alloc_text(PAGE, DBCLASS_SyncGetBayStatus)
#endif


/*
    Global Data Structures
*/

PDBC_CONTEXT DBCLASS_ControllerList;
KSPIN_LOCK DBCLASS_ControllerListSpin;

// Internal list of Device PDOs 
LIST_ENTRY DBCLASS_DevicePdoList;

//Internal list of Bus filter MDOs
LIST_ENTRY DBCLASS_BusFilterMdoList;

//
// We currently only allow one USB hub to be associated with 
// an ACPI Device Bay Controller.
// This hub may be the root hub or a real hub connected to one of 
// the root hub ports (ie Tier 1 only)
//
// We use the following global variable (since we only support
// one ACPI DBC) to indicate the upstream port for the hub 
// 0 indicates the hub is the root hub
// -1 indicates no ACPI DBC present
//

LONG DBCLASS_AcpiDBCHubParentPort = -1;

// set up the debug variables

#if DBG
ULONG DBCLASS_TotalHeapSace = 0;
ULONG DBCLASS_BreakOn = 0;

// #define DEBUG3

#ifdef DEBUG1
ULONG DBCLASS_Debug_Trace_Level = 1;
#else
    #ifdef DEBUG2
    ULONG DBCLASS_Debug_Trace_Level = 2;    
    #else 
        #ifdef DEBUG3
        ULONG DBCLASS_Debug_Trace_Level = 3;        
        #else
        ULONG DBCLASS_Debug_Trace_Level = 0;
        #endif /* DEBUG 3 */
    #endif /* DEBUG2 */
#endif /* DEBUG1 */

#endif /* DBG */


VOID 
DBCLASS_Wait(
    IN ULONG MilliSeconds
    )
 /* ++
  * 
  * Descriptor:
  * 
  * This causes the thread execution delayed for MiliSeconds.
  * 
  * Argument:
  * 
  * Mili-seconds to delay.
  * 
  * Return:
  * 
  * VOID
  * 
  * -- */
{
    LARGE_INTEGER time;
    ULONG timerIncerent;

    DBCLASS_KdPrint((2,"'Wait for %d ms\n", MilliSeconds));

    //
    // work only when LowPart is not overflown.
    //
    DBCLASS_ASSERT(21474 > MilliSeconds);

    //
    // wait ulMiliSeconds( 10000 100ns unit)
    //
    timerIncerent = KeQueryTimeIncrement() - 1;
    
    time.HighPart = -1;
    // round up to the next highest timer increment
    time.LowPart = -1 * (10000 * MilliSeconds + timerIncerent);
    KeDelayExecutionThread(KernelMode, FALSE, &time);

    return;
}

BOOLEAN
IsBitSet(
    PVOID Bitmap,
    ULONG BitNumber
    )
 /* ++
  *
  * Description:
  *
  * Check if a bit is set given a string of bytes.
  *
  * Arguments:
  *
  * 
  *
  * Return:
  *
  * TRUE - if the corresponding bit is set. FALSE - otherwise
  *
  * -- */
{
    ULONG dwordOffset;
    ULONG bitOffset;
    PULONG l = (PULONG) Bitmap;


    dwordOffset = BitNumber / 32;
    bitOffset = BitNumber % 32;

    return ((l[dwordOffset] & (1 << bitOffset)) ? TRUE : FALSE);
}


LONG
DBCLASS_DecrementIoCount(
    IN PDBC_CONTEXT DbcContext
    )
/*++

Routine Description:

Arguments:

Return Value:


--*/
{
    LONG ioCount;

    ioCount = InterlockedDecrement(&DbcContext->PendingIoCount);
    LOGENTRY(LOG_MISC, 'ioc-', DbcContext->PendingIoCount, ioCount, 0);
    
    DBCLASS_KdPrint ((2, "'Dec Pending io count = %x\n", ioCount));

    if (ioCount==0) {
        LOGENTRY(LOG_MISC, 'wRMe', DbcContext, 0, 0);
        KeSetEvent(&DbcContext->RemoveEvent,
                   1,
                   FALSE);
    }

    return ioCount;
}


VOID
DBCLASS_IncrementIoCount(
    IN PDBC_CONTEXT DbcContext
    )
/*++

Routine Description:

Arguments:

Return Value:


--*/
{
    InterlockedIncrement(&DbcContext->PendingIoCount);
    LOGENTRY(LOG_MISC, 'ioc+', DbcContext->PendingIoCount, 0, 0);
}

#if 0
VOID
SetBit(
    PVOID Bitmap,
    ULONG BitNumber
    )
 /* ++
  *
  * Description:
  *
  * Set a bit in a given a string of bytes.
  *
  * Arguments:
  *
  * Return:
  *
  * -- */
{
    ULONG dwordOffset;
    ULONG bitOffset;
    PULONG l = (PULONG) Bitmap;


    dwordOffset = BitNumber / 32;
    bitOffset = BitNumber % 32;

    l[dwordOffset] | =(1 << bitOffset);
}
#endif


NTSTATUS 
DBCLASS_SyncSubmitDrb(
    IN PDBC_CONTEXT DbcContext,
    IN PDEVICE_OBJECT DeviceObject,
    IN PDRB Drb
    )
 /* ++
  * 
  * Routine Description:
  * 
  * Passes a DRB to the DB Port Driver, and waits for return.
  * 
  * Arguments:
  * 
  * DeviceObject - Device object of the to of the port driver
  *                 stack
  * 
  * Return Value:
  * 
  * STATUS_SUCCESS if successful
  * 
  * -- */
{
    NTSTATUS ntStatus, status;
    PIRP irp;
    KEVENT event;
    IO_STATUS_BLOCK ioStatus;
    PIO_STACK_LOCATION nextStack;
#ifdef DEADMAN_TIMER  
    BOOLEAN haveTimer = FALSE;
    KDPC timeoutDpc;
    KTIMER timeoutTimer;
#endif

    PAGED_CODE();

    DBCLASS_BEGIN_SERIALIZED_DRB(DbcContext);
    
    KeInitializeEvent(&event, NotificationEvent, FALSE);

    irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_DBC_SUBMIT_DRB,
                                        DeviceObject,
                                        NULL,
                                        0,
                                        NULL,
                                        0,
                                        TRUE,  // INTERNAL
                                        &event,
                                        &ioStatus);

    if (NULL == irp) {
        DBCLASS_KdPrint((0, "'could not allocate an irp!\n"));
        TRAP();
        return STATUS_INSUFFICIENT_RESOURCES;
    }
    
    //
    // Call the port driver to perform the operation.  If the returned
    // status
    // is PENDING, wait for the request to complete.
    //
    nextStack = IoGetNextIrpStackLocation(irp);

    //
    // pass the DRB 
    //
    nextStack->Parameters.Others.Argument1 = Drb;
    ntStatus = IoCallDriver(DeviceObject, irp);
    
    if (ntStatus == STATUS_PENDING) {

#ifdef DEADMAN_TIMER
        LARGE_INTEGER dueTime;

        KeInitializeTimer(&timeoutTimer);
        KeInitializeDpc(&timeoutDpc,
                        UsbhTimeoutDPC,
                        irp);

        dueTime.QuadPart = -10000 * DEADMAN_TIMEOUT;

        KeSetTimer(&timeoutTimer,
                   dueTime,
                   &timeoutDpc);        

        haveTimer = TRUE;
#endif
    
        status = KeWaitForSingleObject(&event,
                                       Suspended,
                                       KernelMode,
                                       FALSE,
                                       NULL);

    } else {
        ioStatus.Status = ntStatus;
    }

#ifdef DEADMAN_TIMER
    //
    // remove our timeoutDPC from the queue
    //
    if (haveTimer) {
        KeCancelTimer(&timeoutTimer);
    }                
#endif /* DEADMAN_TIMER */

    DBCLASS_END_SERIALIZED_DRB(DbcContext);
    
    ntStatus = ioStatus.Status;

    DBCLASS_KdPrint((2,"'DBCLASS_SyncSubmitDrb (%x)\n", ntStatus));

    return ntStatus;
}


NTSTATUS
DBCLASS_StartController(
    IN PDBC_CONTEXT DbcContext,
    IN PIRP Irp,
    IN PBOOLEAN HandledByClass
    )
/*++

Routine Description:

    This routine starts the DBC note that we should never set
    HandledByClass to TRUE because the port driver always completes
    this request (if we get an error we just need to set the 
    ntStatus code) 
    
Arguments:

    DbcContext - context for controller

    Irp - 

    HandledByClass - not used

Return Value:

    STATUS_SUCCESS if successful

--*/
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    USHORT bay;
    CHAR stackSize;
    BOOLEAN Device1394IsPresent = FALSE;

    DBCLASS_KdPrint((1, "'Starting DBC\n"));
    BRK_ON_TRAP();

    LOGENTRY(LOG_MISC, 'STRd', DbcContext, 0, 0);

    INITIALIZE_DRB_SERIALIZATION(DbcContext);
    KeInitializeSpinLock(&DbcContext->FlagsSpin);
    KeInitializeEvent(&DbcContext->RemoveEvent, NotificationEvent, FALSE);
    DbcContext->PendingIoCount = 0;
    DbcContext->Flags = 0;
    // this will be set when the filter registers
    DbcContext->BusFilterMdo1394 = NULL;
    DbcContext->BusFilterMdoUSB = NULL;
    
    stackSize = DbcContext->TopOfStack->StackSize;
    DbcContext->ChangeIrp = IoAllocateIrp(stackSize, FALSE);
    DbcContext->Bus1394PortInfo = NULL;
    DbcContext->LinkDeviceObject = NULL;
    
    if (NULL == DbcContext->ChangeIrp ) {
        DBCLASS_KdPrint((0, "'could not allocate an irp!\n"));
        TRAP();
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }

    if (NT_SUCCESS(ntStatus)) {
        // get the susbsystem descriptor
        ntStatus = DBCLASS_SyncGetSubsystemDescriptor(DbcContext);
    }        

    if (NT_SUCCESS(ntStatus)) {
        ntStatus = DBCLASS_SyncGetAllBayDescriptors(DbcContext);
    }

    //
    // if this is a USBDBC then write the approptiate info
    // to the registry
    //
    if (NT_SUCCESS(ntStatus)) {
        if (DbcContext->ControllerSig == DBC_USB_CONTROLLER_SIG) {
            // this will set the registry keys for the hub
            // the USBDBC is attached to -- marking it as a 
            // DBC associated hub
            ntStatus = DBCLASS_SetupUSB_DBC(DbcContext);
            
        } else {

            // controller is ACPI therefore the 1394 bus guid is the 
            // same as the 1394 guid reported by the DBC -- ie there 
            // is no extra Link Phy for an ACPI DBC.
            
            // get the ACPIDBC Hub register now from the 
            // registry
            
            DBCLASS_GetRegistryKeyValueForPdo(DbcContext->ControllerPdo,
                                      FALSE,
                                      ACPI_HUB_KEY,
                                      sizeof(ACPI_HUB_KEY),
                                      &DBCLASS_AcpiDBCHubParentPort,
                                      sizeof(DBCLASS_AcpiDBCHubParentPort));
                                      
            DBCLASS_KdPrint((1, "'ACPI USB Parent Port: %d\n", 
                    DBCLASS_AcpiDBCHubParentPort));

#if DBG
            if (DBCLASS_AcpiDBCHubParentPort == -1) {
                DBCLASS_KdPrint((0, "'ACPI USB Parent Port not set!\n"));
                TEST_TRAP();
            }
#endif
            
            RtlCopyMemory(&DbcContext->Guid1394Bus[0], 
                          &DbcContext->SubsystemDescriptor.guid1394Link[0],
                          8);

        }
    }
    
    //
    // initialize per bay structures
    //

    DbcContext->BayInformation[0].DeviceFilterObject = (PVOID) -1;
    for (bay=1; bay <=NUMBER_OF_BAYS(DbcContext); bay++) {
    
        PDBC_BAY_DESCRIPTOR bayDescriptor;

        bayDescriptor = 
            &DbcContext->BayInformation[bay].BayDescriptor;
        
        // initilaize current status to 'empty'
        
        DbcContext->BayInformation[bay].LastBayStatus.us = 0;
        DbcContext->BayInformation[bay].DeviceFilterObject = NULL;

        // set the hub port number based on what the 
        // controller reported
        DbcContext->BayInformation[bay].UsbHubPort = 
             bayDescriptor->bHubPortNumber;
        

#if DBG
        DBCLASS_KdPrint((1,"'>BAY[%d].BayDescriptor.bBayNumber %d\n", 
                            bay, bayDescriptor->bBayNumber));                                     
        DBCLASS_KdPrint((1,"'>BAY[%d].BayDescriptor.bHubPortNumber %d\n", 
                            bay, bayDescriptor->bHubPortNumber));                                     
        DBCLASS_KdPrint((1,"'>BAY[%d].BayDescriptor.bPHYPortNumber %d\n", 
                            bay, bayDescriptor->bPHYPortNumber));                                     
        DBCLASS_KdPrint((1,"'>BAY[%d].BayDescriptor.bFormFactor %d\n", 
                            bay, bayDescriptor->bFormFactor));                                     
#endif        
    }
    
    // now post a notification
    if (NT_SUCCESS(ntStatus)) {
        
        DBCLASS_PostChangeRequest(DbcContext);
    } 

    if (NT_SUCCESS(ntStatus)) {
        // Bays initialized:
        //
        // Enable change indications for all bays, we post a change irp 
        // in case we start getting notifications right away
        //

        for (bay=1; bay <= NUMBER_OF_BAYS(DbcContext); bay++) {
            DBCLASS_SyncBayFeatureRequest(DbcContext,
                                      DRB_FUNCTION_SET_BAY_FEATURE, 
                                      bay, 
                                      DEVICE_STATUS_CHANGE_ENABLE); 
                                      
            DBCLASS_SyncBayFeatureRequest(DbcContext, 
                                      DRB_FUNCTION_SET_BAY_FEATURE, 
                                      bay, 
                                      REMOVAL_REQUEST_ENABLE);            
        }     

        //
        // check the initial state of the bays
        //
        // see if any devices are present 
        // 

        DBCLASS_KdPrint((1,"'STARTCONTROLLER: Checking Bays\n")); 
        
        for (bay=1; bay <= NUMBER_OF_BAYS(DbcContext); bay++) {

            NTSTATUS status;
            BAY_STATUS bayStatus;        

            status = DBCLASS_SyncGetBayStatus(DbcContext,
                                              bay,
                                              &bayStatus);

            if (NT_SUCCESS(status)) {
                DBCLASS_KdPrint((1,"'STARTCONTROLLER: init state - bay[%d] %x\n",
                    bay, bayStatus.us));                                     

                // 
                
                if (bayStatus.DeviceUsbIsPresent || 
                    bayStatus.Device1394IsPresent) {

                    if(bayStatus.Device1394IsPresent)
                        Device1394IsPresent = TRUE;
                        
                    // we have a device in the bay
                    DBCLASS_KdPrint((1,"'STARTCONTROLLER: detected device in bay[%d] %x\n",                    
                                    bay));
                                    
                    switch(bayStatus.CurrentBayState) {
                    
                    case BAY_STATE_EMPTY:
                    case BAY_STATE_DEVICE_ENABLED:  
                        // note:
                        // on the TI dbc if the bay state is enabled the device does
                        // not appear on the native bus
                    case BAY_STATE_DEVICE_INSERTED:
                        // note:
                        // on the TI dbc if the bay state is inserted the device does
                        // not appear on the native bus
                    
                        DBCLASS_KdPrint((1,"'STARTCONTROLLER: setting bay %d to inserted state\n", bay));                                     

                        DBCLASS_SyncBayFeatureRequest(DbcContext,
                                                      DRB_FUNCTION_SET_BAY_FEATURE,
                                                      bay,
                                                      REQUEST_DEVICE_INSERTED_STATE);

                        // get the new status and process it                                           
                        status = DBCLASS_SyncGetBayStatus(DbcContext,
                                                          bay,
                                                          &bayStatus);
                                                      
                        if (NT_SUCCESS(status)) {
                            DBCLASS_ProcessCurrentBayState(DbcContext,
                                                           bayStatus,
                                                           bay,
                                                           NULL);                                              
                        }                                                   
                        break;
                        
                    default:
                        break;
                        
                    } /* switch  bayStatus.CurrentBayState */
                }                
            }            
        }
    } /* nt_success */

    if (NT_SUCCESS(ntStatus)) {

        // get registry controled features
        {
        NTSTATUS status;
        ULONG release_on_shutdown = 0;

        // check unlock on shutdown, the default is on            
        status = DBCLASS_GetRegistryKeyValueForPdo(
                                           DbcContext->ControllerPdo,
                                           TRUE,
                                           RELEASE_ON_SHUTDOWN,
                                           sizeof(RELEASE_ON_SHUTDOWN),
                                           &release_on_shutdown,
                                           sizeof(release_on_shutdown));
                                           
        DBCLASS_KdPrint((1,"'STARTCONTROLLER: release_on_shutdown = %d\n", 
            release_on_shutdown));                                               
        if (release_on_shutdown) {
            DbcContext->Flags |= DBCLASS_FLAG_RELEASE_ON_SHUTDOWN;                                             
        }
        }
        
        //
        // consider ourselves started
        // note: if we return an error stopcontroller
        // will not be called
        //
        
        DbcContext->Stopped = FALSE;

        // set our current power state to 'D0' ie ON
        DbcContext->CurrentDevicePowerState = PowerDeviceD0;

        // in order for the DBC to be linked to a 1394 bus
        // we need to do an IoInvalidateDeviceRelations on 
        // all 1394 busses

        if(Device1394IsPresent)
            DBCLASS_Refresh1394();
   
    }

    // transition to zero signals stop/remove
    //
    // since we will get a stop even if we return
    // an error we alaways increment
    DBCLASS_IncrementIoCount(DbcContext);

    DBCLASS_KdPrint((1,"'STARTCONTROLLER: ntStatus = %x\n", ntStatus));  
    LOGENTRY(LOG_MISC, 'STRT', DbcContext, 0, ntStatus);
    return ntStatus;
}    


NTSTATUS
DBCLASS_CleanupController(
    IN PDBC_CONTEXT DbcContext
    )
/*++

Routine Description:

    cleans up the DBC on stop or remove
    
Arguments:

    DbcContext - context for controller

Return Value:

    STATUS_SUCCESS if successful

--*/
{

    NTSTATUS ntStatus;

    LOGENTRY(LOG_MISC, 'clUP', DbcContext, 0, 0);

    ntStatus = STATUS_SUCCESS;

    return ntStatus;

}


NTSTATUS
DBCLASS_StopController(
    IN PDBC_CONTEXT DbcContext,
    IN PIRP Irp,
    IN PBOOLEAN HandledByClass
    )
/*++

Routine Description:

    Stops the DBC
    
Arguments:

    DbcContext - context for controller

    Irp - 

    HandledByClass - set to true if we need to complete the Irp


Return Value:

    STATUS_SUCCESS if successful

--*/
{

    NTSTATUS ntStatus = STATUS_SUCCESS;
    KIRQL irql;
    BOOLEAN needCancel;

    DBCLASS_KdPrint((1, "'Stopping DBC\n"));
    LOGENTRY(LOG_MISC, 'STP>', DbcContext, 0, 0);

    // disable bay locks here
    if (DbcContext->Flags |= DBCLASS_FLAG_RELEASE_ON_SHUTDOWN) 
    {
        USHORT bay;
                    
        for (bay = 1; bay <= NUMBER_OF_BAYS(DbcContext); bay++) 
        {
          	PDRB drb;

           	//
           	// notify filter of a stop
           	// note that the filter may not veto the stop
           	//
           	drb = DbcExAllocatePool(NonPagedPool, 
                   	sizeof(struct _DRB_START_DEVICE_IN_BAY));

           	if (drb) 
           	{ 
           
               	drb->DrbHeader.Length = sizeof(struct _DRB_STOP_DEVICE_IN_BAY);
               	drb->DrbHeader.Function = DRB_FUNCTION_STOP_DEVICE_IN_BAY;
               	drb->DrbHeader.Flags = 0;

               	drb->DrbStartDeviceInBay.BayNumber = bay;
                    
               	// make the request
               	ntStatus = DBCLASS_SyncSubmitDrb(DbcContext,
                                               	 DbcContext->TopOfStack, 
                                               	 drb);
    			DbcExFreePool(drb);
           	}

        }
    }


    // cancel any pending notification Irps
    KeAcquireSpinLock(&DbcContext->FlagsSpin, &irql);            
    
    DbcContext->Flags |= DBCLASS_FLAG_STOPPING;
    needCancel = (BOOLEAN) (DbcContext->Flags & DBCLASS_FLAG_REQ_PENDING);
    
    KeReleaseSpinLock(&DbcContext->FlagsSpin, irql);
    DBCLASS_DecrementIoCount(DbcContext);

    if (needCancel) {
        LOGENTRY(LOG_MISC, 'kIRP', DbcContext, DbcContext->ChangeIrp, 0);
        IoCancelIrp(DbcContext->ChangeIrp);
    }

    {
        NTSTATUS status;

        // wait for any io request pending in our driver to
        // complete for finishing the remove

        LOGENTRY(LOG_MISC, 'STwt', DbcContext, 0, 0);
        status = KeWaitForSingleObject(
                    &DbcContext->RemoveEvent,
                    Suspended,
                    KernelMode,
                    FALSE,
                    NULL);
        LOGENTRY(LOG_MISC, 'STwd', DbcContext, 0, 0);
    }
            
    
    LOGENTRY(LOG_MISC, 'STP<', DbcContext, 0, ntStatus);
    
    return ntStatus;

}


PDBC_CONTEXT
DBCLASS_GetDbcContext(
    IN PDEVICE_OBJECT ControllerFdo
    )
/*++

Routine Description:

    Stops the DBC
    
Arguments:


Return Value:

    STATUS_SUCCESS if successful

--*/
{
    PDBC_CONTEXT dbcContext;
    KIRQL irql;
    
    // search our list if we find the TopOfStack device
    // object then this must be a filter driver, otherwise
    // allocate a new context structure for this controller

    KeAcquireSpinLock(&DBCLASS_ControllerListSpin, &irql);

    dbcContext = DBCLASS_ControllerList;

    while (dbcContext) {

        if (dbcContext->ControllerFdo == ControllerFdo) {
            break;
        }

        dbcContext = dbcContext->Next;
    
    } 

    KeReleaseSpinLock(&DBCLASS_ControllerListSpin, irql);

    return dbcContext;

}


NTSTATUS 
DBCLASS_SyncGetSubsystemDescriptor(
    IN PDBC_CONTEXT DbcContext
    )
 /* ++
  * 
  * Description:
  *
  * fetch the susbsystem descriptor from the port driver, and allocate 
  * a structure for the bay information
  * 
  * Arguments:
  * 
  * Return:
  * 
  * -- */
{
    NTSTATUS ntStatus;
    PDRB drb;
    
    PAGED_CODE();

    //
    // Allocate Drb from the non-paged pool
    //
    
    drb = DbcExAllocatePool(NonPagedPool, 
                            sizeof(struct _DRB_GET_SUBSYSTEM_DESCRIPTOR));

    if (drb) {

        drb->DrbHeader.Length = sizeof(struct _DRB_GET_SUBSYSTEM_DESCRIPTOR);
        drb->DrbHeader.Function = DRB_FUNCTION_GET_SUBSYSTEM_DESCRIPTOR;
        drb->DrbHeader.Flags = 0;

        // make the request
        ntStatus = DBCLASS_SyncSubmitDrb(DbcContext,
                                         DbcContext->TopOfStack, 
                                         drb);

        if (NT_SUCCESS(ntStatus)) {
            RtlCopyMemory(&DbcContext->SubsystemDescriptor,
                          &drb->DrbGetSubsystemDescriptor.SubsystemDescriptor,
                          sizeof(DbcContext->SubsystemDescriptor));
                          
            // dump susbsystem descriptor to debugger
            DBCLASS_KdPrint((1, "'DBC Susbsystem Descriptor:\n"));
            DBCLASS_KdPrint((1, "'>bLength = (%08X)\n", 
                DbcContext->SubsystemDescriptor.bLength));
            DBCLASS_KdPrint((1, "'>bDescriptorType = (%08X)\n", 
                DbcContext->SubsystemDescriptor.bDescriptorType));            
            DBCLASS_KdPrint((1, "'>SUBSYSTEM_DESCR = (%08X)\n", 
                DbcContext->SubsystemDescriptor.bmAttributes));            
            DBCLASS_KdPrint((1, "'>>SUBSYSTEM_DESCR.BayCount = (%08X)\n", 
                DbcContext->SubsystemDescriptor.bmAttributes.BayCount));            
            DBCLASS_KdPrint((1, "'>>SUBSYSTEM_DESCR.HasSecurityLock = (%08X)\n", 
                DbcContext->SubsystemDescriptor.bmAttributes.HasSecurityLock)); 
            DBCLASS_KdPrint((1, "'>>SUBSYSTEM_DESCR.LinkGuid\n"));
#if DBG
            DBCLASS_KdPrintGuid(1, 
                                &DbcContext->SubsystemDescriptor.guid1394Link[0]);
#endif                                
        }
        
        DbcExFreePool(drb);
    } else {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }
    
    return ntStatus;
}


NTSTATUS 
DBCLASS_SyncGetBayDescriptor(
    IN PDBC_CONTEXT DbcContext,
    IN USHORT BayNumber,
    IN PDBC_BAY_DESCRIPTOR BayDescriptor
    )
 /* ++
  * 
  * Description:
  *
  * fetch the bay descriptor from the port driver,
  * 
  * Arguments:
  * 
  * Return:
  * 
  * -- */
{
    NTSTATUS ntStatus;
    PDRB drb;

    PAGED_CODE();

    //
    // Allocate Drb from the non-paged pool
    //
    
    drb = DbcExAllocatePool(NonPagedPool, 
                           sizeof(struct _DRB_GET_BAY_DESCRIPTOR));

    if (drb) {

        drb->DrbHeader.Length = sizeof(struct _DRB_GET_BAY_DESCRIPTOR);
        drb->DrbHeader.Function = DRB_FUNCTION_GET_BAY_DESCRIPTOR;
        drb->DrbHeader.Flags = 0;
        drb->DrbGetBayDescriptor.BayNumber = BayNumber;
        
        // make the request
        ntStatus = DBCLASS_SyncSubmitDrb(DbcContext,
                                         DbcContext->TopOfStack, 
                                         drb);

        if (NT_SUCCESS(ntStatus)) {
            RtlCopyMemory(BayDescriptor,
                          &drb->DrbGetBayDescriptor.BayDescriptor,
                          sizeof(*BayDescriptor));
        }
        
        DbcExFreePool(drb);
    } else {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }
    
    return ntStatus;
}


NTSTATUS 
DBCLASS_SyncGetControllerStatus(
    IN PDBC_CONTEXT DbcContext
    )
 /* ++
  * 
  * Description:
  *
  * Arguments:
  * 
  * Return:
  * 
  * -- */
{
    NTSTATUS ntStatus;
    PDRB drb;

    PAGED_CODE();

    //
    // Allocate Drb from the non-paged pool
    //
    
    drb = DbcExAllocatePool(NonPagedPool, 
                            sizeof(struct _DRB_GET_CONTROLLER_STATUS));

    if (drb) {

        drb->DrbHeader.Length = sizeof(struct _DRB_GET_CONTROLLER_STATUS);
        drb->DrbHeader.Function = DRB_FUNCTION_GET_CONTROLLER_STATUS;
        
        // make the request
        ntStatus = DBCLASS_SyncSubmitDrb(DbcContext,
                                         DbcContext->TopOfStack, 
                                         drb);

        DbcExFreePool(drb);
    } else {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }
    
    return ntStatus;
}


NTSTATUS 
DBCLASS_SyncGetAllBayDescriptors(
    IN PDBC_CONTEXT DbcContext
    )
 /* ++
  * 
  * Description:
  *
  * fetch the susbsystem descriptor from the port driver, and allocate 
  * a structure for the bay information
  * 
  * Arguments:
  * 
  * Return:
  * 
  * -- */
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    USHORT bay;
    
    PAGED_CODE();

    for (bay=1; bay <= NUMBER_OF_BAYS(DbcContext); bay++) {
        ntStatus = DBCLASS_SyncGetBayDescriptor(DbcContext,
                                                bay,
                                                &DbcContext->BayInformation[bay].BayDescriptor);
    }                                                     

    return ntStatus;
}


NTSTATUS 
DBCLASS_SyncBayFeatureRequest(
    IN PDBC_CONTEXT DbcContext,
    IN USHORT Op,
    IN USHORT BayNumber,
    IN USHORT FeatureSelector
    )
 /* ++
  * 
  * Description:
  *
  * fetch the susbsystem descriptor from the port driver, and allocate 
  * a structure for the bay information
  * 
  * Arguments:
  * 
  * Return:
  * 
  * -- */
{
    NTSTATUS ntStatus;
    PDRB drb;

    PAGED_CODE();

    //
    // Allocate Drb from the non-paged pool
    //

    DBCLASS_ASSERT(BayNumber != 0);
    DBCLASS_ASSERT(BayNumber <= MAX_BAY_NUMBER);
    
    drb = DbcExAllocatePool(NonPagedPool, 
                           sizeof(struct _DRB_BAY_FEATURE_REQUEST));

    if (drb) {

        drb->DrbHeader.Length = sizeof(struct _DRB_BAY_FEATURE_REQUEST);
        drb->DrbHeader.Function = Op;
        drb->DrbHeader.Flags = 0;
        drb->DrbBayFeatureRequest.BayNumber = BayNumber;
        drb->DrbBayFeatureRequest.FeatureSelector = FeatureSelector;
        // make the request
        ntStatus = DBCLASS_SyncSubmitDrb(DbcContext,
                                         DbcContext->TopOfStack, 
                                         drb);

        DbcExFreePool(drb);
    } else {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }
    
    return ntStatus;
}


NTSTATUS 
DBCLASS_SyncGetBayStatus(
    IN PDBC_CONTEXT DbcContext,
    IN USHORT BayNumber,
    IN PBAY_STATUS BayStatus
    )
 /* ++
  * 
  * Description:
  *
  * fetch the susbsystem descriptor from the port driver, and allocate 
  * a structure for the bay information
  * 
  * Arguments:
  * 
  * Return:
  * 
  * -- */
{
    NTSTATUS ntStatus;
    PDRB drb;

    PAGED_CODE();

    //
    // Allocate Drb from the non-paged pool
    //

    DBCLASS_ASSERT(BayNumber != 0);
    DBCLASS_ASSERT(BayNumber <= MAX_BAY_NUMBER);
    
    drb = DbcExAllocatePool(NonPagedPool, 
                           sizeof(struct _DRB_GET_BAY_STATUS));

    if (drb) {

        drb->DrbHeader.Length = sizeof(struct _DRB_GET_BAY_STATUS);
        drb->DrbHeader.Function = DRB_FUNCTION_GET_BAY_STATUS;
        drb->DrbHeader.Flags = 0;
        drb->DrbGetBayStatus.BayNumber = BayNumber;
        
        // make the request
        ntStatus = DBCLASS_SyncSubmitDrb(DbcContext,
                                         DbcContext->TopOfStack, 
                                         drb);

        if (NT_SUCCESS(ntStatus)) {
            BayStatus->us = drb->DrbGetBayStatus.BayStatus.us;    
            // dump status to debugger
            DBCLASS_KdPrint((2, "'status for bay (%d) 0x%0x:\n", BayNumber,
                drb->DrbGetBayStatus.BayStatus.us));
            DBCLASS_KdPrint((2, "'>VidEnabled = (%08X)\n", 
                 BayStatus->VidEnabled));
            DBCLASS_KdPrint((2, "'>RemovalWakeupEnabled = (%08X)\n", 
                BayStatus->RemovalWakeupEnabled));                
            DBCLASS_KdPrint((2, "'>DeviceStatusChangeEnabled = (%08X)\n", 
                BayStatus->DeviceStatusChangeEnabled));                
            DBCLASS_KdPrint((2, "'>RemovalRequestEnabled = (%08X)\n", 
                BayStatus->RemovalRequestEnabled));                
            DBCLASS_KdPrint((2, "'>LastBayStateRequested = (%08X)\n", 
                BayStatus->LastBayStateRequested));                
            DBCLASS_KdPrint((2, "'>InterlockEngaged = (%08X)\n", 
                BayStatus->InterlockEngaged));                
            DBCLASS_KdPrint((2, "'>DeviceUsbIsPresent = (%08X)\n", 
                BayStatus->DeviceUsbIsPresent)); 
            DBCLASS_KdPrint((2, "'>Device1394IsPresent = (%08X)\n", 
                 BayStatus->Device1394IsPresent));                
            DBCLASS_KdPrint((2, "'>DeviceStatusChange = (%08X)\n", 
                 BayStatus->DeviceStatusChange));                 
            DBCLASS_KdPrint((2, "'>RemovalRequestChange = (%08X)\n", 
                 BayStatus->RemovalRequestChange));     
            DBCLASS_KdPrint((2, "'>CurrentBayState = (%08X)\n", 
                 BayStatus->CurrentBayState));     
            DBCLASS_KdPrint((2, "'>SecurityLockEngaged = (%08X)\n", 
                 BayStatus->SecurityLockEngaged));     
        }
        
        DbcExFreePool(drb);
    } else {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }
    
    return ntStatus;
}


NTSTATUS
DBCLASS_ChangeIndication(
    IN PDEVICE_OBJECT PNull,
    IN PIRP Irp,
    IN PVOID Context
    )
 /* ++
  *
  * Description:
  *
  * Arguments:
  *
  * Return:
  *
  * -- */
{
    PDBC_CONTEXT dbcContext = Context;
    KIRQL irql;
    BOOLEAN stopping;
    PDBCLASS_WORKITEM workItem;
    
    // status change
    //
    // we should not get here unless something really has
    // changed
    
    KeAcquireSpinLock(&dbcContext->FlagsSpin, &irql);     
    dbcContext->Flags &= ~DBCLASS_FLAG_REQ_PENDING;
    stopping = (BOOLEAN) (dbcContext->Flags & DBCLASS_FLAG_STOPPING);
    KeReleaseSpinLock(&dbcContext->FlagsSpin, irql);

    LOGENTRY(LOG_MISC, 'CHid', dbcContext, 0, Irp->IoStatus.Status);
    BRK_ON_TRAP();

    if (!stopping) {

        //
        // we have a legitimate change
        //

        // schedule a workitem to process the change
        LOGENTRY(LOG_MISC, 'QCH>', dbcContext, 0, 0);

        workItem = DbcExAllocatePool(NonPagedPool, sizeof(DBCLASS_WORKITEM));

        if (workItem) {
            DBCLASS_KdPrint((1, "'Schedule Workitem for Change Request\n"));

            LOGENTRY(LOG_MISC, 'qITM', dbcContext,
                workItem, 0);

            workItem->Sig = DBC_WORKITEM_SIG;
            workItem->DbcContext = dbcContext;
            workItem->IrpStatus = Irp->IoStatus.Status;
            
            ExInitializeWorkItem(&workItem->WorkQueueItem,
                                 DBCLASS_ChangeIndicationWorker,
                                 workItem);

            ExQueueWorkItem(&workItem->WorkQueueItem,
                            DelayedWorkQueue);

        } else {
            
            DBCLASS_DecrementIoCount(dbcContext);
            DBCLASS_KdPrint((0, "'No Memory for workitem!\n"));
            TRAP();
            
        }            
        
    } else {
        DBCLASS_DecrementIoCount(dbcContext);
    }

    return STATUS_MORE_PROCESSING_REQUIRED;
}


NTSTATUS
DBCLASS_ProcessCurrentBayState(
    IN PDBC_CONTEXT DbcContext,
    IN BAY_STATUS BayStatus,
    IN USHORT Bay,
    IN PBOOLEAN PostChangeRequest
    )
 /* ++
  *
  * Description:
  *
  * Arguments:
  *
  * Return:
  *
  * -- */
{
    ULONG last,current;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    
    current = BayStatus.CurrentBayState;
    last = DbcContext->BayInformation[Bay].LastBayStatus.CurrentBayState;
    
    DBCLASS_KdPrint((0, "'>PBS - current bay state: %x\n", current));
    DBCLASS_KdPrint((0, "'>last bay state: %x\n", last));
    
    // figure out what current state of the bay is and deal with it
    
    if (current == last) {
        DBCLASS_KdPrint((0, "'>>no changes detected %x\n", current));     
    } else {
        switch (current) {
        
        case BAY_STATE_DEVICE_INSERTED:

            LOGENTRY(LOG_MISC, 'byIN', DbcContext, ntStatus, Bay);
            
            // engage the interlock, lock that baby in to the bay
            ntStatus = DBCLASS_SyncBayFeatureRequest(DbcContext,
                                                     DRB_FUNCTION_SET_BAY_FEATURE,
                                                     Bay,
                                                     LOCK_CTL);
            DBCLASS_Wait(1000);                                                         


            // enable Vid so the device will appear on the native bus
            ntStatus = DBCLASS_SyncBayFeatureRequest(DbcContext,
                                                     DRB_FUNCTION_SET_BAY_FEATURE,
                                                     Bay,
                                                     ENABLE_VID_POWER);

            DBCLASS_Wait(1000);

            //
            // The device will now appear on the native bus 
            // allowing the OS to load appropriate drivers
            //
            DBCLASS_KdPrint((0, "'>>Bay Vid Power Enabled\n"));

//            *PostChangeRequest = FALSE;

/* remove when filter is ready */
   ntStatus = DBCLASS_SyncBayFeatureRequest(DbcContext,
                                             DRB_FUNCTION_SET_BAY_FEATURE,
                                             Bay,
                                             REQUEST_DEVICE_ENABLED_STATE);    


    DBCLASS_SyncGetBayStatus(DbcContext, Bay, &BayStatus);                
    
    DbcContext->BayInformation[Bay].LastBayStatus = BayStatus;       
/**/    
                         
            break;
            
        case BAY_STATE_DEVICE_REMOVAL_REQUESTED:                        
            {

            PIO_STACK_LOCATION nextStack;
            PDRB drb;
            CHAR stackSize;
            PIRP irp;
            PEJECT_CONTEXT ejectContext;
            PUCHAR pch;
            
            DBCLASS_KdPrint((0, "'>>Request Device Eject BAY[%d]\n", Bay));

            // need to OK the eject                    
            
            pch = DbcExAllocatePool(NonPagedPool, 
                    sizeof(struct _DRB_START_DEVICE_IN_BAY) + sizeof(EJECT_CONTEXT));

            ejectContext = (PEJECT_CONTEXT) pch;
            drb = (PDRB) (pch + sizeof(EJECT_CONTEXT));

            if (pch) { 

                ejectContext->Bay = Bay;
                ejectContext->DbcContext = DbcContext;
            
                drb->DrbHeader.Length = sizeof(struct _DRB_EJECT_DEVICE_IN_BAY);
                drb->DrbHeader.Function = DRB_FUNCTION_EJECT_DEVICE_IN_BAY;
                drb->DrbHeader.Flags = 0;

                drb->DrbEjectDeviceInBay.BayNumber = Bay;

//                deviceExtensionPdoFilter = 
//                    DbcContext->BayInformation[Bay].DeviceFilterObject->DeviceExtension;
                                   
                // make the request asynchronously
                //
                // normally this request is just completed by the port 
                // driver but we make the request so that the oem filter 
                // may intervene in the removal process

                stackSize = DbcContext->TopOfStack->StackSize;
                irp = IoAllocateIrp(stackSize, FALSE);

                if (NULL == irp) {
                
                    DBCLASS_KdPrint((0, "'could not allocate an irp!\n"));
                    if (PostChangeRequest) {
                        *PostChangeRequest = TRUE;                
                    } 
                    TRAP();
                    ntStatus = STATUS_INSUFFICIENT_RESOURCES;
                    
                } else {
    
                    //
                    // Call the port driver to perform the operation.  If the returned
                    //
                    nextStack = IoGetNextIrpStackLocation(irp);
                    nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
                    nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_DBC_SUBMIT_DRB;
                    nextStack->Parameters.Others.Argument1 = drb;
                    
                    //
                    // pass the DRB 
                    //
                    IoSetCompletionRoutine(irp,
                                   DBCLASS_EjectBayComplete,
                                   ejectContext,
                                   TRUE,
                                   TRUE,
                                   TRUE);

                    ntStatus = IoCallDriver(DbcContext->TopOfStack, irp);
                                                
                    if (PostChangeRequest) {
                        *PostChangeRequest = FALSE;                
                    }                               
                }    
            } else {
            
                // no memory for drb, re-post the change request and
                // try again
                
                DBCLASS_KdPrint((0, "'could not allocate an drb!\n"));
                if (PostChangeRequest) {
                    *PostChangeRequest = TRUE;                
                }                   
                TRAP();
                ntStatus = STATUS_INSUFFICIENT_RESOURCES;
            }
            
            }
            break;

        case BAY_STATE_EMPTY:                      
        
            //
            // we should have no PDO if the bay is empty
            //
            
//            DBCLASS_ASSERT(
//                DbcContext->BayInformation[Bay].DeviceFilterObject == NULL);

            DBCLASS_KdPrint((0, "'>>Found Bay empty\n"));
            break;            

        case BAY_STATE_DEVICE_ENABLED:                      
            //
            // we may get here because the bay was enabled at boot
            // in this case we have nothing to do
            //
            
            DBCLASS_KdPrint((0, "'>>Found Bay enabled\n"));
            
            break;
            
        default:
            DBCLASS_KdPrint((0, "'>>Bay State not handled\n"));
            TRAP();
            break;

        } /* switch */
    }

    return ntStatus;
}   


VOID
DBCLASS_ChangeIndicationWorker(
    IN PVOID Context
    )
 /* ++
  *
  * Description:
  *
  * Arguments:
  *
  * Return:
  *
  * NTSTATUS
  *
  * -- */
{
    PDBCLASS_WORKITEM workItem = Context;
    PDBC_CONTEXT dbcContext = workItem->DbcContext;
    BAY_STATUS bayStatus;
    USHORT bay;
    NTSTATUS ntStatus, irpStatus;
    BOOLEAN found = FALSE;
    BOOLEAN postChangeRequest = TRUE;

    LOGENTRY(LOG_MISC, 'cWK+', 0, Context, 0);

    irpStatus = workItem->IrpStatus; 

    DbcExFreePool(workItem);

    if (!NT_SUCCESS(irpStatus)) {
        // we got an error
        // get the controller status
        NTSTATUS status;
        
        status = DBCLASS_SyncGetControllerStatus(dbcContext);
        
        LOGENTRY(LOG_MISC, 'cERR', dbcContext, status, irpStatus);

        if (status == STATUS_DEVICE_NOT_CONNECTED) {
            // controller was removed, don't
            // re-post the request
            postChangeRequest = FALSE;
        } 
        
        // problem may be temporary
        goto DBCLASS_ChangeIndicationWorker_Done;        
    }

    // find out which bay changed
    // find out what changed
    // clear the change, note that we only 
    // clear the change that we process

    for (bay = 0; bay <= MAX_BAY_NUMBER; bay++) {
        if (IsBitSet(&dbcContext->ChangeDrb.BayChange, bay)) {
            found = TRUE;
            break;
        }
    }
    
    // no change?
    if (!found) {
        DBCLASS_KdPrint((0, "'no bay indicated! -- bug in port driver\n", bay));
        bay = 0;
        TRAP();
    }        
    
    DBCLASS_KdPrint((0, "'Process status change, bay = (%d)\n", bay));

    if (bay > 0) {
        
        ntStatus = DBCLASS_SyncGetBayStatus(dbcContext, bay, &bayStatus);

        if (NT_SUCCESS(ntStatus)) {

            // acknowlege any status change bits now
            //
            // NOTE that the status change bits are redundent 
            // we process the change base on the current state 
            // of the bay compared to the last known state 
            // 

            if (bayStatus.DeviceStatusChange) {

                DBCLASS_KdPrint((0, "'>>device status change bit set\n"));

                DBCLASS_SyncBayFeatureRequest(dbcContext,
                                              DRB_FUNCTION_CLEAR_BAY_FEATURE,
                                              bay,
                                              C_DEVICE_STATUS_CHANGE);
                                              
                DBCLASS_KdPrint((0, "'>>>cleared\n"));                                              
                
            } 

            if (bayStatus.RemovalRequestChange) {

                DBCLASS_KdPrint((0, "'>>remove request change bit set\n"));

                DBCLASS_SyncBayFeatureRequest(dbcContext,
                                              DRB_FUNCTION_CLEAR_BAY_FEATURE,
                                              bay,
                                              C_REMOVE_REQUEST);
                DBCLASS_KdPrint((0, "'>>>cleared\n"));                                                 
            }                

            ntStatus = DBCLASS_ProcessCurrentBayState(dbcContext,
                                                      bayStatus,
                                                      bay,
                                                      &postChangeRequest);
                                                      
        }                        
    } else {
        DBCLASS_KdPrint((0, "'Global status change on DB subsystem\n"));
        TEST_TRAP();
    }

DBCLASS_ChangeIndicationWorker_Done:

    LOGENTRY(LOG_MISC, 'chDN', dbcContext, ntStatus, bay);

    // finished with this request, dec the count
    DBCLASS_DecrementIoCount(dbcContext);           
    
    if (postChangeRequest) {

        LOGENTRY(LOG_MISC, 'chPS', dbcContext, ntStatus, bay);
        DBCLASS_PostChangeRequest(dbcContext);
    }                

    return;
}


VOID
DBCLASS_PostChangeRequest(
    IN PDBC_CONTEXT DbcContext
    )
 /* ++
  * 
  * Description:
  *
  * fetch the bay descriptor from the port driver,
  * 
  * Arguments:
  * 
  * Return:
  *     This routine never fails
  * 
  * -- */
{
    PDRB drb;
    PIRP irp;
    KIRQL irql;
    CHAR stackSize;
    PIO_STACK_LOCATION nextStack;        

    PAGED_CODE();
    
    // Drb and irp are pre-allocated
    
    drb = (PDRB) &DbcContext->ChangeDrb;
    irp = DbcContext->ChangeIrp;
    
    stackSize = DbcContext->TopOfStack->StackSize;

    drb->DrbHeader.Length = sizeof(struct _DRB_CHANGE_REQUEST);
    drb->DrbHeader.Function = DRB_FUNCTION_CHANGE_REQUEST ;
    drb->DrbHeader.Flags = 0;
    drb->DrbChangeRequest.BayChange = 0;

    
    IoInitializeIrp(irp,
                    (USHORT) (sizeof(IRP) + stackSize * sizeof(IO_STACK_LOCATION)),
                    (CCHAR) stackSize);

    nextStack = IoGetNextIrpStackLocation(irp);
    nextStack->Parameters.Others.Argument1 = drb;
    nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
    nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_DBC_SUBMIT_DRB;

    IoSetCompletionRoutine(irp,    // Irp
                           DBCLASS_ChangeIndication,
                           DbcContext, // context
                           TRUE,    // invoke on success
                           TRUE,    // invoke on error
                           TRUE);   // invoke on cancel

    //
    // Call the port driver 
    //

    KeAcquireSpinLock(&DbcContext->FlagsSpin, &irql);            
    if (DbcContext->Flags & DBCLASS_FLAG_STOPPING) {
        LOGENTRY(LOG_MISC, 'stpX', DbcContext, 0, 0);
        KeReleaseSpinLock(&DbcContext->FlagsSpin, irql);   
    } else {

        DbcContext->Flags |= DBCLASS_FLAG_REQ_PENDING;
        KeReleaseSpinLock(&DbcContext->FlagsSpin, irql);
        DBCLASS_IncrementIoCount(DbcContext);

        DBCLASS_BEGIN_SERIALIZED_DRB(DbcContext);

        DBCLASS_KdPrint((1, "'Post Change Request\n"));                             

        LOGENTRY(LOG_MISC, 'post', DbcContext, 0, irp);
        // let the completion routine handle any errors
        IoCallDriver(DbcContext->TopOfStack, irp);    

        DBCLASS_END_SERIALIZED_DRB(DbcContext);
    }
    
    return;
}

#if 0
USHORT
DBCLASS_GetBayNumber(
    IN PDEVICE_OBJECT DeviceFilterObject
    )
 /* ++
  * 
  * Description:
  *
  * given a device filter object, find the bay with this device
  * 
  * Arguments:
  * 
  * Return:
  *     This routine never fails
  * 
  * -- */
{
    PDBC_CONTEXT dbcContext;
    USHORT bay;        
    PDEVICE_EXTENSION deviceExtension;

    DBCLASS_ASSERT(DeviceFilterObject);

    deviceExtension = 
        DeviceFilterObject->DeviceExtension;

    if(deviceExtension)
    {
        dbcContext = deviceExtension->DbcContext;  

        LOGENTRY(LOG_MISC, 'GBNn', dbcContext, DeviceFilterObject, 0);


        if(dbcContext)
        {
            for (bay=1; bay <=NUMBER_OF_BAYS(dbcContext); bay++) 
            {

                if (dbcContext->BayInformation[bay].DeviceFilterObject == DeviceFilterObject) {
                    LOGENTRY(LOG_MISC, 'GBNr', dbcContext, DeviceFilterObject, bay);
                    return bay;
                }
            }
        }
    }

    return 0;
}    
#endif

#if 0
NTSTATUS
DBCLASS_EnableDevice(
    IN PDEVICE_OBJECT DeviceFilterObject
    )
 /* ++
  * 
  * Description:
  *
  * given a device filter object, eject the device in the bay
  * 
  * Arguments:
  * 
  * Return:
  *     This routine never fails
  * 
  * -- */
{
    PDBC_CONTEXT dbcContext;
    PDEVICE_EXTENSION deviceExtension;
    USHORT bay;        
    BAY_STATUS bayStatus;
    NTSTATUS ntStatus = STATUS_SUCCESS;

    DBCLASS_ASSERT(DeviceFilterObject);
    
    deviceExtension = 
        DeviceFilterObject->DeviceExtension;
    dbcContext = deviceExtension->DbcContext;          
    bay = DBCLASS_GetBayNumber(DeviceFilterObject);
    
    DBCLASS_KdPrint((0, "'>>Bay to Enabled state\n"));

    ntStatus = DBCLASS_SyncBayFeatureRequest(dbcContext,
                                             DRB_FUNCTION_SET_BAY_FEATURE,
                                             bay,
                                             REQUEST_DEVICE_ENABLED_STATE);    


    DBCLASS_SyncGetBayStatus(dbcContext, bay, &bayStatus);                
    
    dbcContext->BayInformation[bay].LastBayStatus = bayStatus;       

    DBCLASS_DecrementIoCount(dbcContext);
    
    DBCLASS_PostChangeRequest(dbcContext);        
             
    return STATUS_SUCCESS;
}
#endif


NTSTATUS
DBCLASS_EjectPdo(
    IN PDEVICE_OBJECT DeviceFilterObject
    )
 /* ++
  * 
  * Description:
  *
  * given a device filter object, eject the device in the bay by Pdo
  * 
  * Arguments:
  * 
  * Return:
  *     This routine never fails
  * 
  * -- */
{
    PDBC_CONTEXT dbcContext;
    PDEVICE_EXTENSION deviceExtension = 
        DeviceFilterObject->DeviceExtension;
    NTSTATUS ntStatus = STATUS_SUCCESS;

    dbcContext = deviceExtension->DbcContext;          

    ntStatus = DBCLASS_EjectBay(dbcContext, 
                                deviceExtension->Bay);

    DBCLASS_DecrementIoCount(dbcContext);
    
    DBCLASS_PostChangeRequest(dbcContext);             
    
    return ntStatus;
}


NTSTATUS
DBCLASS_EjectBay(
    IN PDBC_CONTEXT DbcContext,
    IN USHORT Bay
    )
 /* ++
  * 
  * Description:
  *
  * given a bay, eject the device in it
  * 
  * Arguments:
  * 
  * Return:
  *     This routine never fails
  * 
  * -- */
{
    BAY_STATUS bayStatus;
    NTSTATUS ntStatus = STATUS_SUCCESS;

    DBCLASS_KdPrint((0, "'>EJECT Bay[%d]\n", Bay));
    
    // disengage the interlock
    DBCLASS_KdPrint((0, "'>>disable VID\n"));

    ntStatus = DBCLASS_SyncBayFeatureRequest(DbcContext,
                                             DRB_FUNCTION_CLEAR_BAY_FEATURE,
                                             Bay,
                                             ENABLE_VID_POWER);

    DBCLASS_KdPrint((0, "'>>disengage interlock\n"));
    
    ntStatus = DBCLASS_SyncBayFeatureRequest(DbcContext,
                                             DRB_FUNCTION_CLEAR_BAY_FEATURE,
                                             Bay,
                                             LOCK_CTL);
    
    DBCLASS_KdPrint((0, "'>>Bay to Removal allowed state\n"));

    ntStatus = DBCLASS_SyncBayFeatureRequest(DbcContext,
                                             DRB_FUNCTION_SET_BAY_FEATURE,
                                             Bay,
                                             REQUEST_REMOVAL_ALLOWED_STATE);    


    
    DBCLASS_SyncGetBayStatus(DbcContext, Bay, &bayStatus);                
    
    DbcContext->BayInformation[Bay].LastBayStatus = bayStatus;                            

    return ntStatus;
}


NTSTATUS
DBCLASS_AddDevicePDOToList(
    IN PDEVICE_OBJECT FilterDeviceObject,
    IN PDEVICE_OBJECT PdoDeviceObject
    )

/*++

Routine Description:

    Adds a bus emumerated PDO to our list of PDOs

    Currently we only track 1394 PDOs

Arguments:

    FilterDeviceObject - filter MDO for the 1394 bus this device is on
    PdoDeviceObject - 1394 Enumerated PDO

Return Value:

    STATUS_INSUFFICIENT_RESOURCES if a buffer could not be allocated
    STATUS_SUCCESS otherwise

--*/

{
    PDBCLASS_PDO_LIST newEntry;

    newEntry = ExAllocatePool(NonPagedPool, sizeof(DBCLASS_PDO_LIST));

    if (newEntry == NULL) {
        TRAP();
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    // Fill in fields in new entry

     newEntry->FilterDeviceObject = FilterDeviceObject;
     newEntry->PdoDeviceObject = PdoDeviceObject;

    // Add new entry to end of list
    InsertTailList(&DBCLASS_DevicePdoList, &newEntry->ListEntry);

    return STATUS_SUCCESS;
}


VOID
DBCLASS_RemoveDevicePDOFromList(
    IN PDEVICE_OBJECT PdoDeviceObject
    )

/*++

Routine Description:

    Removes a 1394 emumerated PDO to our list of 1394 PDOs

Arguments:

    PdoDeviceObject - 1394 Enumerated PDO to remove from list

Return Value:

    VOID

--*/

{
    PDBCLASS_PDO_LIST entry;
    PLIST_ENTRY listEntry;

    listEntry = &DBCLASS_DevicePdoList;
    if (!IsListEmpty(listEntry)) {
       listEntry = DBCLASS_DevicePdoList.Flink;
    }
     
    while (listEntry != &DBCLASS_DevicePdoList) {
    
        entry = CONTAINING_RECORD(listEntry, 
                                  DBCLASS_PDO_LIST, 
                                  ListEntry);
                                  
        DBCLASS_ASSERT(entry);
        if (entry->PdoDeviceObject == PdoDeviceObject) {
            break;
        }

        listEntry = entry->ListEntry.Flink;

    }            

    // we should always find it
    DBCLASS_ASSERT(listEntry != &DBCLASS_DevicePdoList);

    RemoveEntryList(listEntry);
    ExFreePool(entry);
    
}


PDEVICE_OBJECT
DBCLASS_FindDevicePdo(
    PDEVICE_OBJECT PdoDeviceObject
    )
/*++

Routine Description:

    find a device PDO -- return tru if we know about it 
    
Arguments:

     

Return Value:

    NTSTATUS
    
--*/
{
    PDBCLASS_PDO_LIST entry;
    PLIST_ENTRY listEntry;
    // we keep a global list of PDOs we know about

    listEntry = &DBCLASS_DevicePdoList;
    
    if (!IsListEmpty(listEntry)) {
       listEntry = DBCLASS_DevicePdoList.Flink;
    }
        
    while (listEntry != &DBCLASS_DevicePdoList) {
    
        entry = CONTAINING_RECORD(listEntry, 
                                  DBCLASS_PDO_LIST, 
                                  ListEntry);
                                  
        DBCLASS_ASSERT(entry);
        if (entry->PdoDeviceObject == PdoDeviceObject) {
            return entry->FilterDeviceObject;    
        }

        listEntry = entry->ListEntry.Flink;

    }            

    return NULL;
}


PDBC_CONTEXT
DBCLASS_FindControllerACPI(
    PDRIVER_OBJECT FilterDriverObject,
    PDEVICE_OBJECT FilterMdo
    )
/*++

Routine Description:

    find the DBC controller in our list for a given Filter MDO
    if found the filter MDO is linked to the controller 

    This routine searches the list for an ACPI DBC
    currently we support only one.
    
Arguments:

Return Value:

    Controller Context
    
--*/
{
    PDBC_CONTEXT dbcContext = NULL;    
    PDEVICE_EXTENSION deviceExtension;
    
    deviceExtension = FilterMdo->DeviceExtension;            
    dbcContext = DBCLASS_ControllerList;

    //
    // NOTE: We support only one ACPI DBC controller
    //
    
    while (dbcContext) {
        if (dbcContext->ControllerSig == DBC_ACPI_CONTROLLER_SIG) {
            if (deviceExtension->FdoType == DB_FDO_USBHUB_BUS) {
                dbcContext->BusFilterMdoUSB = FilterMdo;
            } else if (deviceExtension->FdoType == DB_FDO_1394_BUS) {
                dbcContext->BusFilterMdo1394 = FilterMdo;
            } else {
                TRAP();
            }
            dbcContext->BusFilterDriverObject = FilterDriverObject;
            
            break;
        }
        dbcContext = dbcContext->Next;
    }
    
    LOGENTRY(LOG_MISC, 'FIN1', dbcContext, 0, 0);
    
#if DBG
    if (dbcContext == NULL) {
        DBCLASS_KdPrint((0, "'ACPI Controller not Found\n"));
    }
#endif

    return dbcContext;
}


PDBC_CONTEXT
DBCLASS_FindControllerUSB(
    PDRIVER_OBJECT FilterDriverObject,
    PDEVICE_OBJECT FilterMdo,
    PDEVICE_OBJECT UsbHubPdo
    )
/*++

Routine Description:

    find the DBC controller in our list for a given Filter MDO
    if found the filter MDO is linked to the controller 

    This routine will query the hub to see if an DBC controller
    is attached.
    
Arguments:

Return Value:

    Controller Context
    
--*/
{
    UCHAR dbcGuid[8];
    NTSTATUS ntStatus;
    PDBC_CONTEXT dbcContext = NULL;   
    PDEVICE_EXTENSION deviceExtension;
    
    deviceExtension = FilterMdo->DeviceExtension;      

    // first get the guid for this hub
    ntStatus = DBCLASS_GetHubDBCGuid(FilterMdo, 
                                     dbcGuid);

    dbcContext = DBCLASS_ControllerList;

    while (dbcContext) {
        if (RtlCompareMemory(&dbcContext->SubsystemDescriptor.guid1394Link[0],
                             &dbcGuid[0], 8) == 8) {
            if (deviceExtension->FdoType == DB_FDO_USBHUB_BUS) {
                dbcContext->BusFilterMdoUSB = FilterMdo;
            } else if (deviceExtension->FdoType == DB_FDO_1394_BUS) {
                dbcContext->BusFilterMdo1394 = FilterMdo;
            } else {
                TRAP();
            }
            dbcContext->BusFilterDriverObject = FilterDriverObject;
            break;
        }
        dbcContext = dbcContext->Next;
    }

    return dbcContext;
}


NTSTATUS
DBCLASS_Find1394DbcLinks(
    PDEVICE_OBJECT DevicePdo1394
    )
/*++

Routine Description:

    find the DBC controller in our list for a given Filter MDO
    if found the filter MDO is linked to the controller 

    Given the 1394 device PDO and the bus guid we try to find the
    appropriate controller
    
Arguments:

Return Value:

    Controller Context
    
--*/
{
    PDBC_CONTEXT dbcContext;

    DBCLASS_KdPrint((1, "'Find DBC Links\n")); 
    dbcContext = DBCLASS_ControllerList;
    
    while (dbcContext) {
    
        if (DBCLASS_IsLinkDeviceObject(dbcContext,
                                       DevicePdo1394)) {
                                                   
            DBCLASS_KdPrint((1, "'1394 PDO is DBC Link\n"));                             
            // set the bus guid for the context
            DBCLASS_1394GetBusGuid(DevicePdo1394,
                                   &dbcContext->Guid1394Bus[0]); 
        }                             

        dbcContext = dbcContext->Next;
    }

    return STATUS_SUCCESS; 
}




PDBC_CONTEXT
DBCLASS_FindController1394DevicePdo(
    PDRIVER_OBJECT FilterDriverObject,
    PDEVICE_OBJECT FilterMdo,
    PDEVICE_OBJECT DevicePdo1394,
    PUCHAR BusGuid
    )
/*++

Routine Description:

    find the DBC controller in our list for a given Filter MDO
    if found the filter MDO is linked to the controller 

    Given the 1394 device PDO and the bus guid we try to find the
    appropriate DB controller

    busguid is the 1394 controller guid for the bus this device is on

Arguments:

Return Value:

    Controller Context
    
--*/
{
    PDBC_CONTEXT dbcContext = NULL;   
    PDBC_CONTEXT foundDbcContext = NULL;     
    // if a DBC reports the magic guid then we match on that
    
    UCHAR magicGuid[8] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07};
    
    DBCLASS_KdPrint((1, "'>Find Controller -> 1394 DEV PDO (%x)\n", DevicePdo1394));
    DBCLASS_KdPrint((1, "'>>1394 CONTROLLER (BUS) GUID\n"));
    DBCLASS_KdPrintGuid(1, 
                        BusGuid);
   
    BRK_ON_TRAP();

    //
    // first loop through controllers
    // checking to see if this is a PDO
    // for a link, if so we mark the context as NULL
    // ie not a DB device
    //
    
    dbcContext = DBCLASS_ControllerList;
    
    while (dbcContext) {
    
        if (DBCLASS_IsLinkDeviceObject(dbcContext,
                                       DevicePdo1394)) {
                                       
            DBCLASS_KdPrint((1, "'>>>1394 PDO is DBC Link\n"));                             
            return NULL;                                       
        }                             

        dbcContext = dbcContext->Next;
    }

    //
    // loop through the controllers, checking each one
    // until we have a match
    
    dbcContext = DBCLASS_ControllerList;

    while (dbcContext && foundDbcContext == NULL) {
        NTSTATUS status;
        
        DBCLASS_KdPrint((2, "'Checking DBC (%x)\n", dbcContext));
        DBCLASS_KdPrint((2, "'BUS GUID (%x)\n", dbcContext));
        DBCLASS_KdPrintGuid(2, 
                            &dbcContext->Guid1394Bus[0]);    
                            
        // only look at controllers on the same bus                            
        if (
            (RtlCompareMemory(&dbcContext->Guid1394Bus[0],
                             BusGuid, 8) == 8) ||
            (RtlCompareMemory(&dbcContext->Guid1394Bus[0],
                             &magicGuid[0], 8) == 8)                 
                             ) {

            // OK we found a match, now see if this PDO is part of 
            // this controller
            status = DBCLASS_Check1394DevicePDO(FilterMdo,
                                                dbcContext,
                                                DevicePdo1394);
                                                
            if (NT_SUCCESS(status)) {
                foundDbcContext = dbcContext;
                dbcContext->BusFilterMdo1394 = FilterMdo;
                dbcContext->BusFilterDriverObject = FilterDriverObject;
            }
                    
        }
        dbcContext = dbcContext->Next;
    }    

#if DBG
    if (foundDbcContext) {
        DBCLASS_KdPrint((1, "'>>>>Found DBC (%x)\n", foundDbcContext));   
    } else {
        DBCLASS_KdPrint((1, "'>>>>DBC not found\n"));   
    }
#endif

    return foundDbcContext;
}


NTSTATUS
DBCLASS_PdoSetLockComplete(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    )

/*++

Routine Description:

Arguments:

    DeviceObject - a pointer to the device object

    Irp - a pointer to the irp

    Context - NULL ptr

Return Value:

    STATUS_SUCCESS

--*/

{
    Irp->IoStatus.Status = STATUS_SUCCESS;

    LOGENTRY(LOG_MISC, 'LOKc', 0, 0, 0);
    
    return STATUS_SUCCESS;
}


#define IS_1394_DEVICE(de) ((de)->FdoType == DB_FDO_1394_DEVICE)

NTSTATUS
DBCLASS_PdoFilterDispatch(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PBOOLEAN Handled
    )
/*++

Routine Description:

    Common filter function for both 1394 and USB PDOs

Arguments:

Return Value:

    NTSTATUS

--*/
{
    PIO_STACK_LOCATION irpStack;
    NTSTATUS ntStatus;
    PDEVICE_EXTENSION deviceExtensionPdoFilter;

    deviceExtensionPdoFilter = DeviceObject->DeviceExtension;
    ntStatus = Irp->IoStatus.Status;
    *Handled = FALSE;
    irpStack = IoGetCurrentIrpStackLocation (Irp);

    LOGENTRY(LOG_MISC, 'pdo>', 0, DeviceObject, Irp);
    
    DBCLASS_KdPrint((1, "'(dbfilter)(device)(%x)IRP_MJ_ (%08X)  IRP_MN_ (%08X)\n",
         DeviceObject, irpStack->MajorFunction, irpStack->MinorFunction));  

    switch (irpStack->MajorFunction) 
    {

    case IRP_MJ_PNP:
        switch (irpStack->MinorFunction) 
        {    
        case IRP_MN_START_DEVICE:            
            {
            PDBC_CONTEXT dbcContext;
            USHORT bay = 0;
            KEVENT event;
            
            DBCLASS_KdPrint((1, "'(dbfilter)(device)(%x)IRP_MN_START_DEVICE\n",
                deviceExtensionPdoFilter->PhysicalDeviceObject));    

            *Handled = TRUE;

            KeInitializeEvent(&event, NotificationEvent, FALSE);

            IoCopyCurrentIrpStackLocationToNext(Irp);  
            IoSetCompletionRoutine(Irp,
                                   DBCLASS_DeferIrpCompletion,
                                   &event,
                                   TRUE,
                                   TRUE,
                                   TRUE);


            //
            // send the request down the stack before we eject
            //

            ntStatus = IoCallDriver(deviceExtensionPdoFilter->TopOfStackDeviceObject, 
                                    Irp);
            
            if (ntStatus == STATUS_PENDING) 
            {
                 // wait for irp to complete
        
                KeWaitForSingleObject(
                    &event,
                    Suspended,
                    KernelMode,
                    FALSE,
                    NULL);
            }

            // started a 1394 device, figure out what bay it is in

            if (IS_1394_DEVICE(deviceExtensionPdoFilter)) 
            {
            
                DBCLASS_KdPrint((1, "'**> Starting 1394 PDO (%x)\n", 
                    deviceExtensionPdoFilter->PhysicalDeviceObject)); 
                    
                bay = deviceExtensionPdoFilter->Bay; 
                dbcContext = deviceExtensionPdoFilter->DbcContext;
                
            } 

            // we have no USB device bay devices
#if 0
            else 
            {

                dbcContext = deviceExtensionPdoFilter->DbcContext;

                DBCLASS_KdPrint((0, "'**> Starting USB PDO (%08X)  Filter DevObj (%08X)  DbcContext (%08X)\n",
                    deviceExtensionPdoFilter->PhysicalDeviceObject, DeviceObject, dbcContext));


                if (dbcContext != NULL) 
                {            
                    bay = DBCLASS_GetBayForUSBPdo(
                                dbcContext,
                                deviceExtensionPdoFilter->PhysicalDeviceObject);        

                    if (bay == 0) 
                    {
                        // ignore this PDO from now on
                        deviceExtensionPdoFilter->FdoType = DB_FDO_BUS_IGNORE;
                        DBCLASS_KdPrint((1, 
                                "'>>>> USB PDO(%x) is not a DB device  <<<<\n",
                                    deviceExtensionPdoFilter->PhysicalDeviceObject));
                    } 
                    else 
                    {
                        DBCLASS_KdPrint((1, 
                        "'>>>> USB PDO(%x) is in BAY[%d] <<<<\n",
                            deviceExtensionPdoFilter->PhysicalDeviceObject,
                            bay));   
                    }
                                                
                } 
                else 
                {
                    // no controller yet -- can't be a device bay device
                    bay = 0;
                    DBCLASS_KdPrint((1, "'No Controller Available\n")); 
                }
                
                //TEST_TRAP();
            }
#endif

            // keep track of the bay we are in in case
            // we get an eject irp
            deviceExtensionPdoFilter->Bay = bay;

            if (bay) 
            {

                DBCLASS_KdPrint((1, 
                    "'**>> PDO is in Bay %d\n", bay));  

                DBCLASS_ASSERT(dbcContext != NULL);
                if(dbcContext)
                {
                dbcContext->BayInformation[bay].DeviceFilterObject = 
                        DeviceObject;    
                if (DeviceObject) {
                     // need to OK the start                    
                    PDRB drb;
                    
                    drb = DbcExAllocatePool(NonPagedPool, 
                            sizeof(struct _DRB_START_DEVICE_IN_BAY));

                    if (drb) { 
                    
                        drb->DrbHeader.Length = sizeof(struct _DRB_START_DEVICE_IN_BAY);
                        drb->DrbHeader.Function = DRB_FUNCTION_START_DEVICE_IN_BAY;
                        drb->DrbHeader.Flags = 0;

                        drb->DrbStartDeviceInBay.BayNumber = bay;

                        drb->DrbStartDeviceInBay.PdoDeviceObject1394 =
                            drb->DrbStartDeviceInBay.PdoDeviceObjectUsb = NULL;
                            
                        if (IS_1394_DEVICE(deviceExtensionPdoFilter)) {        
                            drb->DrbStartDeviceInBay.PdoDeviceObject1394 =                         
                                DeviceObject;
                        } else {
                            drb->DrbStartDeviceInBay.PdoDeviceObjectUsb = 
                                DeviceObject;
                        }
                            
                        // make the request
                        ntStatus = DBCLASS_SyncSubmitDrb(dbcContext,
                                                         dbcContext->TopOfStack, 
                                                         drb);
                    } else {
                        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
                    }
                }
                }

                // report that this PDO must be removed it the 
                // db controller is removed.

                // note: we invalidate on the DBC PDO 
                // this should trigger a QBR for the device bay controller

                                       
            } else {
                DBCLASS_KdPrint((1, 
                    "'**>> PDO is not for a device bay device\n"));    

                // detach and delete our MDO here if possible
                // otherwise mark as ignore

                BRK_ON_TRAP();
            }

            Irp->IoStatus.Status = ntStatus;

            //
            // detach and delete ourselves now
            //
            
            IoCompleteRequest(Irp,
                              IO_NO_INCREMENT);

            }
            break;

        case IRP_MN_STOP_DEVICE:            
        case IRP_MN_REMOVE_DEVICE:   
            if (irpStack->MinorFunction == IRP_MN_STOP_DEVICE) {
                DBCLASS_KdPrint((1, 
                "'(dbfilter)(device)(%x)IRP_MN_STOP_DEVICE\n",
                    deviceExtensionPdoFilter->PhysicalDeviceObject));  
            } else {                
                DBCLASS_KdPrint((1, 
                    "'(dbfilter)(device)(%x)IRP_MN_REMOVE_DEVICE\n",    
                        deviceExtensionPdoFilter->PhysicalDeviceObject));  
            }                
            // set the device filter object to NULL
            // this will allow the dbc to eject the device if requested
            {
            USHORT bay = 0;
            PDBC_CONTEXT dbcContext;

            dbcContext = deviceExtensionPdoFilter->DbcContext;
//            bay = DBCLASS_GetBayNumber(DeviceObject);

            if (bay) { 
                PDRB drb;
                
                DBCLASS_KdPrint((1, 
                    "'(dbfilter)(device)REMOVE/STOP, Bay[%d]\n", bay)); 

                //
                // notify filter of a stop
                // note that the filter may not veto the stop
                //
                drb = DbcExAllocatePool(NonPagedPool, 
                        sizeof(struct _DRB_START_DEVICE_IN_BAY));

                if (drb) { 
                
                    drb->DrbHeader.Length = sizeof(struct _DRB_STOP_DEVICE_IN_BAY);
                    drb->DrbHeader.Function = DRB_FUNCTION_STOP_DEVICE_IN_BAY;
                    drb->DrbHeader.Flags = 0;

                    drb->DrbStartDeviceInBay.BayNumber = bay;
                    
                    // make the request
                    ntStatus = DBCLASS_SyncSubmitDrb(dbcContext,
                                                     dbcContext->TopOfStack, 
                                                     drb);
                                                     
                    DBCLASS_KdPrint((1, 
                        "'OK to stop <%x>\n", ntStatus)); 

        			DbcExFreePool(drb);
                                                     
                } else {
                    ntStatus = STATUS_INSUFFICIENT_RESOURCES;
                }
                    
                dbcContext->BayInformation[bay].DeviceFilterObject = NULL;
            }                

			deviceExtensionPdoFilter->DbcContext = NULL;
            
            }
            break;       
#if 0                     
        case IRP_MN_QUERY_REMOVE_DEVICE:            
        case IRP_MN_QUERY_STOP_DEVICE:  
            {
            USHORT bay;
            
            bay = DBCLASS_GetBayNumber(DeviceObject);
            DBCLASS_KdPrint((1, 
                    "'(dbfilter)(device)(%x)Q_REMOVE/STOP, Bay[%d]\n", 
                       deviceExtensionPdoFilter->PhysicalDeviceObject, bay));        
            }                    
            break;
#endif            
        case IRP_MN_EJECT:

            {
            KEVENT event;
            USHORT bay;
            PDBC_CONTEXT dbcContext;
            
            dbcContext = deviceExtensionPdoFilter->DbcContext;

            DBCLASS_KdPrint((1, "'(dbfilter)(device)(%x)IRP_MN_EJECT, Bay[%d]\n",
                deviceExtensionPdoFilter->PhysicalDeviceObject,  
                deviceExtensionPdoFilter->Bay));    
            
            *Handled = TRUE;

            KeInitializeEvent(&event, NotificationEvent, FALSE);

            IoCopyCurrentIrpStackLocationToNext(Irp);  
            IoSetCompletionRoutine(Irp,
                                   DBCLASS_DeferIrpCompletion,
                                   &event,
                                   TRUE,
                                   TRUE,
                                   TRUE);

            //
            // send the request down the stack before we eject
            //

            ntStatus = IoCallDriver(deviceExtensionPdoFilter->TopOfStackDeviceObject, 
                                    Irp);
            
            if (ntStatus == STATUS_PENDING) {
                 // wait for irp to complete
        
                // TEST_TRAP(); // first time we hit this
                   
                KeWaitForSingleObject(
                    &event,
                    Suspended,
                    KernelMode,
                    FALSE,
                    NULL);
            }

            IoDetachDevice(deviceExtensionPdoFilter->TopOfStackDeviceObject);

            //
            // ounce we eject the device will disappear from the 
            // native bus.
            //

            ntStatus = Irp->IoStatus.Status;

//            TEST_TRAP();
//            if (NT_SUCCESS(ntStatus)) {
                bay = deviceExtensionPdoFilter->Bay;
            
                if (bay) {
                    ntStatus = DBCLASS_EjectPdo(DeviceObject);
                
                    DBCLASS_ASSERT(dbcContext->BayInformation[bay].DeviceFilterObject 
                        == NULL);
                }                     
#if DBG            
                  else {
                     DBCLASS_KdPrint((1, "'No Bay to EJECT (%x)\n", ntStatus));  
                     // TEST_TRAP();
                     ntStatus = STATUS_SUCCESS;
                }              
#endif
//            }
            
            Irp->IoStatus.Status = STATUS_SUCCESS;
            
            //
            // delete ourselves now
            //
            
            IoCompleteRequest(Irp,
                              IO_NO_INCREMENT);

			deviceExtensionPdoFilter->DbcContext = NULL;

            // remove PDO from our internal list
            DBCLASS_RemoveDevicePDOFromList(
                deviceExtensionPdoFilter->PhysicalDeviceObject);
            
            // free our device object
            IoDeleteDevice (DeviceObject);
            }
            
            break;       
            
        case IRP_MN_SET_LOCK: 
        
            DBCLASS_KdPrint((1, "'(dbfilter)(device)IRP_MN_SET_LOCK, Bay[%x]\n",
                deviceExtensionPdoFilter->Bay));    
            
            if (irpStack->Parameters.SetLock.Lock) { 
                DBCLASS_KdPrint((1, "'Request to LOCK device\n"));    
                DBCLASS_KdPrint((1, "'NOT ENABLING BAY ON LOCK!\n"));
                LOGENTRY(LOG_MISC, 'LOCK', 0, 0, 0);
                //ntStatus = DBCLASS_EnableDevice(DeviceObject);
            } else {
            
                //
                // cancel the eject timeout
                // if we get here no one vetoed the remove
                //

                DBCLASS_CancelEjectTimeout(DeviceObject);

                DBCLASS_KdPrint((1, "' Request to UNLOCK device\n"));    
                LOGENTRY(LOG_MISC, 'ULOC', 0, 0, 0);
            }
            
            *Handled = TRUE;

            IoCopyCurrentIrpStackLocationToNext(Irp);

            // Set up a completion routine to handle marking the IRP.
            IoSetCompletionRoutine(Irp,
                                   DBCLASS_PdoSetLockComplete,
                                   DeviceObject,
                                   TRUE,
                                   TRUE,
                                   TRUE);

            // Now Pass down the IRP

            ntStatus = IoCallDriver(deviceExtensionPdoFilter->TopOfStackDeviceObject, Irp);
            LOGENTRY(LOG_MISC, 'sLOC', ntStatus, 0, 0);
                              
            break;    

        case IRP_MN_CANCEL_REMOVE_DEVICE:
            DBCLASS_KdPrint((1, "'(dbfilter)(device)IRP_MN_CANCEL_REMOVE_DEVICE\n"));
            // TEST_TRAP();
            break;
            
        case IRP_MN_QUERY_CAPABILITIES:            
            DBCLASS_KdPrint((1, 
                "'(dbfilter)(device)(%x)IRP_MN_QUERY_CAPABILITIES\n",    
                    deviceExtensionPdoFilter->PhysicalDeviceObject));
//
// Do this for all 1394 PDOs regardless of if they are device bay
// PDOs
//            if (deviceExtensionPdoFilter->DbcContext &&
//                deviceExtensionPdoFilter->Bay) {
                *Handled = TRUE;

                DBCLASS_KdPrint((1,"'>>QCAPS 1394/USB Device Bay PDO\n"));
                IoCopyCurrentIrpStackLocationToNext(Irp);

                // Set up a completion routine to handle marking the IRP.
                IoSetCompletionRoutine(Irp,
                                       DBCLASS_DevicePdoQCapsComplete,
                                       DeviceObject,
                                       TRUE,
                                       TRUE,
                                       TRUE);
                                       
                // Now Pass down the IRP

                ntStatus = IoCallDriver(deviceExtensionPdoFilter->TopOfStackDeviceObject, Irp);
//            }                
            break;                
        } /* irpStack->MinorFunction */
        break;
    } /* irpStack->MajorFunction */      

    LOGENTRY(LOG_MISC, 'pdo<', 0, DeviceObject, ntStatus);
    
    return ntStatus;
}


NTSTATUS
DBCLASS_SyncGetFdoType(
    IN PDEVICE_OBJECT FilterDeviceObject,
    IN PULONG FdoType
    )

/*++
Routine Description:

    This gets the bus type for this PDO (1394 or USB) 

    The goal of this function is to identify the stack the 
    filter is sitting on. 

    It is either:
    1. The USB root bus
    2. The 1394 HC 
    3. A USB hub (bus)
    4. A USB Device
    5. A 1394 Device

    The filter sits above the FDO (upperfilter) for the 
    respective PDO -- it is loaded as a class filter for
    USB and 1394.

    We do not need to identify USB devices and 1394 devices (4,5)
    since we attach sapartely to these when we hook QBRelations.


Arguments:

    DeviceObject - Physical DeviceObject for the bus.

    FdoType - set to DB_FDO_USBHUB_FILTER or 
                     DB_FDO_1394_FILTER  or 
                     0 if bus type is neither

Return Value:

    None.

--*/

{
    PIO_STACK_LOCATION nextStack;
    PIRP irp;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    KEVENT event;
    PPNP_BUS_INFORMATION busInfo;
    PDEVICE_EXTENSION deviceExtension;
    PDEVICE_OBJECT pdoDeviceObject;
    PDEVICE_OBJECT topDeviceObject;
    PULONG         Tag;

    PAGED_CODE();
    
    deviceExtension = FilterDeviceObject->DeviceExtension;

    pdoDeviceObject = deviceExtension->PhysicalDeviceObject;
    topDeviceObject = deviceExtension->TopOfStackDeviceObject;
    
    DBCLASS_KdPrint((1, "'*>Filter -> QUERY BUS TYPE filter do %x\n", 
        FilterDeviceObject));
    DBCLASS_KdPrint((1, "'*>Filter -> QUERY BUS TYPE pdo %x\n", 
        pdoDeviceObject));        
    DBCLASS_KdPrint((1, "'*>Filter -> QUERY BUS TYPE top %x\n", 
        topDeviceObject));           
        
    irp = IoAllocateIrp(FilterDeviceObject->StackSize, FALSE);

    if (!irp) {
        TRAP(); //"failed to allocate Irp
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    nextStack = IoGetNextIrpStackLocation(irp);
    ASSERT(nextStack != NULL);
    nextStack->MajorFunction= IRP_MJ_PNP;
    nextStack->MinorFunction= IRP_MN_QUERY_BUS_INFORMATION;
    irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
    irp->IoStatus.Information = 0;
    
    KeInitializeEvent(&event, NotificationEvent, FALSE);

    IoSetCompletionRoutine(irp,
                           DBCLASS_DeferIrpCompletion,
                           &event,
                           TRUE,
                           TRUE,
                           TRUE);

    ntStatus = IoCallDriver(FilterDeviceObject,
                            irp);

    if (ntStatus == STATUS_PENDING) {
       // wait for irp to complete
       
       KeWaitForSingleObject(
            &event,
            Suspended,
            KernelMode,
            FALSE,
            NULL);
    }

    busInfo = (PPNP_BUS_INFORMATION) irp->IoStatus.Information;    

    *FdoType = DB_FDO_BUS_UNKNOWN;  

    Tag = (PULONG) topDeviceObject->DeviceExtension;

    // see if this is a 1394 bus
    if(*Tag == PORT_EXTENSION_TAG)
    {
        DBCLASS_KdPrint((1, "'1394 Device\n")); 
        *FdoType = DB_FDO_1394_BUS;
        return STATUS_SUCCESS;
    }

    DBCLASS_KdPrint((1, "'Tag (%08X) DevExt (%08X) DevObj(%08X)\n", *Tag, Tag, topDeviceObject)); 
    DBCLASS_KdPrint((1, "'Status (%08X) Information (%08X)\n", irp->IoStatus.Status, irp->IoStatus.Information)); 
            

    if (busInfo) {
#if DBG    
        DBCLASS_KdPrint((1, "'USB GUID\n"));
        DBCLASS_KdPrintGuid(1, (PUCHAR) &GUID_BUS_TYPE_USB);

        DBCLASS_KdPrint((1, "'RETURNED GUID\n"));            
        DBCLASS_KdPrintGuid(1, (PUCHAR) &busInfo->BusTypeGuid);
#endif
        if (RtlCompareMemory(&busInfo->BusTypeGuid, 
                             &GUID_BUS_TYPE_USB, 
                             sizeof(GUID)) == sizeof(GUID)) {
                             
            *FdoType = DB_FDO_USBHUB_BUS;        

            DBCLASS_KdPrint((1, "'*>>Filter is for USB HUB\n"));    
        }
        
        ExFreePool(busInfo);
    } else {
        DBCLASS_KdPrint((2, "'no busInfo returned\n"));

        // this is either the 1394 or USB root bus
        // send down an private IOCTL to see if it is USB
        
        nextStack = IoGetNextIrpStackLocation(irp);
        ASSERT(nextStack != NULL);
        
        nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
        nextStack->Parameters.DeviceIoControl.IoControlCode = 
            IOCTL_INTERNAL_USB_GET_BUSGUID_INFO;
        
        irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
        irp->IoStatus.Information = 0;
    
        KeInitializeEvent(&event, NotificationEvent, FALSE);
    
        IoSetCompletionRoutine(irp,
                               DBCLASS_DeferIrpCompletion,
                               &event,
                               TRUE,
                               TRUE,
                               TRUE);

        ntStatus = IoCallDriver(FilterDeviceObject,
                                irp);

        if (ntStatus == STATUS_PENDING) {
           // wait for irp to complete
       
           KeWaitForSingleObject(
                &event,
                Suspended,
                KernelMode,
                FALSE,
                NULL);
        }

        busInfo = (PPNP_BUS_INFORMATION) irp->IoStatus.Information;    

    	DBCLASS_KdPrint((1, "'Status (%08X) Information (%08X)\n", irp->IoStatus.Status, irp->IoStatus.Information)); 

        if (busInfo) {
#if DBG        
            DBCLASS_KdPrint((1, "'USB GUID\n"));
        	DBCLASS_KdPrintGuid(1, (PUCHAR) &GUID_BUS_TYPE_USB);

            DBCLASS_KdPrint((1, "'RETURNED GUID\n"));            
            DBCLASS_KdPrintGuid(1, (PUCHAR) &busInfo->BusTypeGuid);
#endif
            if (RtlCompareMemory(&busInfo->BusTypeGuid, 
                                 &GUID_BUS_TYPE_USB, 
                                 sizeof(GUID)) == sizeof(GUID)) {
                                 
                *FdoType = DB_FDO_BUS_IGNORE;        
                DBCLASS_KdPrint((1, "'*>>Filter is for USB HC\n"));    
            }
            
            ExFreePool(busInfo);
        }            
        else{
            // see if this is a 1394 bus
            if(*Tag == PORT_EXTENSION_TAG){
                DBCLASS_KdPrint((1, "'1394 Device\n")); 
                *FdoType = DB_FDO_1394_BUS;
            }
        }
    }
    
    ntStatus = STATUS_SUCCESS;

    IoFreeIrp(irp);

#if DBG 
    switch(*FdoType) {
    case DB_FDO_BUS_IGNORE:
        DBCLASS_KdPrint((1, "'*>>>FdoType: DB_FDO_BUS_IGNORE\n")); 
        break;
    case DB_FDO_BUS_UNKNOWN:   
        DBCLASS_KdPrint((1, "'*>>>FdoType: DB_FDO_BUS_UNKNOWN\n")); 
        break;
    case DB_FDO_USB_DEVICE: 
        DBCLASS_KdPrint((1, "'*>>>FdoType: DB_FDO_USB_DEVICE\n")); 
        break;
    case DB_FDO_USBHUB_BUS: 
        DBCLASS_KdPrint((1, "'*>>>FdoType: DB_FDO_USBHUB_BUS\n")); 
        break;
    case DB_FDO_1394_BUS: 
        DBCLASS_KdPrint((1, "'*>>>FdoType: DB_FDO_1394_BUS\n")); 
        break;
    case DB_FDO_1394_DEVICE:
        DBCLASS_KdPrint((1, "'*>>>FdoType: DB_FDO_1394_DEVICE\n")); 
        break;
    }
#endif

    BRK_ON_TRAP();
    return ntStatus;
}


NTSTATUS
DBCLASS_BusFilterDispatch(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PBOOLEAN Handled
    )
/*++

Routine Description:

Arguments:

    DeviceObject - Device bay Filter FDO 

Return Value:

    NTSTATUS

--*/
{
    PIO_STACK_LOCATION irpStack;
    NTSTATUS ntStatus;
    PDEVICE_EXTENSION deviceExtension;

    deviceExtension = DeviceObject->DeviceExtension;
    ntStatus = Irp->IoStatus.Status;
    *Handled = FALSE;
    irpStack = IoGetCurrentIrpStackLocation (Irp);

    LOGENTRY(LOG_MISC, 'dbf>', 0, DeviceObject, Irp);
    
    DBCLASS_KdPrint((1, "'(dbfilter)(bus)(%08X)IRP_MJ_ (%08X)  IRP_MN_ (%08X)\n",
        DeviceObject, irpStack->MajorFunction, irpStack->MinorFunction));  

    switch (irpStack->MajorFunction) {

    case IRP_MJ_PNP:
        switch (irpStack->MinorFunction) {    
        case IRP_MN_START_DEVICE:            
            // see if we can id the bus we are on

            ntStatus = 
            DBCLASS_SyncGetFdoType(DeviceObject,
                                   &deviceExtension->FdoType);

            if (deviceExtension->FdoType == DB_FDO_USBHUB_BUS) {
                // filter is sitting on a USB HUB 
            
                // null context indicates that this hub is not
                // part of a DBC
                deviceExtension->DbcContext = NULL;

                if (DBCLASS_IsHubPartOfACPI_DBC(DeviceObject)) {
                    
                    deviceExtension->DbcContext = 
                        DBCLASS_FindControllerACPI(deviceExtension->DriverObject,
                                                   DeviceObject);                                        
                                    
                    if (deviceExtension->DbcContext) {
                        USHORT bay;
                        // set the dbContext to point at this
                        // hub

                        // may need to handle multiple hubs
                        // currently we do not

                        DBCLASS_KdPrint(
                            (1, "'** Found ACPI DBC controller, linked to USBHUB\n"));
                            
                        for (bay=1; bay <=NUMBER_OF_BAYS(deviceExtension->DbcContext); bay++) {
                            deviceExtension->DbcContext->BayInformation[bay].UsbHubPdo = 
                                deviceExtension->PhysicalDeviceObject;                                                           
                        }
                    } 
#if DBG
                      else {
                         DBCLASS_KdPrint(
                            (0, "'** Could not find an ACPI DBC controller\n"));
                    }
#endif
                    
                } else {
                    // hub is not part of DBC (for now)
                    // if is part of USB dbc we will need to 
                    // wait for Q_BUS_RELATIONS
                    deviceExtension->DbcContext = NULL;
                }
                                                
            }            
            
            DBCLASS_KdPrint((1, "'(dbfilter)(bus)(%08X)IRP_MN_START_DEVICE\n", DeviceObject));    
            break;

        case IRP_MN_STOP_DEVICE:            

            DBCLASS_KdPrint((1, "'(dbfilter)(bus)(%08X)IRP_MN_STOP_DEVICE\n", DeviceObject));    
            break;

        case IRP_MN_REMOVE_DEVICE:            

            DBCLASS_KdPrint((1, "'(dbfilter)(bus)(%08X)IRP_MN_REMOVE_DEVICE\n", DeviceObject));    

            DBCLASS_RemoveBusFilterMDOFromList(DeviceObject);

            IoDetachDevice(deviceExtension->TopOfStackDeviceObject);

            IoDeleteDevice(DeviceObject);
            break;
            
        break;
        } /* irpStack->MinorFunction */
    } /* irpStack->MajorFunction */      

    LOGENTRY(LOG_MISC, 'dbf<', 0, DeviceObject, 0);
    
    return ntStatus;
}


NTSTATUS 
DBCLASS_GetRegistryKeyValueForPdo(
    IN PDEVICE_OBJECT PhysicalDeviceObject,
    IN BOOLEAN SoftwareBranch,
    IN PWCHAR KeyNameString,
    IN ULONG KeyNameStringLength,
    IN PVOID Data,
    IN ULONG DataLength
    )
/*++

Routine Description:
    
Arguments:

Return Value:

--*/
{
    NTSTATUS ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    UNICODE_STRING keyNameUnicodeString;
    ULONG length;
    PKEY_VALUE_FULL_INFORMATION fullInfo;
    HANDLE handle;
    
    PAGED_CODE();

    if (SoftwareBranch) {
        ntStatus=IoOpenDeviceRegistryKey(PhysicalDeviceObject,
                                     PLUGPLAY_REGKEY_DRIVER,
                                     STANDARD_RIGHTS_ALL,
                                     &handle);
    } else {
        ntStatus=IoOpenDeviceRegistryKey(PhysicalDeviceObject,
                                         PLUGPLAY_REGKEY_DEVICE,
                                         STANDARD_RIGHTS_ALL,
                                         &handle);
    }
    
    if (NT_SUCCESS(ntStatus)) {
    
        RtlInitUnicodeString(&keyNameUnicodeString, KeyNameString);
        
        length = sizeof(KEY_VALUE_FULL_INFORMATION) + 
                KeyNameStringLength + DataLength;
                
        fullInfo = ExAllocatePoolWithTag(PagedPool, length, DBC_TAG); 
        
        DBCLASS_KdPrint((2,"' DBCLASS_GetRegistryKeyValueForPdo buffer = (%08X)\n", fullInfo));  
        
        if (fullInfo) {        
            ntStatus = ZwQueryValueKey(handle,
                            &keyNameUnicodeString,
                            KeyValueFullInformation,
                            fullInfo,
                            length,
                            &length);
                            
            if (NT_SUCCESS(ntStatus)){
                DBCLASS_ASSERT(DataLength == fullInfo->DataLength);                       
                RtlCopyMemory(Data, ((PUCHAR) fullInfo) + fullInfo->DataOffset, DataLength);
            }            

            ExFreePool(fullInfo);
        }        
    }
    
    return ntStatus;
}


NTSTATUS 
DBCLASS_SetRegistryKeyValueForPdo(
    IN PDEVICE_OBJECT PhysicalDeviceObject,
    IN BOOLEAN SoftwareBranch,
    IN ULONG Type,
    IN PWCHAR KeyNameString,
    IN ULONG KeyNameStringLength,
    IN PVOID Data,
    IN ULONG DataLength
    )
/*++

Routine Description:
    
Arguments:

Return Value:

--*/
{
    NTSTATUS ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    UNICODE_STRING keyNameUnicodeString;
    HANDLE handle;
    
    PAGED_CODE();

    if (SoftwareBranch) {
        ntStatus=IoOpenDeviceRegistryKey(PhysicalDeviceObject,
                                         PLUGPLAY_REGKEY_DRIVER,
                                         STANDARD_RIGHTS_ALL,
                                         &handle);
    } else {
        ntStatus=IoOpenDeviceRegistryKey(PhysicalDeviceObject,
                                         PLUGPLAY_REGKEY_DEVICE,
                                         STANDARD_RIGHTS_ALL,
                                         &handle);
    }

    if (NT_SUCCESS(ntStatus)) {
    
        RtlInitUnicodeString(&keyNameUnicodeString, KeyNameString);
        
        ntStatus = ZwSetValueKey(handle,
                        &keyNameUnicodeString,
                        0,
                        Type,
                        Data,
                        DataLength);
                            
    }
    
    return ntStatus;
}



NTSTATUS
DBCLASS_DevicePdoQCapsComplete(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    )

/*++

Routine Description:

Arguments:

    DeviceObject - a pointer to the device object

    Irp - a pointer to the irp

    Context - NULL ptr

Return Value:

    STATUS_SUCCESS

--*/

{
    PDEVICE_OBJECT deviceFilterObject = Context;
    PDEVICE_EXTENSION deviceExtension;
    PDBC_CONTEXT dbcContext;
    PDEVICE_CAPABILITIES deviceCapabilities;
    PIO_STACK_LOCATION ioStack;
    BOOLEAN				linkDeviceObject = FALSE;

    deviceExtension = deviceFilterObject->DeviceExtension;
    dbcContext = deviceExtension->DbcContext;
    ioStack = IoGetCurrentIrpStackLocation(Irp);


#if DBG
    if (deviceExtension->Bay) 
    {
        DBCLASS_KdPrint((1, "'>QCAPS cmplt 1394/USB PDO %x -- Bay[%d]\n", 
                deviceExtension->PhysicalDeviceObject,
                deviceExtension->Bay));
    } 
    else 
    {
        DBCLASS_KdPrint((1, "'>QCAPS cmplt 1394/USB PDO %x -- No Bay\n", 
                deviceExtension->PhysicalDeviceObject));
    }
#endif            

    deviceCapabilities = ioStack->
            Parameters.DeviceCapabilities.Capabilities;


    dbcContext = DBCLASS_ControllerList;
    
    while(dbcContext) 
    {
    
        if (DBCLASS_IsLinkDeviceObject(dbcContext,
                                       deviceExtension->PhysicalDeviceObject)) 
        {
                                       
            DBCLASS_KdPrint((1, "'>>>1394 PDO is DBC Link, set suprise remove\n"));                             
            linkDeviceObject = TRUE; 
            break;
        }                             

        dbcContext = dbcContext->Next;
    }


    if(linkDeviceObject)
    {
    	// set surprise remove O.K. for device bay phy/link
        deviceCapabilities->SurpriseRemovalOK = TRUE;                   
	}
	else
	{
    	// indicate eject is supported for regular devices
    	deviceCapabilities->EjectSupported = 1;
    	deviceCapabilities->LockSupported = 1;
    }

#if DBG
    {
    ULONG i;
    
    DBCLASS_KdPrint((1, "'DEVICE PDO: Device Caps\n"));                          
    DBCLASS_KdPrint(
        (1, "'>>\n LockSupported = %d\n EjectSupported = %d \n Removable = %d \n DockDevice = %x\n",
        deviceCapabilities->LockSupported,  
        deviceCapabilities->EjectSupported,
        deviceCapabilities->Removable,
        deviceCapabilities->DockDevice));            
        
    DBCLASS_KdPrint(
        (1, "'>>\n UniqueId = %d\n SilentInstall = %d \n RawDeviceOK = %d \n SurpriseRemovalOK = %x\n",
        deviceCapabilities->UniqueID,  
        deviceCapabilities->SilentInstall,
        deviceCapabilities->RawDeviceOK,
        deviceCapabilities->SurpriseRemovalOK));                   
        
    DBCLASS_KdPrint((1, "'Device State Map:\n"));
        
    for (i=0; i< PowerSystemHibernate; i++) {
        DBCLASS_KdPrint((1, "'-->S%d = D%d\n", i-1, 
             deviceCapabilities->DeviceState[i]-1));       
    }
    }
#endif    

    
    
    return STATUS_SUCCESS;
}


VOID
DBCLASS_EjectCancelWorker(
    IN PVOID Context
    )
 /* ++
  *
  * Description:
  *
  * Arguments:
  *
  * Return:
  *
  * NTSTATUS
  *
  * -- */
{
    PDBCLASS_WORKITEM workItem = Context;
    PDBC_CONTEXT dbcContext = workItem->DbcContext;
    PDBC_EJECT_TIMEOUT_CONTEXT timeoutContext =
        workItem->TimeoutContext;
    PDEVICE_EXTENSION deviceExtension;        

    LOGENTRY(LOG_MISC, 'eWK+', dbcContext, Context, timeoutContext);

    DbcExFreePool(workItem);

	// make sure the bay was mapped correctly before setting time out context
	if(dbcContext->BayInformation[timeoutContext->BayNumber].DeviceFilterObject)
	{

	    deviceExtension = 
	        dbcContext->BayInformation[
	            timeoutContext->BayNumber].DeviceFilterObject->DeviceExtension;

    	deviceExtension->TimeoutContext = NULL;            

    }

    DBCLASS_SyncBayFeatureRequest(dbcContext,
                                  DRB_FUNCTION_SET_BAY_FEATURE,
                                  timeoutContext->BayNumber,
                                  REQUEST_DEVICE_ENABLED_STATE);    

    DBCLASS_DecrementIoCount(dbcContext);            
    DBCLASS_PostChangeRequest(dbcContext);        

    DbcExFreePool(timeoutContext);
    LOGENTRY(LOG_MISC, 'eWK-', 0, Context, 0);

    return;
}


VOID
DBCLASS_EjectTimeoutDPC(
    IN PKDPC Dpc,
    IN PVOID DeferredContext,
    IN PVOID SystemArgument1,
    IN PVOID SystemArgument2
    )
/*++

Routine Description:

    This routine runs at DISPATCH_LEVEL IRQL. 

    
    
Arguments:

    Dpc - Pointer to the DPC object.

    DeferredContext - 

    SystemArgument1 - not used.
    
    SystemArgument2 - not used.

Return Value:

    None.

--*/
{
    PDBC_EJECT_TIMEOUT_CONTEXT
        timeoutContext = DeferredContext;
    PDBCLASS_WORKITEM workItem;        

    LOGENTRY(LOG_MISC, 'EJCo', 0, 0,  timeoutContext);    
        
    DBCLASS_KdPrint((1, "'**>Eject Timeout for Bay[%d]\n",
            timeoutContext->BayNumber));      

    workItem = DbcExAllocatePool(NonPagedPool, sizeof(DBCLASS_WORKITEM));

    if (workItem) {
        LOGENTRY(LOG_MISC, 'qETM', 0,
            workItem, 0);

        workItem->Sig = DBC_WORKITEM_SIG;
        workItem->DbcContext = timeoutContext->DbcContext;
        workItem->TimeoutContext = timeoutContext;
        //workItem->IrpStatus = Irp->IoStatus.Status;
        
        ExInitializeWorkItem(&workItem->WorkQueueItem,
                             DBCLASS_EjectCancelWorker,
                             workItem);

        DBCLASS_IncrementIoCount(timeoutContext->DbcContext); 
        ExQueueWorkItem(&workItem->WorkQueueItem,
                        DelayedWorkQueue);

    } else {
        TRAP();
        DbcExFreePool(timeoutContext);
    }                        
}


NTSTATUS
DBCLASS_SetEjectTimeout(
    PDEVICE_OBJECT DeviceFilterMDO
    )
/*++

Routine Description:

    for a given device PDO set the eject timeout for it

Arguments:

Return Value:

    None.

--*/
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PDBC_EJECT_TIMEOUT_CONTEXT timeoutContext;
    PDEVICE_EXTENSION deviceExtension;
    
    timeoutContext = DbcExAllocatePool(NonPagedPool, 
                                       sizeof(DBC_EJECT_TIMEOUT_CONTEXT));

    // extension for the device filter PDO
    deviceExtension = DeviceFilterMDO->DeviceExtension;
    
    if (timeoutContext) {
        LARGE_INTEGER dueTime;

        LOGENTRY(LOG_MISC, 'EJCs', deviceExtension->Bay, 0,  timeoutContext);    
        
        timeoutContext->BayNumber = 
            deviceExtension->Bay;
        timeoutContext->DbcContext = 
            deviceExtension->DbcContext;            
        DBCLASS_KdPrint((1, "'**>Set Eject Timeout for Bay[%d]\n",
            timeoutContext->BayNumber));      

//        DBCLASS_ASSERT(deviceExtension->TimeoutContext == NULL);
        deviceExtension->TimeoutContext = 
            timeoutContext;    

        KeInitializeTimer(&timeoutContext->TimeoutTimer);
        KeInitializeDpc(&timeoutContext->TimeoutDpc,
                        DBCLASS_EjectTimeoutDPC,
                        timeoutContext);

        dueTime.QuadPart = -10000 * DBCLASS_EJECT_TIMEOUT;

        KeSetTimer(&timeoutContext->TimeoutTimer,
                   dueTime,
                   &timeoutContext->TimeoutDpc);        

    } else {
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }

    return ntStatus;
}


NTSTATUS
DBCLASS_CancelEjectTimeout(
    PDEVICE_OBJECT DeviceFilterMDO
    )
/*++

Routine Description:

    for a given device PDO set the eject timeout for it

Arguments:

Return Value:

    None.

--*/
{    
    PDBC_EJECT_TIMEOUT_CONTEXT timeoutContext;
    NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
    PDEVICE_EXTENSION deviceExtension;

    deviceExtension = DeviceFilterMDO->DeviceExtension;
    timeoutContext = deviceExtension->TimeoutContext;
    deviceExtension->TimeoutContext = NULL;
    
    LOGENTRY(LOG_MISC, 'EJCc', 0, 0,  timeoutContext);

    if (timeoutContext) {
        if (KeCancelTimer(&timeoutContext->TimeoutTimer)) {
        // timer was pulled out of the queue

            DBCLASS_KdPrint((1, "'**>Canceled Eject Timeout for Bay[%d]\n",
                timeoutContext->BayNumber));    
            LOGENTRY(LOG_MISC, 'EJCk', 0, 0,  timeoutContext);    
            DbcExFreePool(timeoutContext);
        }                        
    } else {
        DBCLASS_KdPrint((1, "'**>Cancel Eject Timeout, No Timeout\n"));    
        
    }
    
    ntStatus = STATUS_SUCCESS;

    return ntStatus;
}


NTSTATUS
DBCLASS_CheckPhyLink(
    PDEVICE_OBJECT DevicePdo1394
    )
/*++

Routine Description:

    Given a 1394 PDO see if it is the phy/link for any of our DBC 
    controllers

Arguments:

Return Value:

    STATUS_SUCCESS 
    
--*/
{
    PDBC_CONTEXT dbcContext;
    
    dbcContext = DBCLASS_ControllerList;

    while (dbcContext) {

        LOGENTRY(LOG_MISC, 'FINl', dbcContext, 0, DevicePdo1394);
    
        if (DBCLASS_IsLinkDeviceObject(dbcContext, DevicePdo1394)) 
        {
            dbcContext->LinkDeviceObject = DevicePdo1394;                                                   
            DBCLASS_KdPrint((1, "'>PDO is DBC Link \n"));
            DBCLASS_KdPrint((1, "'>LinkDevObj (%08x) \n", dbcContext->LinkDeviceObject));
        
        }                                       

        dbcContext = dbcContext->Next;                                            
    }    

    return STATUS_SUCCESS;
}
   

NTSTATUS
DBCLASS_GetConfigValue(
    IN PWSTR ValueName,
    IN ULONG ValueType,
    IN PVOID ValueData,
    IN ULONG ValueLength,
    IN PVOID Context,
    IN PVOID EntryContext
    )
/*++

Routine Description:

    This routine is a callback routine for RtlQueryRegistryValues
    It is called for each entry in the Parameters
    node to set the config values. The table is set up
    so that this function will be called with correct default
    values for keys that are not present.

Arguments:

    ValueName - The name of the value (ignored).
    ValueType - The type of the value
    ValueData - The data for the value.
    ValueLength - The length of ValueData.
    Context - A pointer to the CONFIG structure.
    EntryContext - The index in Config->Parameters to save the value.

Return Value:

--*/
{
    NTSTATUS ntStatus = STATUS_SUCCESS;

    DBCLASS_KdPrint((2, "'Type (%08X), Length (%08X)\n", ValueType, ValueLength));

    switch (ValueType) {
    case REG_DWORD:
        *(PVOID*)EntryContext = *(PVOID*)ValueData;
        break;
    case REG_BINARY:
        // we are only set up to read a byte
        RtlCopyMemory(EntryContext, ValueData, 1);
        break;
    default:
        ntStatus = STATUS_INVALID_PARAMETER;
    }
    return ntStatus;
}

#if 0

NTSTATUS
DBCLASS_GetClassGlobalRegistryParameters(
    )
/*++

Routine Description:

Arguments:

Return Value:

--*/
{
    NTSTATUS ntStatus;
    RTL_QUERY_REGISTRY_TABLE QueryTable[2];
    PWCHAR usb = L"class\\dbc";

    PAGED_CODE();
    
    //
    // Set up QueryTable to do the following:
    //

    // spew level
    QueryTable[0].QueryRoutine = DBCLASS_GetConfigValue;
    QueryTable[0].Flags = 0;
    QueryTable[0].Name = ACPI_HUB_KEY;
    QueryTable[0].EntryContext = &DBCLASS_AcpiDBCHubParentPort;
    QueryTable[0].DefaultType = REG_DWORD;
    QueryTable[0].DefaultData = &DBCLASS_AcpiDBCHubParentPort;
    QueryTable[0].DefaultLength = sizeof(DBCLASS_AcpiDBCHubParentPort);

    //
    // Stop
    //
    QueryTable[1].QueryRoutine = NULL;
    QueryTable[1].Flags = 0;
    QueryTable[1].Name = NULL;

    ntStatus = RtlQueryRegistryValues(
                RTL_REGISTRY_SERVICES,
                usb,
                QueryTable,     // QueryTable
                NULL,           // Context
                NULL);          // Environment

    if (NT_SUCCESS(ntStatus)) {
    
         DBCLASS_KdPrint((1, "'AcpiDBCHubParentPort Set: (%d)\n", 
            DBCLASS_AcpiDBCHubParentPort));
  
    }
    
    if ( STATUS_OBJECT_NAME_NOT_FOUND == ntStatus ) {
        ntStatus = STATUS_SUCCESS;
    }

    return ntStatus;
}

#endif


NTSTATUS
DBCLASS_EjectBayComplete(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    )

/*++

Routine Description:

Arguments:

    DeviceObject - a pointer to the device object

    Irp - a pointer to the irp

    Context - NULL ptr

Return Value:

    STATUS_SUCCESS

--*/

{
    PEJECT_CONTEXT ejectContext = Context;
    PDEVICE_EXTENSION deviceExtension;
    PDBC_CONTEXT dbcContext;
    USHORT bay;
    NTSTATUS ntStatus;

    bay = ejectContext->Bay;
    dbcContext = ejectContext->DbcContext;
    DbcExFreePool(ejectContext);

    ntStatus = Irp->IoStatus.Status;
    
    DBCLASS_KdPrint((0, "'>>Request Device Eject BAY[%d] complete  Status (%08X)\n", 
    				bay, ntStatus));

    // set a timeout for the eject
    // if the request failed the timeout will kick in and
    // re-post the chage request for us
    
    if (NT_SUCCESS(ntStatus)) {

	    // check and see if the bay is locked

   		if((!dbcContext->SubsystemDescriptor.bmAttributes.HasSecurityLock) ||
   			(dbcContext->SubsystemDescriptor.bmAttributes.HasSecurityLock &&
   			!dbcContext->BayInformation[bay].LastBayStatus.SecurityLockEngaged))
		{
#if 0
        	if (dbcContext->BayInformation[bay].DeviceFilterObject) {

            	DBCLASS_ASSERT(dbcContext->BayInformation[bay].DeviceFilterObject);    

            	DBCLASS_SetEjectTimeout(dbcContext->BayInformation[bay].DeviceFilterObject);
            
            	deviceExtension = 
                	dbcContext->BayInformation[bay].DeviceFilterObject->DeviceExtension;
                
            	DBCLASS_KdPrint((0, "'>>>Ejecting Filter %x PDO %x\n",
                	dbcContext->BayInformation[bay].DeviceFilterObject,
                	deviceExtension->PhysicalDeviceObject));
            	LOGENTRY(LOG_MISC, 'EJE+', 0, 0, deviceExtension->PhysicalDeviceObject);                       

            	IoRequestDeviceEject(deviceExtension->PhysicalDeviceObject);

        	} else {
    
#endif
            	DBCLASS_KdPrint((0, "'>>>No PDO for this bay\n"));
#if 0

            	if (dbcContext->BusFilterMdo1394 != NULL ||
                	dbcContext->BusFilterMdoUSB != NULL) 
#endif
            	{
                	PDRB drb;

                	//
                	// notify filter of a stop
                	// note that the filter may not veto the stop
                	//
                	drb = DbcExAllocatePool(NonPagedPool, 
                        	sizeof(struct _DRB_START_DEVICE_IN_BAY));

                	if (drb) 
                	{ 
                
                    	drb->DrbHeader.Length = sizeof(struct _DRB_STOP_DEVICE_IN_BAY);
                    	drb->DrbHeader.Function = DRB_FUNCTION_STOP_DEVICE_IN_BAY;
                    	drb->DrbHeader.Flags = 0;

                    	drb->DrbStartDeviceInBay.BayNumber = bay;
                    
                    	// make the request
                    	ntStatus = DBCLASS_SyncSubmitDrb(dbcContext,
                                                     	 dbcContext->TopOfStack, 
                                                     	 drb);
        				DbcExFreePool(drb);
                	}

                	// just pop out the device --
                	// surprise remove is OK at this point
                	DBCLASS_EjectBay(dbcContext, bay); 

    				DBCLASS_PostChangeRequest(dbcContext);        
                
            	}
#if 0
#if DBG                
              	else {
                	DBCLASS_KdPrint((0, "'>>Filter has not registered\n"));
                	TRAP();
            	}                  
#endif        
#endif
#if 0
        	}
#endif
     	} 
	}

     IoFreeIrp(Irp);

     return STATUS_MORE_PROCESSING_REQUIRED;
}