// $Header: G:/SwDev/WDM/Video/bt848/rcs/Capmain.c 1.19 1998/05/11 23:59:54 tomz Exp $

//==========================================================================;
//
//  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) 1992 - 1996  Microsoft Corporation.  All Rights Reserved.
//
//==========================================================================;

#define INITGUID
#define BT848_MEDIUMS

#ifdef __cplusplus
extern "C" {
#endif
#include "strmini.h"
#include "ksmedia.h"
#ifdef __cplusplus
}
#endif

#include "device.h"
#include "capmain.h"
#include "capstrm.h"
#include "capdebug.h"
#include "capprop.h"

LONG  PinTypes_ [MaxInpPins]; // just allocate maximum possible
DWORD xtals_ [2]; // no more than 2 xtals

extern PsDevice *gpPsDevice;
extern BYTE     *gpjBaseAddr;
extern VOID     *gpHwDeviceExtension;

void AdapterFormatFromRange( IN PHW_STREAM_REQUEST_BLOCK pSrb );
VOID ReadRegistryValues( IN PDEVICE_OBJECT PhysicalDeviceObject );
inline void CompleteDeviceSRB( IN OUT PHW_STREAM_REQUEST_BLOCK pSrb );

extern DWORD GetSizeHwDeviceExtension( );
extern DWORD GetSizeStreamEx( );
extern PsDevice *GetCurrentDevice( );
extern void SetCurrentDevice( PsDevice *dev );

extern BYTE *GetBase();
extern void SetBase(BYTE *base);

PHW_STREAM_REQUEST_BLOCK StreamIdxToSrb[4];

void CheckSrbStatus( PHW_STREAM_REQUEST_BLOCK pSrb )
{
   VideoStream StreamNumber = (VideoStream)pSrb->StreamObject->StreamNumber;

   DebugOut((1, "  *** completing pSrb(%x) strm(%d) status(%x)\n", pSrb, StreamNumber, pSrb->Status ));

   switch ( pSrb->Status )
   {
   case STATUS_SUCCESS:
   case STATUS_CANCELLED:
      break;
   default:
      DebugOut((0, "*** pSrb->Status = %x\n", pSrb->Status ));
      DEBUG_BREAKPOINT();
   }
}

/* Function: GetRequestedSize
 * Purpose: Figures out what the image size should be
 * Input: vidHdr: KS_VIDEOINFOHEADER &
 *   size: MSize &
 * Output: None
*/
void GetRequestedSize2( const KS_VIDEOINFOHEADER2 &vidHdr, MSize &size )
{
   Trace t("GetRequestedSize()");

   size.Set( vidHdr.bmiHeader.biWidth, abs(vidHdr.bmiHeader.biHeight) );

   MRect dst( vidHdr.rcTarget );
   // if writing to a DD surface maybe ?
   if ( !dst.IsNull() && !dst.IsEmpty() )
      size.Set( dst.Width(), dst.Height() );
}

void GetRequestedSize( const KS_VIDEOINFOHEADER &vidHdr, MSize &size )
{
   Trace t("GetRequestedSize()");

   size.Set( vidHdr.bmiHeader.biWidth, abs(vidHdr.bmiHeader.biHeight) );

   MRect dst( vidHdr.rcTarget );
   // if writing to a DD surface maybe ?
   if ( !dst.IsNull() && !dst.IsEmpty() )
      size.Set( dst.Width(), dst.Height() );
}

/* Function: VerifyVideoStream
 * Purpose: Checks the paramaters passed in for opening steam
 * Input: vidHDR: KS_DATAFORMAT_VIDEOINFOHEADER
 * Output: Success or Fail
 */
ErrorCode VerifyVideoStream( const KS_DATAFORMAT_VIDEOINFOHEADER &vidHDR )
{
   Trace t("VerifyVideoStream()");

   // [WRK] - add guid for VideoInfoHeader2

   // simply verify that major and format are of video nature...
   if ( IsEqualGUID( vidHDR.DataFormat.MajorFormat, KSDATAFORMAT_TYPE_VIDEO ) &&
        IsEqualGUID( vidHDR.DataFormat.Specifier, KSDATAFORMAT_SPECIFIER_VIDEOINFO ) ) {

      MSize size;
      GetRequestedSize( vidHDR.VideoInfoHeader, size );
      // ... and here see if the subtype is one of those supported by us
      ColorSpace tmpCol( vidHDR.DataFormat.SubFormat );
      MRect dst( vidHDR.VideoInfoHeader.rcTarget );

      // make sure the dimentions are acceptable
      if ( tmpCol.IsValid() && tmpCol.CheckDimentions( size ) &&
         tmpCol.CheckLeftTop( dst.TopLeft() ) ) {
         DebugOut((1, "VerifyVideoStream succeeded\n"));
         return Success;
      }
   }
   DebugOut((0, "VerifyVideoStream failed\n"));
   return Fail;
}

ErrorCode VerifyVideoStream2( const KS_DATAFORMAT_VIDEOINFOHEADER2 &vidHDR )
{
   Trace t("VerifyVideoStream2()");

   // [WRK] - add guid for VideoInfoHeader2

   // simply verify that major and format are of video nature...
   if ( IsEqualGUID( vidHDR.DataFormat.MajorFormat, KSDATAFORMAT_TYPE_VIDEO ) &&
        IsEqualGUID( vidHDR.DataFormat.Specifier, KSDATAFORMAT_SPECIFIER_VIDEOINFO2 ) ) {

      MSize size;
      GetRequestedSize2( vidHDR.VideoInfoHeader2, size );
      // ... and here see if the subtype is one of those supported by us
      ColorSpace tmpCol( vidHDR.DataFormat.SubFormat );
      MRect dst( vidHDR.VideoInfoHeader2.rcTarget );

      // make sure the dimentions are acceptable
      if ( tmpCol.IsValid() && tmpCol.CheckDimentions( size ) &&
         tmpCol.CheckLeftTop( dst.TopLeft() ) ) {
         DebugOut((1, "VerifyVideoStream2 succeeded\n"));
         return Success;
      }
   }
   DebugOut((0, "VerifyVideoStream2 failed\n"));
   return Fail;
}

/* Function: VerifyVBIStream
 * Purpose: Checks that VBI stream info during open is correct
 * Input: rKSDataFormat: KS_DATAFORMAT &
 * Output: ErrorCode
 */
ErrorCode VerifyVBIStream( const KS_DATAFORMAT_VBIINFOHEADER &rKSDataFormat )
{
   Trace t("VerifyVBIStream()");

   if ( IsEqualGUID( rKSDataFormat.DataFormat.MajorFormat, KSDATAFORMAT_TYPE_VBI ) &&
        IsEqualGUID( rKSDataFormat.DataFormat.Specifier,
        KSDATAFORMAT_SPECIFIER_VBI ) &&
        rKSDataFormat.VBIInfoHeader.StartLine == VBIStart &&
        rKSDataFormat.VBIInfoHeader.EndLine   == VBIEnd   &&
        rKSDataFormat.VBIInfoHeader.SamplesPerLine == VBISamples )
      return Success;
   return Fail;
}

/*
** DriverEntry()
**
**   This routine is called when an SRB_INITIALIZE_DEVICE request is received
**
** Arguments:
**
**   Context1 and Context2
**
** Returns:
**
**   Results of StreamClassRegisterAdapter()
**
** Side Effects:  none
*/

extern "C" ULONG DriverEntry( PVOID Arg1, PVOID Arg2 )
{
   Trace t("DriverEntry()");

   //
   // Entry points for Port Driver
   //
   HW_INITIALIZATION_DATA  HwInitData;
   RtlZeroMemory( &HwInitData, sizeof( HwInitData ));
   HwInitData.HwInitializationDataSize = sizeof( HwInitData );

   HwInitData.HwInterrupt              = (PHW_INTERRUPT)&HwInterrupt;

   HwInitData.HwReceivePacket          = &AdapterReceivePacket;
   HwInitData.HwCancelPacket           = &AdapterCancelPacket;
   HwInitData.HwRequestTimeoutHandler  = &AdapterTimeoutPacket;

   HwInitData.DeviceExtensionSize      = GetSizeHwDeviceExtension( );
   HwInitData.PerRequestExtensionSize  = sizeof(SRB_EXTENSION);
   HwInitData.FilterInstanceExtensionSize = 0;
   // double to support alternating/interleaved
   HwInitData.PerStreamExtensionSize   = GetSizeStreamEx( );
   HwInitData.BusMasterDMA             = true;
   HwInitData.Dma24BitAddresses        = FALSE;
   HwInitData.BufferAlignment          = 4;
   HwInitData.TurnOffSynchronization   = FALSE;
   HwInitData.DmaBufferSize            = RISCProgramsSize;

   return (StreamClassRegisterAdapter(Arg1, Arg2,&HwInitData));
}

