//===========================================================================
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//
// Copyright (c) 1996 - 1998  Microsoft Corporation.  All Rights Reserved.
//
//===========================================================================
/*++

Module Name:

    dcampkt.c

Abstract:

    This is a driver for the Sony Desktop Camera (CCM-DS250 V1.0x).
    This file contains code to handle the stream class packets.

Author:
    
    Shaun Pierce 25-May-96

Modified:

    Yee J. Wu 15-Oct-97

Environment:

    Kernel mode only

Revision History:


--*/


#include "strmini.h"
#include "ksmedia.h"
#include "1394.h"
#include "wdm.h"       // for DbgBreakPoint() defined in dbg.h
#include "dbg.h"
#include "dcamdef.h"
#include "dcampkt.h"
#include "strmdata.h"  // stream format and data ranges; static data
#include "capprop.h"   // Video and camera property function prototype

#define WAIT_FOR_SLOW_DEVICE


#ifdef ALLOC_PRAGMA
    #pragma alloc_text(PAGE, DCamProcessPnpIrp)
    #pragma alloc_text(PAGE, DCamGetStreamInfo)
    #pragma alloc_text(PAGE, DCamFreeIsochResource)    
    #pragma alloc_text(PAGE, InitializeStreamExtension)    
    #pragma alloc_text(PAGE, DCamOpenStream)   
    #pragma alloc_text(PAGE, DCamCloseStream)           
    #pragma alloc_text(PAGE, AdapterCompareGUIDsAndFormatSize)    
    #pragma alloc_text(PAGE, AdapterVerifyFormat)    
    #pragma alloc_text(PAGE, AdapterFormatFromRange)    
    #pragma alloc_text(PAGE, VideoGetProperty)    
    #pragma alloc_text(PAGE, VideoGetState)    
    #pragma alloc_text(PAGE, VideoStreamGetConnectionProperty)    
    #pragma alloc_text(PAGE, VideoStreamGetDroppedFramesProperty)    
    #pragma alloc_text(PAGE, VideoIndicateMasterClock)    
    #pragma alloc_text(PAGE, DCamReceivePacket)
    #pragma alloc_text(PAGE, DCamChangePower)
#endif

void 
tmGetStreamTime(
    IN PHW_STREAM_REQUEST_BLOCK Srb,
    PSTREAMEX pStrmEx, 
    ULONGLONG * ptmStream) 
/*++

Routine Description:

   Query the current time used to timestamp the frame or calculating the dropped frame.
   This is used in IsochCallback so must be paged in always.

Arguments:

    Srb - Pointer to Stream request block

Return Value:

    Nothing

--*/
{

    HW_TIME_CONTEXT TimeContext;

    TimeContext.HwDeviceExtension = (PVOID) Srb->HwDeviceExtension;
    TimeContext.HwStreamObject    = Srb->StreamObject;
    TimeContext.Function          = TIME_GET_STREAM_TIME;
    TimeContext.Time              = 0;
    TimeContext.SystemTime        = 0;

    StreamClassQueryMasterClockSync(
        pStrmEx->hMasterClock,
        &TimeContext);

    *ptmStream = TimeContext.Time;
}

BOOL
DCamAllocateIrbAndIrp(
    PIRB * ppIrb,
    PIRP * ppIrp,
    CCHAR StackSize
    )
{

    // Allocate Irb and Irp
    *ppIrb = ExAllocatePoolWithTag(NonPagedPool, sizeof(IRB), 'macd');
    DCAM_ASSERT(*ppIrb);
    if(!*ppIrb) {           
        return FALSE;
    }

    *ppIrp = IoAllocateIrp(StackSize, FALSE);
    DCAM_ASSERT(*ppIrp);
    if(!*ppIrp) {
        ExFreePool(*ppIrb);
        return FALSE;
    }

    // Initialize IRB
    RtlZeroMemory(*ppIrb, sizeof(IRB));

    return TRUE;
}


BOOL
DCamAllocateIrbIrpAndContext(
    PDCAM_IO_CONTEXT * ppDCamIoContext,
    PIRB * ppIrb,
    PIRP * ppIrp,
    CCHAR StackSize
    )
{

    // Allocate DCamIoContext
    *ppDCamIoContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(DCAM_IO_CONTEXT), 'macd');
    DCAM_ASSERT(*ppDCamIoContext);
    if(!*ppDCamIoContext) {            
        return FALSE;
    }

    // Allocate Irb and Irp
    *ppIrb = ExAllocatePoolWithTag(NonPagedPool, sizeof(IRB), 'macd');
    DCAM_ASSERT(*ppIrb);
    if(!*ppIrb) {
        ExFreePool(*ppDCamIoContext);            
        return FALSE;
    }

    *ppIrp = IoAllocateIrp(StackSize, FALSE);
    DCAM_ASSERT(*ppIrp);
    if(!*ppIrp) {
        ExFreePool(*ppDCamIoContext);
        ExFreePool(*ppIrb);
        return FALSE;
    }


    // Initialize this context
    RtlZeroMemory(*ppDCamIoContext, sizeof(DCAM_IO_CONTEXT));
    (*ppDCamIoContext)->dwSize      = sizeof(DCAM_IO_CONTEXT);
    (*ppDCamIoContext)->pIrb        = *ppIrb;

    // Initialize IRB
    RtlZeroMemory(*ppIrb, sizeof(IRB));

    return TRUE;
}

void
DCamFreeIrbIrpAndContext(
    PDCAM_IO_CONTEXT pDCamIoContext,
    PIRB pIrb,
    PIRP pIrp   
    )
{
    if(pIrp)
        IoFreeIrp(pIrp);
    if(pIrb)
        ExFreePool(pIrb);
    if(pDCamIoContext)
        ExFreePool(pDCamIoContext);
}


void
DCamProcessPnpIrp(
    IN PHW_STREAM_REQUEST_BLOCK Srb,
    PIO_STACK_LOCATION IrpStack,
    PDCAM_EXTENSION pDevExt
    )

/*++

Routine Description:

    Process PnP Irp. 

Arguments:

    Srb - Pointer to Stream request block

Return Value:

    Nothing

--*/
{
    NTSTATUS Status, StatusWait;
    PIRB pIrb;
    PIRP pIrp;
    PSTREAMEX pStrmEx;

    PAGED_CODE();

    switch (IrpStack->MinorFunction) {
  
    case IRP_MN_BUS_RESET:
    //
    // We will realocate the resource (bandwith and channel) in IRQL PASSIVE level.
    //
        Srb->Status = STATUS_SUCCESS;

        //
        // The generation count is updated in the bus reset callback notification only.
        // Continue iff the generation count has been updated.
        // Else, we are assuming another bus reset has occurred,
        // and we will pass to us later.
        //
        if(pDevExt->CurrentGeneration != *((PULONG) &IrpStack->Parameters.Others.Argument4)) {
            ERROR_LOG(("DCamProcessPnpIrp: Generation count old (%d) != new (%d); STOP!\n", 
               pDevExt->CurrentGeneration, *((PULONG) &IrpStack->Parameters.Others.Argument4)) );
            break;
        }

        pStrmEx = (PSTREAMEX) pDevExt->pStrmEx;
        DbgMsg2(("\'%d:%s) SonyDCamProcessPnpIrp: pDevExt %x; pStrmEx %x; CurGen %d\n", 
            pDevExt->idxDev, pDevExt->pchVendorName, pDevExt, pStrmEx, pDevExt->CurrentGeneration));

        //
        // If the stream was open (pStrmEx != NULL && pStrmEx->pVideoInfoHeader != NULL);
        // We need to ask controller to allocate bandwidth and channel. 
        //
        if(pStrmEx &&
           pStrmEx->pVideoInfoHeader != NULL) {

            DbgMsg2(("\'%d:%s) DCamProcessPnpIrp: Stream was open so re-allocate resource.\n", pDevExt->idxDev, pDevExt->pchVendorName));


            // Allocate Irb 
            pIrb = ExAllocatePoolWithTag(NonPagedPool, sizeof(IRB), 'macd');
            if(!pIrb) {            
                ERROR_LOG(("\'DCamProcessPnpIrp: allocate IRB failed; insufficient resource.\n"));
                DCAM_ASSERT(pIrb);
                Srb->Status = STATUS_INSUFFICIENT_RESOURCES;

            } else {
                ULONG ulChannel;
                HANDLE hResource;

                RtlZeroMemory(pIrb, sizeof(IRB));

                //
                // No need to free channel and bandwidth after a bus reset.
                // As a matter of fact, it can trigger a race condition
                // when connecting two host controllers together.  We may
                // end up freeing a device's "just allocated" channel after 
                // a bus reset.  This is in the spec.
                // 
                // Initialize the bandwidth.  We keep the "old" channel number for reallocation.
                //

                pDevExt->hBandwidth = NULL;


                //
                // Before we assign the new hResource, we wait for it attaching buffer to complete.
                // For buffer that completed with previous hResource,
                // It will complete with error ?
                //

                StatusWait = KeWaitForSingleObject( &pStrmEx->hMutex, Executive, KernelMode, FALSE, 0 ); 


                //
                // Reallocate bandwidth and channel, and resource if necessary.
                // IF THIS FAIL, we are consider illegally streaming, and need to STOP streaming.
                //

                ulChannel = pDevExt->IsochChannel;
                hResource = pDevExt->hResource;

                Status = DCamAllocateIsochResource(pDevExt, pIrb, FALSE);
 
                if(Status) {
                    ULONG ulAsyncData;

                    ERROR_LOG(("\'%d:%s) DCamProcessPnpIrp: Re-AllocateIsochResource failed! Status=%x; Treat as device removed.\n\n", 
                        pDevExt->idxDev, pDevExt->pchVendorName, Status));
                    DCAM_ASSERT(Status == STATUS_SUCCESS);

                    //
                    // No resource so let's treat this situation as
                    // Device has been removed because there is no
                    // way to restart this.
                    // This will stop future SRB_READ until stream is STOP and RUN again.
                    //

                    pDevExt->bDevRemoved = TRUE;                   
                    Srb->Status = STATUS_INSUFFICIENT_RESOURCES;

                    // 
                    // Stop tranmission so it will not send data to the old channel,
                    // which might be "owned" by other device.
                    //

                    if(pStrmEx->KSState == KSSTATE_RUN) {
                        ulAsyncData = STOP_ISOCH_TRANSMISSION;
                        Status = DCamWriteRegister ((PIRB) Srb->SRBExtension, pDevExt, FIELDOFFSET(CAMERA_REGISTER_MAP, IsoEnable), ulAsyncData);
                        DbgMsg2(("IRP_MN_BUS_RESET: stop xmission (%x) status %x\n", ulAsyncData, Status));   
                    }

                    KeReleaseMutex(&pStrmEx->hMutex, FALSE);                
                    ExFreePool(pIrb);

                    return;
                }

                //
                // If channel number change due to bus reset, we must
                //    - continue to blocking incoming SRB_READ (with mutex)
                //    - if RUN state, stop transmission
                //    - detach all pending buffer(s)
                //    - free "stale" isoch resource
                //    - if RUN state, program device to use the new channel
                //    - if RUN state, restart transmission
                //

                if(pDevExt->IsochChannel != ISOCH_ANY_CHANNEL &&
                   ulChannel != pDevExt->IsochChannel) {

                    ULONG ulAsyncData;


                    // 
                    // Stop tranmission so it will not send data to the old channel,
                    // which might be "owned" by other device.
                    //

                    if(pStrmEx->KSState == KSSTATE_RUN) {
                        ulAsyncData = STOP_ISOCH_TRANSMISSION;
                        Status = DCamWriteRegister ((PIRB) Srb->SRBExtension, pDevExt, FIELDOFFSET(CAMERA_REGISTER_MAP, IsoEnable), ulAsyncData);
                        DbgMsg2(("IRP_MN_BUS_RESET: stop xmission (%x) status %x\n", ulAsyncData, Status));   
                    }

 
                    //
                    // Detach pending packets using the hOldRources and reattached using the new hResource
                    // Note: incoming SRB_READ is block right now.
                    //       free old resource after all pending reads are detached.
                    //

                    if(pDevExt->PendingReadCount > 0) {

                        Status = DCamReSubmitPacket(hResource, pDevExt, pStrmEx, pDevExt->PendingReadCount);
                        if(Status) {
                        }
                    }


                    //
                    // Free "stale" isoch resource 
                    //
                    pIrp = IoAllocateIrp(pDevExt->BusDeviceObject->StackSize, FALSE);
                    if(pIrp) {
                        if(pDevExt->hResource != hResource) {

                            DbgMsg2(("DCamReSubmitPacket: Attempt to free hStaleResource %x\n", hResource));
                            pIrb->FunctionNumber = REQUEST_ISOCH_FREE_RESOURCES;
                            pIrb->Flags = 0;
                            pIrb->u.IsochFreeResources.hResource = hResource;
                            Status = DCamSubmitIrpSynch(pDevExt, pIrp, pIrb);    
                            if (Status) {
                                ERROR_LOG(("\'DCamFreeIsochResource: Error %x while trying to free Isoch resources\n\n", Status));
                                DCAM_ASSERT(Status == STATUS_SUCCESS);
                            }    
                        }

                        IoFreeIrp(pIrp);
                    } 

                    //
                    // Getting ready to accept callback
                    //
                    pDevExt->bStopIsochCallback = FALSE;
                    
                    //
                    // Restore to its initial Streaming state
                    // mainly, programming device.
                    //

                    DCamSetKSStateInitialize(pDevExt);                    
                }

                KeReleaseMutex(&pStrmEx->hMutex, FALSE);                
                ExFreePool(pIrb);

            }
        }


        if(Status == STATUS_SUCCESS) {
            //
            // Set to last saved configuration
            //

            SetCurrentDevicePropertyValues(pDevExt, (PIRB) Srb->SRBExtension);
        }

        DbgMsg2(("\'DCamProcessPnpIrp, IRP_MN_BUS_RESET: Done, Status %x\n", Status));

        break;
            
    case IRP_MN_QUERY_CAPABILITIES: 
        ERROR_LOG(("\'SonyDCamProcessPnpIrp: IRP_MN_QUERY_CAPABILITIES: Srb->Status = STATUS_NOT_IMPLEMENTED.\n"));
    default:
        Srb->Status = STATUS_NOT_IMPLEMENTED;
        break;
    }
}


