You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1855 lines
57 KiB
1855 lines
57 KiB
//==========================================================================;
|
|
//
|
|
// 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 - 1999 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
//==========================================================================;
|
|
|
|
#include "strmini.h"
|
|
#include "ksmedia.h"
|
|
#include "capmain.h"
|
|
#include "mediums.h"
|
|
#include "capstrm.h"
|
|
#include "capprop.h"
|
|
#include "capdebug.h"
|
|
|
|
// The only global used by this driver. It is used to keep track of the instance count of
|
|
// the number of times the driver is loaded. This is used to create unique Mediums so that
|
|
// the correct capture, crossbar, tuner, and tvaudio filters all get connected together.
|
|
|
|
UINT GlobalDriverMediumInstanceCount = 0;
|
|
|
|
// Debug Logging
|
|
// 0 = Errors only
|
|
// 1 = Info, stream state changes, stream open close
|
|
// 2 = Verbose trace
|
|
ULONG gDebugLevel = 0;
|
|
|
|
/*
|
|
** DriverEntry()
|
|
**
|
|
** This routine is called when the driver is first loaded by PnP.
|
|
** It in turn, calls upon the stream class to perform registration services.
|
|
**
|
|
** Arguments:
|
|
**
|
|
** DriverObject -
|
|
** Driver object for this driver
|
|
**
|
|
** RegistryPath -
|
|
** Registry path string for this driver's key
|
|
**
|
|
** Returns:
|
|
**
|
|
** Results of StreamClassRegisterAdapter()
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
|
|
ULONG
|
|
DriverEntry (
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
{
|
|
|
|
HW_INITIALIZATION_DATA HwInitData;
|
|
ULONG ReturnValue;
|
|
|
|
DbgLogInfo(("TestCap: DriverEntry\n"));
|
|
|
|
RtlZeroMemory(&HwInitData, sizeof(HwInitData));
|
|
|
|
HwInitData.HwInitializationDataSize = sizeof(HwInitData);
|
|
|
|
//
|
|
// Set the Adapter entry points for the driver
|
|
//
|
|
|
|
HwInitData.HwInterrupt = NULL; // HwInterrupt;
|
|
|
|
HwInitData.HwReceivePacket = AdapterReceivePacket;
|
|
HwInitData.HwCancelPacket = AdapterCancelPacket;
|
|
HwInitData.HwRequestTimeoutHandler = AdapterTimeoutPacket;
|
|
|
|
HwInitData.DeviceExtensionSize = sizeof(HW_DEVICE_EXTENSION);
|
|
HwInitData.PerRequestExtensionSize = sizeof(SRB_EXTENSION);
|
|
HwInitData.FilterInstanceExtensionSize = 0;
|
|
HwInitData.PerStreamExtensionSize = sizeof(STREAMEX);
|
|
HwInitData.BusMasterDMA = FALSE;
|
|
HwInitData.Dma24BitAddresses = FALSE;
|
|
HwInitData.BufferAlignment = 3;
|
|
HwInitData.DmaBufferSize = 0;
|
|
|
|
// Don't rely on the stream class using raised IRQL to synchronize
|
|
// execution. This single paramter most affects the overall structure
|
|
// of the driver.
|
|
|
|
HwInitData.TurnOffSynchronization = TRUE;
|
|
|
|
ReturnValue = StreamClassRegisterAdapter(DriverObject, RegistryPath, &HwInitData);
|
|
|
|
DbgLogInfo(("Testcap: StreamClassRegisterAdapter = %x\n", ReturnValue));
|
|
|
|
return ReturnValue;
|
|
}
|
|
|
|
//==========================================================================;
|
|
// 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
|
|
STREAMAPI
|
|
HwInitialize (
|
|
IN OUT PHW_STREAM_REQUEST_BLOCK pSrb
|
|
)
|
|
{
|
|
STREAM_PHYSICAL_ADDRESS adr;
|
|
ULONG Size;
|
|
PUCHAR pDmaBuf;
|
|
int j;
|
|
|
|
PPORT_CONFIGURATION_INFORMATION ConfigInfo = pSrb->CommandData.ConfigInfo;
|
|
|
|
PHW_DEVICE_EXTENSION pHwDevExt =
|
|
(PHW_DEVICE_EXTENSION)ConfigInfo->HwDeviceExtension;
|
|
|
|
DbgLogInfo(("Testcap: HwInitialize()\n"));
|
|
|
|
if (ConfigInfo->NumberOfAccessRanges != 0) {
|
|
DbgLogError(("Testcap: illegal config info\n"));
|
|
|
|
pSrb->Status = STATUS_NO_SUCH_DEVICE;
|
|
return (FALSE);
|
|
}
|
|
|
|
DbgLogInfo(("TestCap: Number of access ranges = %lx\n", ConfigInfo->NumberOfAccessRanges));
|
|
DbgLogInfo(("TestCap: Memory Range = %lx\n", pHwDevExt->ioBaseLocal));
|
|
DbgLogInfo(("TestCap: IRQ = %lx\n", ConfigInfo->BusInterruptLevel));
|
|
|
|
if (ConfigInfo->NumberOfAccessRanges != 0) {
|
|
pHwDevExt->ioBaseLocal
|
|
= (PULONG)(ULONG_PTR) (ConfigInfo->AccessRanges[0].RangeStart.LowPart);
|
|
}
|
|
|
|
pHwDevExt->Irq = (USHORT)(ConfigInfo->BusInterruptLevel);
|
|
|
|
ConfigInfo->StreamDescriptorSize = sizeof (HW_STREAM_HEADER) +
|
|
DRIVER_STREAM_COUNT * sizeof (HW_STREAM_INFORMATION);
|
|
|
|
pDmaBuf = StreamClassGetDmaBuffer(pHwDevExt);
|
|
|
|
adr = StreamClassGetPhysicalAddress(pHwDevExt,
|
|
NULL, pDmaBuf, DmaBuffer, &Size);
|
|
|
|
// Init Crossbar properties
|
|
pHwDevExt->VideoInputConnected = 0; // TvTuner video is the default
|
|
pHwDevExt->AudioInputConnected = 5; // TvTuner audio is the default
|
|
|
|
// Init VideoProcAmp properties
|
|
pHwDevExt->Brightness = BrightnessDefault;
|
|
pHwDevExt->BrightnessFlags = KSPROPERTY_VIDEOPROCAMP_FLAGS_AUTO;
|
|
pHwDevExt->Contrast = ContrastDefault;
|
|
pHwDevExt->ContrastFlags = KSPROPERTY_VIDEOPROCAMP_FLAGS_AUTO;
|
|
pHwDevExt->ColorEnable = ColorEnableDefault;
|
|
pHwDevExt->ColorEnableFlags = KSPROPERTY_VIDEOPROCAMP_FLAGS_MANUAL;
|
|
|
|
// Init CameraControl properties
|
|
pHwDevExt->Focus = FocusDefault;
|
|
pHwDevExt->FocusFlags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO;
|
|
pHwDevExt->Zoom = ZoomDefault;
|
|
pHwDevExt->ZoomFlags = KSPROPERTY_CAMERACONTROL_FLAGS_AUTO;
|
|
|
|
// Init TvTuner properties
|
|
pHwDevExt->TunerMode = KSPROPERTY_TUNER_MODE_TV;
|
|
pHwDevExt->Channel = 4;
|
|
pHwDevExt->TunerInput = 0;
|
|
pHwDevExt->Busy = 0;
|
|
|
|
// Init TvAudio properties
|
|
pHwDevExt->TVAudioMode = KS_TVAUDIO_MODE_MONO |
|
|
KS_TVAUDIO_MODE_LANG_A ;
|
|
|
|
// Init AnalogVideoDecoder properties
|
|
pHwDevExt->VideoDecoderVideoStandard = KS_AnalogVideo_NTSC_M;
|
|
pHwDevExt->VideoDecoderOutputEnable = FALSE;
|
|
pHwDevExt->VideoDecoderVCRTiming = FALSE;
|
|
|
|
// Init VideoControl properties
|
|
pHwDevExt->VideoControlMode = 0;
|
|
|
|
// Init VideoCompression properties
|
|
pHwDevExt->CompressionSettings.CompressionKeyFrameRate = 15;
|
|
pHwDevExt->CompressionSettings.CompressionPFramesPerKeyFrame = 3;
|
|
pHwDevExt->CompressionSettings.CompressionQuality = 5000;
|
|
|
|
pHwDevExt->PDO = ConfigInfo->RealPhysicalDeviceObject;
|
|
DbgLogInfo(("TestCap: Physical Device Object = %lx\n", pHwDevExt->PDO));
|
|
|
|
for (j = 0; j < MAX_TESTCAP_STREAMS; j++){
|
|
|
|
// For each stream, maintain a separate queue for data and control
|
|
InitializeListHead (&pHwDevExt->StreamSRBList[j]);
|
|
InitializeListHead (&pHwDevExt->StreamControlSRBList[j]);
|
|
KeInitializeSpinLock (&pHwDevExt->StreamSRBSpinLock[j]);
|
|
pHwDevExt->StreamSRBListSize[j] = 0;
|
|
}
|
|
|
|
// Init ProtectionStatus
|
|
pHwDevExt->ProtectionStatus = 0;
|
|
|
|
|
|
// The following allows multiple instance of identical hardware
|
|
// to be installed. GlobalDriverMediumInstanceCount is set in the Medium.Id field.
|
|
|
|
pHwDevExt->DriverMediumInstanceCount = GlobalDriverMediumInstanceCount++;
|
|
AdapterSetInstance (pSrb);
|
|
|
|
DbgLogInfo(("TestCap: Exit, HwInitialize()\n"));
|
|
|
|
pSrb->Status = STATUS_SUCCESS;
|
|
|
|
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
|
|
*/
|
|
|
|
BOOLEAN
|
|
STREAMAPI
|
|
HwUnInitialize (
|
|
PHW_STREAM_REQUEST_BLOCK pSrb
|
|
)
|
|
{
|
|
pSrb->Status = STATUS_SUCCESS;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
** AdapterPowerState()
|
|
**
|
|
** This routine is called when an SRB_CHANGE_POWER_STATE request is received
|
|
**
|
|
** Arguments:
|
|
**
|
|
** pSrb - pointer to stream request block for the Change Power state command
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
|
|
BOOLEAN
|
|
STREAMAPI
|
|
AdapterPowerState (
|
|
PHW_STREAM_REQUEST_BLOCK pSrb
|
|
)
|
|
{
|
|
PHW_DEVICE_EXTENSION pHwDevExt = ((PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension);
|
|
|
|
pHwDevExt->DeviceState = pSrb->CommandData.DeviceState;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
** AdapterSetInstance()
|
|
**
|
|
** This routine is called to set all of the Medium instance fields
|
|
**
|
|
** Arguments:
|
|
**
|
|
** pSrb - pointer to stream request block
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
|
|
VOID
|
|
STREAMAPI
|
|
AdapterSetInstance (
|
|
PHW_STREAM_REQUEST_BLOCK pSrb
|
|
)
|
|
{
|
|
int j;
|
|
PHW_DEVICE_EXTENSION pHwDevExt = ((PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension);
|
|
|
|
// Use our HwDevExt as the instance data on the Mediums
|
|
// This allows multiple instances to be uniquely identified and
|
|
// connected. The value used in .Id is not important, only that
|
|
// it is unique for each hardware connection
|
|
|
|
for (j = 0; j < SIZEOF_ARRAY (TVTunerMediums); j++) {
|
|
TVTunerMediums[j].Id = pHwDevExt->DriverMediumInstanceCount;
|
|
}
|
|
for (j = 0; j < SIZEOF_ARRAY (TVAudioMediums); j++) {
|
|
TVAudioMediums[j].Id = pHwDevExt->DriverMediumInstanceCount;
|
|
}
|
|
for (j = 0; j < SIZEOF_ARRAY (CrossbarMediums); j++) {
|
|
CrossbarMediums[j].Id = pHwDevExt->DriverMediumInstanceCount;
|
|
}
|
|
for (j = 0; j < SIZEOF_ARRAY (CaptureMediums); j++) {
|
|
CaptureMediums[j].Id = pHwDevExt->DriverMediumInstanceCount;
|
|
}
|
|
|
|
pHwDevExt->AnalogVideoInputMedium = CaptureMediums[2];
|
|
}
|
|
|
|
/*
|
|
** AdapterCompleteInitialization()
|
|
**
|
|
** This routine is called when an SRB_COMPLETE_INITIALIZATION request is received
|
|
**
|
|
** Arguments:
|
|
**
|
|
** pSrb - pointer to stream request block
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
|
|
VOID
|
|
STREAMAPI
|
|
AdapterCompleteInitialization (
|
|
PHW_STREAM_REQUEST_BLOCK pSrb
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PHW_DEVICE_EXTENSION pHwDevExt = ((PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension);
|
|
KIRQL KIrql;
|
|
|
|
KIrql = KeGetCurrentIrql();
|
|
|
|
// Create the Registry blobs that DShow uses to create
|
|
// graphs via Mediums
|
|
|
|
// Register the TVTuner
|
|
Status = StreamClassRegisterFilterWithNoKSPins (
|
|
pHwDevExt->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
|
|
);
|
|
|
|
// Register the Crossbar
|
|
Status = StreamClassRegisterFilterWithNoKSPins (
|
|
pHwDevExt->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 (
|
|
pHwDevExt->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 (
|
|
pHwDevExt->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,
|
|
NULL // IN GUID * CategoryList
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
** AdapterOpenStream()
|
|
**
|
|
** This routine is called when an OpenStream SRB request is received.
|
|
** A stream is identified by a stream number, which indexes an array
|
|
** of KSDATARANGE structures. The particular KSDATAFORMAT format to
|
|
** be used is also passed in, which should be verified for validity.
|
|
**
|
|
** Arguments:
|
|
**
|
|
** pSrb - pointer to stream request block for the Open command
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
|
|
VOID
|
|
STREAMAPI
|
|
AdapterOpenStream (
|
|
PHW_STREAM_REQUEST_BLOCK pSrb
|
|
)
|
|
{
|
|
//
|
|
// the stream extension structure is allocated by the stream class driver
|
|
//
|
|
|
|
PSTREAMEX pStrmEx = (PSTREAMEX)pSrb->StreamObject->HwStreamExtension;
|
|
PHW_DEVICE_EXTENSION pHwDevExt = ((PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension);
|
|
int StreamNumber = pSrb->StreamObject->StreamNumber;
|
|
PKSDATAFORMAT pKSDataFormat = pSrb->CommandData.OpenFormat;
|
|
|
|
|
|
RtlZeroMemory(pStrmEx, sizeof(STREAMEX));
|
|
|
|
DbgLogInfo(("TestCap: ------- ADAPTEROPENSTREAM ------- StreamNumber=%d\n", StreamNumber));
|
|
|
|
//
|
|
// check that the stream index requested isn't too high
|
|
// or that the maximum number of instances hasn't been exceeded
|
|
//
|
|
|
|
if (StreamNumber >= DRIVER_STREAM_COUNT || StreamNumber < 0) {
|
|
|
|
pSrb->Status = STATUS_INVALID_PARAMETER;
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check that we haven't exceeded the instance count for this stream
|
|
//
|
|
|
|
if (pHwDevExt->ActualInstances[StreamNumber] >=
|
|
Streams[StreamNumber].hwStreamInfo.NumberOfPossibleInstances) {
|
|
|
|
pSrb->Status = STATUS_INVALID_PARAMETER;
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check the validity of the format being requested
|
|
//
|
|
|
|
if (!AdapterVerifyFormat (pKSDataFormat, StreamNumber)) {
|
|
|
|
pSrb->Status = STATUS_INVALID_PARAMETER;
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// And set the format for the stream
|
|
//
|
|
|
|
if (!VideoSetFormat (pSrb)) {
|
|
|
|
return;
|
|
}
|
|
|
|
ASSERT (pHwDevExt->pStrmEx [StreamNumber] == NULL);
|
|
|
|
// Maintain an array of all the StreamEx structures in the HwDevExt
|
|
// so that we can cancel IRPs from any stream
|
|
|
|
pHwDevExt->pStrmEx [StreamNumber] = (PSTREAMX) pStrmEx;
|
|
|
|
// Set up pointers to the handlers for the stream data and control handlers
|
|
|
|
pSrb->StreamObject->ReceiveDataPacket =
|
|
(PVOID) Streams[StreamNumber].hwStreamObject.ReceiveDataPacket;
|
|
pSrb->StreamObject->ReceiveControlPacket =
|
|
(PVOID) Streams[StreamNumber].hwStreamObject.ReceiveControlPacket;
|
|
|
|
//
|
|
// The DMA flag must be set when the device will be performing DMA directly
|
|
// to the data buffer addresses passed in to the ReceiceDataPacket routines.
|
|
//
|
|
|
|
pSrb->StreamObject->Dma = Streams[StreamNumber].hwStreamObject.Dma;
|
|
|
|
//
|
|
// The PIO flag must be set when the mini driver will be accessing the data
|
|
// buffers passed in using logical addressing
|
|
//
|
|
|
|
pSrb->StreamObject->Pio = Streams[StreamNumber].hwStreamObject.Pio;
|
|
|
|
//
|
|
// How many extra bytes will be passed up from the driver for each frame?
|
|
//
|
|
|
|
pSrb->StreamObject->StreamHeaderMediaSpecific =
|
|
Streams[StreamNumber].hwStreamObject.StreamHeaderMediaSpecific;
|
|
|
|
pSrb->StreamObject->StreamHeaderWorkspace =
|
|
Streams[StreamNumber].hwStreamObject.StreamHeaderWorkspace;
|
|
|
|
//
|
|
// Indicate the clock support available on this stream
|
|
//
|
|
|
|
pSrb->StreamObject->HwClockObject =
|
|
Streams[StreamNumber].hwStreamObject.HwClockObject;
|
|
|
|
//
|
|
// Increment the instance count on this stream
|
|
//
|
|
pHwDevExt->ActualInstances[StreamNumber]++;
|
|
|
|
|
|
// Retain a private copy of the HwDevExt and StreamObject in the stream extension
|
|
// so we can use a timer
|
|
|
|
pStrmEx->pHwDevExt = pHwDevExt; // For timer use
|
|
pStrmEx->pStreamObject = pSrb->StreamObject; // For timer use
|
|
|
|
// Initialize the compression settings
|
|
// These may have been changed from the default values in the HwDevExt
|
|
// before the stream was opened
|
|
pStrmEx->CompressionSettings.CompressionKeyFrameRate =
|
|
pHwDevExt->CompressionSettings.CompressionKeyFrameRate;
|
|
pStrmEx->CompressionSettings.CompressionPFramesPerKeyFrame =
|
|
pHwDevExt->CompressionSettings.CompressionPFramesPerKeyFrame;
|
|
pStrmEx->CompressionSettings.CompressionQuality =
|
|
pHwDevExt->CompressionSettings.CompressionQuality;
|
|
|
|
// Init VideoControl properties
|
|
pStrmEx->VideoControlMode = pHwDevExt->VideoControlMode;
|
|
|
|
// Init VBI variables
|
|
pStrmEx->SentVBIInfoHeader = 0;
|
|
|
|
// init lock for pVideoInfoHeader
|
|
// we need it to serialize using/setting of VideoInfoHeader
|
|
KeInitializeSpinLock (&pStrmEx->lockVideoInfoHeader);
|
|
|
|
DbgLogInfo(("TestCap: AdapterOpenStream Exit\n"));
|
|
|
|
}
|
|
|
|
/*
|
|
** AdapterCloseStream()
|
|
**
|
|
** Close the requested data stream.
|
|
**
|
|
** Note that a stream could be closed arbitrarily in the midst of streaming
|
|
** if a user mode app crashes. Therefore, you must release all outstanding
|
|
** resources, disable interrupts, complete all pending SRBs, and put the
|
|
** stream back into a quiescent condition.
|
|
**
|
|
** Arguments:
|
|
**
|
|
** pSrb the request block requesting to close the stream
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
|
|
VOID
|
|
STREAMAPI
|
|
AdapterCloseStream (
|
|
PHW_STREAM_REQUEST_BLOCK pSrb
|
|
)
|
|
{
|
|
PSTREAMEX pStrmEx = (PSTREAMEX)pSrb->StreamObject->HwStreamExtension;
|
|
PHW_DEVICE_EXTENSION pHwDevExt = ((PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension);
|
|
int StreamNumber = pSrb->StreamObject->StreamNumber;
|
|
KS_VIDEOINFOHEADER *pVideoInfoHdr = pStrmEx->pVideoInfoHeader;
|
|
|
|
DbgLogInfo(("TestCap: -------- ADAPTERCLOSESTREAM ------ StreamNumber=%d\n", StreamNumber));
|
|
|
|
if (pHwDevExt->StreamSRBListSize > 0) {
|
|
VideoQueueCancelAllSRBs (pStrmEx);
|
|
DbgLogError(("TestCap: Outstanding SRBs at stream close!!!\n"));
|
|
}
|
|
|
|
pHwDevExt->ActualInstances[StreamNumber]--;
|
|
|
|
ASSERT (pHwDevExt->pStrmEx [StreamNumber] != 0);
|
|
|
|
pHwDevExt->pStrmEx [StreamNumber] = 0;
|
|
|
|
//
|
|
// the minidriver should free any resources that were allocate at
|
|
// open stream time etc.
|
|
//
|
|
|
|
// Free the variable length VIDEOINFOHEADER
|
|
|
|
if (pVideoInfoHdr) {
|
|
ExFreePool(pVideoInfoHdr);
|
|
pStrmEx->pVideoInfoHeader = NULL;
|
|
}
|
|
|
|
// Make sure we no longer reference the clock
|
|
pStrmEx->hMasterClock = NULL;
|
|
|
|
// Make sure the state is reset to stopped,
|
|
pStrmEx->KSState = KSSTATE_STOP;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
** 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
|
|
STREAMAPI
|
|
AdapterStreamInfo (
|
|
PHW_STREAM_REQUEST_BLOCK pSrb
|
|
)
|
|
{
|
|
int j;
|
|
|
|
PHW_DEVICE_EXTENSION pHwDevExt =
|
|
((PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension);
|
|
|
|
//
|
|
// 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 array of stream information data structures
|
|
//
|
|
|
|
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);
|
|
|
|
// Ugliness. To allow mulitple instances, modify the pointer to the
|
|
// AnalogVideoMedium and save it in our device extension
|
|
|
|
Streams[STREAM_AnalogVideoInput].hwStreamInfo.Mediums =
|
|
&pHwDevExt->AnalogVideoInputMedium;
|
|
// pHwDevExt->AnalogVideoInputMedium = CrossbarMediums[9];
|
|
// pHwDevExt->AnalogVideoInputMedium.Id = pHwDevExt->DriverMediumInstanceCount;
|
|
|
|
//
|
|
// 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 (j = 0; j < DRIVER_STREAM_COUNT; j++) {
|
|
*pstrinfo++ = Streams[j].hwStreamInfo;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
** AdapterReceivePacket()
|
|
**
|
|
** Main entry point for receiving adapter based request SRBs. This routine
|
|
** will always be called at Passive level.
|
|
**
|
|
** Note: This is an asyncronous entry point. The request does not necessarily
|
|
** 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
|
|
)
|
|
{
|
|
PHW_DEVICE_EXTENSION pHwDevExt = ((PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension);
|
|
BOOL Busy;
|
|
|
|
DEBUG_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
|
|
|
|
DbgLogTrace(("TestCap: Receiving Adapter SRB %8x, %x\n", pSrb, pSrb->Command));
|
|
|
|
// The very first time through, we need to initialize the adapter spinlock
|
|
// and queue
|
|
if (!pHwDevExt->AdapterQueueInitialized) {
|
|
InitializeListHead (&pHwDevExt->AdapterSRBList);
|
|
KeInitializeSpinLock (&pHwDevExt->AdapterSpinLock);
|
|
pHwDevExt->AdapterQueueInitialized = TRUE;
|
|
pHwDevExt->ProcessingAdapterSRB = FALSE;
|
|
}
|
|
|
|
//
|
|
// If we're already processing an SRB, add it to the queue
|
|
//
|
|
Busy = AddToListIfBusy (
|
|
pSrb,
|
|
&pHwDevExt->AdapterSpinLock,
|
|
&pHwDevExt->ProcessingAdapterSRB,
|
|
&pHwDevExt->AdapterSRBList);
|
|
|
|
if (Busy) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// This will run until the queue is empty
|
|
//
|
|
while (TRUE) {
|
|
//
|
|
// Assume success
|
|
//
|
|
pSrb->Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// determine the type of packet.
|
|
//
|
|
|
|
switch (pSrb->Command)
|
|
{
|
|
|
|
case SRB_INITIALIZE_DEVICE:
|
|
|
|
// open the device
|
|
|
|
HwInitialize(pSrb);
|
|
|
|
break;
|
|
|
|
case SRB_UNINITIALIZE_DEVICE:
|
|
|
|
// close the device.
|
|
|
|
HwUnInitialize(pSrb);
|
|
|
|
break;
|
|
|
|
case SRB_OPEN_STREAM:
|
|
|
|
// open a stream
|
|
|
|
AdapterOpenStream(pSrb);
|
|
|
|
break;
|
|
|
|
case SRB_CLOSE_STREAM:
|
|
|
|
// close a stream
|
|
|
|
AdapterCloseStream(pSrb);
|
|
|
|
break;
|
|
|
|
case SRB_GET_STREAM_INFO:
|
|
|
|
//
|
|
// return a block describing all the streams
|
|
//
|
|
|
|
AdapterStreamInfo(pSrb);
|
|
|
|
break;
|
|
|
|
case SRB_GET_DATA_INTERSECTION:
|
|
|
|
//
|
|
// Return a format, given a range
|
|
//
|
|
|
|
AdapterFormatFromRange(pSrb);
|
|
|
|
break;
|
|
|
|
case SRB_OPEN_DEVICE_INSTANCE:
|
|
case SRB_CLOSE_DEVICE_INSTANCE:
|
|
|
|
//
|
|
// We should never get these since this is a single instance device
|
|
//
|
|
|
|
TRAP;
|
|
pSrb->Status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
case SRB_GET_DEVICE_PROPERTY:
|
|
|
|
//
|
|
// Get adapter wide properties
|
|
//
|
|
|
|
AdapterGetProperty (pSrb);
|
|
break;
|
|
|
|
case SRB_SET_DEVICE_PROPERTY:
|
|
|
|
//
|
|
// Set adapter wide properties
|
|
//
|
|
|
|
AdapterSetProperty (pSrb);
|
|
break;
|
|
|
|
case SRB_PAGING_OUT_DRIVER:
|
|
|
|
//
|
|
// The driver is being paged out
|
|
// Disable Interrupts if you have them!
|
|
//
|
|
DbgLogInfo(("'Testcap: Receiving SRB_PAGING_OUT_DRIVER -- SRB=%x\n", pSrb));
|
|
break;
|
|
|
|
case SRB_CHANGE_POWER_STATE:
|
|
|
|
//
|
|
// Changing the device power state, D0 ... D3
|
|
//
|
|
DbgLogInfo(("'Testcap: Receiving SRB_CHANGE_POWER_STATE ------ SRB=%x\n", pSrb));
|
|
AdapterPowerState(pSrb);
|
|
break;
|
|
|
|
case SRB_INITIALIZATION_COMPLETE:
|
|
|
|
//
|
|
// Stream class has finished initialization.
|
|
// Now create DShow Medium interface BLOBs.
|
|
// This needs to be done at low priority since it uses the registry
|
|
//
|
|
DbgLogInfo(("'Testcap: Receiving SRB_INITIALIZATION_COMPLETE-- SRB=%x\n", pSrb));
|
|
|
|
AdapterCompleteInitialization (pSrb);
|
|
break;
|
|
|
|
|
|
case SRB_UNKNOWN_DEVICE_COMMAND:
|
|
default:
|
|
|
|
//
|
|
// this is a request that we do not understand. Indicate invalid
|
|
// command and complete the request
|
|
//
|
|
pSrb->Status = STATUS_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
//
|
|
// Indicate back to the Stream Class that we're done with this SRB
|
|
//
|
|
CompleteDeviceSRB (pSrb);
|
|
|
|
//
|
|
// See if there's anything else on the queue
|
|
//
|
|
Busy = RemoveFromListIfAvailable (
|
|
&pSrb,
|
|
&pHwDevExt->AdapterSpinLock,
|
|
&pHwDevExt->ProcessingAdapterSRB,
|
|
&pHwDevExt->AdapterSRBList);
|
|
|
|
if (!Busy) {
|
|
break;
|
|
}
|
|
} // end of while there's anything in the queue
|
|
}
|
|
|
|
/*
|
|
** 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
|
|
)
|
|
{
|
|
PHW_DEVICE_EXTENSION pHwDevExt = (PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension;
|
|
PSTREAMEX pStrmEx;
|
|
int StreamNumber;
|
|
BOOL Found = FALSE;
|
|
|
|
//
|
|
// Run through all the streams the driver has available
|
|
//
|
|
|
|
for (StreamNumber = 0; !Found && (StreamNumber < DRIVER_STREAM_COUNT); StreamNumber++) {
|
|
|
|
//
|
|
// Check to see if the stream is in use
|
|
//
|
|
|
|
if (pStrmEx = (PSTREAMEX) pHwDevExt->pStrmEx[StreamNumber]) {
|
|
|
|
Found = VideoQueueCancelOneSRB (
|
|
pStrmEx,
|
|
pSrb
|
|
);
|
|
|
|
} // if the stream is open
|
|
} // for all streams
|
|
|
|
DbgLogInfo(("TestCap: Cancelling SRB %8x Succeeded=%d\n", pSrb, Found));
|
|
}
|
|
|
|
/*
|
|
** 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
|
|
)
|
|
{
|
|
//
|
|
// Unlike most devices, we need to hold onto data SRBs indefinitely,
|
|
// since the graph could be in a pause state indefinitely
|
|
//
|
|
|
|
DbgLogInfo(("TestCap: Timeout Adapter SRB %8x\n", pSrb));
|
|
|
|
pSrb->TimeoutCounter = pSrb->TimeoutOriginal;
|
|
|
|
}
|
|
|
|
/*
|
|
** CompleteDeviceSRB ()
|
|
**
|
|
** This routine is called when a packet is being completed.
|
|
** The optional second notification type is used to indicate ReadyForNext
|
|
**
|
|
** Arguments:
|
|
**
|
|
** pSrb - pointer to the request packet that timed out
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects:
|
|
**
|
|
*/
|
|
|
|
VOID
|
|
STREAMAPI
|
|
CompleteDeviceSRB (
|
|
IN PHW_STREAM_REQUEST_BLOCK pSrb
|
|
)
|
|
{
|
|
DbgLogTrace(("TestCap: Completing Adapter SRB %8x\n", pSrb));
|
|
|
|
StreamClassDeviceNotification( DeviceRequestComplete, pSrb->HwDeviceExtension, pSrb);
|
|
}
|
|
|
|
/*
|
|
** IsEqualOrWildGUID()
|
|
**
|
|
** Compares two GUIDS like IsEqualGUID(), except allows wildcard matches
|
|
**
|
|
** Arguments:
|
|
**
|
|
** IN GUID *g1
|
|
** IN GUID *g2
|
|
**
|
|
** Returns:
|
|
**
|
|
** TRUE if both GUIDs match or only one is a wildcard
|
|
** FALSE if GUIDs are different or both are wildcards
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
|
|
BOOL
|
|
STREAMAPI
|
|
IsEqualOrWildGUID(IN GUID *g1, IN GUID *g2)
|
|
{
|
|
return (IsEqualGUID(g1, g2) && !IsEqualGUID(g1, &KSDATAFORMAT_TYPE_WILDCARD)
|
|
|| ((IsEqualGUID(g1, &KSDATAFORMAT_TYPE_WILDCARD)
|
|
|| IsEqualGUID(g2, &KSDATAFORMAT_TYPE_WILDCARD))
|
|
&& !IsEqualGUID(g1, g2))
|
|
);
|
|
}
|
|
|
|
/*
|
|
** AdapterCompareGUIDsAndFormatSize()
|
|
**
|
|
** Checks for a match on the three GUIDs and FormatSize
|
|
**
|
|
** Arguments:
|
|
**
|
|
** IN DataRange1
|
|
** IN DataRange2
|
|
** BOOL fCompareFormatSize - TRUE when comparing ranges
|
|
** - FALSE when comparing formats
|
|
**
|
|
** Returns:
|
|
**
|
|
** TRUE if all elements match
|
|
** FALSE if any are different
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
|
|
BOOL
|
|
STREAMAPI
|
|
AdapterCompareGUIDsAndFormatSize(
|
|
IN PKSDATARANGE DataRange1,
|
|
IN PKSDATARANGE DataRange2,
|
|
BOOL fCompareFormatSize
|
|
)
|
|
{
|
|
return (
|
|
IsEqualOrWildGUID (
|
|
&DataRange1->MajorFormat,
|
|
&DataRange2->MajorFormat) &&
|
|
IsEqualOrWildGUID (
|
|
&DataRange1->SubFormat,
|
|
&DataRange2->SubFormat) &&
|
|
IsEqualOrWildGUID (
|
|
&DataRange1->Specifier,
|
|
&DataRange2->Specifier) &&
|
|
(fCompareFormatSize ?
|
|
(DataRange1->FormatSize == DataRange2->FormatSize) : TRUE ));
|
|
}
|
|
|
|
/*
|
|
** MultiplyCheckOverflow()
|
|
**
|
|
** Perform a 32-bit unsigned multiplication and return if the multiplication
|
|
** did not overflow.
|
|
**
|
|
** Arguments:
|
|
**
|
|
** a - first operand
|
|
** b - second operand
|
|
** pab - result
|
|
**
|
|
** Returns:
|
|
**
|
|
** TRUE - no overflow
|
|
** FALSE - overflow occurred
|
|
**
|
|
*/
|
|
|
|
BOOL
|
|
MultiplyCheckOverflow (
|
|
ULONG a,
|
|
ULONG b,
|
|
ULONG *pab
|
|
)
|
|
|
|
{
|
|
|
|
*pab = a * b;
|
|
if ((a == 0) || (((*pab) / a) == b)) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
** AdapterVerifyFormat()
|
|
**
|
|
** Checks the validity of a format request by walking through the
|
|
** array of supported KSDATA_RANGEs for a given stream.
|
|
**
|
|
** Arguments:
|
|
**
|
|
** pKSDataFormat - pointer of a KSDATAFORMAT 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
|
|
STREAMAPI
|
|
AdapterVerifyFormat(
|
|
PKSDATAFORMAT pKSDataFormatToVerify,
|
|
int StreamNumber
|
|
)
|
|
{
|
|
BOOL fOK = FALSE;
|
|
ULONG j;
|
|
ULONG NumberOfFormatArrayEntries;
|
|
PKSDATAFORMAT *pAvailableFormats;
|
|
|
|
|
|
//
|
|
// Check that the stream number is valid
|
|
//
|
|
|
|
if (StreamNumber >= DRIVER_STREAM_COUNT) {
|
|
TRAP;
|
|
return FALSE;
|
|
}
|
|
|
|
NumberOfFormatArrayEntries =
|
|
Streams[StreamNumber].hwStreamInfo.NumberOfFormatArrayEntries;
|
|
|
|
//
|
|
// Get the pointer to the array of available formats
|
|
//
|
|
|
|
pAvailableFormats = Streams[StreamNumber].hwStreamInfo.StreamFormatsArray;
|
|
|
|
|
|
DbgLogInfo(("TestCap: AdapterVerifyFormat, Stream=%d\n", StreamNumber));
|
|
DbgLogInfo(("TestCap: FormatSize=%d\n", pKSDataFormatToVerify->FormatSize));
|
|
DbgLogInfo(("TestCap: MajorFormat=%x\n", pKSDataFormatToVerify->MajorFormat));
|
|
|
|
//
|
|
// Walk the formats supported by the stream
|
|
//
|
|
|
|
for (j = 0; j < NumberOfFormatArrayEntries; j++, pAvailableFormats++) {
|
|
|
|
// Check for a match on the three GUIDs and format size
|
|
|
|
if (!AdapterCompareGUIDsAndFormatSize(
|
|
pKSDataFormatToVerify,
|
|
*pAvailableFormats,
|
|
FALSE /* CompareFormatSize */ )) {
|
|
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 (&pKSDataFormatToVerify->Specifier,
|
|
&KSDATAFORMAT_SPECIFIER_VIDEOINFO) &&
|
|
pKSDataFormatToVerify->FormatSize >=
|
|
sizeof (KS_DATAFORMAT_VIDEOINFOHEADER)) {
|
|
|
|
PKS_DATAFORMAT_VIDEOINFOHEADER pDataFormatVideoInfoHeader =
|
|
(PKS_DATAFORMAT_VIDEOINFOHEADER) pKSDataFormatToVerify;
|
|
PKS_VIDEOINFOHEADER pVideoInfoHdrToVerify =
|
|
(PKS_VIDEOINFOHEADER) &pDataFormatVideoInfoHeader->VideoInfoHeader;
|
|
PKS_DATARANGE_VIDEO pKSDataRangeVideo = (PKS_DATARANGE_VIDEO) *pAvailableFormats;
|
|
KS_VIDEO_STREAM_CONFIG_CAPS *pConfigCaps = &pKSDataRangeVideo->ConfigCaps;
|
|
RECT rcImage;
|
|
|
|
DbgLogInfo(("TestCap: AdapterVerifyFormat\n"));
|
|
DbgLogInfo(("TestCap: pVideoInfoHdrToVerify=%x\n", pVideoInfoHdrToVerify));
|
|
DbgLogInfo(("TestCap: KS_VIDEOINFOHEADER size=%d\n",
|
|
KS_SIZE_VIDEOHEADER (pVideoInfoHdrToVerify)));
|
|
DbgLogInfo(("TestCap: Width=%d Height=%d BitCount=%d\n",
|
|
pVideoInfoHdrToVerify->bmiHeader.biWidth,
|
|
pVideoInfoHdrToVerify->bmiHeader.biHeight,
|
|
pVideoInfoHdrToVerify->bmiHeader.biBitCount));
|
|
DbgLogInfo(("TestCap: biSizeImage=%d\n",
|
|
pVideoInfoHdrToVerify->bmiHeader.biSizeImage));
|
|
|
|
/*
|
|
** HOW BIG IS THE IMAGE REQUESTED (pseudocode follows)
|
|
**
|
|
** if (IsRectEmpty (&rcTarget) {
|
|
** SetRect (&rcImage, 0, 0,
|
|
** BITMAPINFOHEADER.biWidth,
|
|
BITMAPINFOHEADER.biHeight);
|
|
** }
|
|
** else {
|
|
** // Probably rendering to a DirectDraw surface,
|
|
** // where biWidth is used to expressed the "stride"
|
|
** // in units of pixels (not bytes) of the destination surface.
|
|
** // Therefore, use rcTarget to get the actual image size
|
|
**
|
|
** rcImage = rcTarget;
|
|
** }
|
|
*/
|
|
|
|
if ((pVideoInfoHdrToVerify->rcTarget.right -
|
|
pVideoInfoHdrToVerify->rcTarget.left <= 0) ||
|
|
(pVideoInfoHdrToVerify->rcTarget.bottom -
|
|
pVideoInfoHdrToVerify->rcTarget.top <= 0)) {
|
|
|
|
rcImage.left = rcImage.top = 0;
|
|
rcImage.right = pVideoInfoHdrToVerify->bmiHeader.biWidth;
|
|
rcImage.bottom = pVideoInfoHdrToVerify->bmiHeader.biHeight;
|
|
}
|
|
else {
|
|
rcImage = pVideoInfoHdrToVerify->rcTarget;
|
|
}
|
|
|
|
//
|
|
// Check that bmiHeader.biSize is valid since we use it later.
|
|
//
|
|
{
|
|
ULONG VideoHeaderSize = KS_SIZE_VIDEOHEADER (
|
|
pVideoInfoHdrToVerify
|
|
);
|
|
|
|
ULONG DataFormatSize = FIELD_OFFSET (
|
|
KS_DATAFORMAT_VIDEOINFOHEADER, VideoInfoHeader
|
|
) + VideoHeaderSize;
|
|
|
|
if (
|
|
VideoHeaderSize < pVideoInfoHdrToVerify->bmiHeader.biSize ||
|
|
DataFormatSize < VideoHeaderSize ||
|
|
DataFormatSize > pKSDataFormatToVerify -> FormatSize
|
|
) {
|
|
|
|
fOK = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Compute the minimum size of our buffers to validate against.
|
|
// The image synthesis routines synthesize |biHeight| rows of
|
|
// biWidth pixels in either RGB24 or UYVY. In order to ensure
|
|
// safe synthesis into the buffer, we need to know how large an
|
|
// image this will produce.
|
|
//
|
|
// I do this explicitly because of the method that the data is
|
|
// synthesized. A variation of this may or may not be necessary
|
|
// depending on the mechanism the driver in question fills the
|
|
// capture buffers. The important thing is to ensure that they
|
|
// aren't overrun during capture.
|
|
//
|
|
{
|
|
ULONG ImageSize;
|
|
|
|
if (!MultiplyCheckOverflow (
|
|
(ULONG)pVideoInfoHdrToVerify->bmiHeader.biWidth,
|
|
(ULONG)abs (pVideoInfoHdrToVerify->bmiHeader.biHeight),
|
|
&ImageSize
|
|
)) {
|
|
|
|
fOK = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We only support KS_BI_RGB (24) and KS_BI_YUV422 (16), so
|
|
// this is valid for those formats.
|
|
//
|
|
if (!MultiplyCheckOverflow (
|
|
ImageSize,
|
|
(ULONG)(pVideoInfoHdrToVerify->bmiHeader.biBitCount / 8),
|
|
&ImageSize
|
|
)) {
|
|
|
|
fOK = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Valid for the formats we use. Otherwise, this would be
|
|
// checked later.
|
|
//
|
|
if (pVideoInfoHdrToVerify->bmiHeader.biSizeImage <
|
|
ImageSize) {
|
|
|
|
fOK = FALSE;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Perform all other verification tests here!!!
|
|
//
|
|
|
|
//
|
|
// HOORAY, the format passed all of the tests, so we support it
|
|
//
|
|
|
|
fOK = TRUE;
|
|
break;
|
|
|
|
} // End of VIDEOINFOHEADER specifier
|
|
|
|
// -------------------------------------------------------------------
|
|
// Specifier FORMAT_AnalogVideo for KS_ANALOGVIDEOINFO
|
|
// -------------------------------------------------------------------
|
|
|
|
else if (IsEqualGUID (&pKSDataFormatToVerify->Specifier,
|
|
&KSDATAFORMAT_SPECIFIER_ANALOGVIDEO) &&
|
|
pKSDataFormatToVerify->FormatSize >=
|
|
sizeof (KS_DATARANGE_ANALOGVIDEO)) {
|
|
|
|
//
|
|
// For analog video, the DataRange and DataFormat
|
|
// are identical, so just copy the whole structure
|
|
//
|
|
|
|
PKS_DATARANGE_ANALOGVIDEO DataRangeVideo =
|
|
(PKS_DATARANGE_ANALOGVIDEO) *pAvailableFormats;
|
|
|
|
//
|
|
// Perform all other verification tests here!!!
|
|
//
|
|
|
|
fOK = TRUE;
|
|
break;
|
|
|
|
} // End of KS_ANALOGVIDEOINFO specifier
|
|
|
|
// -------------------------------------------------------------------
|
|
// Specifier FORMAT_VBI for KS_VIDEO_VBI
|
|
// -------------------------------------------------------------------
|
|
|
|
else if (IsEqualGUID (&pKSDataFormatToVerify->Specifier,
|
|
&KSDATAFORMAT_SPECIFIER_VBI) &&
|
|
pKSDataFormatToVerify->FormatSize >=
|
|
sizeof (KS_DATAFORMAT_VBIINFOHEADER))
|
|
{
|
|
//
|
|
// Do some VBI-specific tests
|
|
//
|
|
PKS_DATAFORMAT_VBIINFOHEADER pKSVBIDataFormat;
|
|
|
|
DbgLogInfo(("Testcap: This is a VBIINFOHEADER format pin.\n" ));
|
|
|
|
pKSVBIDataFormat = (PKS_DATAFORMAT_VBIINFOHEADER)pKSDataFormatToVerify;
|
|
|
|
//
|
|
// Check VideoStandard, we only support NTSC_M
|
|
//
|
|
if (KS_AnalogVideo_NTSC_M
|
|
== pKSVBIDataFormat->VBIInfoHeader.VideoStandard)
|
|
{
|
|
fOK = TRUE;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
DbgLogError(
|
|
("Testcap: AdapterVerifyFormat : VideoStandard(%d) != NTSC_M\n",
|
|
pKSVBIDataFormat->VBIInfoHeader.VideoStandard));
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
// Type FORMAT_NABTS for NABTS pin
|
|
// -------------------------------------------------------------------
|
|
|
|
else if (IsEqualGUID (&pKSDataFormatToVerify->SubFormat,
|
|
&KSDATAFORMAT_SUBTYPE_NABTS))
|
|
{
|
|
fOK = TRUE;
|
|
break;
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
// for CC pin
|
|
// -------------------------------------------------------------------
|
|
|
|
else if (IsEqualGUID (&pKSDataFormatToVerify->SubFormat,
|
|
&KSDATAFORMAT_SUBTYPE_CC))
|
|
{
|
|
fOK = TRUE;
|
|
break;
|
|
}
|
|
|
|
} // End of loop on all formats for this stream
|
|
|
|
return fOK;
|
|
}
|
|
|
|
/*
|
|
** AdapterFormatFromRange()
|
|
**
|
|
** Produces a DATAFORMAT given a DATARANGE.
|
|
**
|
|
** Think of a DATARANGE as a multidimensional space of all of the possible image
|
|
** sizes, cropping, scaling, and framerate possibilities. Here, the caller
|
|
** is saying "Out of this set of possibilities, could you verify that my
|
|
** request is acceptable?". The resulting singular output is a DATAFORMAT.
|
|
** Note that each different colorspace (YUV vs RGB8 vs RGB24)
|
|
** must be represented as a separate DATARANGE.
|
|
**
|
|
** Generally, the resulting DATAFORMAT will be immediately used to open a stream
|
|
** in that format.
|
|
**
|
|
** Arguments:
|
|
**
|
|
** IN PHW_STREAM_REQUEST_BLOCK pSrb
|
|
**
|
|
** Returns:
|
|
**
|
|
** TRUE if the format is supported
|
|
** FALSE if the format cannot be suppored
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
|
|
BOOL
|
|
STREAMAPI
|
|
AdapterFormatFromRange(
|
|
IN PHW_STREAM_REQUEST_BLOCK pSrb
|
|
)
|
|
{
|
|
PSTREAM_DATA_INTERSECT_INFO IntersectInfo;
|
|
PKSDATARANGE DataRange;
|
|
BOOL OnlyWantsSize;
|
|
BOOL MatchFound = FALSE;
|
|
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;
|
|
TRAP;
|
|
return FALSE;
|
|
}
|
|
|
|
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?
|
|
//
|
|
|
|
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,
|
|
TRUE /* CompareFormatSize */)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Now that the three GUIDs match, 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;
|
|
PKS_DATAFORMAT_VIDEOINFOHEADER DataFormatVideoInfoHeaderOut;
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// KS_SIZE_VIDEOHEADER() below is relying on bmiHeader.biSize from
|
|
// the caller's data range. This **MUST** be validated; the
|
|
// extended bmiHeader size (biSize) must not extend past the end
|
|
// of the range buffer. Possible arithmetic overflow is also
|
|
// checked for.
|
|
//
|
|
{
|
|
ULONG VideoHeaderSize = KS_SIZE_VIDEOHEADER (
|
|
&DataRangeVideoToVerify->VideoInfoHeader
|
|
);
|
|
|
|
ULONG DataRangeSize =
|
|
FIELD_OFFSET (KS_DATARANGE_VIDEO, VideoInfoHeader) +
|
|
VideoHeaderSize;
|
|
|
|
//
|
|
// Check that biSize does not extend past the buffer. The
|
|
// first two checks are for arithmetic overflow on the
|
|
// operations to compute the alleged size. (On unsigned
|
|
// math, a+b < a iff an arithmetic overflow occurred).
|
|
//
|
|
if (
|
|
VideoHeaderSize < DataRangeVideoToVerify->
|
|
VideoInfoHeader.bmiHeader.biSize ||
|
|
DataRangeSize < VideoHeaderSize ||
|
|
DataRangeSize > DataRangeVideoToVerify ->
|
|
DataRange.FormatSize
|
|
) {
|
|
|
|
pSrb->Status = STATUS_INVALID_PARAMETER;
|
|
return FALSE;
|
|
|
|
}
|
|
}
|
|
|
|
// MATCH FOUND!
|
|
MatchFound = TRUE;
|
|
FormatSize = sizeof (KSDATAFORMAT) +
|
|
KS_SIZE_VIDEOHEADER (&DataRangeVideoToVerify->VideoInfoHeader);
|
|
|
|
if (OnlyWantsSize) {
|
|
break;
|
|
}
|
|
|
|
// Caller wants the full data format
|
|
if (IntersectInfo->SizeOfDataFormatBuffer < FormatSize) {
|
|
pSrb->Status = STATUS_BUFFER_TOO_SMALL;
|
|
return FALSE;
|
|
}
|
|
|
|
// Copy over the KSDATAFORMAT, followed by the
|
|
// actual VideoInfoHeader
|
|
|
|
DataFormatVideoInfoHeaderOut = (PKS_DATAFORMAT_VIDEOINFOHEADER) IntersectInfo->DataFormatBuffer;
|
|
|
|
// Copy over the KSDATAFORMAT
|
|
RtlCopyMemory(
|
|
&DataFormatVideoInfoHeaderOut->DataFormat,
|
|
&DataRangeVideoToVerify->DataRange,
|
|
sizeof (KSDATARANGE));
|
|
|
|
DataFormatVideoInfoHeaderOut->DataFormat.FormatSize = FormatSize;
|
|
|
|
// Copy over the callers requested VIDEOINFOHEADER
|
|
|
|
RtlCopyMemory(
|
|
&DataFormatVideoInfoHeaderOut->VideoInfoHeader,
|
|
&DataRangeVideoToVerify->VideoInfoHeader,
|
|
KS_SIZE_VIDEOHEADER (&DataRangeVideoToVerify->VideoInfoHeader));
|
|
|
|
// Calculate biSizeImage for this request, and put the result in both
|
|
// the biSizeImage field of the bmiHeader AND in the SampleSize field
|
|
// of the DataFormat.
|
|
//
|
|
// Note that for compressed sizes, this calculation will probably not
|
|
// be just width * height * bitdepth
|
|
|
|
DataFormatVideoInfoHeaderOut->VideoInfoHeader.bmiHeader.biSizeImage =
|
|
DataFormatVideoInfoHeaderOut->DataFormat.SampleSize =
|
|
KS_DIBSIZE(DataFormatVideoInfoHeaderOut->VideoInfoHeader.bmiHeader);
|
|
|
|
//
|
|
// Perform other validation such as cropping and scaling checks
|
|
//
|
|
|
|
break;
|
|
|
|
} // End of VIDEOINFOHEADER 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 DataRangeVideo =
|
|
(PKS_DATARANGE_ANALOGVIDEO) *pAvailableFormats;
|
|
|
|
// MATCH FOUND!
|
|
MatchFound = TRUE;
|
|
FormatSize = sizeof (KS_DATARANGE_ANALOGVIDEO);
|
|
|
|
if (OnlyWantsSize) {
|
|
break;
|
|
}
|
|
|
|
// Caller wants the full data format
|
|
if (IntersectInfo->SizeOfDataFormatBuffer < FormatSize) {
|
|
pSrb->Status = STATUS_BUFFER_TOO_SMALL;
|
|
return FALSE;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
IntersectInfo->DataFormatBuffer,
|
|
DataRangeVideo,
|
|
sizeof (KS_DATARANGE_ANALOGVIDEO));
|
|
|
|
((PKSDATAFORMAT)IntersectInfo->DataFormatBuffer)->FormatSize = FormatSize;
|
|
|
|
break;
|
|
|
|
} // End of KS_ANALOGVIDEOINFO specifier
|
|
|
|
// -------------------------------------------------------------------
|
|
// Specifier FORMAT_VBI for KS_VIDEO_VBI
|
|
// -------------------------------------------------------------------
|
|
|
|
else if (IsEqualGUID (&DataRange->Specifier,
|
|
&KSDATAFORMAT_SPECIFIER_VBI))
|
|
{
|
|
PKS_DATARANGE_VIDEO_VBI pDataRangeVBI =
|
|
(PKS_DATARANGE_VIDEO_VBI)*pAvailableFormats;
|
|
PKS_DATAFORMAT_VBIINFOHEADER InterVBIHdr =
|
|
(PKS_DATAFORMAT_VBIINFOHEADER)IntersectInfo->DataFormatBuffer;
|
|
|
|
// MATCH FOUND!
|
|
MatchFound = TRUE;
|
|
|
|
FormatSize = sizeof (KS_DATAFORMAT_VBIINFOHEADER);
|
|
|
|
// Is the caller trying to get the format, or the size of it?
|
|
if (OnlyWantsSize)
|
|
break;
|
|
|
|
// Verify that there is enough room in the supplied buffer
|
|
// for the whole thing
|
|
if (IntersectInfo->SizeOfDataFormatBuffer < FormatSize)
|
|
{
|
|
if (IntersectInfo->SizeOfDataFormatBuffer > 0) {
|
|
DbgLogError(
|
|
("Testcap::AdapterFormatFromRange: "
|
|
"Specifier==VBI, Buffer too small=%d vs. %d\n",
|
|
IntersectInfo->SizeOfDataFormatBuffer,
|
|
FormatSize));
|
|
}
|
|
pSrb->Status = STATUS_BUFFER_TOO_SMALL;
|
|
return FALSE;
|
|
}
|
|
|
|
// If there is room, go ahead...
|
|
|
|
RtlCopyMemory(&InterVBIHdr->DataFormat,
|
|
&pDataRangeVBI->DataRange,
|
|
sizeof (KSDATARANGE));
|
|
|
|
((PKSDATAFORMAT)IntersectInfo->DataFormatBuffer)->FormatSize = FormatSize;
|
|
|
|
RtlCopyMemory(&InterVBIHdr->VBIInfoHeader,
|
|
&pDataRangeVBI->VBIInfoHeader,
|
|
sizeof(KS_VBIINFOHEADER));
|
|
|
|
break;
|
|
|
|
} // End of KS_VIDEO_VBI specifier
|
|
|
|
// -------------------------------------------------------------------
|
|
// Type FORMAT_NABTS for NABTS pin
|
|
// -------------------------------------------------------------------
|
|
|
|
else if (IsEqualGUID (&DataRange->SubFormat,
|
|
&KSDATAFORMAT_SUBTYPE_NABTS))
|
|
{
|
|
PKSDATARANGE pDataRange = (PKSDATARANGE)*pAvailableFormats;
|
|
|
|
// MATCH FOUND!
|
|
MatchFound = TRUE;
|
|
|
|
FormatSize = sizeof (KSDATAFORMAT);
|
|
|
|
// Is the caller trying to get the format, or the size of it?
|
|
if (OnlyWantsSize)
|
|
break;
|
|
|
|
// Verify that there is enough room in the supplied buffer
|
|
// for the whole thing
|
|
if (IntersectInfo->SizeOfDataFormatBuffer >= FormatSize)
|
|
{
|
|
RtlCopyMemory(IntersectInfo->DataFormatBuffer,
|
|
pDataRange,
|
|
FormatSize);
|
|
|
|
((PKSDATAFORMAT)IntersectInfo->DataFormatBuffer)->FormatSize = FormatSize;
|
|
}
|
|
else
|
|
{
|
|
if (IntersectInfo->SizeOfDataFormatBuffer > 0) {
|
|
DbgLogError(
|
|
("Testcap::AdapterFormatFromRange: "
|
|
"SubFormat==NABTS, Buffer too small=%d vs. %d\n",
|
|
IntersectInfo->SizeOfDataFormatBuffer,
|
|
FormatSize));
|
|
}
|
|
pSrb->Status = STATUS_BUFFER_TOO_SMALL;
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
} // End of KS_SUBTYPE_NABTS
|
|
|
|
// -------------------------------------------------------------------
|
|
// for CC pin
|
|
// -------------------------------------------------------------------
|
|
|
|
else if (IsEqualGUID (&DataRange->SubFormat,
|
|
&KSDATAFORMAT_SUBTYPE_CC))
|
|
{
|
|
PKSDATARANGE pDataRange = (PKSDATARANGE)*pAvailableFormats;
|
|
|
|
// MATCH FOUND!
|
|
MatchFound = TRUE;
|
|
|
|
FormatSize = sizeof (KSDATAFORMAT);
|
|
|
|
// Is the caller trying to get the format, or the size of it?
|
|
if (OnlyWantsSize)
|
|
break;
|
|
|
|
// Verify that there is enough room in the supplied buffer
|
|
// for the whole thing
|
|
if (IntersectInfo->SizeOfDataFormatBuffer >= FormatSize)
|
|
{
|
|
RtlCopyMemory(IntersectInfo->DataFormatBuffer,
|
|
pDataRange,
|
|
FormatSize);
|
|
|
|
((PKSDATAFORMAT)IntersectInfo->DataFormatBuffer)->FormatSize = FormatSize;
|
|
}
|
|
else
|
|
{
|
|
if (IntersectInfo->SizeOfDataFormatBuffer > 0) {
|
|
DbgLogError(
|
|
("Testcap::AdapterFormatFromRange: "
|
|
"SubFormat==CC, Buffer too small=%d vs. %d\n",
|
|
IntersectInfo->SizeOfDataFormatBuffer,
|
|
FormatSize));
|
|
}
|
|
pSrb->Status = STATUS_BUFFER_TOO_SMALL;
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
} // End of CC pin format check
|
|
|
|
else {
|
|
pSrb->Status = STATUS_NO_MATCH;
|
|
return FALSE;
|
|
}
|
|
|
|
} // End of loop on all formats for this stream
|
|
|
|
if (!MatchFound) {
|
|
pSrb->Status = STATUS_NO_MATCH;
|
|
return FALSE;
|
|
}
|
|
|
|
if (OnlyWantsSize) {
|
|
*(PULONG) IntersectInfo->DataFormatBuffer = FormatSize;
|
|
FormatSize = sizeof(ULONG);
|
|
}
|
|
pSrb->ActualBytesTransferred = FormatSize;
|
|
return TRUE;
|
|
}
|