/******************************************************************************

                   Adapter Based Request Handling Routines

******************************************************************************/
/*
** HwInitialize()
**
**   This routine is called when an SRB_INITIALIZE_DEVICE request is received
**
** Arguments:
**
**   pSrb - pointer to stream request block for the Initialize command
**
** Returns:
**
** Side Effects:  none
*/
BOOLEAN HwInitialize( IN OUT PHW_STREAM_REQUEST_BLOCK pSrb )
{
   Trace t("HwInitialize()");

   DebugOut((1, "HwInitialize()\n"));

   // initialize ourselves

   PPORT_CONFIGURATION_INFORMATION ConfigInfo = 
      pSrb->CommandData.ConfigInfo;

   gpHwDeviceExtension = ConfigInfo->HwDeviceExtension;
   DebugOut((0, "*** gpHwDeviceExtension = %x\n", gpHwDeviceExtension));

   PHW_DEVICE_EXTENSION HwDeviceExtension =
      (PHW_DEVICE_EXTENSION) gpHwDeviceExtension;

   DWORD dwBase = ConfigInfo->AccessRanges[0].RangeStart.LowPart;
   SetBase((BYTE *)dwBase);

   if ( ConfigInfo->NumberOfAccessRanges != 1 ) {
      DebugOut((1, "illegal config info\n"));
      pSrb->Status = STATUS_NO_SUCH_DEVICE;
   }

   // read info from the registry
   ReadXBarRegistryValues( ConfigInfo->PhysicalDeviceObject );
   ReadXTalRegistryValues( ConfigInfo->PhysicalDeviceObject );
   ReadTunerRegistryValues( ConfigInfo->PhysicalDeviceObject );

   HwDeviceExtension->psdevice =
      new ( &(HwDeviceExtension->psdevicemem) ) PsDevice( dwBase );

   DebugOut((0, "psdevice = %x\n", HwDeviceExtension->psdevice ));
   DebugOut((0, "&psdevicemem = %x\n", &HwDeviceExtension->psdevicemem ));

   PsDevice *adapter = HwDeviceExtension->psdevice;

   // save for later use when phys address if obtained
   SetCurrentDevice( adapter );

   // make sure initialization is successful
   if ( !adapter->InitOK() ) {
      DebugOut((1, "Error initializing\n"));
      pSrb->Status = STATUS_INSUFFICIENT_RESOURCES;
   }

   // save our PDO
   adapter->PDO = ConfigInfo->PhysicalDeviceObject;

   ConfigInfo->StreamDescriptorSize = sizeof( HW_STREAM_HEADER ) +
      DRIVER_STREAM_COUNT * sizeof( HW_STREAM_INFORMATION );

   DebugOut((1, "Exit : HwInitialize()\n"));

   // go to usual priority, completing the SRB at the same time
   StreamClassCallAtNewPriority( pSrb->StreamObject, HwDeviceExtension, LowToHigh,
      PHW_PRIORITY_ROUTINE( CompleteDeviceSRB ), pSrb );

   return (TRUE);
}

/*
** HwUnInitialize()
**
**   This routine is called when an SRB_UNINITIALIZE_DEVICE request is received
**
** Arguments:
**
**   pSrb - pointer to stream request block for the UnInitialize command
**
** Returns:
**
** Side Effects:  none
*/
void HwUnInitialize( IN PHW_STREAM_REQUEST_BLOCK pSrb )
{
   Trace t("HwUnInitialize()");

   PHW_DEVICE_EXTENSION HwDeviceExtension =
      (PHW_DEVICE_EXTENSION) pSrb->HwDeviceExtension;

   DebugOut((0, "HwUnInitialize - pSrb(%x)\n", pSrb));

   PsDevice *adapter = HwDeviceExtension->psdevice;
   adapter->~PsDevice();
}

/*
** AdapterOpenStream()
**
**   This routine is called when an OpenStream SRB request is received
**
** Arguments:
**
**   pSrb - pointer to stream request block for the Open command
**
** Returns:
**
** Side Effects:  none
*/

VOID AdapterOpenStream( IN PHW_STREAM_REQUEST_BLOCK pSrb )
{
   Trace t("AdapterOpenStream()");

   //
   // the stream extension structure is allocated by the stream class driver
   //

   // retrieve the device object pointer
   PHW_DEVICE_EXTENSION HwDeviceExtension =
      (PHW_DEVICE_EXTENSION) pSrb->HwDeviceExtension;

   PsDevice *adapter = HwDeviceExtension->psdevice;

   VideoStream StreamNumber = (VideoStream)pSrb->StreamObject->StreamNumber;
   StreamIdxToSrb[StreamNumber] = pSrb;

   DebugOut((1, "AdapterOpenStream(%d) - pSrb(%x)\n", StreamNumber, pSrb));

   // [STRM] [!!!]
   //if ( !( StreamNumber >= VS_Field1 && StreamNumber <= DRIVER_STREAM_COUNT ) ) {
   //   pSrb->Status = STATUS_INVALID_PARAMETER; // ?change to the proper error code?
   //   return;
   //}
   
   if ( StreamNumber == STREAM_IDX_ANALOG )   // [TMZ] [!!] was 3
   {
      pSrb->StreamObject->ReceiveDataPacket    = AnalogReceiveDataPacket;
      pSrb->StreamObject->ReceiveControlPacket = AnalogReceiveCtrlPacket;
      return; // nothing to do for the analog stream
   }
   PSTREAMEX pStrmEx = (PSTREAMEX)pSrb->StreamObject->HwStreamExtension;
   RtlZeroMemory( &pStrmEx->FrameInfo, sizeof( pStrmEx->FrameInfo ) );
   pStrmEx->StreamNumber = StreamNumber;

   // size of the media specific data
   UINT MediaSpecific = sizeof( KS_FRAME_INFO );

   // Always open VBI stream as Alternating fields
   if ( StreamNumber == STREAM_IDX_VBI ) 
	{
      const KS_DATAFORMAT_VBIINFOHEADER &rKSVBIDataFormat =
         *(PKS_DATAFORMAT_VBIINFOHEADER) pSrb->CommandData.OpenFormat;

      if ( VerifyVBIStream( rKSVBIDataFormat ) != Success )
		{
         DebugOut((0, "*** VerifyVBIStream failed - aborting\n"));
         pSrb->Status = STATUS_INVALID_PARAMETER; // ?change to the proper error code?
         return;
      }
      if ( adapter->OpenVBIChannel( pStrmEx ) != Success )
      {
         DebugOut((0, "*** OpenVBIChannel failed - aborting\n"));
         pSrb->Status = STATUS_INVALID_PARAMETER; // ?change to the proper error code?
         return;
      }

      VBIAlterChannel *chan = (VBIAlterChannel *)pStrmEx->videochannel;
      //chan->pStrmEx = pStrmEx;
      chan->SetVidHdr( rKSVBIDataFormat );

      MediaSpecific = sizeof( KS_VBI_FRAME_INFO );

   } 
	else 
	{

      // is it where the size, fourcc, etc. are specified ? they should be settable
      // via properies sets
      const KS_DATAFORMAT_VIDEOINFOHEADER &rKSDataFormat = *(PKS_DATAFORMAT_VIDEOINFOHEADER) pSrb->CommandData.OpenFormat;
      const KS_VIDEOINFOHEADER &rVideoInfoHdrRequested = rKSDataFormat.VideoInfoHeader;
      const KS_DATAFORMAT_VIDEOINFOHEADER2 &rKSDataFormat2 = *(PKS_DATAFORMAT_VIDEOINFOHEADER2) pSrb->CommandData.OpenFormat;
      const KS_VIDEOINFOHEADER2 &rVideoInfoHdrRequested2 = rKSDataFormat2.VideoInfoHeader2;

      DebugOut((1, "AdapterOpenStream\n"));
      if ( IsEqualGUID( rKSDataFormat.DataFormat.Specifier, KSDATAFORMAT_SPECIFIER_VIDEOINFO ) ) 
		{
			DebugOut((1, "StreamNumber=%d\n", pSrb->StreamObject->StreamNumber));
			DebugOut((1, "FormatSize=%d\n", rKSDataFormat.DataFormat.FormatSize));
			DebugOut((1, "MajorFormat=%x\n", rKSDataFormat.DataFormat.MajorFormat));
			DebugOut((1, "pVideoInfoHdrRequested=%x\n", &rVideoInfoHdrRequested));
			DebugOut((1, "Bpp =%d\n", rVideoInfoHdrRequested.bmiHeader.biBitCount ) );
			DebugOut((1, "Width =%d\n", rVideoInfoHdrRequested.bmiHeader.biWidth));
			DebugOut((1, "Height =%d\n", rVideoInfoHdrRequested.bmiHeader.biHeight));
			DebugOut((1, "biSizeImage =%d\n", rVideoInfoHdrRequested.bmiHeader.biSizeImage));
			if ( VerifyVideoStream( rKSDataFormat ) != Success ) 
			{
				pSrb->Status = STATUS_INVALID_PARAMETER; // ?change to the proper error code?
				return;
			}
		}
		else
		{
			DebugOut((1, "StreamNumber=%d\n", pSrb->StreamObject->StreamNumber));
			DebugOut((1, "FormatSize=%d\n", rKSDataFormat2.DataFormat.FormatSize));
			DebugOut((1, "MajorFormat=%x\n", rKSDataFormat2.DataFormat.MajorFormat));
			DebugOut((1, "pVideoInfoHdrRequested2=%x\n", &rVideoInfoHdrRequested2));
			DebugOut((1, "Bpp =%d\n", rVideoInfoHdrRequested2.bmiHeader.biBitCount ) );
			DebugOut((1, "Width =%d\n", rVideoInfoHdrRequested2.bmiHeader.biWidth));
			DebugOut((1, "Height =%d\n", rVideoInfoHdrRequested2.bmiHeader.biHeight));
			DebugOut((1, "biSizeImage =%d\n", rVideoInfoHdrRequested2.bmiHeader.biSizeImage));
			if ( VerifyVideoStream2( rKSDataFormat2 ) != Success ) 
			{
				pSrb->Status = STATUS_INVALID_PARAMETER; // ?change to the proper error code?
				return;
			}
		}

      // at this point have to see what type of channel is to be opened:
      // single field, alternating or interleaved
      // algorithm is like this:
      // 1. look at the video format Specifier guid. If it's a infoheader2, it
      //    will tell us type of stream to open.
      // 2. else look at the vertical size to decide single field vs. interleaved

      if ( IsEqualGUID( rKSDataFormat.DataFormat.Specifier, KSDATAFORMAT_SPECIFIER_VIDEOINFO ) ) 
		{

         MSize size;

         GetRequestedSize( rVideoInfoHdrRequested, size );

         // different video standards have different vertical sizes
         int threshold = adapter->GetFormat() == VFormat_NTSC ? 240 : 288;

         if ( size.cy > threshold ) 
			{
            if ( adapter->OpenInterChannel( pStrmEx, StreamNumber ) != Success ) 
				{
               pSrb->Status = STATUS_INVALID_PARAMETER; // ?change to the proper error code?
               return;
            }
         } 
			else 
			{
            if ( adapter->OpenChannel( pStrmEx, StreamNumber ) != Success ) 
				{
               pSrb->Status = STATUS_INVALID_PARAMETER; // ?change to the proper error code?
               return;
            }
         }
			VideoChannel *chan = (VideoChannel *)pStrmEx->videochannel;
         //chan->pStrmEx = pStrmEx;
			chan->SetVidHdr( rVideoInfoHdrRequested );
      }
      else if ( IsEqualGUID( rKSDataFormat2.DataFormat.Specifier, KSDATAFORMAT_SPECIFIER_VIDEOINFO2 ) ) 
		{
         MSize size;
         GetRequestedSize2( rVideoInfoHdrRequested2, size );

         // different video standards have different vertical sizes
         int threshold = adapter->GetFormat() == VFormat_NTSC ? 240 : 288;

         if ( size.cy > threshold ) 
			{
            if ( adapter->OpenInterChannel( pStrmEx, StreamNumber ) != Success ) 
				{
               pSrb->Status = STATUS_INVALID_PARAMETER; // ?change to the proper error code?
               return;
            }
         } 
			else 
			{
            if ( adapter->OpenChannel( pStrmEx, StreamNumber ) != Success ) 
				{
               pSrb->Status = STATUS_INVALID_PARAMETER; // ?change to the proper error code?
               return;
            }
         }
			VideoChannel *chan = (VideoChannel *)pStrmEx->videochannel;
         //chan->pStrmEx = pStrmEx;
			chan->SetVidHdr2( rVideoInfoHdrRequested2 );
      } 
		else 
		{
         if ( adapter->OpenInterChannel( pStrmEx, StreamNumber ) != Success ) 
			{
            pSrb->Status = STATUS_INVALID_PARAMETER; // ?change to the proper error code?
            return;
         }
         if ( adapter->OpenAlterChannel( pStrmEx, StreamNumber ) != Success ) 
			{
            pSrb->Status = STATUS_INVALID_PARAMETER; // ?change to the proper error code?
            return;
         }
         // [WRK] - check the height of the image for alter channel !> threshold
			VideoChannel *chan = (VideoChannel *)pStrmEx->videochannel;
         //chan->pStrmEx = pStrmEx;
			chan->SetVidHdr( rVideoInfoHdrRequested );
      }
   }

#ifdef ENABLE_DDRAW_STUFF
	//BUGBUG should we check to see what kind of stream type this is?
	if( OpenKernelDirectDraw( pSrb ) )
	{
		OpenKernelDDrawSurfaceHandle( pSrb );
		RegisterForDirectDrawEvents( pSrb ); 
	}
#endif

   // the structure of the driver is such that a single callback could be used
   // for all stream requests. But the code below could be used to supply
   // different entry points for different streams
   pSrb->StreamObject->ReceiveDataPacket    = VideoReceiveDataPacket;
   pSrb->StreamObject->ReceiveControlPacket = VideoReceiveCtrlPacket;

   pSrb->StreamObject->Dma = true;

   pSrb->StreamObject->Allocator = Streams[StreamNumber].hwStreamObject.Allocator;

   //
   // The PIO flag must be set when the mini driver will be accessing the data
   // buffers passed in using logical addressing
   //

   pSrb->StreamObject->Pio = true;
   pSrb->StreamObject->StreamHeaderMediaSpecific = MediaSpecific;
   pSrb->StreamObject->HwClockObject.ClockSupportFlags = 0;
   pSrb->StreamObject->HwClockObject.HwClockFunction = 0;

   DebugOut((1, "AdapterOpenStream Exit\n"));
}