VOID 
DCamChangePower(
    IN PHW_STREAM_REQUEST_BLOCK pSrb
)

/*++

Routine Description:

    Process chnaging this device's power state.  

Arguments:

    Srb - Pointer to Stream request block

Return Value:

    Nothing

--*/
{
    PDCAM_EXTENSION pDevExt;
    PSTREAMEX pStrmEx;
    PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(pSrb->Irp);
    DEVICE_POWER_STATE DevicePowerState = pSrb->CommandData.DeviceState;

    PAGED_CODE();

    pDevExt = (PDCAM_EXTENSION) pSrb->HwDeviceExtension;
    DbgMsg2(("\'DCamChangePower: pSrb=%x; pDevExt=%x\n", pSrb, pDevExt));

    DCAM_ASSERT(pDevExt != NULL);
    if(!pDevExt) {   
        pSrb->Status = STATUS_INVALID_PARAMETER;
        ERROR_LOG(("DCamChangePower: pDevExt is NULL!\n"));
        return;
    }

    pStrmEx = (PSTREAMEX) pDevExt->pStrmEx;     
    if (pStrmEx ==NULL) {        
        pSrb->Status = STATUS_SUCCESS;
        pDevExt->CurrentPowerState = DevicePowerState;
        DbgMsg2(("DCamChangePower: pStrmEx is NULL => Stream is not open. That is Ok!!\n"));
        return;
    }
   

    // 
    // We can honor power state change:
    //
    //    D0: device is on and running
    //    D1,D2: not implemented.
    //    D3: device is off and not running.  Device context is lost.  
    //        Power can be removed from the device.
    //        when power is back on, we will get a bus reset.
    //
    //    (0) Remove DontSuspendIfStreamsAreRunning from INF
    //    save current state.
    //    (1) ->D3, to PAUSE/STOP state (depends on if pending buffers can be kept by its lower driver)
    //    (2) ->D0, to restore saved state
    //
    // We can do the above but we do not know at this point 
    // how our client application react
    //
    if(IrpStack->MinorFunction == IRP_MN_SET_POWER) {
        DbgMsg2(("DCamChangePower: changin power state from %d to %d.\n", pDevExt->CurrentPowerState, DevicePowerState));
        pSrb->Status = STATUS_SUCCESS;

        if(pDevExt->CurrentPowerState != DevicePowerState) {

            switch (DevicePowerState) {
            case PowerDeviceD3:        // D0->D3: save state, stop streaming and Sleep
                if( pDevExt->CurrentPowerState == PowerDeviceD0 ) {
                    DbgMsg1(("DCamChangePower: Switching from D0 to D3; Save current state.\n"));
                    // Save current state to be restored when awake
                    pStrmEx->KSSavedState = pStrmEx->KSState;
                }
                break;

            case PowerDeviceD0:  // to Wakeup, restore state and running
                if( pDevExt->CurrentPowerState == PowerDeviceD3 ) {
                    DbgMsg1(("DCamChangePower: Switching from D3 to D0; restore state.\n"));
                    pStrmEx->KSState = pStrmEx->KSSavedState;                         
                }
                break;

            // These state are not defined and noe used.
            case PowerDeviceD1:
            case PowerDeviceD2:               
            default:
                ERROR_LOG(("DCamChangePower: Invalid PowerState %d\n", DevicePowerState));                  
                pSrb->Status = STATUS_INVALID_PARAMETER;
                break;
            }
        }            

        if(pSrb->Status == STATUS_SUCCESS) 
            pDevExt->CurrentPowerState = DevicePowerState;         

    } else {
       
        pSrb->Status = STATUS_NOT_IMPLEMENTED;

    }


}




VOID
DCamGetStreamInfo(
    IN PHW_STREAM_REQUEST_BLOCK Srb
    )

/*++

Routine Description:

    Returns the information of all streams that are supported by the driver

Arguments:

    Srb - Pointer to Stream request block

Return Value:

    Nothing

--*/

{
    //
    // pick up the pointer to the stream information data structure
    //

    PHW_STREAM_HEADER StreamHeader = &(Srb->CommandData.StreamBuffer->StreamHeader);        
    PDCAM_EXTENSION pDevExt = (PDCAM_EXTENSION) Srb->HwDeviceExtension;
    PHW_STREAM_INFORMATION StreamInfo = &(Srb->CommandData.StreamBuffer->StreamInfo);

    PAGED_CODE();

    //
    // set number of streams
    //

    ASSERT (Srb->NumberOfBytesToTransfer >= 
            sizeof (HW_STREAM_HEADER) +
            sizeof (HW_STREAM_INFORMATION));

    //
    // initialize stream header
    //

    RtlZeroMemory(StreamHeader, 
                sizeof (HW_STREAM_HEADER) +
                sizeof (HW_STREAM_INFORMATION));

    //
    // initialize the number of streams supported
    //

    StreamHeader->NumberOfStreams = 1;
    StreamHeader->SizeOfHwStreamInformation = sizeof(HW_STREAM_INFORMATION);


    //
    // set the VideoProcAmp and CameraControl property information in a static array
    // for the video stream.  
    // It also queries the property value range, set default value.
    // 
    InitializePropertyArray(Srb);


    //
    // Initialize the stream structure.
    //
    // Number of instances field indicates the number of concurrent streams
    // of this type the device can support.  
    //

    StreamInfo->NumberOfPossibleInstances = 1;

    //
    // indicates the direction of data flow for this stream, relative to 
    // the driver
    //

    StreamInfo->DataFlow = KSPIN_DATAFLOW_OUT;

    //
    // dataAccessible - Indicates whether the data is "seen" by the host
    // processor.
    //

    StreamInfo->DataAccessible = TRUE;

    //
    // Return how many formats this stream supports 
    //

    //
    // Until we build the format table dynamically,
    // we need to hardcode this.
    //
    //  TI 1394 DCam supports all modes of video format 0 (uncompressed).
    //
    if(RtlCompareMemory(pDevExt->pchVendorName, "TI", 2) == 2) 
        StreamInfo->NumberOfFormatArrayEntries = NUM_DCAM_STREAM_FORMATS;
    else
        StreamInfo->NumberOfFormatArrayEntries = 1;  // 320x240 UYVY

    StreamInfo->StreamFormatsArray = &DCAM_StreamFormats[0];

    //
    // set the property information for the video stream
    //


    StreamInfo->NumStreamPropArrayEntries = NUMBER_VIDEO_STREAM_PROPERTIES;
    StreamInfo->StreamPropertiesArray = (PKSPROPERTY_SET) VideoStreamProperties;

    //
    // set the pin name and category
    //

    StreamInfo->Name = (GUID *) &PINNAME_VIDEO_CAPTURE;
    StreamInfo->Category = (GUID *) &PINNAME_VIDEO_CAPTURE;


    //
    // store a pointer to the topology for the device
    //
        
    Srb->CommandData.StreamBuffer->StreamHeader.Topology = &Topology;


    //
    // indicate success
    //

    Srb->Status = STATUS_SUCCESS;

    DbgMsg2(("\'DCamGetStreamInfo: NumFormat %d, StreamFormatArray %x\n",
        StreamInfo->NumberOfFormatArrayEntries,  StreamInfo->StreamFormatsArray));

}

#define TIME_ROUNDING                        10   // Give it some rounding error of 1microsec
#define TIME_0750FPS      1333333+TIME_ROUNDING   // 1/7.50 * 10,000,000 (unit=100ns)
#define TIME_1500FPS       666666+TIME_ROUNDING   // 1/15.0 * 10,000,000 (unit=100ns)  do not round to 666667
#define TIME_3000FPS       333333+TIME_ROUNDING   // 1/30.0 * 10,000,000 (unit=100ns)