/*
** AdapterCloseStream()
**
**   Close the requested data stream
**
** Arguments:
**
**   pSrb the request block requesting to close the stream
**
** Returns:
**
** Side Effects:  none
*/

VOID AdapterCloseStream( PHW_STREAM_REQUEST_BLOCK pSrb )
{
   Trace t("AdapterCloseStream()");

   PHW_DEVICE_EXTENSION HwDeviceExtension =
      (PHW_DEVICE_EXTENSION) pSrb->HwDeviceExtension;

   PsDevice *adapter = HwDeviceExtension->psdevice;
   VideoChannel *chan = (VideoChannel *)((PSTREAMEX)pSrb->StreamObject->HwStreamExtension)->videochannel;
   VideoStream StreamNumber = (VideoStream)pSrb->StreamObject->StreamNumber;

   DebugOut((1, "AdapterCloseStream(%d) - pSrb(%x)\n", StreamNumber, pSrb));

   if ( !( StreamNumber >= 0 && StreamNumber < DRIVER_STREAM_COUNT ) ) {
      DebugOut((0, "   AdapterCloseStream - failed to close stream %d\n", StreamNumber));
      DEBUG_BREAKPOINT();
      pSrb->Status = STATUS_INVALID_PARAMETER; // ?change to the proper error code?
      return;
   }
   if ( StreamNumber == STREAM_IDX_ANALOG ) // nothing to close for analog
   {
      DebugOut((1, "   AdapterCloseStream - doing nothing, stream (%d) was assumed to be analog\n", StreamNumber));
      return;
   }
#ifdef ENABLE_DDRAW_STUFF
	//BUGBUG should we check to see what kind of stream type this is?
	UnregisterForDirectDrawEvents( pSrb );
	CloseKernelDDrawSurfaceHandle( pSrb );
	CloseKernelDirectDraw( pSrb );
#endif

   // CloseChannel() has a bit of ugly code to take care of paired channels
   adapter->CloseChannel( chan );
}

/*
** AdapterStreamInfo()
**
**   Returns the information of all streams that are supported by the
**   mini-driver
**
** Arguments:
**
**   pSrb - Pointer to the STREAM_REQUEST_BLOCK
**        pSrb->HwDeviceExtension - will be the hardware device extension for
**                                  as initialised in HwInitialise
**
** Returns:
**
** Side Effects:  none
*/

VOID AdapterStreamInfo( PHW_STREAM_REQUEST_BLOCK pSrb )
{
   Trace t("AdapterStreamInfo()");

   //
   // pick up the pointer to header which preceeds the stream info structs
   //

   PHW_STREAM_HEADER pstrhdr =
      (PHW_STREAM_HEADER)&(pSrb->CommandData.StreamBuffer->StreamHeader);

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

   PHW_STREAM_INFORMATION pstrinfo =
       (PHW_STREAM_INFORMATION)&(pSrb->CommandData.StreamBuffer->StreamInfo);

   //
   // verify that the buffer is large enough to hold our return data
   //
   DEBUG_ASSERT( pSrb->NumberOfBytesToTransfer >=
      sizeof( HW_STREAM_HEADER ) +
      sizeof( HW_STREAM_INFORMATION ) * DRIVER_STREAM_COUNT );


     //
     // Set the header
     //
   StreamHeader.NumDevPropArrayEntries = NUMBER_OF_ADAPTER_PROPERTY_SETS;
   StreamHeader.DevicePropertiesArray  = (PKSPROPERTY_SET) AdapterPropertyTable;
   *pstrhdr = StreamHeader;

   //
   // stuff the contents of each HW_STREAM_INFORMATION struct
   //

   for ( int j = 0; j < DRIVER_STREAM_COUNT; j++ ) {
      *pstrinfo++ = Streams[j].hwStreamInfo;
   }

}
#ifdef HAUPPAUGEI2CPROVIDER

// new private members of PsDevice for Hauppauge I2C Provider:
//    LARGE_INTEGER LastI2CAccessTime;
//    DWORD       dwExpiredCookie = 0;
//
//

/* Method: PsDevice::I2COpen
 * Purpose: Tries to allocate I2C port to the caller
 */
NTSTATUS STDMETHODCALLTYPE PsDevice::I2COpen( PDEVICE_OBJECT pdo, ULONG ToOpen, PI2CControl ctrl )
{
   Trace t("PsDevice::I2COpen()");

   DebugOut((1, "*** pdo->DeviceExtension = %x\n", pdo->DeviceExtension));
   
   LARGE_INTEGER CurTime;

   // need a way to obtain the device pointer
   PsDevice *adapter = GetCurrentDevice();

   KeQuerySystemTime( &CurTime );

   ctrl->Status = I2C_STATUS_NOERROR;

   // cookie is not NULL if I2C is open
   if ( ToOpen && adapter->dwCurCookie_ ) {
//   Check time stamp against current time to detect if current Cookie has timed out.
//    If it has remember the last timed out cookie and grant the new requestor access.
      if ( ( adapter->dwI2CClientTimeout != 0 ) && ( (CurTime - adapter->LastI2CAccessTime) >  adapter->dwI2CClientTimeout ) ) {
         adapter->dwExpiredCookie = adapter->dwCurCookie_;
      } else {
         ctrl->dwCookie = 0;
         return STATUS_INVALID_HANDLE;
     }
   }

   // want to close ?
   if ( !ToOpen ) {
      if ( adapter->dwCurCookie_ == ctrl->dwCookie ) {
         adapter->dwCurCookie_ = 0;
         ctrl->dwCookie = 0;
         return STATUS_SUCCESS;
      } else {
         if ( (adapter->dwExpiredCookie != 0 ) && (adapter->dwExpiredCookie == ctrl->dwCookie ) ) {
            ctrl->Status = I2C_STATUS_ERROR;
         } else {
            ctrl->dwCookie = 0;
            ctrl->Status = I2C_STATUS_NOERROR;
         }
         return STATUS_INVALID_HANDLE;
      }
   }

   adapter->dwCurCookie_ = CurTime.LowPart;
   adapter->LastI2CAccessTime = CurTime;
   ctrl->dwCookie = adapter->dwCurCookie_;
   ctrl->ClockRate = 100000;

   return STATUS_SUCCESS;
}

NTSTATUS STDMETHODCALLTYPE PsDevice::I2CAccess( PDEVICE_OBJECT pdo, PI2CControl ctrl )
{
   Trace t("PsDevice::I2CAccess()");

   DebugOut((1, "*** pdo->DeviceExtension = %x\n", pdo->DeviceExtension));
   
   ErrorCode error;
   PsDevice *adapter = GetCurrentDevice();

   ctrl->Status = I2C_STATUS_NOERROR;

   if ( ctrl->dwCookie != adapter->dwCurCookie_ ) {
      if ( (adapter->dwExpiredCookie != 0 ) && (adapter->dwExpiredCookie == ctrl->dwCookie ) )
         ctrl->Status = I2C_STATUS_ERROR;
      else
         ctrl->Status = I2C_STATUS_NOERROR;
      return STATUS_INVALID_HANDLE;
   }

// Record time of this transaction to enable checking for timeout
   KeQuerySystemTime( &adapter->LastI2CAccessTime );

// Check for valid combinations of I2C command & flags

   switch( ctrl->Command ) {
   case I2C_COMMAND_NULL:
     if ( ( ctrl->Flags & ~(I2C_FLAGS_START | I2C_FLAGS_STOP) ) ||
           ( ( ctrl->Flags & (I2C_FLAGS_START | I2C_FLAGS_STOP) ) == (I2C_FLAGS_START | I2C_FLAGS_STOP) ) ) {
        // Illegal combination of Command & Flags
        return STATUS_INVALID_PARAMETER;
     }
     if ( ctrl->Flags & I2C_FLAGS_START ) {
         if ( adapter->I2CSWStart() ) {
            ctrl->Status = I2C_STATUS_ERROR;
           return STATUS_SUCCESS;
         }
      }
     if ( ctrl->Flags & I2C_FLAGS_STOP ) {
         if ( adapter->I2CSWStop() ) {
            ctrl->Status = I2C_STATUS_ERROR;
           return STATUS_SUCCESS;
         }
      }
      break;

   case I2C_COMMAND_READ:
     if ( ctrl->Flags & ~(I2C_FLAGS_STOP | I2C_FLAGS_ACK) ) {
        // Illegal combination of Command & Flags
        return STATUS_INVALID_PARAMETER;
     }
      if ( adapter->I2CSWRead( &ctrl->Data ) ) {
            ctrl->Status = I2C_STATUS_ERROR;
           return STATUS_SUCCESS;
      }
     if ( ctrl->Flags & I2C_FLAGS_ACK ) {
         if ( adapter->I2CSWSendACK() ) {
            ctrl->Status = I2C_STATUS_ERROR;
           return STATUS_SUCCESS;
         }
     } else {
         if ( adapter->I2CSWSendNACK() ) {
            ctrl->Status = I2C_STATUS_ERROR;
           return STATUS_SUCCESS;
         }
      }
     if ( ctrl->Flags & I2C_FLAGS_STOP ) {
         if ( adapter->I2CSWStop() ) {
            ctrl->Status = I2C_STATUS_ERROR;
           return STATUS_SUCCESS;
         }
      }
     break;

   case I2C_COMMAND_WRITE:
     if ( ctrl->Flags & ~(I2C_FLAGS_START | I2C_FLAGS_STOP | I2C_FLAGS_ACK | I2C_FLAGS_DATACHAINING) ) {
        // Illegal combination of Command & Flags
        return STATUS_INVALID_PARAMETER;
     }
     if ( ctrl->Flags & I2C_FLAGS_DATACHAINING ) {
         if ( adapter->I2CSWStop() ) {
            ctrl->Status = I2C_STATUS_ERROR;
           return STATUS_SUCCESS;
         }
         if ( adapter->I2CSWStart() ) {
            ctrl->Status = I2C_STATUS_ERROR;
           return STATUS_SUCCESS;
         }
      }
     if ( ctrl->Flags & I2C_FLAGS_START ) {
         if ( adapter->I2CSWStart() ) {
            ctrl->Status = I2C_STATUS_ERROR;
           return STATUS_SUCCESS;
         }
      }
      error = adapter->I2CSWWrite(ctrl->Data);

      switch ( error ) {

     case I2CERR_NOACK:
         if ( ctrl->Flags & I2C_FLAGS_ACK ) {
            ctrl->Status = I2C_STATUS_ERROR;
           return STATUS_SUCCESS;
         }
       break;

     case I2CERR_OK:
         if ( ( ctrl->Flags & I2C_FLAGS_ACK ) == 0 ) {
            ctrl->Status = I2C_STATUS_ERROR;
           return STATUS_SUCCESS;
         }
       break;

      default:
         ctrl->Status = I2C_STATUS_ERROR;
         return STATUS_SUCCESS;
      }

     if ( ctrl->Flags & I2C_FLAGS_STOP ) {
         if ( adapter->I2CSWStop() ) {
            ctrl->Status = I2C_STATUS_ERROR;
           return STATUS_SUCCESS;
         }
      }
     break;

   case I2C_COMMAND_STATUS:
     // Flags are ignored
     return STATUS_NOT_IMPLEMENTED;

   case I2C_COMMAND_RESET:
        // Flags are ignored
      if ( adapter->I2CSWStart() ) {
         ctrl->Status = I2C_STATUS_ERROR;
        return STATUS_SUCCESS;
      }
      if ( adapter->I2CSWStop() ) {
         ctrl->Status = I2C_STATUS_ERROR;
        return STATUS_SUCCESS;
      }
     break;

   default:
     return STATUS_INVALID_PARAMETER;
   }
   return STATUS_SUCCESS;
}

#else


/* Method: PsDevice::I2COpen
 * Purpose: Tries to allocate I2C port to the caller
 */
NTSTATUS STDMETHODCALLTYPE PsDevice::I2COpen( PDEVICE_OBJECT pdo, ULONG ToOpen,
   PI2CControl ctrl )
{
   Trace t("PsDevice::I2COpen()");

   DebugOut((1, "*** pdo->DeviceExtension = %x\n", pdo->DeviceExtension));
   
   // need a way to obtain the device pointer
   PsDevice *adapter = GetCurrentDevice();

   // cookie is not NULL if I2C is open
   if ( ToOpen && adapter->dwCurCookie_ ) {
      ctrl->Flags = I2C_STATUS_BUSY;
      return STATUS_DEVICE_BUSY;
   }

   // want to close ?
   if ( !ToOpen )
      if ( adapter->dwCurCookie_ == ctrl->dwCookie ) {
         adapter->dwCurCookie_ = 0;
         return STATUS_SUCCESS;
      } else {
         ctrl->Flags = I2C_STATUS_BUSY;
         return STATUS_DEVICE_BUSY;
      }

   // now we are opening
   LARGE_INTEGER CurTime;
   KeQuerySystemTime( &CurTime );

   adapter->dwCurCookie_ = CurTime.LowPart;
   ctrl->dwCookie = adapter->dwCurCookie_;
   ctrl->ClockRate = 100000;

   return STATUS_SUCCESS;
}