NTSTATUS
DCamAllocateIsochResource(
    PDCAM_EXTENSION pDevExt,
    PIRB Irb,
    BOOL bAllocateResource
    )
{
    PIRP Irp;
    CCHAR StackSize;
    ULONG ModeIndex;
    PSTREAMEX pStrmEx;
    DWORD dwAvgTimePerFrame, dwCompression;
    ULONG fulSpeed;
    NTSTATUS Status = STATUS_SUCCESS;


    DCAM_ASSERT(pDevExt);
    pStrmEx = (PSTREAMEX) pDevExt->pStrmEx;
    DCAM_ASSERT(pStrmEx);


    DbgMsg2(("\'DCamAllocateIsochResource: enter; pStrmEx %x; pVideoInfo %x\n", pStrmEx, pStrmEx->pVideoInfoHeader));
    //
    // Now if they're on a YUV4:2:2 format, we've gotta check what
    // resolution they want it at, since we support this format
    // but in two different resolutions (modes on the camera).
    //

    // This is the INDEX to the frame rate and resource allocation; see IsochInfoTable.
    // 0 : reserved
    // 1 : 3.75
    // 2 : 7.5
    // 3 : 15 (DEFAULT_FRAME_RATE)
    // 4 : 30 
    // 5 : 60 (Not supported for Mode 1 & 3)
    dwAvgTimePerFrame = (DWORD) pStrmEx->pVideoInfoHeader->AvgTimePerFrame;
    dwCompression = (DWORD) pStrmEx->pVideoInfoHeader->bmiHeader.biCompression;



    // Determine the Frame rate
    if (dwAvgTimePerFrame      > TIME_0750FPS) 
        pDevExt->FrameRate = 1;        //  3.75FPS
    else if (dwAvgTimePerFrame >  TIME_1500FPS) 
        pDevExt->FrameRate = 2;        //  7.5FPS
    else if (dwAvgTimePerFrame >  TIME_3000FPS) 
        pDevExt->FrameRate = 3;        // 15 FPS
    else 
        pDevExt->FrameRate = 4;        // 30 FPS


    DbgMsg2(("\'DCamAllocateIsochResource: FrameRate: %d FPS\n", (1 << (pDevExt->FrameRate-1)) * 15 / 4));

    // Determine the Video Mode
    switch(dwCompression) {
          
    case FOURCC_UYVY:     // Mode 1 or 3
         if (pStrmEx->pVideoInfoHeader->bmiHeader.biWidth == 640 &&
             (pStrmEx->pVideoInfoHeader->bmiHeader.biHeight == 480 || 
             pStrmEx->pVideoInfoHeader->bmiHeader.biHeight == -480)) {
              ModeIndex = 3;
              // Max frame rate is 15
              if(pDevExt->FrameRate > 3)
                 pDevExt->FrameRate = 3;
         } else
              ModeIndex = 1;
         break;

    case FOURCC_IYUV:     // Mode 2
         ModeIndex = 2;
         break;

    case KS_BI_RGB:  // = 0
         ModeIndex = 4;
         // Max frame rate is 15
         if(pDevExt->FrameRate > 3)
            pDevExt->FrameRate = 3;
         break;

    default:          
         Status = STATUS_NOT_IMPLEMENTED;;
         return Status;;
    }


    DbgMsg2(("\'DCamAllocateIsochResource: ModeIndex=%d, AvgTimePerFrame=%d, FrameRate=%d\n", 
             ModeIndex, dwAvgTimePerFrame, pDevExt->FrameRate));

    //
    // Get an Irp so we can send some allocation commands down
    //

    StackSize = pDevExt->BusDeviceObject->StackSize;
    Irp = IoAllocateIrp(StackSize, FALSE);

    if (!Irp) {
        DCAM_ASSERT(Irp != NULL);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // Calculate the index to use to reference the ISOCH table
    //
    pStrmEx->idxIsochTable = ModeIndex * NUM_POSSIBLE_RATES + pDevExt->FrameRate;

    DCAM_ASSERT(pStrmEx->pVideoInfoHeader->bmiHeader.biSizeImage == IsochInfoTable[pStrmEx->idxIsochTable].CompletePictureSize);
    DbgMsg2(("\'DCamAllocateIsochResource: ModeIndex=%d, idxIsochTable=%d, biSizeImage=%d, CompletePictureSize=%d\n", 
             ModeIndex, pStrmEx->idxIsochTable, pStrmEx->pVideoInfoHeader->bmiHeader.biSizeImage, IsochInfoTable[pStrmEx->idxIsochTable].CompletePictureSize));          

    //
    // 0. Determine the MAX_SPEED and not use the speed defined in the static table.
    //
    Irb->FunctionNumber = REQUEST_GET_SPEED_BETWEEN_DEVICES;
    Irb->Flags = 0;
    Irb->u.GetMaxSpeedBetweenDevices.fulFlags = USE_LOCAL_NODE;
    Irb->u.GetMaxSpeedBetweenDevices.ulNumberOfDestinations = 0;
    Status = DCamSubmitIrpSynch(pDevExt, Irp, Irb);
    if(Status) {
        ERROR_LOG(("\'DCamAllocateIsochResource: Error %x while trying to get maximun speed between devices.\n", Status));        

        IoFreeIrp(Irp);
        return STATUS_INSUFFICIENT_RESOURCES;
    }     
    
    fulSpeed = Irb->u.GetMaxSpeedBetweenDevices.fulSpeed;


    if(fulSpeed != SPEED_FLAGS_100 &&
       fulSpeed != SPEED_FLAGS_200 &&
       fulSpeed != SPEED_FLAGS_400) {
        // Use what is in the table!  Tabel only support these three speeds.
        fulSpeed = IsochInfoTable[pStrmEx->idxIsochTable].SpeedRequired;
    }
    
    switch (fulSpeed) {
    case SPEED_FLAGS_100:
        DbgMsg2(("\'DCamAllocateIsochResource: fulSpeed=100Mbit\n"));
        pDevExt->SpeedCode = 0;  // For ISO speed code
        break;
    case SPEED_FLAGS_200:
        DbgMsg2(("\'DCamAllocateIsochResource: fulSpeed=200Mbit\n"));
        pDevExt->SpeedCode = 1;       
        break;
    case SPEED_FLAGS_400:
        DbgMsg2(("\'DCamAllocateIsochResource: fulSpeed=400Mbit\n"));
        pDevExt->SpeedCode = 2;
        break;
    default:
        DCAM_ASSERT(fulSpeed != SPEED_FLAGS_100 || fulSpeed != SPEED_FLAGS_200 || fulSpeed != SPEED_FLAGS_400);
        break;
    }


    //
    // 1. Allocate CHANNEL
    //       First try to re-allocate the same channel
    //       If it is used, try to get any channel.  1394DCam can only be on channel 0..15
    //
    Irb->FunctionNumber = REQUEST_ISOCH_ALLOCATE_CHANNEL;
    Irb->Flags = 0;

    //
    //      ULONG           nRequestedChannel;      // Need a specific channel
    //      ULONG           Channel;                // Returned channel
    //      LARGE_INTEGER   ChannelsAvailable;      // Channels available
    // Instead of hardcoded '0'; use -1 to ask the bus driver to get the next available channel for us.
    // -1 (any channel) or an existing channel
    Irb->u.IsochAllocateChannel.nRequestedChannel = pDevExt->IsochChannel;  
    Status = DCamSubmitIrpSynch(pDevExt, Irp, Irb);
    if(Status) {

        //
        // Due to channel change, 
        // all Pending read will be either resubmitted, 
        // or cancelled (if out of resource).
        //
        pDevExt->bStopIsochCallback = TRUE;  // Set back to FALSE after pending buffer are attached.


        //
        // If this is an initial request and no channel available,
        // free all resource and abort.
        //
        if(pDevExt->IsochChannel == ISOCH_ANY_CHANNEL)
            goto NoResource_abort;

        DbgMsg1(("DCamAllocateIsochResource: last allocated channel %d is not available; pending count %d.\n",  
            pDevExt->IsochChannel, pDevExt->PendingReadCount));                      

        // Try gettting any channel.
        Irb->FunctionNumber = REQUEST_ISOCH_ALLOCATE_CHANNEL;
        Irb->Flags = 0;
        Irb->u.IsochAllocateChannel.nRequestedChannel = ISOCH_ANY_CHANNEL;  
        Status = DCamSubmitIrpSynch(pDevExt, Irp, Irb);
        if(Status) {
            ERROR_LOG(("DCamAllocateIsochResource: allocate any channel failed, status %x!\n",  Status));
            DCAM_ASSERT(Status == STATUS_SUCCESS);
            goto NoResource_abort;           
        }

        //
        // Channel changed, we MUST reallocate resource.
        // The "stale" resrouce will be free later when 
        // pending packet are detached.
        //

        bAllocateResource = TRUE;
    }   
    
    DbgMsg1(("**IsochAlloc: Channel(Old) %d, requested %d, got %d, HiLo(0x%x:%x), PendingRead %d\n", 
         pDevExt->IsochChannel, 
         Irb->u.IsochAllocateChannel.nRequestedChannel, 
         Irb->u.IsochAllocateChannel.Channel, 
         Irb->u.IsochAllocateChannel.ChannelsAvailable.u.HighPart,
         Irb->u.IsochAllocateChannel.ChannelsAvailable.u.LowPart,
         pDevExt->PendingReadCount));

    // New channel
    pDevExt->IsochChannel = Irb->u.IsochAllocateChannel.Channel;  // Used in allocating iso. resource and reallocation


    //
    // 2. Allocate BANDWIDTH
    //
    Irb->FunctionNumber = REQUEST_ISOCH_ALLOCATE_BANDWIDTH;
    Irb->Flags = 0;
    Irb->u.IsochAllocateBandwidth.nMaxBytesPerFrameRequested = IsochInfoTable[pStrmEx->idxIsochTable].QuadletPayloadPerPacket << 2;
    Irb->u.IsochAllocateBandwidth.fulSpeed = fulSpeed;
    Irb->u.IsochAllocateBandwidth.hBandwidth = 0;
    Status = DCamSubmitIrpSynch(pDevExt, Irp, Irb);
    
    if(Status) {
        ERROR_LOG(("DCamAllocateIsochResource: Error %x while trying to allocate Isoch bandwidth\n", Status));                  
        goto NoResource_abort;
    }

    pDevExt->hBandwidth = Irb->u.IsochAllocateBandwidth.hBandwidth;
    DbgMsg2(("**IsochAlloc: nMaxBytesPerFrameRequested %d, fulSpeed %d; hBandWidth 0x%x\n",
         IsochInfoTable[pStrmEx->idxIsochTable].QuadletPayloadPerPacket << 2, fulSpeed, pDevExt->hBandwidth));



    //
    // 3. Allocate RESOURCES
    //    Note: after a bus reset, we need not free and re-allocate this resoruce again.
    //
    if(bAllocateResource) {
        Irb->FunctionNumber = REQUEST_ISOCH_ALLOCATE_RESOURCES;
        Irb->Flags = 0;
        Irb->u.IsochAllocateResources.fulSpeed = fulSpeed;
        Irb->u.IsochAllocateResources.nChannel = pDevExt->IsochChannel;
        Irb->u.IsochAllocateResources.nMaxBytesPerFrame = IsochInfoTable[pStrmEx->idxIsochTable].QuadletPayloadPerPacket << 2;
        // For slower frame rate use smaller quadlets
        // smaller frame size will use more packet to fill the same amount of data
        // this is why smaller frame rate actually demand more resource !!
        Irb->u.IsochAllocateResources.nNumberOfBuffers = MAX_BUFFERS_SUPPLIED;
        Irb->u.IsochAllocateResources.nMaxBufferSize = IsochInfoTable[pStrmEx->idxIsochTable].CompletePictureSize;
        if (pDevExt->HostControllerInfomation.HostCapabilities & HOST_INFO_SUPPORTS_RETURNING_ISO_HDR) {       
            Irb->u.IsochAllocateResources.nQuadletsToStrip = 1;
            Irb->u.IsochAllocateResources.fulFlags = RESOURCE_USED_IN_LISTENING | RESOURCE_STRIP_ADDITIONAL_QUADLETS;

        } else {
            Irb->u.IsochAllocateResources.nQuadletsToStrip = 0;
            Irb->u.IsochAllocateResources.fulFlags = RESOURCE_USED_IN_LISTENING;
        }

        Irb->u.IsochAllocateResources.hResource = 0;
        DbgMsg2(("\'DCamAllocateIsochResource: fullSpeed(%d), nMaxBytesPerFrame(%d), nMaxBufferSize(%d)\n", 
                              Irb->u.IsochAllocateResources.fulSpeed,
                              Irb->u.IsochAllocateResources.nMaxBytesPerFrame,
                              Irb->u.IsochAllocateResources.nMaxBufferSize));
        Status = DCamSubmitIrpSynch(pDevExt, Irp, Irb);          

        if(Status) {
            ERROR_LOG(("DCamAllocateIsochResource: Error %x while trying to allocate Isoch resources\n", Status));
            goto NoResource_abort;
        }


        pDevExt->hResource = Irb->u.IsochAllocateResources.hResource;

    }

    pDevExt->CurrentModeIndex = ModeIndex;
    DbgMsg2(("**IsochAlloc: hResource = %x\n", pDevExt->hResource));

    IoFreeIrp(Irp);     

    return STATUS_SUCCESS;



NoResource_abort:

    // Free bandwidth
    if(pDevExt->hBandwidth != NULL) {

        Irb->FunctionNumber = REQUEST_ISOCH_FREE_BANDWIDTH;
        Irb->Flags = 0;
        Irb->u.IsochFreeBandwidth.hBandwidth = pDevExt->hBandwidth;
        Status = DCamSubmitIrpSynch(pDevExt, Irp, Irb);
        pDevExt->hBandwidth = NULL;
        if(Status) {
            ERROR_LOG(("DCamAllocateIsochResource: Error %x while trying to free Isoch bandwidth\n", Status));
        }
    }

    // Free channel
    if (pDevExt->IsochChannel != ISOCH_ANY_CHANNEL) {

        Irb->FunctionNumber = REQUEST_ISOCH_FREE_CHANNEL;
        Irb->Flags = 0;
        Irb->u.IsochFreeChannel.nChannel = pDevExt->IsochChannel;
        Status = DCamSubmitIrpSynch(pDevExt, Irp, Irb);
        pDevExt->IsochChannel = ISOCH_ANY_CHANNEL;  // Reset it.

        if(Status) {
            ERROR_LOG(("DCamAllocateIsochResource: Error %x while trying to free Isoch channel\n", Status));
        }
    }


    IoFreeIrp(Irp);
    return STATUS_INSUFFICIENT_RESOURCES;
    
}


NTSTATUS
DCamFreeIsochResource (
    PDCAM_EXTENSION pDevExt,
    PIRB Irb,
    BOOL bFreeResource
    )
/*++

Routine Description:

    Free resource allocated in DCamAllocateIsochResource().

Arguments:

    Srb - Pointer to Stream request block

Return Value:

    Nothing

--*/
{
    PIRP Irp;
    CCHAR StackSize;
    NTSTATUS Status = STATUS_SUCCESS;


    PAGED_CODE();

    DbgMsg2(("\'DCamFreeIsochResource: enter; DevExt=%x, Irb=%x\n", pDevExt, Irb));

    DCAM_ASSERT(pDevExt);
    DCAM_ASSERT(Irb);


    if(Irb == 0 ||
       pDevExt == 0) {
       DbgMsg2(("\'DCamFreeIsochResource: ABORTED!\n"));
       return STATUS_SUCCESS;
    }
    //
    // Get an Irp so we can send some free commands down
    //
    StackSize = pDevExt->BusDeviceObject->StackSize;
    Irp = IoAllocateIrp(StackSize, FALSE);

    if (!Irp) {   
        ERROR_LOG(("DCamFreeIsochResource: Error %x while trying to allocate an Irp\n\n", Status));
        DCAM_ASSERT(Irp != NULL);
        return STATUS_INSUFFICIENT_RESOURCES;
    }


    //
    // 1. Free Resource
    //
    if (pDevExt->hResource && bFreeResource) {

        DbgMsg2(("\'DCamFreeIsochResource: Attempt to free ->hResource\n"));

        Irb->FunctionNumber = REQUEST_ISOCH_FREE_RESOURCES;
        Irb->Flags = 0;
        Irb->u.IsochFreeResources.hResource = pDevExt->hResource;
        Status = DCamSubmitIrpSynch(pDevExt, Irp, Irb);

        pDevExt->hResource = NULL;
        if (Status) {

            ERROR_LOG(("DCamFreeIsochResource: Error %x while trying to free Isoch resources\n\n", Status));
            DCAM_ASSERT(Status == STATUS_SUCCESS);
        }
    }

    //
    // 2. Free Channel
    //
    if (pDevExt->IsochChannel != ISOCH_ANY_CHANNEL) {

        DbgMsg2(("\'DCamFreeIsochResource: Attempt to free ->IsochChannel\n"));

        Irb->FunctionNumber = REQUEST_ISOCH_FREE_CHANNEL;
        Irb->Flags = 0;
        Irb->u.IsochFreeChannel.nChannel = pDevExt->IsochChannel;
        Status = DCamSubmitIrpSynch(pDevExt, Irp, Irb);

        pDevExt->IsochChannel = ISOCH_ANY_CHANNEL;

        if(Status) {
            
            ERROR_LOG(("DCamFreeIsochResource: Error %x while trying to free Isoch channel\n\n", Status));
            DCAM_ASSERT(Status == STATUS_SUCCESS);
        }
    }

    //
    // 3. Free Bandwidth
    //
    if (pDevExt->hBandwidth) {

        DbgMsg2(("\'DCamFreeIsochResource: Attempt to free ->hBandwidth\n"));

        Irb->FunctionNumber = REQUEST_ISOCH_FREE_BANDWIDTH;
        Irb->Flags = 0;
        Irb->u.IsochFreeBandwidth.hBandwidth = pDevExt->hBandwidth;
        Status = DCamSubmitIrpSynch(pDevExt, Irp, Irb);

        pDevExt->hBandwidth = NULL;

        if (Status) {

            ERROR_LOG(("DCamFreeIsochResource: Error %x while trying to free Isoch bandwidth\n", Status));
            DCAM_ASSERT(Status == STATUS_SUCCESS);
        }
    }

    DbgMsg2(("\'DCamFreeIsochResource: hResource = %x\n", pDevExt->hResource));


    IoFreeIrp(Irp);

    return STATUS_SUCCESS;

}



VOID 
InitializeStreamExtension(
    PDCAM_EXTENSION pDevExt,
    PHW_STREAM_OBJECT   pStreamObject,
    PSTREAMEX           pStrmEx
    )
{
    PAGED_CODE();

    pStrmEx->hMasterClock = 0;
    pStrmEx->FrameInfo.ExtendedHeaderSize = sizeof(KS_FRAME_INFO);
    pStrmEx->FrameInfo.PictureNumber = 0;
    pStrmEx->FrameInfo.DropCount     = 0;
    pStrmEx->FrameInfo.dwFrameFlags  = 0;     
    pStrmEx->FirstFrameTime    = 0;
    pStrmEx->pVideoInfoHeader  = 0;
    pStrmEx->KSState           = KSSTATE_STOP;
    pStrmEx->KSSavedState      = KSSTATE_STOP;


    KeInitializeMutex( &pStrmEx->hMutex, 0);  // Level 0 and in Signal state

}

BOOL
DCamDeviceInUse(
    PIRB pIrb,
    PDCAM_EXTENSION pDevExt
)
/*++

Routine Description:

    See if this device is in used.  
    We check ISO_ENABLE since this is the only register
    in a 1394DCam that we can set/get and 99%+ of time
    this bit is set by its owner.

Arguments:

    pIrb - Pointer to IEEE 1394 Request Block definition (IRB)
    pDevExt - this device extension

Return Value:

    TRUE:  Iso_enable != 0
    FALSE: iso_enable == 0

--*/

{
    DCamRegArea RegArea;
    NTSTATUS status;


    // If a device is removed, it is not available.
    if(pDevExt->bDevRemoved)
        return TRUE;

    RegArea.AsULONG = 0;
    status = DCamReadRegister(pIrb, pDevExt, FIELDOFFSET(CAMERA_REGISTER_MAP, IsoEnable), &(RegArea.AsULONG));
    DbgMsg2(("DCamDeviceInUse: Status %x, ISO_ENABLE %x\n", status, RegArea.AsULONG));

    if(status == STATUS_SUCCESS) {
        if(RegArea.AsULONG)
            return TRUE;
        else 
            return FALSE;
    }

    // failed to query the device.
    return TRUE;
}


VOID
DCamOpenStream(
    IN PHW_STREAM_REQUEST_BLOCK pSrb
    )

/*++

Routine Description:

    Called when an OpenStream Srb request is received

Arguments:

    pSrb - Pointer to Stream request block

Return Value:

    Nothing

--*/

{

    PIRB Irb;
    ULONG nSize;
    PDCAM_EXTENSION pDevExt;
    PSTREAMEX pStrmEx;
    PKS_DATAFORMAT_VIDEOINFOHEADER  pKSDataFormat = 
                (PKS_DATAFORMAT_VIDEOINFOHEADER) pSrb->CommandData.OpenFormat;
    PKS_VIDEOINFOHEADER     pVideoInfoHdrRequested = 
                &pKSDataFormat->VideoInfoHeader;


    PAGED_CODE();

    Irb = (PIRB) pSrb->SRBExtension;
    pDevExt = (PDCAM_EXTENSION) pSrb->HwDeviceExtension;
    pStrmEx = (PSTREAMEX)pSrb->StreamObject->HwStreamExtension;

    DbgMsg2(("\'DCamOpenStream: >>> !!! pDevEx %x; pStrmEx %x !!!\n", pDevExt, pStrmEx));


    //
    // Cache the stream extension.
    //

    pDevExt->pStrmEx = pStrmEx; 


    //
    // default to success
    //

    pSrb->Status = STATUS_SUCCESS;

    //
    // determine which stream number is being opened.  This number indicates
    // the offset into the array of streaminfo structures that was filled out
    // in the AdapterStreamInfo call.
    //
    // So:
    //   0 - Video data from camera
    //

    switch (pSrb->StreamObject->StreamNumber) {

    case 0:

         //
         // Make sure that this device is not in used 
         //
         if(DCamDeviceInUse(Irb, pDevExt)) {
             pDevExt->pStrmEx = NULL; 
             pSrb->Status = STATUS_UNSUCCESSFUL;
             return;
         }


         //
         // Figure out what format they're trying to open first
         //

         if (!AdapterVerifyFormat (pKSDataFormat, pSrb->StreamObject->StreamNumber)) {
             pDevExt->pStrmEx = NULL; 
             ERROR_LOG(("DCamOpenStream: AdapterVerifyFormat failed.\n"));
             pSrb->Status = STATUS_INVALID_PARAMETER;
             return;
         }

         InitializeStreamExtension(pDevExt, pSrb->StreamObject, pStrmEx);

         // It should already been freed by DCamCloseStream()
         DCAM_ASSERT(pStrmEx->pVideoInfoHeader == NULL);
         DCAM_ASSERT(pVideoInfoHdrRequested != (PKS_VIDEOINFOHEADER) 0);

         // Use this instead of sizeof(KS_VIDEOINFOHEADER) to handle variable size structure
         nSize = KS_SIZE_VIDEOHEADER (pVideoInfoHdrRequested);

         pStrmEx->pVideoInfoHeader = ExAllocatePoolWithTag(NonPagedPool, nSize, 'macd');
         if (pStrmEx->pVideoInfoHeader == NULL) {

             ERROR_LOG(("DCamOpenStream: ExAllocatePool (->pVideoInfoHeader) failed!\n"));
             DCAM_ASSERT(pStrmEx->pVideoInfoHeader != NULL);

             pDevExt->pStrmEx = NULL;

             pSrb->Status = STATUS_INSUFFICIENT_RESOURCES;
             return;
         }

         // Copy the VIDEOINFOHEADER requested to our storage
         RtlCopyMemory(
                    pStrmEx->pVideoInfoHeader,
                    pVideoInfoHdrRequested,
                    nSize);

         DbgMsg3(("\'DCamOpenStream: Copied biSizeImage=%d Duration=%ld (100ns)\n", 
                    pStrmEx->pVideoInfoHeader->bmiHeader.biSizeImage, (DWORD) pStrmEx->pVideoInfoHeader->AvgTimePerFrame));

         // Allocate ISOCH resource
         pSrb->Status = DCamAllocateIsochResource(pDevExt, pSrb->SRBExtension, TRUE);               
         
         if (pSrb->Status) {

             ERROR_LOG(("DCamOpenStream: !!!! Allocate ISOCH resource failed.  CanNOT STREAM!!!!!\n"));
             
             ExFreePool(pStrmEx->pVideoInfoHeader);
             pStrmEx->pVideoInfoHeader = NULL;             
             pDevExt->pStrmEx = NULL;
             pSrb->Status = STATUS_INSUFFICIENT_RESOURCES;             
             return;
         } 

         pSrb->StreamObject->ReceiveDataPacket    = (PVOID) DCamReceiveDataPacket;
         pSrb->StreamObject->ReceiveControlPacket = (PVOID) DCamReceiveCtrlPacket;

         // If bus reset failed and user close the stream and reopen the stream successfully,
         // This must be reset !!
         if(pDevExt->bDevRemoved || pDevExt->bStopIsochCallback) {
            DbgMsg1(("Stream Open successful, reset bDevRemoved and bStopCallback!!\n"));
            pDevExt->bStopIsochCallback = FALSE;
            pDevExt->bDevRemoved = FALSE;
         }

         //
         // initialize the stream extension data handling information
         //

         break;

    default:
         ERROR_LOG(("DCamOpenStream: Hit a non-support pSrb->StreamObject->StreamNumber (%d).\n", pSrb->StreamObject->StreamNumber));
         DCAM_ASSERT(FALSE);
         pDevExt->pStrmEx = NULL; 
         pSrb->Status = STATUS_INVALID_PARAMETER;
         return;
    }


    pSrb->StreamObject->HwClockObject.ClockSupportFlags = 0;

    // We don't use DMA.
    pSrb->StreamObject->Dma = FALSE;
    pSrb->StreamObject->StreamHeaderMediaSpecific = sizeof(KS_FRAME_INFO);

    //
    // The PIO flag must be set when the mini driver will be accessing the data
    // buffers passed in using logical addressing.  We are not going to touch these 
    // buffer at all.
    //
    pSrb->StreamObject->Pio = FALSE;


    //
    // Set to last saved configuration
    //
    SetCurrentDevicePropertyValues(pDevExt, (PIRB) pSrb->SRBExtension);


    DbgMsg1((" #OPEN_STREAM#: %s DCam, Status %x, pDevExt %x, pStrmEx %x, IsochDescriptorList is at %x\n", 
              pDevExt->pchVendorName, pSrb->Status, pDevExt, pDevExt->pStrmEx, &pDevExt->IsochDescriptorList));

    DCAM_ASSERT(pSrb->Status == STATUS_SUCCESS);

}




VOID
DCamCloseStream(
    IN PHW_STREAM_REQUEST_BLOCK pSrb
    )
/*++

Routine Description:

    Called when an CloseStream Srb request is received.  We get this when calling user 
    application do a CloseHandle() on the pin connection handle.  This can happen after
    HwUninitialize().

Arguments:

    pSrb - Pointer to Stream request block

Return Value:

    Nothing

--*/
{
    PDCAM_EXTENSION pDevExt;
    PSTREAMEX     pStrmEx;
    PIRB pIrb;

    PAGED_CODE();

    pSrb->Status = STATUS_SUCCESS;

    pDevExt = (PDCAM_EXTENSION) pSrb->HwDeviceExtension;
    DCAM_ASSERT(pDevExt);      
    if(!pDevExt) {
        StreamClassDeviceNotification(DeviceRequestComplete, pSrb->HwDeviceExtension, pSrb);
        return;
    }

    pStrmEx = (PSTREAMEX)pDevExt->pStrmEx;
    DCAM_ASSERT(pStrmEx);
    if(!pStrmEx ) {
        StreamClassDeviceNotification(DeviceRequestComplete, pSrb->HwDeviceExtension, pSrb);
        return;    
    } 

    //
    // pDevExt->Irb might have been freed in HwUninitialize() 
    // due to Surprise removal; so we must use this:
    // 
    pIrb = (PIRB) pSrb->SRBExtension;


    //
    // If it is still in use (setting it to stop failed?),
    // we will diable ISO_ENABLE so other application can use it.
    //

    if(!pDevExt->bDevRemoved && 
       DCamDeviceInUse(pIrb, pDevExt)) {
        DCamRegArea RegArea;
        NTSTATUS status;

        RegArea.AsULONG = STOP_ISOCH_TRANSMISSION;
        status = DCamWriteRegister(pIrb, pDevExt, FIELDOFFSET(CAMERA_REGISTER_MAP, IsoEnable), RegArea.AsULONG);
        ERROR_LOG(("DCamCloseStream: Is still in use! Status %x, ISO_ENABLE %x\n", status, RegArea.AsULONG));
    }


    //
    // Save current state and free resource alllocaed in OpenStream()
    //
    SetPropertyValuesToRegistry(pDevExt);


    //
    // Free Isoch resource and master clock
    //

    DCamFreeIsochResource (pDevExt, pIrb, TRUE);
    if(pStrmEx->pVideoInfoHeader) {
        ExFreePool(pStrmEx->pVideoInfoHeader);
        pStrmEx->pVideoInfoHeader = NULL;
    }

    pStrmEx->hMasterClock = 0;
   

    //                                                 
    // If there are pening read, cancel them all.                                
    //
    if(pDevExt->PendingReadCount > 0) {

        DCamCancelAllPackets(
            pSrb,
            pDevExt,
            &pDevExt->PendingReadCount
            );
        
        pDevExt->pStrmEx = 0;

        return;  // SRB completed in CancelAllPackets       
    }
    
    pDevExt->pStrmEx = 0;

    StreamClassDeviceNotification(DeviceRequestComplete, pSrb->HwDeviceExtension, pSrb);

}




VOID
DCamTimeoutHandler(
    IN PHW_STREAM_REQUEST_BLOCK pSrb
    )
/*++

Routine Description:

    This routine is called when a packet has been in the minidriver too long (Srb->TimeoutCounter == 0).
    We will cancel the SRB if we are in the RUN state; else set ->TimeoutCounter and return.
    We assume the cancel SRB is serialized and in the same order as it is read.  So this timeout is
    applying to the head of the queue.

Arguments:

    pSrb - Pointer to Stream request block that has timeout.

Return Value:

    Nothing

--*/

{
    PDCAM_EXTENSION pDevExt;
    PSTREAMEX pStrmEx;

    // Called from StreamClass at DisptchLevel


    //
    // We only expect stream SRB, but not device SRB.  
    //

    if ( (pSrb->Flags & SRB_HW_FLAGS_STREAM_REQUEST) != SRB_HW_FLAGS_STREAM_REQUEST) {
        ERROR_LOG(("DCamTimeoutHandler: Device SRB %x timed out!\n", pSrb));
        DCAM_ASSERT( (pSrb->Flags & SRB_HW_FLAGS_STREAM_REQUEST) == SRB_HW_FLAGS_STREAM_REQUEST );
        return;
    } 


    //
    // StreamSRB only valid if we have a stream extension
    //

    pDevExt = (PDCAM_EXTENSION) pSrb->HwDeviceExtension;
    DCAM_ASSERT(pDevExt);
    pStrmEx = (PSTREAMEX) pDevExt->pStrmEx;

    if(!pStrmEx) {
        ERROR_LOG(("DCamTimeoutHandler: Stream SRB %x timeout with pDevExt %x, pStrmEx %x\n", pSrb, pDevExt, pStrmEx));
        DCAM_ASSERT(pStrmEx);
        return;
    }
 
    //
    // Cancel IRP only if in RUN state, BUT...
    // Note: if we are TIMEOUT and in RUN state, something is terribley wrong.  
    //       but I guess that can happen when it is being suspended;
    //       so we will extend the time out for all states.
    //

    DbgMsg2(("\'DCamTimeoutHandler: pSrb %x, %s state, PendingReadCount %d.\n", 
        pSrb, 
        pStrmEx->KSState == KSSTATE_RUN   ? "RUN" : 
        pStrmEx->KSState == KSSTATE_PAUSE ? "PAUSE":
        pStrmEx->KSState == KSSTATE_STOP  ? "STOP": "Unknown",
        pDevExt->PendingReadCount));   

    DCAM_ASSERT(pStrmEx->KSState == KSSTATE_PAUSE);


    //
    // Reset Timeout counter, or we are going to get this call immediately.
    //

    pSrb->TimeoutCounter = pSrb->TimeoutOriginal;

}


NTSTATUS
DCamStartListenCR(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP pIrp,
    IN PDCAM_IO_CONTEXT pDCamIoContext    
    )

/*++

Routine Description:

    Returns more processing required so the IO Manager will leave us alone

Arguments:

    DriverObject - Pointer to driver object created by system.

    pIrp - Irp that just completed

    pDCamIoContext - Context 

Return Value:

    None.

--*/

{
    PDCAM_EXTENSION pDevExt;
    NTSTATUS Status;
    PIRB pIrb; 
    PIO_STACK_LOCATION NextIrpStack;

#ifdef WAIT_FOR_SLOW_DEVICE
    KeStallExecutionProcessor(5000);  // 5 msec
#endif

    DbgMsg2(("\'DCamStartListenCR: pIrp->IoStatus.Status=%x\n", pIrp->IoStatus.Status));

    if(STATUS_SUCCESS != pIrp->IoStatus.Status) {

        pDevExt = pDCamIoContext->pDevExt;
        pIrb = pDCamIoContext->pIrb;

        if(pDevExt->lRetries > 0) {

            pDevExt->lRetries--;
            DbgMsg1(("DCamStartListenCR: Try DCAM_RUNSTATE_SET_REQUEST_ISOCH_LISTEN again!\n"));
            
            pIrb->FunctionNumber = REQUEST_ISOCH_LISTEN;
            pIrb->Flags = 0;
            pIrb->u.IsochListen.hResource = pDevExt->hResource;
            pIrb->u.IsochListen.fulFlags = 0;

            NextIrpStack = IoGetNextIrpStackLocation(pIrp);
            NextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
            NextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_1394_CLASS;
            NextIrpStack->Parameters.Others.Argument1 = pIrb;

            IoSetCompletionRoutine(
                pIrp,
                DCamStartListenCR,
                pDCamIoContext,
                TRUE,
                TRUE,
                TRUE
                );

            Status =
                IoCallDriver(
                    pDevExt->BusDeviceObject, 
                    pIrp);

            return STATUS_MORE_PROCESSING_REQUIRED;

        } else {
            ERROR_LOG(("Start Listening has failed Status=%x; try again in next read.\n", pIrp->IoStatus.Status)); 
            pDCamIoContext->pDevExt->bNeedToListen = TRUE;
        }
    }

    DCamFreeIrbIrpAndContext(pDCamIoContext, pDCamIoContext->pIrb, pIrp);

    // No StreamClassDeviceNotification() here since 
    // this is local initiated Irb (as part of AttachBufferCR().

    return STATUS_MORE_PROCESSING_REQUIRED;

}





/*
** AdapterCompareGUIDsAndFormatSize()
**
**   Checks for a match on the three GUIDs and FormatSize
**
** Arguments:
**
**         IN DataRange1
**         IN DataRange2
**
** Returns:
** 
**   TRUE if all elements match
**   FALSE if any are different
**
** Side Effects:  none
*/

BOOL 
AdapterCompareGUIDsAndFormatSize(
    IN PKSDATARANGE DataRange1,
    IN PKSDATARANGE DataRange2)
{
    PAGED_CODE();

    return (
        IsEqualGUID (
            &DataRange1->MajorFormat, 
            &DataRange2->MajorFormat) &&
        IsEqualGUID (
            &DataRange1->SubFormat, 
            &DataRange2->SubFormat) &&
        IsEqualGUID (
            &DataRange1->Specifier, 
            &DataRange2->Specifier) &&
        (DataRange1->FormatSize == DataRange2->FormatSize));
}

/*
** AdapterVerifyFormat()
**
**   Checks the validity of a format request by walking through the
**       array of supported PKSDATARANGEs for a given stream.
**
** Arguments:
**
**   pKSDataFormatVideoToVerify - pointer of a KS_DATAFORMAT_VIDEOINFOHEADER structure.
**   StreamNumber - index of the stream being queried / opened.
**
** Returns:
** 
**   TRUE if the format is supported
**   FALSE if the format cannot be suppored
**
** Side Effects:  none
*/

BOOL 
AdapterVerifyFormat(
    PKS_DATAFORMAT_VIDEOINFOHEADER pDataFormatVideoToVerify, 
    int StreamNumber)
{
    PKS_VIDEOINFOHEADER         pVideoInfoHdrToVerify = &pDataFormatVideoToVerify->VideoInfoHeader;
    PKSDATAFORMAT               *paDataFormatsVideoAvail;  // an array of PKSDATAFORMAT (not PKS_DATARANGE_VIDEO !!)
    PKS_DATARANGE_VIDEO         pDataRangeVideo;
    KS_VIDEO_STREAM_CONFIG_CAPS *pConfigCaps; 
    PKS_BITMAPINFOHEADER        pbmiHeader, 
                                pbmiHeaderToVerify;
    int                         j;

    PAGED_CODE();
    
    //
    // Make sure the stream index is valid
    // We only has one capure pin/stream (index 0).
    //
    if (StreamNumber >= 1) {
        return FALSE;
    }

    //
    // Get the pointer to the array of available formats
    //
    paDataFormatsVideoAvail = &DCAM_StreamFormats[0];

    //
    // Walk the array, searching for a match
    //
    for (j = 0; j < NUM_DCAM_STREAM_FORMATS; j++, paDataFormatsVideoAvail++) {

        pDataRangeVideo = (PKS_DATARANGE_VIDEO) *paDataFormatsVideoAvail;
        
        //
        // Check for matching size, Major Type, Sub Type, and Specifier
        //

        //
        // Check for matching size, Major Type, Sub Type, and Specifier
        //

        if (!IsEqualGUID (&pDataRangeVideo->DataRange.MajorFormat, 
            &pDataFormatVideoToVerify->DataFormat.MajorFormat)) {
               DbgMsg2(("\'%d) AdapterVerifyFormat: MajorFormat mismatch!\n", j));
               continue;
        }

        if (!IsEqualGUID (&pDataRangeVideo->DataRange.SubFormat, 
            &pDataFormatVideoToVerify->DataFormat.SubFormat)) {
               DbgMsg2(("\'%d) AdapterVerifyFormat: SubFormat mismatch!\n", j));
               continue;
        }

        if (!IsEqualGUID (&pDataRangeVideo->DataRange.Specifier,
            &pDataFormatVideoToVerify->DataFormat.Specifier)) {
               DbgMsg2(("\'%d) AdapterVerifyFormat: Specifier mismatch!\n", j));
               continue;
        }


        //
        // Only if we get here, we are certain that we are dealing with video info.
        //

        // We do not support scaling or cropping so the dimension 
        // (biWidth, biHeight, biBitCount and biCompression)
        // must match.
        // 
        pbmiHeader         = &pDataRangeVideo->VideoInfoHeader.bmiHeader;
        pbmiHeaderToVerify = &pDataFormatVideoToVerify->VideoInfoHeader.bmiHeader;

        if(pbmiHeader->biWidth       != pbmiHeaderToVerify->biWidth    ||
           pbmiHeader->biHeight      != pbmiHeaderToVerify->biHeight   ||
           pbmiHeader->biBitCount    != pbmiHeaderToVerify->biBitCount ||
           pbmiHeader->biCompression != pbmiHeaderToVerify->biCompression
           ) {

            DbgMsg2(("AdapterVerifyFormat: Supported: %dx%dx%d [%x] != ToVerify: %dx%dx%d [%x]\n",
                    pbmiHeader->biWidth, pbmiHeader->biHeight,  pbmiHeader->biBitCount, pbmiHeader->biCompression,
                    pbmiHeaderToVerify->biWidth, pbmiHeaderToVerify->biHeight,  pbmiHeaderToVerify->biBitCount, pbmiHeaderToVerify->biCompression));
            continue;
        }

        // biSizeImage must be to be BIG ENOUGH
        if(pbmiHeaderToVerify->biSizeImage < pbmiHeader->biSizeImage) {

            DbgMsg2(("AdapterVerifyFormat: biSizeImageToVerify %d < required %x\n", 
                pbmiHeaderToVerify->biSizeImage, pbmiHeader->biSizeImage));
            continue;
        }

        // Frame rate needs to be within range
        pConfigCaps = &pDataRangeVideo->ConfigCaps;
        if(pDataFormatVideoToVerify->VideoInfoHeader.AvgTimePerFrame > pConfigCaps->MaxFrameInterval &&
           pDataFormatVideoToVerify->VideoInfoHeader.AvgTimePerFrame < pConfigCaps->MinFrameInterval) {

           DbgMsg2(("\'format index %d) AdapterVerifyFormat: Frame rate %ld is not within range(%ld, %ld)!\n", 
              j, pDataFormatVideoToVerify->VideoInfoHeader.AvgTimePerFrame,
              pConfigCaps->MaxFrameInterval, pConfigCaps->MinFrameInterval));
           continue;
        }


        //
        // The format passed all of the tests, so we support it
        //

        DbgMsg2(("\'(format idx %d) AdapterVerifyFormat: Verify!! Width=%d, Height=%d, biBitCount=%d, biSizeImage=%d\n", j,
            pbmiHeaderToVerify->biWidth, pbmiHeaderToVerify->biHeight, pbmiHeaderToVerify->biBitCount,pbmiHeaderToVerify->biSizeImage));
        DbgMsg2(("AdapterVerifyFormat: AvgTimePerFrame = %ld\n", pDataFormatVideoToVerify->VideoInfoHeader.AvgTimePerFrame));
        DbgMsg2(("AdapterVerifyFormat: (Max %ld\n", pConfigCaps->MaxFrameInterval));
        DbgMsg2(("AdapterVerifyFormat:               Min %ld)\n", pConfigCaps->MinFrameInterval));

        return TRUE;
    } 

    //
    // The format requested didn't match any of our listed ranges,
    // so refuse the connection.
    //
    DbgMsg2(("AdapterVerifyFormat: This format is not supported!\n"));

    return FALSE;
}



/*
** AdapterFormatFromRange()
**
**   Examine the given data format with many key fields and 
**   return a complete data format that can be used to open a stream.
**
** Arguments:
**
**   IN PHW_STREAM_REQUEST_BLOCK Srb 
**
** Returns:
** 
**   TRUE if the format is supported
**   FALSE if the format cannot be suppored
**
** Side Effects:  none
*/
BOOL 
AdapterFormatFromRange(
    IN PHW_STREAM_REQUEST_BLOCK Srb)
{
    PDCAM_EXTENSION             pDevExt = (PDCAM_EXTENSION) Srb->HwDeviceExtension;
    PSTREAM_DATA_INTERSECT_INFO IntersectInfo;
    PKSDATARANGE                DataRange,
                                *pAvailableFormats;  // KSDATARANGE == KSDATAFORMAT
    PKS_DATARANGE_VIDEO         DataRangeVideoToVerify,
                                DataRangeVideo;
    PKS_BITMAPINFOHEADER        pbmiHeader, 
                                pbmiHeaderToVerify;
    ULONG                       FormatSize;
    BOOL                        MatchFound = FALSE;
    ULONG                       j;


    PAGED_CODE();

    Srb->Status = STATUS_SUCCESS;
    IntersectInfo = Srb->CommandData.IntersectInfo;
    DataRange = IntersectInfo->DataRange;
    DbgMsg2(("IntersectIfo->DataFormatBuffer=%x, size=%d\n", IntersectInfo->DataFormatBuffer, IntersectInfo->SizeOfDataFormatBuffer));


    //
    // Check that the stream number is valid
    // We support only one capture pin/stream (index 0)
    //

    if (IntersectInfo->StreamNumber >= 1) {

        Srb->Status = STATUS_NOT_IMPLEMENTED;
        ERROR_LOG(("\'AdapterFormatFromRange: StreamNumber(=%d) is not implemented.\n", IntersectInfo->StreamNumber));
        DCAM_ASSERT(FALSE);
        return FALSE;
    }


    //
    // Get the pointer to the array of available formats
    //

    pAvailableFormats = &DCAM_StreamFormats[0];


    //
    // Walk the formats supported by the stream searching for a match
    // of the three GUIDs which together define a DATARANGE
    //
    
    DataRangeVideoToVerify = (PKS_DATARANGE_VIDEO) DataRange;

    for (j = 0; j < NUM_DCAM_STREAM_FORMATS; j++, pAvailableFormats++) {
       
        DataRangeVideo = (PKS_DATARANGE_VIDEO) *pAvailableFormats;

        //
        // STREAM_DATA_INTERSECT_INFO
        //  [IN]   ULONG        StreamNumber;
        //  [IN]   PKSDATARANGE DataRange;   
        //  [OUT]  PVOID        DataFormatBuffer;   // == PKS_DATAFORMAT_VIDEOINFOHEADER
        //  [OUT]  ULONG        SizeOfDataFormatBuffer;
        //
        
        //
        // KS_DATAFORMAT_VIDEOINFOHEADER:
        //    fields marked with 'm' must match; 
        //           marked with 'r' must within range;
        //           marked with 'f' is filled by us
        //
        //     KSDATAFORMAT == KSDATARANGE
        //       m ULONG   FormatSize;
        //         ULONG   Flags;
        //         ULONG   SampleSize;
        //         ULONG   Reserved;
        //       m GUID    MajorFormat;
        //       m GUID    SubFormat;
        //       m GUID    Specifier;.
        //  m  BOOL                         bFixedSizeSamples;      // all samples same size?
        //  m  BOOL                         bTemporalCompression;   // all I frames?
        //  m  DWORD                        StreamDescriptionFlags; // KS_VIDEO_DESC_*
        //  m  DWORD                        MemoryAllocationFlags;  // KS_VIDEO_ALLOC_*
        //  m  KS_VIDEO_STREAM_CONFIG_CAPS  ConfigCaps;
        //     KS_VIDEOINFOHEADER 
        //         RECT                rcSource;          // The bit we really want to use
        //         RECT                rcTarget;          // Where the video should go
        //         DWORD               dwBitRate;         // Approximate bit data rate
        //         DWORD               dwBitErrorRate;    // Bit error rate for this stream
        //     r/f REFERENCE_TIME      AvgTimePerFrame;   // Average time per frame (100ns units)
        //         KS_BITMAPINFOHEADER bmiHeader;
        //             DWORD      biSize;
        //       m     LONG       biWidth;
        //       m     LONG       biHeight;
        //             WORD       biPlanes;
        //       m     WORD       biBitCount;
        //       m     DWORD      biCompression;
        //       f     DWORD      biSizeImage;
        //             LONG       biXPelsPerMeter;
        //             LONG       biYPelsPerMeter;
        //             DWORD      biClrUsed;
        //             DWORD      biClrImportant;
        //     

        // Verify that it is a VIDEO format/range.
        if (!AdapterCompareGUIDsAndFormatSize((PKSDATARANGE)DataRangeVideoToVerify, (PKSDATARANGE)DataRangeVideo)) {
            continue;
        }
    
        //
        // It is valid video format/range; now check that the other fields match
        //
        if ((DataRangeVideoToVerify->bFixedSizeSamples      != DataRangeVideo->bFixedSizeSamples)      ||
            (DataRangeVideoToVerify->bTemporalCompression   != DataRangeVideo->bTemporalCompression)   ||
            (DataRangeVideoToVerify->StreamDescriptionFlags != DataRangeVideo->StreamDescriptionFlags) ||
            (DataRangeVideoToVerify->MemoryAllocationFlags  != DataRangeVideo->MemoryAllocationFlags)  ||
            (RtlCompareMemory (&DataRangeVideoToVerify->ConfigCaps, &DataRangeVideo->ConfigCaps, sizeof(KS_VIDEO_STREAM_CONFIG_CAPS)) != sizeof(KS_VIDEO_STREAM_CONFIG_CAPS))) {

            continue;
        }

        //
        // We do not support scaling or cropping so the dimension 
        // (biWidth, biHeight, biBitCount and biCompression)
        // must match, and we will filled in the biSizeImage and others.
        // 
        pbmiHeader         = &DataRangeVideo->VideoInfoHeader.bmiHeader;
        pbmiHeaderToVerify = &DataRangeVideoToVerify->VideoInfoHeader.bmiHeader;

        if(pbmiHeader->biWidth       != pbmiHeaderToVerify->biWidth    ||
           pbmiHeader->biHeight      != pbmiHeaderToVerify->biHeight   ||
           pbmiHeader->biBitCount    != pbmiHeaderToVerify->biBitCount ||
           pbmiHeader->biCompression != pbmiHeaderToVerify->biCompression
           ) {

            DbgMsg1(("AdapterFormatFromRange: Supported: %dx%dx%d [%x] != ToVerify: %dx%dx%d [%x]\n",
                    pbmiHeader->biWidth, pbmiHeader->biHeight,  pbmiHeader->biBitCount, pbmiHeader->biCompression,
                    pbmiHeaderToVerify->biWidth, pbmiHeaderToVerify->biHeight,  pbmiHeaderToVerify->biBitCount, pbmiHeaderToVerify->biCompression));
            continue;
        }


        // MATCH FOUND!
        MatchFound = TRUE; 
        


        // KS_DATAFORMAT_VIDEOINFOHEADER
        //    KSDATAFORMAT            DataFormat;
        //    KS_VIDEOINFOHEADER      VideoInfoHeader;
        FormatSize = sizeof (KSDATAFORMAT) +  KS_SIZE_VIDEOHEADER (&DataRangeVideo->VideoInfoHeader);

        //    
        // 1st query:  Srb->ActualBytesTransferred = FormatSize
        //

        if(IntersectInfo->SizeOfDataFormatBuffer == 0) {

            Srb->Status = STATUS_BUFFER_OVERFLOW;
            // We actually have not returned this much data,
            // this "size" will be used by Ksproxy to send down 
            // a buffer of that size in next query.
            Srb->ActualBytesTransferred = FormatSize;
            break;
        }


        //
        // 2nd time: pass back the format information
        //

        if (IntersectInfo->SizeOfDataFormatBuffer < FormatSize) {
            Srb->Status = STATUS_BUFFER_TOO_SMALL;
            DbgMsg2(("IntersectInfo->SizeOfDataFormatBuffer=%d, FormatSize=%d\n", IntersectInfo->SizeOfDataFormatBuffer, FormatSize));
            return FALSE;
        }

        //
        // A match is found,  Copy from our supported/matched data range and set frame rate:
        // KS_DATAFORMAT_VIDEOINFOHEADER
        //    KSDATAFORMAT            DataFormat;
        //    KS_VIDEOINFOHEADER      VideoInfoHeader;
        //
        
        RtlCopyMemory(
            &((PKS_DATAFORMAT_VIDEOINFOHEADER)IntersectInfo->DataFormatBuffer)->DataFormat,
            &DataRangeVideo->DataRange,
            sizeof (KSDATAFORMAT));

        RtlCopyMemory(
            &((PKS_DATAFORMAT_VIDEOINFOHEADER) IntersectInfo->DataFormatBuffer)->VideoInfoHeader,  // KS_VIDEOINFOHEADER
            &DataRangeVideo->VideoInfoHeader,                                                      // KS_VIDEOINFOHEADER
            KS_SIZE_VIDEOHEADER (&DataRangeVideoToVerify->VideoInfoHeader));  // Use KS_SIZE_VIDEOHEADER() since this is variable size       

        //
        // Special atttention to these two fields: biSizeImage and AvgTimePerFrame.
        // We do not scale or stretch so biSizeImage is fixed.
        // However, AvgTimePerFrame (FrameRate) can/need to be within (ConfigCaps.MinFrameInterval, ConfigCaps.MaxFrameInterval)
        //

        if (DataRangeVideoToVerify->VideoInfoHeader.AvgTimePerFrame > DataRangeVideoToVerify->ConfigCaps.MaxFrameInterval ||      
            DataRangeVideoToVerify->VideoInfoHeader.AvgTimePerFrame < DataRangeVideoToVerify->ConfigCaps.MinFrameInterval) {
         
            ((PKS_DATAFORMAT_VIDEOINFOHEADER) IntersectInfo->DataFormatBuffer)->VideoInfoHeader.AvgTimePerFrame = 
                 DataRangeVideo->VideoInfoHeader.AvgTimePerFrame;
            DbgMsg2(("AdapterFormatFromRange: out of range; so set it to default (%ld)\n",DataRangeVideo->VideoInfoHeader.AvgTimePerFrame));

        } else {

            ((PKS_DATAFORMAT_VIDEOINFOHEADER) IntersectInfo->DataFormatBuffer)->VideoInfoHeader.AvgTimePerFrame = 
                  DataRangeVideoToVerify->VideoInfoHeader.AvgTimePerFrame;
        }

        ((PKSDATAFORMAT)IntersectInfo->DataFormatBuffer)->FormatSize = FormatSize;
        Srb->ActualBytesTransferred = FormatSize;

        DbgMsg2(("AdapterFormatFromRange: match found: [%x], %dx%dx%d=%d, AvgTimePerFrame %ld\n",
                pbmiHeader->biCompression, pbmiHeader->biWidth, pbmiHeader->biHeight,  pbmiHeader->biBitCount, pbmiHeader->biSizeImage,
                ((PKS_DATAFORMAT_VIDEOINFOHEADER) IntersectInfo->DataFormatBuffer)->VideoInfoHeader.AvgTimePerFrame));
        break;

    } // End of loop on all formats for this stream

    if(!MatchFound) {

        DbgMsg2(("AdapterFormatFromRange: No match !!\n"));
        Srb->Status = STATUS_NO_MATCH;
        return FALSE;
    }

    return TRUE;
}


/*
** VideoGetProperty()
**
**    Routine to process video property requests
**
** Arguments:
**
**    Srb - pointer to the stream request block for properties
**
** Returns:
**
** Side Effects:  none
*/

VOID 
VideoGetProperty(
    PHW_STREAM_REQUEST_BLOCK Srb)
{
    PSTREAM_PROPERTY_DESCRIPTOR pSPD = Srb->CommandData.PropertyInfo;


    // preset to success

    Srb->Status = STATUS_SUCCESS;

    if (IsEqualGUID (&KSPROPSETID_Connection, &pSPD->Property->Set)) {
        VideoStreamGetConnectionProperty (Srb);
    } else if (IsEqualGUID (&PROPSETID_VIDCAP_DROPPEDFRAMES, &pSPD->Property->Set)) {
        VideoStreamGetDroppedFramesProperty (Srb);
    } else {
        Srb->Status = STATUS_NOT_IMPLEMENTED;
    }

}


/*
** VideoGetState()
**
**    Gets the current state of the requested stream
**
** Arguments:
**
**    Srb - pointer to the stream request block for properties
**
** Returns:
**
** Side Effects:  none
*/

VOID 
VideoGetState(
    PHW_STREAM_REQUEST_BLOCK Srb)
{
    PDCAM_EXTENSION pDevExt = (PDCAM_EXTENSION) Srb->HwDeviceExtension;
    PSTREAMEX pStrmEx = pDevExt->pStrmEx;

    PAGED_CODE();

    DbgMsg2(("\'%d:%s) VideoGetState: KSSTATE=%s.\n", 
          pDevExt->idxDev, pDevExt->pchVendorName, 
          pStrmEx->KSState == KSSTATE_STOP ? "STOP" : 
          pStrmEx->KSState == KSSTATE_PAUSE ? "PAUSE" :     
          pStrmEx->KSState == KSSTATE_RUN ? "RUN" : "ACQUIRE"));

    Srb->CommandData.StreamState = pStrmEx->KSState;
    Srb->ActualBytesTransferred = sizeof (KSSTATE);

    // A very odd rule:
    // When transitioning from stop to pause, DShow tries to preroll
    // the graph.  Capture sources can't preroll, and indicate this
    // by returning VFW_S_CANT_CUE in user mode.  To indicate this
    // condition from drivers, they must return ERROR_NO_DATA_DETECTED

    Srb->Status = STATUS_SUCCESS;


    if (pStrmEx->KSState == KSSTATE_PAUSE) {
       Srb->Status = STATUS_NO_DATA_DETECTED;
    }
}


VOID  
VideoStreamGetConnectionProperty (
    PHW_STREAM_REQUEST_BLOCK Srb)
{
    PDCAM_EXTENSION pDevExt = (PDCAM_EXTENSION) Srb->HwDeviceExtension;
    PSTREAM_PROPERTY_DESCRIPTOR pSPD = Srb->CommandData.PropertyInfo;
    ULONG Id = pSPD->Property->Id;              // index of the property
    PSTREAMEX pStrmEx = (PSTREAMEX) pDevExt->pStrmEx;
    DCAM_ASSERT(pStrmEx == (PSTREAMEX)Srb->StreamObject->HwStreamExtension);

    PAGED_CODE();

    switch (Id) {

    case KSPROPERTY_CONNECTION_ALLOCATORFRAMING:

        if (pStrmEx->pVideoInfoHeader) {

            PKSALLOCATOR_FRAMING Framing = 
                (PKSALLOCATOR_FRAMING) pSPD->PropertyInfo;
            Framing->RequirementsFlags =
                KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY |
                KSALLOCATOR_REQUIREMENTF_INPLACE_MODIFIER |
                KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY;
            Framing->PoolType = PagedPool;
            Framing->Frames = MAX_BUFFERS_SUPPLIED; 
            Framing->FrameSize = pStrmEx->pVideoInfoHeader->bmiHeader.biSizeImage;
            Framing->FileAlignment = 4096-1; // Page aligned
            Framing->Reserved = 0;
            Srb->ActualBytesTransferred = sizeof (KSALLOCATOR_FRAMING);
            Srb->Status = STATUS_SUCCESS;
               DbgMsg2(("\'VideoStreamGetConnectionProperty: status=0x%x, Alignment %d, Frame %d, FrameSize %d\n",
                    Srb->Status, Framing->FileAlignment+1, Framing->Frames, Framing->FrameSize));

        } else {

            Srb->Status = STATUS_INVALID_PARAMETER;
            DbgMsg2(("\'VideoStreamGetConnectionProperty: status=0x\n",Srb->Status));
        }
        break;

    default:
        ERROR_LOG(("VideoStreamGetConnectionProperty: Unsupported property id=%d\n",Id));
        DCAM_ASSERT(FALSE);
        break;
    }
}

/*
** VideoStreamGetConnectionProperty()
**
**    Gets the current state of the requested stream
**
** Arguments:
**
**    pSrb - pointer to the stream request block for properties
**
** Returns:
**
** Side Effects:  none
*/

VOID
VideoStreamGetDroppedFramesProperty(
    PHW_STREAM_REQUEST_BLOCK Srb
    )
{
    PSTREAMEX pStrmEx = (PSTREAMEX)Srb->StreamObject->HwStreamExtension;
    PSTREAM_PROPERTY_DESCRIPTOR pSPD = Srb->CommandData.PropertyInfo;
    ULONG Id = pSPD->Property->Id;              // index of the property
    ULONGLONG tmStream;

    PAGED_CODE();

    switch (Id) {

    case KSPROPERTY_DROPPEDFRAMES_CURRENT:
         {

         PKSPROPERTY_DROPPEDFRAMES_CURRENT_S pDroppedFrames = 
                     (PKSPROPERTY_DROPPEDFRAMES_CURRENT_S) pSPD->PropertyInfo;

         if (pStrmEx->hMasterClock) {
                    
             tmGetStreamTime(Srb, pStrmEx, &tmStream);

             if (tmStream < pStrmEx->FirstFrameTime) {
                 DbgMsg2(("\'*DroppedFP: Tm(%dms) < 1stFrameTm(%d)\n",
                           (LONG) tmStream/10000, (LONG)pStrmEx->FirstFrameTime));
                 pDroppedFrames->DropCount = 0;
             } else {
                 pDroppedFrames->DropCount = (tmStream - pStrmEx->FirstFrameTime)
                                / pStrmEx->pVideoInfoHeader->AvgTimePerFrame + 1 - pStrmEx->FrameCaptured;
             }

             if (pDroppedFrames->DropCount < 0)
                 pDroppedFrames->DropCount = 0;
                    
         } else {
             pDroppedFrames->DropCount = 0;
         }

         // Update our drop frame here. "pDroppedFrames->DropCount" is return when a frame is returned. 
         if (pDroppedFrames->DropCount > pStrmEx->FrameInfo.DropCount) {
             pStrmEx->FrameInfo.DropCount = pDroppedFrames->DropCount;
             //pStrmEx->bDiscontinue = TRUE;
         } else {
             pDroppedFrames->DropCount = pStrmEx->FrameInfo.DropCount;
         }

         pDroppedFrames->AverageFrameSize = pStrmEx->pVideoInfoHeader->bmiHeader.biSizeImage;
         pDroppedFrames->PictureNumber = pStrmEx->FrameCaptured + pDroppedFrames->DropCount;

         // Correction if no picture has been successfully capture in the IsochCallback.
         if (pDroppedFrames->PictureNumber < pDroppedFrames->DropCount)
             pDroppedFrames->PictureNumber = pDroppedFrames->DropCount;

         DbgMsg2(("\'*DroppedFP: tm(%d); Pic#(%d)=?Cap(%d)+Drp(%d)\n",
                  (ULONG) tmStream/10000,
                  (LONG) pDroppedFrames->PictureNumber,
                  (LONG) pStrmEx->FrameCaptured,
                  (LONG) pDroppedFrames->DropCount));
               
         Srb->ActualBytesTransferred = sizeof (KSPROPERTY_DROPPEDFRAMES_CURRENT_S);
               Srb->Status = STATUS_SUCCESS;

         }
         break;

    default:
        ERROR_LOG(("VideoStreamGetDroppedFramesProperty: Unsupported property id=%d\n",Id));
        DCAM_ASSERT(FALSE);
        break;
    }
}




VOID 
VideoIndicateMasterClock(
    PHW_STREAM_REQUEST_BLOCK Srb)
/*++

Routine Description:

    Assign a master clock for this stream.

Arguments:

    pSrb - Pointer to Stream request block

Return Value:

    Nothing

--*/
{


    PDCAM_EXTENSION pDevExt = (PDCAM_EXTENSION) Srb->HwDeviceExtension;
    PSTREAMEX pStrmEx = (PSTREAMEX) pDevExt->pStrmEx;

    PAGED_CODE();

    DCAM_ASSERT(pStrmEx == (PSTREAMEX)Srb->StreamObject->HwStreamExtension);

    pStrmEx->hMasterClock = Srb->CommandData.MasterClockHandle;

    DbgMsg2(("\'%d:%s)VideoIndicateMasterClock: hMasterClock = 0x%x\n", pDevExt->idxDev, pDevExt->pchVendorName, pStrmEx->hMasterClock));

}


VOID
DCamReceivePacket(
    IN PHW_STREAM_REQUEST_BLOCK pSrb
    )

/*++

Routine Description:

    This is where most of the interesting Stream requests come to us

Arguments:

    pSrb - Pointer to Stream request block

Return Value:

    Nothing

--*/

{
    PIO_STACK_LOCATION IrpStack;
    PDCAM_EXTENSION pDevExt = (PDCAM_EXTENSION) pSrb->HwDeviceExtension;


    PAGED_CODE();

    pSrb->Status = STATUS_SUCCESS;

    //
    // Switch on the command within the Srb itself
    //

    switch (pSrb->Command) {

    case SRB_INITIALIZE_DEVICE:     // Per device
          
         pSrb->Status = DCamHwInitialize(pSrb);
         break;

    case SRB_INITIALIZATION_COMPLETE:

         pSrb->Status = STATUS_NOT_IMPLEMENTED;
         break;

    case SRB_GET_STREAM_INFO:     // Per Device

         //
         // this is a request for the driver to enumerate requested streams
         //
         DCamGetStreamInfo(pSrb);
         break;

    case SRB_OPEN_STREAM:          // Per stream

         DCamOpenStream(pSrb);
         break;

    case SRB_CLOSE_STREAM:          // Per Stream
        DbgMsg1((" #CLOSE_STREAM# (%d) camera: pSrb %x, pDevExt %x, pStrmEx %x, PendingRead %d\n", 
              pDevExt->idxDev, pSrb, pDevExt, pDevExt->pStrmEx, pDevExt->PendingReadCount));
        DCamCloseStream(pSrb);
        return;       // SRB will finish asynchronously in its IoCompletionRoutine if there are pending reads to cancel.
     
    case SRB_SURPRISE_REMOVAL:

        DbgMsg1((" #SURPRISE_REMOVAL# (%d) camera: pSrb %x, pDevExt %x, pStrmEx %x, PendingRead %d\n", 
             pDevExt->idxDev, pSrb, pDevExt, pDevExt->pStrmEx, pDevExt->PendingReadCount));
        DCamSurpriseRemoval(pSrb);
        return;       // SRB will finish asynchronously in its IoCompletionRoutine.

    case SRB_UNKNOWN_DEVICE_COMMAND:

         //
         // We might be interested in unknown commands if they pertain
         // to bus resets.  We will reallocate resource (bandwidth and 
         // channel) if this device is streaming.
         //
         IrpStack = IoGetCurrentIrpStackLocation(pSrb->Irp);

         if (IrpStack->MajorFunction == IRP_MJ_PNP)
             DCamProcessPnpIrp(pSrb, IrpStack, pDevExt);
         else            
             pSrb->Status = STATUS_NOT_IMPLEMENTED;
         break;


    case SRB_UNINITIALIZE_DEVICE:     // Per device

         DbgMsg1((" #UNINITIALIZE_DEVICE# (%d) %s camera : pSrb %x, pDevExt %x, pStrmEx %x\n", 
              pDevExt->idxDev, pDevExt->pchVendorName, pSrb, pDevExt, pDevExt->pStrmEx));
         pSrb->Status = DCamHwUnInitialize(pSrb);
         break;

    case SRB_GET_DATA_INTERSECTION:

         //
         // Return a format, given a range
         //
         AdapterFormatFromRange(pSrb);
         StreamClassDeviceNotification(DeviceRequestComplete, pSrb->HwDeviceExtension, pSrb);
         return;

    case SRB_CHANGE_POWER_STATE:

         DCamChangePower(pSrb);
         break;
            
    // VideoProcAmp and CameraControl requests
    case SRB_GET_DEVICE_PROPERTY:

         AdapterGetProperty(pSrb);
         break;
          
    case SRB_SET_DEVICE_PROPERTY:
    
         AdapterSetProperty(pSrb);
         break;

    case SRB_PAGING_OUT_DRIVER:

         // Once we register bus reset, we can be called at any time;
         // So we cannot page out.
         pSrb->Status = STATUS_NOT_IMPLEMENTED;
         break;


    default:   

         DbgMsg1(("DCamReceivePacket: entry with unknown and unsupported SRB command 0x%x\n", pSrb->Command));
         //
         // this is a request that we do not understand.  Indicate invalid
         // command and complete the request
         //

         pSrb->Status = STATUS_NOT_IMPLEMENTED;
         break;
    }

    //
    // NOTE:
    //
    // all of the commands that we do, or do not understand can all be completed
    // synchronously at this point, so we can use a common callback routine here.
    // If any of the above commands require asynchronous processing, this will
    // have to change
    //

#if DBG
    if (pSrb->Status != STATUS_SUCCESS && 
        pSrb->Status != STATUS_NOT_IMPLEMENTED) {
        DbgMsg1(("pSrb->Command(0x%x) does not return STATUS_SUCCESS or NOT_IMPLEMENTED but 0x%x\n", pSrb->Command, pSrb->Status));
    }
#endif

    StreamClassDeviceNotification(DeviceRequestComplete, pSrb->HwDeviceExtension, pSrb);

}