NTSTATUS STDMETHODCALLTYPE PsDevice::I2CAccess( PDEVICE_OBJECT pdo , PI2CControl ctrl )
{
   Trace t("PsDevice::I2CAccess()");

   DebugOut((1, "*** pdo->DeviceExtension = %x\n", pdo->DeviceExtension));
   
   PsDevice *adapter = GetCurrentDevice();

   if ( ctrl->dwCookie != adapter->dwCurCookie_ ) {
      ctrl->Flags = I2C_STATUS_BUSY;
      return I2C_STATUS_BUSY;
   }

   ctrl->Flags = I2C_STATUS_NOERROR;

  // 848 I2C API currently needs to have an address for both write and read
  // commands. So, if START flag is set an address is passed. Cache it and use
  // later
   switch ( ctrl->Command ) {
   case I2C_COMMAND_READ:
      // got 'write' command first ( with the address )
      if ( adapter->I2CHWRead( adapter->GetI2CAddress(), &ctrl->Data ) != Success )
         ctrl->Flags = I2C_STATUS_ERROR;
      break;
   case I2C_COMMAND_WRITE:
      if ( ctrl->Flags & I2C_FLAGS_START ) {
         adapter->StoreI2CAddress( ctrl->Data );
      } else
         adapter->I2CHWWrite2( adapter->GetI2CAddress(), ctrl->Data );
      break;
   case I2C_COMMAND_STATUS:
      if ( adapter->I2CGetLastError() != I2CERR_OK )
         ctrl->Flags = I2C_STATUS_ERROR;
      break;
   case I2C_COMMAND_RESET:
      adapter->I2CInitHWMode( 100000 );    // assume frequency = 100Khz
      break;
   }
   return STATUS_SUCCESS;
}

#endif

void  HandleIRP( IN PHW_STREAM_REQUEST_BLOCK pSrb )
{
   Trace t("HandleIRP()");

   DebugOut((1, "HandleIRP(%x)\n", pSrb));

   PHW_DEVICE_EXTENSION HwDeviceExtension =
      (PHW_DEVICE_EXTENSION) pSrb->HwDeviceExtension;

   PsDevice *adapter = HwDeviceExtension->psdevice;

   PIRP Irp = pSrb->Irp;
   PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation( Irp );
   switch ( IrpStack->MajorFunction ) {
   case IRP_MJ_PNP:
      if ( IrpStack->MinorFunction == IRP_MN_QUERY_INTERFACE ) {

         if ( IsEqualGUID( *IrpStack->Parameters.QueryInterface.InterfaceType,
              GUID_I2C_INTERFACE ) &&
              IrpStack->Parameters.QueryInterface.Size >= sizeof( I2CINTERFACE ) ) {

            IrpStack->Parameters.QueryInterface.InterfaceType = &GUID_I2C_INTERFACE;
            IrpStack->Parameters.QueryInterface.Size = sizeof( I2CINTERFACE );
            IrpStack->Parameters.QueryInterface.Version = 1;
            I2CINTERFACE *i2ciface =
            (I2CINTERFACE *)IrpStack->Parameters.QueryInterface.Interface;
            i2ciface->i2cOpen   = adapter->I2COpen;
            i2ciface->i2cAccess = adapter->I2CAccess;
            IrpStack->Parameters.QueryInterface.InterfaceSpecificData = 0;

            // complete the irp
            Irp->IoStatus.Status = STATUS_SUCCESS;
            IoCompleteRequest( Irp, IO_NO_INCREMENT );

            break;
         } else {
            Irp->IoStatus.Status = STATUS_INVALID_PARAMETER_1;
            IoCompleteRequest( Irp, IO_NO_INCREMENT );
         }
      }
   default:
       pSrb->Status = STATUS_NOT_SUPPORTED;
   }
}

/** CompleteInitialization()
**
**   This routine is called when an SRB_COMPLETE_INITIALIZATION request is received
**
** Arguments:
**
**   pSrb - pointer to stream request block
**
** Returns:
**
** Side Effects:  none
*/
void STDMETHODCALLTYPE CompleteInitialization( IN OUT PHW_STREAM_REQUEST_BLOCK pSrb )
{
   Trace t("CompleteInitialization()");

   NTSTATUS                Status;
   
   PHW_DEVICE_EXTENSION HwDeviceExtension =
      (PHW_DEVICE_EXTENSION) pSrb->HwDeviceExtension;

   PsDevice *adapter = HwDeviceExtension->psdevice;

    // Create the Registry blobs that DShow uses to create
    // graphs via Mediums

    Status = StreamClassRegisterFilterWithNoKSPins (
                    adapter->PDO,                   // IN PDEVICE_OBJECT   DeviceObject,
                    &KSCATEGORY_TVTUNER,            // IN GUID           * InterfaceClassGUID,
                    SIZEOF_ARRAY (TVTunerMediums),  // IN ULONG            PinCount,
                    TVTunerPinDirection,            // IN ULONG          * Flags,
                    TVTunerMediums,                 // IN KSPIN_MEDIUM   * MediumList,
                    NULL                            // IN GUID           * CategoryList
            );

    Status = StreamClassRegisterFilterWithNoKSPins (
                    adapter->PDO,                   // IN PDEVICE_OBJECT   DeviceObject,
                    &KSCATEGORY_CROSSBAR,           // IN GUID           * InterfaceClassGUID,
                    SIZEOF_ARRAY (CrossbarMediums), // IN ULONG            PinCount,
                    CrossbarPinDirection,           // IN ULONG          * Flags,
                    CrossbarMediums,                // IN KSPIN_MEDIUM   * MediumList,
                    NULL                            // IN GUID           * CategoryList
            );

    // Register the TVAudio decoder

    Status = StreamClassRegisterFilterWithNoKSPins (
                    adapter->PDO,                   // IN PDEVICE_OBJECT   DeviceObject,
                    &KSCATEGORY_TVAUDIO,            // IN GUID           * InterfaceClassGUID,
                    SIZEOF_ARRAY (TVAudioMediums),  // IN ULONG            PinCount,
                    TVAudioPinDirection,            // IN ULONG          * Flags,
                    TVAudioMediums,                 // IN KSPIN_MEDIUM   * MediumList,
                    NULL                            // IN GUID           * CategoryList
            );

    // Register the Capture filter
    // Note:  This should be done automatically be MSKsSrv.sys,
    // when that component comes on line (if ever) ...

    Status = StreamClassRegisterFilterWithNoKSPins (
                    adapter->PDO,                   // IN PDEVICE_OBJECT   DeviceObject,
                    &KSCATEGORY_CAPTURE,            // IN GUID           * InterfaceClassGUID,
                    SIZEOF_ARRAY (CaptureMediums),  // IN ULONG            PinCount,
                    CapturePinDirection,            // IN ULONG          * Flags,
                    CaptureMediums,                 // IN KSPIN_MEDIUM   * MediumList,
                    CaptureCategories               // IN GUID           * CategoryList
            );


   // go to usual priority, completing the SRB at the same time
   StreamClassCallAtNewPriority( pSrb->StreamObject, HwDeviceExtension, LowToHigh,
      PHW_PRIORITY_ROUTINE( CompleteDeviceSRB ), pSrb );
}


/*
** AdapterReceivePacket()
**
**   Main entry point for receiving adapter based request SRBs.  This routine
**   will always be called at High Priority.
**
**   Note: This is an asyncronous entry point.  The request does not complete
**         on return from this function, the request only completes when a
**         StreamClassDeviceNotification on this request block, of type
**         DeviceRequestComplete, is issued.
**
** Arguments:
**
**   pSrb - Pointer to the STREAM_REQUEST_BLOCK
**        pSrb->HwDeviceExtension - will be the hardware device extension for
**                                  as initialised in HwInitialise
**
** Returns:
**
** Side Effects:  none
*/

VOID STREAMAPI AdapterReceivePacket( IN PHW_STREAM_REQUEST_BLOCK pSrb )
{
   Trace t("AdapterReceivePacket()");

   BOOL CompleteRequestSynchronously = TRUE;

   //default to success
   pSrb->Status = STATUS_SUCCESS;

   PHW_DEVICE_EXTENSION HwDeviceExtension =
      (PHW_DEVICE_EXTENSION) pSrb->HwDeviceExtension;

   PsDevice *adapter = HwDeviceExtension->psdevice;
   
   //
   // determine the type of packet.
   //

   DebugOut((1, "'AdapterReceivePacket(%x) cmd(%x)\n", pSrb, pSrb->Command));

   switch ( pSrb->Command )  {
   case SRB_INITIALIZE_DEVICE:
      DebugOut((1, "   SRB_INITIALIZE_DEVICE\n"));

      CompleteRequestSynchronously = FALSE;
      // have to schedule a low priority call to open the device because
      // registry functions are used during initialization

      StreamClassCallAtNewPriority( pSrb->StreamObject, pSrb->HwDeviceExtension,
         Low, PHW_PRIORITY_ROUTINE( HwInitialize ), pSrb );

      break;

   case SRB_UNINITIALIZE_DEVICE:
      DebugOut((1, "   SRB_UNINITIALIZE_DEVICE\n"));

      // close the device.

      HwUnInitialize(pSrb);

      break;

   case SRB_OPEN_STREAM:
      DebugOut((1, "   SRB_OPEN_STREAM\n"));

      // open a stream

      AdapterOpenStream( pSrb );

      break;

   case SRB_CLOSE_STREAM:
      DebugOut((1, "   SRB_CLOSE_STREAM\n"));

      // close a stream

      AdapterCloseStream( pSrb );

      break;

   case SRB_GET_STREAM_INFO:
      DebugOut((1, "   SRB_GET_STREAM_INFO\n"));

      //
      // return a block describing all the streams
      //

      AdapterStreamInfo(pSrb);

      break;

   case SRB_GET_DEVICE_PROPERTY:
      DebugOut((1, "   SRB_GET_DEVICE_PROPERTY\n"));
      AdapterGetProperty( pSrb );
      break;

   case SRB_SET_DEVICE_PROPERTY:
      DebugOut((1, "   SRB_SET_DEVICE_PROPERTY\n"));
      AdapterSetProperty( pSrb );
      break;

   case SRB_GET_DATA_INTERSECTION:
      DebugOut((1, "   SRB_GET_DATA_INTERSECTION\n"));

      //
      // Return a format, given a range
      //

      AdapterFormatFromRange( pSrb );

      break;
    case SRB_INITIALIZATION_COMPLETE:
      DebugOut((1, "   SRB_INITIALIZATION_COMPLETE\n"));

        //
        // Stream class has finished initialization.
        // Now create DShow Medium interface BLOBs.
        // This needs to be done at low priority since it uses the registry, so use a callback
        //
        CompleteRequestSynchronously = FALSE;

        StreamClassCallAtNewPriority( NULL /*pSrb->StreamObject*/, pSrb->HwDeviceExtension,
            Low, PHW_PRIORITY_ROUTINE( CompleteInitialization), pSrb );

        break;

   case SRB_PAGING_OUT_DRIVER:
      if ( (*(DWORD*)(gpjBaseAddr+0x10c) & 3) || (*(DWORD*)(gpjBaseAddr+0x104)) )
      {
         DebugOut((0,  "   ****** SRB_PAGING_OUT_DRIVER ENB(%x) MSK(%x)\n",
                     *(DWORD*)(gpjBaseAddr+0x10c) & 3,
                     *(DWORD*)(gpjBaseAddr+0x104)
         ));

         *(DWORD*)(gpjBaseAddr+0x10c) &= ~3;    // disable interrupts   [TMZ] [!!!]
         *(DWORD*)(gpjBaseAddr+0x104) = 0;      // disable interrupts   [TMZ] [!!!]
      }
      break;

   case SRB_UNKNOWN_DEVICE_COMMAND:
      DebugOut((1, "   SRB_UNKNOWN_DEVICE_COMMAND\n"));
      HandleIRP( pSrb );
      break;

    // We should never get the following 2 since this is a single instance
    // device
   case SRB_OPEN_DEVICE_INSTANCE:
   case SRB_CLOSE_DEVICE_INSTANCE:
   default:
       //
       // this is a request that we do not understand.  Indicate invalid
       // command and complete the request
       //

       DebugOut((0, "SRB(%x) not recognized by this driver\n", pSrb->Command));
       pSrb->Status = STATUS_NOT_IMPLEMENTED;
   }

   //
   // Most, but not all SRBs are handled synchronously here
   //

   if ( CompleteRequestSynchronously )  {
      CompleteDeviceSRB( pSrb );
   }
}

/*
** AdapterCancelPacket()
**
**   Request to cancel a packet that is currently in process in the minidriver
**
** Arguments:
**
**   pSrb - pointer to request packet to cancel
**
** Returns:
**
** Side Effects:  none
*/

VOID STREAMAPI AdapterCancelPacket( PHW_STREAM_REQUEST_BLOCK pSrb )
{
   Trace t("AdapterCancelPacket()");

   VideoStream StreamNumber = (VideoStream)pSrb->StreamObject->StreamNumber;

   DebugOut((1, "AdapterCancelPacket - pSrb(%x) strm(%d)\n", pSrb, StreamNumber));

   VideoChannel *chan = (VideoChannel *)((PSTREAMEX)pSrb->StreamObject->HwStreamExtension)->videochannel;

   pSrb->Status = STATUS_CANCELLED;
   //pSrb->Status = STATUS_SUCCESS;       // bugbug
   
   //
   // it is necessary to call the request back correctly.  Determine which
   // type of command it is
   //

   switch ( pSrb->Flags & (SRB_HW_FLAGS_DATA_TRANSFER | SRB_HW_FLAGS_STREAM_REQUEST ) ) {
   //
   // find all stream commands, and do stream notifications
   //

   case SRB_HW_FLAGS_STREAM_REQUEST | SRB_HW_FLAGS_DATA_TRANSFER:
      DebugOut((1, "   Canceling data SRB\n" ) );
//      adapter->Stop( *chan );  [!!!] [TMZ] [???] why is this commented out???
      
      if (!chan->RemoveSRB( pSrb ))
      {
         DebugOut((0, "   Canceling data SRB failed\n"));
         DEBUG_BREAKPOINT();
      }
      break;
   case SRB_HW_FLAGS_STREAM_REQUEST:
      DebugOut((1, "   Canceling control SRB\n" ) );
      CheckSrbStatus( pSrb );
      StreamClassStreamNotification( ReadyForNextStreamControlRequest,
         pSrb->StreamObject );
      StreamClassStreamNotification( StreamRequestComplete,
         pSrb->StreamObject, pSrb );
      break;
   default:
      //
      // this must be a device request.  Use device notifications
      //
      DebugOut((1, "   Canceling SRB per device request\n" ) );
      StreamClassDeviceNotification( ReadyForNextDeviceRequest,
         pSrb->HwDeviceExtension );

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

/*
** AdapterTimeoutPacket()
**
**   This routine is called when a packet has been in the minidriver for
**   too long.  The adapter must decide what to do with the packet
**
** Arguments:
**
**   pSrb - pointer to the request packet that timed out
**
** Returns:
**
** Side Effects:  none
*/

VOID STREAMAPI AdapterTimeoutPacket( PHW_STREAM_REQUEST_BLOCK pSrb )
{
   Trace t("AdapterTimeoutPacket()");

   DebugOut((0, "AdapterTimeoutPacket (incomplete) - pSrb(%x)\n", pSrb));

   // [TMZ] Fix this
   #if SHOW_BUILD_MSGS
      #pragma message("*** AdapterTimeoutPacket needs to be completed")
   #endif

   DebugOut((0, "   pSrb->Flags = %x\n", pSrb->Flags));

   if ( pSrb->Flags & SRB_HW_FLAGS_STREAM_REQUEST )
   {
      DebugOut((0, "                 SRB_HW_FLAGS_STREAM_REQUEST\n"));
   }
   if ( pSrb->Flags & SRB_HW_FLAGS_DATA_TRANSFER )
   {
      DebugOut((0, "                 SRB_HW_FLAGS_DATA_TRANSFER\n"));
   }

   //
   // if we timeout while playing, then we need to consider this
   // condition an error, and reset the hardware, and reset everything
   // as well as cancelling this and all requests
   //


   //
   // if we are not playing, and this is a CTRL request, we still
   // need to reset everything as well as cancelling this and all requests
   //

   //
   // if this is a data request, and the device is paused, we probably have
   // run out of data buffer, and need more time, so just reset the timer,
   // and let the packet continue
   //

   pSrb->TimeoutCounter = pSrb->TimeoutOriginal;
}

/*
** HwInterrupt()
**
**   Routine is called when an interrupt at the IRQ level specified by the
**   ConfigInfo structure passed to the HwInitialize routine is received.
**
**   Note: IRQs may be shared, so the device should ensure the IRQ received
**         was expected
**
** Arguments:
**
**  pHwDevEx - the device extension for the hardware interrupt
**
** Returns:
**
** Side Effects:  none
*/

BOOLEAN HwInterrupt( IN PHW_DEVICE_EXTENSION HwDeviceExtension )
{
   Trace t("HwInterrupt()");
   DebugOut((1, "HwInterrupt called by system\n"));
   PsDevice *adapter = (PsDevice *)(HwDeviceExtension->psdevice);
   BOOLEAN b = adapter->Interrupt();
   return( b );
}

/* Function: CompleteDeviceSRB
 * Purpose: Called to complete a device SRB
 * Input: pSrb
 */
inline void CompleteDeviceSRB( IN OUT PHW_STREAM_REQUEST_BLOCK pSrb )
{
   Trace t("CompleteDeviceSRB()");
   StreamClassDeviceNotification( DeviceRequestComplete, pSrb->HwDeviceExtension, pSrb );
   StreamClassDeviceNotification( ReadyForNextDeviceRequest, pSrb->HwDeviceExtension );
}

/*
** 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 const PKSDATARANGE DataRange1,
   IN const PKSDATARANGE DataRange2 )
{
   Trace t("AdapterCompareGUIDsAndFormatSize()");

   bool bCheckSize = false;

#if 1 // use old guid verify
   return (
      IsEqualGUID( DataRange1->MajorFormat, DataRange2->MajorFormat ) &&
      IsEqualGUID( DataRange1->SubFormat,   DataRange2->SubFormat   ) &&
      IsEqualGUID( DataRange1->Specifier,   DataRange2->Specifier   ) &&
      ( DataRange1->FormatSize == DataRange2->FormatSize ) );
#else // use new guid verify from cc decoder
   bool rval = false;

   if ( IsEqualGUID(DataRange1->MajorFormat, KSDATAFORMAT_TYPE_WILDCARD)
     || IsEqualGUID(DataRange2->MajorFormat, KSDATAFORMAT_TYPE_WILDCARD)
     || IsEqualGUID(DataRange1->MajorFormat, DataRange2->MajorFormat) )
   {
      if ( !IsEqualGUID(DataRange1->MajorFormat, DataRange2->MajorFormat) )
      {
         DebugOut((0, "Match 1\n" ));
      }

      if ( IsEqualGUID(DataRange1->SubFormat, KSDATAFORMAT_SUBTYPE_WILDCARD)
        || IsEqualGUID(DataRange2->SubFormat, KSDATAFORMAT_SUBTYPE_WILDCARD)
        || IsEqualGUID(DataRange1->SubFormat, DataRange2->SubFormat) )
      {
         if ( !IsEqualGUID(DataRange1->SubFormat, DataRange2->SubFormat) )
         {
            DebugOut((0, "Match 2\n" ));
         }

         if ( IsEqualGUID(DataRange1->Specifier, KSDATAFORMAT_SPECIFIER_WILDCARD)
           || IsEqualGUID(DataRange2->Specifier, KSDATAFORMAT_SPECIFIER_WILDCARD)
           || IsEqualGUID(DataRange1->Specifier, DataRange2->Specifier) )
         {
            if ( !IsEqualGUID(DataRange1->Specifier, DataRange2->Specifier) )
            {
               DebugOut((0, "Match 3\n" ));
            }

            if ( !bCheckSize || DataRange1->FormatSize == DataRange2->FormatSize)
            {
               DebugOut((0, "Victory !!!\n" ));
               rval = true;
            }
            else
            {
               DebugOut((0, "FormatSize Mismatch\n" ));
            }
         }
         else
         {
            DebugOut((0, "Specifier Mismatch\n" ));
         }
      }
      else
      {
         DebugOut((0, "SubFormat Mismatch\n" ));
      }
   }
   else
   {
      DebugOut((0, "MajorFormat Mismatch\n" ));
   }

   DebugOut((0, "CompareGUIDsAndFormatSize(\n"));
   DebugOut((0, "   DataRange1=%x\n", DataRange1));
   DebugOut((0, "   DataRange2=%x\n", DataRange2));
   DebugOut((0, "   bCheckSize=%s\n", bCheckSize ? "TRUE":"FALSE"));
   DebugOut((0, ")\n"));
   DebugOut((0, "returning %s\n", rval? "TRUE":"FALSE"));

   return rval;
#endif
}

/*
** AdapterFormatFromRange()
**
**   Returns a DATAFORMAT from a DATARANGE
**
** Arguments:
**
**         IN PHW_STREAM_REQUEST_BLOCK pSrb
**
** Returns:
**
**   TRUE if the format is supported
**   FALSE if the format cannot be suppored
**
** Side Effects:  none
*/
void AdapterFormatFromRange( IN PHW_STREAM_REQUEST_BLOCK pSrb )
{
   Trace t("AdapterFormatFromRange()");

   PSTREAM_DATA_INTERSECT_INFO IntersectInfo;
   PKSDATARANGE                DataRange;
   ULONG                       FormatSize;
   ULONG                       StreamNumber;
   ULONG                       j;
   ULONG                       NumberOfFormatArrayEntries;
   PKSDATAFORMAT               *pAvailableFormats;

   IntersectInfo = pSrb->CommandData.IntersectInfo;
   StreamNumber = IntersectInfo->StreamNumber;
   DataRange = IntersectInfo->DataRange;

   //
   // Check that the stream number is valid
   //

   if( StreamNumber >= DRIVER_STREAM_COUNT ) 
	{
      pSrb->Status = STATUS_NOT_IMPLEMENTED;
      return;
   }

   NumberOfFormatArrayEntries =
           Streams[StreamNumber].hwStreamInfo.NumberOfFormatArrayEntries;

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

   pAvailableFormats = Streams[StreamNumber].hwStreamInfo.StreamFormatsArray;

   //
   // Is the caller trying to get the format, or the size of the format?
   //

   bool OnlyWantsSize = (IntersectInfo->SizeOfDataFormatBuffer == sizeof( ULONG ) );

   //
   // Walk the formats supported by the stream searching for a match
   // of the three GUIDs which together define a DATARANGE
   //

   for ( j = 0; j < NumberOfFormatArrayEntries; j++, pAvailableFormats++ ) 
	{

      if ( !AdapterCompareGUIDsAndFormatSize( DataRange, *pAvailableFormats ) ) 
		{
          continue;
      }

      //
      // Now that the three GUIDs match, switch on the Specifier
      // to do a further type specific check
      //

      // -------------------------------------------------------------------
      // Specifier FORMAT_VideoInfo for VIDEOINFOHEADER
      // -------------------------------------------------------------------

      if ( IsEqualGUID( DataRange->Specifier, KSDATAFORMAT_SPECIFIER_VIDEOINFO ) ) 
		{

         PKS_DATARANGE_VIDEO DataRangeVideoToVerify = (PKS_DATARANGE_VIDEO)DataRange;
         PKS_DATARANGE_VIDEO DataRangeVideo = (PKS_DATARANGE_VIDEO)*pAvailableFormats;

         //
         // 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 ) ) ) 
			{
            DebugOut(( 1, "AdapterFormatFromRange(): at least one field does not match\n" ));
            continue;
         }

         // MATCH FOUND!
         FormatSize = sizeof( KSDATAFORMAT ) +
            KS_SIZE_VIDEOHEADER( &DataRangeVideoToVerify->VideoInfoHeader );

         if ( OnlyWantsSize )
			{
            break;
			}

         // Caller wants the full data format
         if ( IntersectInfo->SizeOfDataFormatBuffer < FormatSize ) 
			{
            DebugOut(( 1, "AdapterFormatFromRange(): STATUS_BUFFER_TOO_SMALL\n" ));
            pSrb->Status = STATUS_BUFFER_TOO_SMALL;
            return;
         }

         // Copy over the KSDATAFORMAT, followed by the
         // actual VideoInfoHeader
         PKS_DATAFORMAT_VIDEOINFOHEADER InterVidHdr =
            (PKS_DATAFORMAT_VIDEOINFOHEADER)IntersectInfo->DataFormatBuffer;

         RtlCopyMemory( &InterVidHdr->DataFormat,
            &DataRangeVideoToVerify->DataRange, sizeof( KSDATARANGE ) );

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

         RtlCopyMemory( &InterVidHdr->VideoInfoHeader,
            &DataRangeVideoToVerify->VideoInfoHeader,
            KS_SIZE_VIDEOHEADER( &DataRangeVideoToVerify->VideoInfoHeader ) );


         // report back the omage size as we know it
         KS_VIDEOINFOHEADER &vidHDR = DataRangeVideoToVerify->VideoInfoHeader;

         #ifdef HACK_FUDGE_RECTANGLES
            // [!!!] [TMZ] - hack
            if( vidHDR.rcTarget.bottom == 0 ) 
				{
               vidHDR.rcTarget.left    = 0;
               vidHDR.rcTarget.top     = 0;
               vidHDR.rcTarget.right   = vidHDR.bmiHeader.biWidth;
               vidHDR.rcTarget.bottom  = abs(vidHDR.bmiHeader.biHeight);
            }
            if( InterVidHdr->VideoInfoHeader.rcTarget.bottom == 0 ) 
				{
               InterVidHdr->VideoInfoHeader.rcTarget.left    = 0;
               InterVidHdr->VideoInfoHeader.rcTarget.top     = 0;
               InterVidHdr->VideoInfoHeader.rcTarget.right   = vidHDR.bmiHeader.biWidth;
               InterVidHdr->VideoInfoHeader.rcTarget.bottom  = abs(vidHDR.bmiHeader.biHeight);
            }
         #endif

         MSize			size;
         GetRequestedSize( vidHDR, size );

         ColorSpace	tmpCol( DataRange->SubFormat );
         MRect			dst( vidHDR.rcTarget );

         // make sure the dimentions are acceptable
         if ( tmpCol.IsValid() && tmpCol.CheckDimentions( size ) &&
              tmpCol.CheckLeftTop( dst.TopLeft() ) ) 
			{
            // if width is different, use it ( in bytes ) to calculate the size
            if ( vidHDR.bmiHeader.biWidth != size.cx )
				{
               InterVidHdr->VideoInfoHeader.bmiHeader.biSizeImage =
                  vidHDR.bmiHeader.biWidth * abs(vidHDR.bmiHeader.biHeight);
				}
            else
				{
               InterVidHdr->VideoInfoHeader.bmiHeader.biSizeImage = size.cx *
                  tmpCol.GetBitCount() * abs(vidHDR.bmiHeader.biHeight) / 8;
				}

            DebugOut((1, "InterVidHdr->VideoInfoHeader.bmiHeader.biSizeImage = %d\n", InterVidHdr->VideoInfoHeader.bmiHeader.biSizeImage));
            break;
         } 
			else 
			{
            pSrb->Status = STATUS_BUFFER_TOO_SMALL;
            DebugOut((1, "AdapterFormatFromRange: Buffer too small\n"));
            return;
         }
      } // End of VIDEOINFOHEADER specifier

      // -------------------------------------------------------------------
      // Specifier FORMAT_VideoInfo2 for VIDEOINFOHEADER2
      // -------------------------------------------------------------------
      else if ( IsEqualGUID( DataRange->Specifier, KSDATAFORMAT_SPECIFIER_VIDEOINFO2 ) ) 
		{

         PKS_DATARANGE_VIDEO2 DataRangeVideoToVerify = (PKS_DATARANGE_VIDEO2) DataRange;
         PKS_DATARANGE_VIDEO2 DataRangeVideo = (PKS_DATARANGE_VIDEO2) *pAvailableFormats;

         //
         // 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 ) ) ) 
			{
            DebugOut(( 1, "AdapterFormatFromRange(): at least one field does not match\n" ));
            continue;
         }

         // MATCH FOUND!
         FormatSize = sizeof( KSDATAFORMAT ) +
            KS_SIZE_VIDEOHEADER2( &DataRangeVideoToVerify->VideoInfoHeader );

         if ( OnlyWantsSize )
			{
            break;
			}

         // Caller wants the full data format
         if ( IntersectInfo->SizeOfDataFormatBuffer < FormatSize ) 
			{
            DebugOut(( 1, "AdapterFormatFromRange(): STATUS_BUFFER_TOO_SMALL\n" ));
            pSrb->Status = STATUS_BUFFER_TOO_SMALL;
            return;
         }

         // Copy over the KSDATAFORMAT, followed by the
         // actual VideoInfoHeader
         PKS_DATAFORMAT_VIDEOINFOHEADER2 InterVidHdr =
            (PKS_DATAFORMAT_VIDEOINFOHEADER2)IntersectInfo->DataFormatBuffer;

         RtlCopyMemory( &InterVidHdr->DataFormat,
            &DataRangeVideoToVerify->DataRange, sizeof( KSDATARANGE ) );

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

         RtlCopyMemory( &InterVidHdr->VideoInfoHeader2,
            &DataRangeVideoToVerify->VideoInfoHeader,
            KS_SIZE_VIDEOHEADER2( &DataRangeVideoToVerify->VideoInfoHeader ) );


         // report back the omage size as we know it
         KS_VIDEOINFOHEADER2 &vidHDR = DataRangeVideoToVerify->VideoInfoHeader;

         #ifdef HACK_FUDGE_RECTANGLES
            // [!!!] [TMZ] - hack
            if( vidHDR.rcTarget.bottom == 0 ) 
				{
               vidHDR.rcTarget.left    = 0;
               vidHDR.rcTarget.top     = 0;
               vidHDR.rcTarget.right   = vidHDR.bmiHeader.biWidth;
               vidHDR.rcTarget.bottom  = abs(vidHDR.bmiHeader.biHeight);
            }
            if( InterVidHdr->VideoInfoHeader.rcTarget.bottom == 0 ) 
				{
               InterVidHdr->VideoInfoHeader.rcTarget.left    = 0;
               InterVidHdr->VideoInfoHeader.rcTarget.top     = 0;
               InterVidHdr->VideoInfoHeader.rcTarget.right   = vidHDR.bmiHeader.biWidth;
               InterVidHdr->VideoInfoHeader.rcTarget.bottom  = abs(vidHDR.bmiHeader.biHeight);
            }
         #endif

         MSize			size;
         GetRequestedSize2( vidHDR, size );

         ColorSpace	tmpCol( DataRange->SubFormat );
         MRect			dst( vidHDR.rcTarget );

         // make sure the dimentions are acceptable
         if ( tmpCol.IsValid() && tmpCol.CheckDimentions( size ) &&
              tmpCol.CheckLeftTop( dst.TopLeft() ) ) 
			{
            // if width is different, use it ( in bytes ) to calculate the size
            if ( vidHDR.bmiHeader.biWidth != size.cx )
				{
               InterVidHdr->VideoInfoHeader2.bmiHeader.biSizeImage =
                  vidHDR.bmiHeader.biWidth * abs(vidHDR.bmiHeader.biHeight);
				}
            else
				{
               InterVidHdr->VideoInfoHeader2.bmiHeader.biSizeImage = size.cx *
                  tmpCol.GetBitCount() * abs(vidHDR.bmiHeader.biHeight) / 8;
				}

            DebugOut((1, "InterVidHdr->VideoInfoHeader2.bmiHeader.biSizeImage = %d\n", InterVidHdr->VideoInfoHeader2.bmiHeader.biSizeImage));
            break;
         } 
			else 
			{
            pSrb->Status = STATUS_BUFFER_TOO_SMALL;
            DebugOut((1, "AdapterFormatFromRange: Buffer too small\n"));
            return;
         }
      } // End of VIDEOINFOHEADER2 specifier

      // -------------------------------------------------------------------
      // Specifier FORMAT_AnalogVideo for KS_ANALOGVIDEOINFO
      // -------------------------------------------------------------------

      else if ( IsEqualGUID( DataRange->Specifier, KSDATAFORMAT_SPECIFIER_ANALOGVIDEO ) ) 
		{

            //
            // For analog video, the DataRange and DataFormat
            // are identical, so just copy the whole structure
            //

            PKS_DATARANGE_ANALOGVIDEO pDataRangeVideo =
               (PKS_DATARANGE_ANALOGVIDEO) *pAvailableFormats;

            // MATCH FOUND!
            FormatSize = sizeof( KS_DATARANGE_ANALOGVIDEO );

            if ( OnlyWantsSize )
				{
               break;
				}

            // Caller wants the full data format
            if ( IntersectInfo->SizeOfDataFormatBuffer < FormatSize ) 
				{
               pSrb->Status = STATUS_BUFFER_TOO_SMALL;
					DebugOut((1, "AdapterFormatFromRange: Buffer too small\n"));
               return;
            }
            RtlCopyMemory( IntersectInfo->DataFormatBuffer,
               pDataRangeVideo, sizeof( KS_DATARANGE_ANALOGVIDEO ) );

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

            break;

      } 
		else 
		{
			if ( IsEqualGUID( DataRange->Specifier, KSDATAFORMAT_SPECIFIER_VBI ) ) 
			{
				PKS_DATARANGE_VIDEO_VBI pDataRangeVBI =
				(PKS_DATARANGE_VIDEO_VBI)*pAvailableFormats;

				FormatSize = sizeof( KS_DATAFORMAT_VBIINFOHEADER );

				if ( OnlyWantsSize )
				{
					break;
				}

				// Caller wants the full data format
				if ( IntersectInfo->SizeOfDataFormatBuffer < FormatSize ) 
				{
					pSrb->Status = STATUS_BUFFER_TOO_SMALL;
					DebugOut((1, "AdapterFormatFromRange: Buffer too small\n"));
					return;
				}
				// Copy over the KSDATAFORMAT, followed by the
				// actual VideoInfoHeader
				PKS_DATAFORMAT_VBIINFOHEADER InterVBIHdr =
					(PKS_DATAFORMAT_VBIINFOHEADER)IntersectInfo->DataFormatBuffer;

				RtlCopyMemory( &InterVBIHdr->DataFormat,
					&pDataRangeVBI->DataRange, sizeof( KSDATARANGE ) );

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

				RtlCopyMemory( &InterVBIHdr->VBIInfoHeader,
					&pDataRangeVBI->VBIInfoHeader, sizeof( KS_VBIINFOHEADER ) );

				break;
			} 
			else 
			{
				DebugOut(( 0, "AdapterFormatFromRange: STATUS_NO_MATCH\n" ));
				pSrb->Status = STATUS_NO_MATCH;
				return;
			}
		}

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

   if ( OnlyWantsSize ) 
	{
		DebugOut(( 2, "AdapterFormatFromRange: only wants size\n" ));
		*(PULONG) IntersectInfo->DataFormatBuffer = FormatSize;
		pSrb->ActualBytesTransferred = sizeof( ULONG );
		return;
   }
   pSrb->ActualBytesTransferred = FormatSize;
	DebugOut(( 2, "AdapterFormatFromRange: done\n" ));

   return;
}
