mirror of https://github.com/tongzx/nt5src
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.
3700 lines
120 KiB
3700 lines
120 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1999 - 2000
|
|
|
|
Module Name:
|
|
|
|
MSTpGuts.c
|
|
|
|
Abstract:
|
|
|
|
Main service functions.
|
|
|
|
Last changed by:
|
|
|
|
Author: Yee J. Wu
|
|
|
|
Environment:
|
|
|
|
Kernel mode only
|
|
|
|
Revision History:
|
|
|
|
$Revision:: $
|
|
$Date:: $
|
|
|
|
--*/
|
|
|
|
#include "strmini.h"
|
|
#include "ksmedia.h"
|
|
|
|
#include "1394.h"
|
|
#include "61883.h"
|
|
#include "avc.h"
|
|
|
|
#include "dbg.h"
|
|
#include "ksguid.h"
|
|
|
|
#include "MsTpFmt.h" // Before MsTpDefs.h
|
|
#include "MsTpDef.h"
|
|
|
|
#include "MsTpGuts.h"
|
|
#include "MsTpUtil.h"
|
|
#include "MsTpAvc.h"
|
|
|
|
#include "XPrtDefs.h"
|
|
#include "EDevCtrl.h"
|
|
|
|
// Support MPEG2TS stride data format MPEG2_TRANSPORT_STRIDE
|
|
#include "BdaTypes.h"
|
|
|
|
//
|
|
// Define formats supported
|
|
//
|
|
#include "strmdata.h"
|
|
|
|
|
|
NTSTATUS
|
|
AVCTapeGetDevInfo(
|
|
IN PDVCR_EXTENSION pDevExt,
|
|
IN PAV_61883_REQUEST pAVReq
|
|
);
|
|
VOID
|
|
AVCTapeIniStrmExt(
|
|
PHW_STREAM_OBJECT pStrmObject,
|
|
PSTREAMEX pStrmExt,
|
|
PDVCR_EXTENSION pDevExt,
|
|
PSTREAM_INFO_AND_OBJ pStream
|
|
);
|
|
NTSTATUS
|
|
DVStreamGetConnectionProperty (
|
|
PDVCR_EXTENSION pDevExt,
|
|
PSTREAM_PROPERTY_DESCRIPTOR pSPD,
|
|
PULONG pulActualBytesTransferred
|
|
);
|
|
NTSTATUS
|
|
DVGetDroppedFramesProperty(
|
|
PDVCR_EXTENSION pDevExt,
|
|
PSTREAMEX pStrmExt,
|
|
PSTREAM_PROPERTY_DESCRIPTOR pSPD,
|
|
PULONG pulBytesTransferred
|
|
);
|
|
|
|
#if 0 // Enable later
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, AVCTapeGetDevInfo)
|
|
#pragma alloc_text(PAGE, AVCTapeInitialize)
|
|
#pragma alloc_text(PAGE, AVCTapeGetStreamInfo)
|
|
#pragma alloc_text(PAGE, AVCTapeVerifyDataFormat)
|
|
#pragma alloc_text(PAGE, AVCTapeGetDataIntersection)
|
|
#pragma alloc_text(PAGE, AVCTapeIniStrmExt)
|
|
#pragma alloc_text(PAGE, AVCTapeOpenStream)
|
|
#pragma alloc_text(PAGE, AVCTapeCloseStream)
|
|
#pragma alloc_text(PAGE, DVChangePower)
|
|
#pragma alloc_text(PAGE, AVCTapeSurpriseRemoval)
|
|
#pragma alloc_text(PAGE, AVCTapeProcessPnPBusReset)
|
|
#pragma alloc_text(PAGE, AVCTapeUninitialize)
|
|
|
|
#pragma alloc_text(PAGE, DVStreamGetConnectionProperty)
|
|
#pragma alloc_text(PAGE, DVGetDroppedFramesProperty)
|
|
#pragma alloc_text(PAGE, DVGetStreamProperty)
|
|
#pragma alloc_text(PAGE, DVSetStreamProperty)
|
|
#pragma alloc_text(PAGE, AVCTapeOpenCloseMasterClock)
|
|
#pragma alloc_text(PAGE, AVCTapeIndicateMasterClock)
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
NTSTATUS
|
|
AVCStrmReqIrpSynchCR(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PKEVENT Event
|
|
)
|
|
{
|
|
#if DBG
|
|
if(!NT_SUCCESS(pIrp->IoStatus.Status)) {
|
|
TRACE(TL_FCP_WARNING,("AVCStrmReqIrpSynchCR: pIrp->IoStatus.Status:%x\n", pIrp->IoStatus.Status));
|
|
}
|
|
#endif
|
|
KeSetEvent(Event, 0, FALSE);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
} // AVCStrmReqIrpSynchCR
|
|
|
|
|
|
NTSTATUS
|
|
AVCStrmReqSubmitIrpSynch(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PAVC_STREAM_REQUEST_BLOCK pAVCStrmReq
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
KEVENT Event;
|
|
PIO_STACK_LOCATION NextIrpStack;
|
|
|
|
|
|
Status = STATUS_SUCCESS;;
|
|
|
|
NextIrpStack = IoGetNextIrpStackLocation(pIrp);
|
|
NextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
NextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_AVCSTRM_CLASS;
|
|
NextIrpStack->Parameters.Others.Argument1 = pAVCStrmReq;
|
|
|
|
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
|
|
|
IoSetCompletionRoutine(
|
|
pIrp,
|
|
AVCStrmReqIrpSynchCR,
|
|
&Event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
Status =
|
|
IoCallDriver(
|
|
DeviceObject,
|
|
pIrp
|
|
);
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
|
|
TRACE(TL_PNP_TRACE,("(AVCStrm) Irp is pending...\n"));
|
|
|
|
if(KeGetCurrentIrql() < DISPATCH_LEVEL) {
|
|
KeWaitForSingleObject(
|
|
&Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
TRACE(TL_PNP_TRACE,("Irp has completed; IoStatus.Status %x\n", pIrp->IoStatus.Status));
|
|
Status = pIrp->IoStatus.Status; // Final status
|
|
|
|
}
|
|
else {
|
|
ASSERT(FALSE && "Pending but in DISPATCH_LEVEL!");
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
TRACE(TL_PNP_TRACE,("AVCStrmReqSubmitIrpSynch: IoCallDriver, Status:%x\n", Status));
|
|
|
|
return Status;
|
|
} // AVCStrmReqSubmitIrpSynch
|
|
|
|
|
|
NTSTATUS
|
|
AVCReqIrpSynchCR(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PKEVENT Event
|
|
)
|
|
{
|
|
#if DBG
|
|
if(!NT_SUCCESS(pIrp->IoStatus.Status)) {
|
|
TRACE(TL_PNP_WARNING,("AVCReqIrpSynchCR: pIrp->IoStatus.Status:%x\n", pIrp->IoStatus.Status));
|
|
}
|
|
#endif
|
|
KeSetEvent(Event, 0, FALSE);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
} // AVCReqIrpSynchCR
|
|
|
|
|
|
NTSTATUS
|
|
AVCReqSubmitIrpSynch(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PAVC_MULTIFUNC_IRB pAvcIrbReq
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
KEVENT Event;
|
|
PIO_STACK_LOCATION NextIrpStack;
|
|
|
|
|
|
Status = STATUS_SUCCESS;;
|
|
|
|
NextIrpStack = IoGetNextIrpStackLocation(pIrp);
|
|
NextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
NextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_AVC_CLASS;
|
|
NextIrpStack->Parameters.Others.Argument1 = pAvcIrbReq;
|
|
|
|
KeInitializeEvent(&Event, NotificationEvent, FALSE);
|
|
|
|
IoSetCompletionRoutine(
|
|
pIrp,
|
|
AVCReqIrpSynchCR,
|
|
&Event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
Status =
|
|
IoCallDriver(
|
|
DeviceObject,
|
|
pIrp
|
|
);
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
|
|
TRACE(TL_PNP_TRACE,("(AVC) Irp is pending...\n"));
|
|
|
|
if(KeGetCurrentIrql() < DISPATCH_LEVEL) {
|
|
KeWaitForSingleObject(
|
|
&Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
TRACE(TL_PNP_TRACE,("Irp has completed; IoStatus.Status %x\n", pIrp->IoStatus.Status));
|
|
Status = pIrp->IoStatus.Status; // Final status
|
|
|
|
}
|
|
else {
|
|
ASSERT(FALSE && "Pending but in DISPATCH_LEVEL!");
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
TRACE(TL_PNP_TRACE,("AVCReqSubmitIrpSynch: IoCallDriver, Status:%x\n", Status));
|
|
|
|
return Status;
|
|
} // AVCReqSubmitIrpSynch
|
|
|
|
VOID
|
|
DVIniDevExtStruct(
|
|
IN PDVCR_EXTENSION pDevExt,
|
|
IN PPORT_CONFIGURATION_INFORMATION pConfigInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialiaze the device extension structure.
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
|
|
|
|
RtlZeroMemory( pDevExt, sizeof(DVCR_EXTENSION) );
|
|
|
|
//
|
|
// Cache what are in ConfigInfo in device extension
|
|
//
|
|
pDevExt->pBusDeviceObject = pConfigInfo->PhysicalDeviceObject; // IoCallDriver()
|
|
pDevExt->pPhysicalDeviceObject = pConfigInfo->RealPhysicalDeviceObject; // Used in PnP API
|
|
|
|
//
|
|
// Allow only one stream open at a time to avoid cyclic format
|
|
//
|
|
pDevExt->cndStrmOpen = 0;
|
|
|
|
//
|
|
// Serialize in the event of getting two consecutive SRB_OPEN_STREAMs
|
|
//
|
|
KeInitializeMutex( &pDevExt->hMutex, 0); // Level 0 and in Signal state
|
|
|
|
|
|
//
|
|
// Initialize our pointer to stream extension
|
|
//
|
|
for (i=0; i<pDevExt->NumOfPins; i++) {
|
|
pDevExt->paStrmExt[i] = NULL;
|
|
}
|
|
|
|
//
|
|
// Bus reset, surprise removal
|
|
//
|
|
pDevExt->bDevRemoved = FALSE;
|
|
|
|
pDevExt->PowerState = PowerDeviceD0;
|
|
|
|
//
|
|
// External device control (AV/C commands)
|
|
//
|
|
KeInitializeSpinLock( &pDevExt->AVCCmdLock ); // To guard the count
|
|
|
|
pDevExt->cntCommandQueued = 0; // Cmd that is completed its life cycle waiting to be read (most for RAW_AVC's Set/Read model)
|
|
|
|
InitializeListHead(&pDevExt->AVCCmdList);
|
|
|
|
// Initialize the list of possible opcode values of the response
|
|
// from a Transport State status or notify command. The first item
|
|
// is the number of values that follow.
|
|
ASSERT(sizeof(pDevExt->TransportModes) == 5);
|
|
pDevExt->TransportModes[0] = 4;
|
|
pDevExt->TransportModes[1] = 0xC1;
|
|
pDevExt->TransportModes[2] = 0xC2;
|
|
pDevExt->TransportModes[3] = 0xC3;
|
|
pDevExt->TransportModes[4] = 0xC4;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AVCTapeGetDevInfo(
|
|
IN PDVCR_EXTENSION pDevExt,
|
|
IN PAV_61883_REQUEST pAVReq
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Issue AVC command to determine basic device information and cache them in the device extension.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PIRP pIrp;
|
|
BYTE bAvcBuf[MAX_FCP_PAYLOAD_SIZE]; // For issue AV/C command within this module
|
|
PKSPROPERTY_EXTXPORT_S pXPrtProperty; // Point to bAvcBuf;
|
|
KSPROPERTY_EXTDEVICE_S XDevProperty; // External device property
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
pIrp = IoAllocateIrp(pDevExt->pBusDeviceObject->StackSize, FALSE);
|
|
if(!pIrp) {
|
|
ASSERT(pIrp && "IoAllocateIrp() failed!");
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
|
|
//
|
|
// The input and output plug arrays are at the end of the device extension
|
|
//
|
|
pDevExt->pDevOutPlugs = (PAVC_DEV_PLUGS) ((PBYTE) pDevExt + sizeof(DVCR_EXTENSION));
|
|
pDevExt->pDevInPlugs = (PAVC_DEV_PLUGS) ((PBYTE) pDevExt + sizeof(DVCR_EXTENSION) + sizeof(AVC_DEV_PLUGS));
|
|
|
|
|
|
//
|
|
// Get unit's capabilities indirectly from 61883.sys
|
|
// Speed
|
|
//
|
|
|
|
Status = DVGetUnitCapabilities(pDevExt, pIrp, pAVReq);
|
|
if(!NT_SUCCESS(Status)) {
|
|
TRACE(TL_61883_ERROR,("Av61883_GetUnitCapabilities Failed %x\n", Status));
|
|
IoFreeIrp(pIrp);
|
|
return Status;
|
|
}
|
|
|
|
IoFreeIrp(pIrp);
|
|
|
|
//
|
|
// Get current power state. Turn it on if it's off.
|
|
//
|
|
Status = DVIssueAVCCommand(pDevExt, AVC_CTYPE_STATUS, DV_GET_POWER_STATE, (PVOID) &XDevProperty);
|
|
TRACE(TL_PNP_WARNING,("GET_POWER_STATE: Status:%x; %s\n", Status, XDevProperty.u.PowerState == ED_POWER_ON ? "PowerON" : "PowerStandby"));
|
|
|
|
if(STATUS_SUCCESS == Status) {
|
|
|
|
#define WAIT_SET_POWER 100 // Wait time when set power state; (msec)
|
|
#define MAX_SET_POWER_RETRIES 3
|
|
|
|
if( XDevProperty.u.PowerState == ED_POWER_STANDBY
|
|
|| XDevProperty.u.PowerState == ED_POWER_OFF
|
|
) {
|
|
NTSTATUS StatusSetPower;
|
|
LONG lRetries = 0;
|
|
|
|
do {
|
|
//
|
|
// Some AVC device, such as D-VHS will return STATUS_DEVICE_DATA_ERROR when
|
|
// this command is issue right after get power state command. Such device
|
|
// might be slow in response to the AVC command. Even though wait is not
|
|
// desirable, but it is the only way.
|
|
//
|
|
DVDelayExecutionThread(WAIT_SET_POWER); // Wait a little
|
|
StatusSetPower = DVIssueAVCCommand(pDevExt, AVC_CTYPE_CONTROL, DV_SET_POWER_STATE_ON, (PVOID) &XDevProperty);
|
|
lRetries++;
|
|
TRACE(TL_PNP_WARNING,("SET_POWER_STATE_ON: (%d) StatusSetPower:%x; Waited (%d msec).\n", lRetries, StatusSetPower, WAIT_SET_POWER));
|
|
|
|
} while ( lRetries < MAX_SET_POWER_RETRIES
|
|
&& ( StatusSetPower == STATUS_REQUEST_ABORTED
|
|
|| StatusSetPower == STATUS_DEVICE_DATA_ERROR
|
|
|| StatusSetPower == STATUS_IO_TIMEOUT
|
|
));
|
|
|
|
TRACE(TL_PNP_WARNING,("SET_POWER_STATE_ON: StatusSetPower:%x; Retries:%d times\n\n", StatusSetPower, lRetries));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Subunit_Info : VCR or camera
|
|
//
|
|
DVDelayExecutionThread(DV_AVC_CMD_DELAY_INTER_CMD);
|
|
Status = DVIssueAVCCommand(pDevExt, AVC_CTYPE_STATUS, DV_SUBUNIT_INFO, (PVOID) bAvcBuf);
|
|
|
|
if(STATUS_SUCCESS == Status) {
|
|
TRACE(TL_PNP_TRACE,("GetDevInfo: Status %x DV_SUBUNIT_INFO (%x %x %x %x)\n",
|
|
Status, bAvcBuf[0], bAvcBuf[1], bAvcBuf[2], bAvcBuf[3]));
|
|
//
|
|
// Cache it. We assume max_subunit_ID is 0 and there is a max of 4 entries.
|
|
//
|
|
pDevExt->Subunit_Type[0] = bAvcBuf[0] & AVC_SUBTYPE_MASK;
|
|
pDevExt->Subunit_Type[1] = bAvcBuf[1] & AVC_SUBTYPE_MASK;
|
|
pDevExt->Subunit_Type[2] = bAvcBuf[2] & AVC_SUBTYPE_MASK;
|
|
pDevExt->Subunit_Type[3] = bAvcBuf[3] & AVC_SUBTYPE_MASK;
|
|
|
|
// This is a tape subunit driver so one of the subunit must be a tape subunit.
|
|
if(pDevExt->Subunit_Type[0] != AVC_DEVICE_TAPE_REC && pDevExt->Subunit_Type[1]) {
|
|
TRACE(TL_PNP_ERROR,("GetDevInfo:Device not supported: %x, %x; (VCR %x, Camera %x)\n",
|
|
pDevExt->Subunit_Type[0], pDevExt->Subunit_Type[1], AVC_DEVICE_TAPE_REC, AVC_DEVICE_CAMERA));
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
} else {
|
|
TRACE(TL_PNP_ERROR,("GetDevInfo: DV_SUBUNIT_INFO failed, Status %x\n", Status));
|
|
|
|
if(STATUS_TIMEOUT == Status) {
|
|
TRACE(TL_PNP_WARNING, ("GetDevInfo: Query DV_SUBUNIT_INFO failed. This could be the MediaDecoder box.\n"));
|
|
// Do not fail this. Making an exception.
|
|
}
|
|
|
|
// Has our device gone away?
|
|
if (STATUS_IO_DEVICE_ERROR == Status || STATUS_REQUEST_ABORTED == Status)
|
|
return Status;
|
|
|
|
pDevExt->Subunit_Type[0] = AVC_DEVICE_UNKNOWN;
|
|
pDevExt->Subunit_Type[1] = AVC_DEVICE_UNKNOWN;
|
|
pDevExt->Subunit_Type[2] = AVC_DEVICE_UNKNOWN;
|
|
pDevExt->Subunit_Type[3] = AVC_DEVICE_UNKNOWN;
|
|
}
|
|
|
|
|
|
//
|
|
// Medium_Info: MediaPresent, MediaType, RecordInhibit
|
|
//
|
|
pXPrtProperty = (PKSPROPERTY_EXTXPORT_S) bAvcBuf;
|
|
DVDelayExecutionThread(DV_AVC_CMD_DELAY_INTER_CMD);
|
|
Status = DVIssueAVCCommand(pDevExt, AVC_CTYPE_STATUS, VCR_MEDIUM_INFO, (PVOID) pXPrtProperty);
|
|
|
|
if(STATUS_SUCCESS == Status) {
|
|
pDevExt->bHasTape = pXPrtProperty->u.MediumInfo.MediaPresent;
|
|
pDevExt->MediaType = pXPrtProperty->u.MediumInfo.MediaType;
|
|
TRACE(TL_PNP_TRACE,("GetDevInfo: Status %x HasTape %s, VCR_MEDIUM_INFO (%x %x %x %x)\n",
|
|
Status, pDevExt->bHasTape ? "Yes" : "No", bAvcBuf[0], bAvcBuf[1], bAvcBuf[2], bAvcBuf[3]));
|
|
} else {
|
|
pDevExt->bHasTape = FALSE;
|
|
TRACE(TL_PNP_ERROR,("GetDevInfo: VCR_MEDIUM_INFO failed, Status %x\n", Status));
|
|
// Has our device gone away?
|
|
if (STATUS_IO_DEVICE_ERROR == Status || STATUS_REQUEST_ABORTED == Status)
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// If this is a Panasonic AVC device, we will detect if it is a DVCPro format;
|
|
// This needs to be called before MediaFormat
|
|
//
|
|
if(pDevExt->ulVendorID == VENDORID_PANASONIC) {
|
|
DVDelayExecutionThread(DV_AVC_CMD_DELAY_INTER_CMD);
|
|
DVGetDevIsItDVCPro(pDevExt);
|
|
}
|
|
|
|
|
|
//
|
|
// Medium format: NTSC or PAL
|
|
//
|
|
pDevExt->VideoFormatIndex = AVCSTRM_FORMAT_SDDV_NTSC; // Default
|
|
DVDelayExecutionThread(DV_AVC_CMD_DELAY_INTER_CMD);
|
|
if(!DVGetDevSignalFormat(
|
|
pDevExt,
|
|
KSPIN_DATAFLOW_OUT,
|
|
0)) {
|
|
ASSERT(FALSE && "IN/OUTPUT SIGNAL MODE is not supported; driver abort.");
|
|
return STATUS_NOT_SUPPORTED;
|
|
} else {
|
|
if(pDevExt->VideoFormatIndex != AVCSTRM_FORMAT_SDDV_NTSC &&
|
|
pDevExt->VideoFormatIndex != AVCSTRM_FORMAT_SDDV_PAL &&
|
|
pDevExt->VideoFormatIndex != AVCSTRM_FORMAT_MPEG2TS
|
|
) {
|
|
TRACE(TL_PNP_ERROR,("**** Format idx %d not supported by this driver ***\n", pDevExt->VideoFormatIndex));
|
|
ASSERT(pDevExt->VideoFormatIndex == AVCSTRM_FORMAT_SDDV_NTSC || pDevExt->VideoFormatIndex == AVCSTRM_FORMAT_SDDV_PAL);
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Mode of Operation: 0(Undetermined), Camera or VCR
|
|
//
|
|
DVDelayExecutionThread(DV_AVC_CMD_DELAY_INTER_CMD);
|
|
DVGetDevModeOfOperation(
|
|
pDevExt
|
|
);
|
|
|
|
|
|
return STATUS_SUCCESS; // Status;
|
|
}
|
|
|
|
#ifdef SUPPORT_NEW_AVC
|
|
|
|
|
|
HANDLE
|
|
AVCTapeGetPlugHandle(
|
|
IN PDVCR_EXTENSION pDevExt,
|
|
IN ULONG PlugNum,
|
|
IN KSPIN_DATAFLOW DataFlow
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PAV_61883_REQUEST pAVReq;
|
|
|
|
PAGED_CODE();
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
pAVReq = &pDevExt->AVReq;
|
|
RtlZeroMemory(pAVReq, sizeof(AV_61883_REQUEST));
|
|
INIT_61883_HEADER(pAVReq, Av61883_GetPlugHandle);
|
|
pAVReq->GetPlugHandle.PlugNum = PlugNum;
|
|
pAVReq->GetPlugHandle.hPlug = 0;
|
|
pAVReq->GetPlugHandle.Type = DataFlow == KSPIN_DATAFLOW_OUT ? CMP_PlugOut : CMP_PlugIn;
|
|
|
|
Status = DVSubmitIrpSynch(pDevExt, pDevExt->pIrpSyncCall, pAVReq);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
TRACE(TL_61883_ERROR,("GetPlugHandle: Failed:%x\n", Status));
|
|
ASSERT(NT_SUCCESS(Status));
|
|
pAVReq->GetPlugHandle.hPlug = NULL;
|
|
return NULL;
|
|
}
|
|
else {
|
|
TRACE(TL_61883_TRACE,("hPlug=%x\n", pAVReq->GetPlugHandle.hPlug));
|
|
}
|
|
|
|
return pAVReq->GetPlugHandle.hPlug;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AVCTapeGetPinInfo(
|
|
IN PDVCR_EXTENSION pDevExt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Acquire pin information from avc.sys. These information will be used to define data range and
|
|
then for doing data interssection.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG i;
|
|
ULONG PinId; // Pin number
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
// Get pin count
|
|
RtlZeroMemory(&pDevExt->AvcMultIrb, sizeof(AVC_MULTIFUNC_IRB));
|
|
pDevExt->AvcMultIrb.Function = AVC_FUNCTION_GET_PIN_COUNT;
|
|
Status = AVCReqSubmitIrpSynch(pDevExt->pBusDeviceObject, pDevExt->pIrpSyncCall, &pDevExt->AvcMultIrb);
|
|
if(!NT_SUCCESS(Status)) {
|
|
TRACE(TL_STRM_ERROR,("GetPinCount Failed:%x\n", Status));
|
|
goto GetPinInfoDone;
|
|
} else {
|
|
TRACE(TL_STRM_TRACE,("There are %d pins\n", pDevExt->AvcMultIrb.PinCount.PinCount));
|
|
if(pDevExt->VideoFormatIndex == AVCSTRM_FORMAT_MPEG2TS) {
|
|
if(pDevExt->AvcMultIrb.PinCount.PinCount > 1) {
|
|
goto GetPinInfoDone;
|
|
}
|
|
} else {
|
|
if(pDevExt->AvcMultIrb.PinCount.PinCount > 3) {
|
|
goto GetPinInfoDone;
|
|
}
|
|
}
|
|
pDevExt->PinCount = pDevExt->AvcMultIrb.PinCount.PinCount; // <<<
|
|
}
|
|
|
|
// Get all pin descriptors
|
|
for(i=0; i<pDevExt->PinCount; i++) {
|
|
|
|
// Get a pin descriptor
|
|
RtlZeroMemory(&pDevExt->AvcMultIrb, sizeof(AVC_MULTIFUNC_IRB));
|
|
pDevExt->AvcMultIrb.Function = AVC_FUNCTION_GET_PIN_DESCRIPTOR;
|
|
pDevExt->AvcMultIrb.PinDescriptor.PinId = i;
|
|
Status = AVCReqSubmitIrpSynch(pDevExt->pBusDeviceObject, pDevExt->pIrpSyncCall, &pDevExt->AvcMultIrb);
|
|
if(!NT_SUCCESS(Status)) {
|
|
TRACE(TL_PNP_ERROR,("GetPinDescriptor Failed:%x\n", Status));
|
|
goto GetPinInfoDone;
|
|
} else {
|
|
// Copy the pDevExt->AvcMultIrb.PinDescriptor.PinDescriptor
|
|
PinId = pDevExt->AvcMultIrb.PinDescriptor.PinId;
|
|
// Anything else ?
|
|
}
|
|
|
|
// Get pre connection info
|
|
RtlZeroMemory(&pDevExt->AvcMultIrb, sizeof(AVC_MULTIFUNC_IRB));
|
|
pDevExt->AvcMultIrb.Function = AVC_FUNCTION_GET_CONNECTINFO;
|
|
pDevExt->AvcMultIrb.PinDescriptor.PinId = PinId;
|
|
Status = AVCReqSubmitIrpSynch(pDevExt->pBusDeviceObject, pDevExt->pIrpSyncCall, &pDevExt->AvcMultIrb);
|
|
if(!NT_SUCCESS(Status)) {
|
|
TRACE(TL_PNP_ERROR,("GetPinDescriptor Failed:%x\n", Status));
|
|
goto GetPinInfoDone;
|
|
} else {
|
|
// Cache connectInfo
|
|
if(pDevExt->VideoFormatIndex == AVCSTRM_FORMAT_MPEG2TS) {
|
|
// Check
|
|
if(pDevExt->AvcMultIrb.PreConnectInfo.ConnectInfo.DataFlow == KSPIN_DATAFLOW_OUT) {
|
|
MPEG2TStreamOut.ConnectInfo = pDevExt->AvcMultIrb.PreConnectInfo.ConnectInfo;
|
|
} else {
|
|
MPEG2TStreamIn.ConnectInfo = pDevExt->AvcMultIrb.PreConnectInfo.ConnectInfo;
|
|
}
|
|
}
|
|
else {
|
|
|
|
if(pDevExt->AvcMultIrb.PreConnectInfo.ConnectInfo.DataFlow == KSPIN_DATAFLOW_OUT) {
|
|
DvcrNTSCiavStream.ConnectInfo = pDevExt->AvcMultIrb.PreConnectInfo.ConnectInfo;
|
|
DvcrPALiavStream.ConnectInfo = pDevExt->AvcMultIrb.PreConnectInfo.ConnectInfo;
|
|
} else if(pDevExt->AvcMultIrb.PreConnectInfo.ConnectInfo.DataFlow == KSPIN_DATAFLOW_IN) {
|
|
DvcrNTSCiavStreamIn.ConnectInfo = pDevExt->AvcMultIrb.PreConnectInfo.ConnectInfo;
|
|
DvcrPALiavStreamIn.ConnectInfo = pDevExt->AvcMultIrb.PreConnectInfo.ConnectInfo;
|
|
} else {
|
|
// Error; unexpected;
|
|
TRACE(TL_PNP_ERROR,("Unexpected index:%d for format:%d\n", i, pDevExt->VideoFormatIndex));
|
|
// goto GetPinInfoDone;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
GetPinInfoDone:
|
|
|
|
TRACE(TL_STRM_TRACE,("GetPinInfo exited with ST:%x\n", Status));
|
|
|
|
return Status;
|
|
}
|
|
|
|
#endif // SUPPORT_NEW_AVC
|
|
|
|
|
|
NTSTATUS
|
|
AVCTapeInitialize(
|
|
IN PDVCR_EXTENSION pDevExt,
|
|
IN PPORT_CONFIGURATION_INFORMATION pConfigInfo,
|
|
IN PAV_61883_REQUEST pAVReq
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This where we perform the necessary initialization tasks.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Initialize the device extension structure
|
|
//
|
|
DVIniDevExtStruct(
|
|
pDevExt,
|
|
pConfigInfo
|
|
);
|
|
|
|
#ifdef READ_CUTOMIZE_REG_VALUES
|
|
//
|
|
// Get values from this device's own registry
|
|
//
|
|
DVGetPropertyValuesFromRegistry(
|
|
pDevExt
|
|
);
|
|
#endif
|
|
|
|
// Allocate an Irp for synchronize call
|
|
pDevExt->pIrpSyncCall = IoAllocateIrp(pDevExt->pBusDeviceObject->StackSize, FALSE);
|
|
if(!pDevExt->pIrpSyncCall) {
|
|
ASSERT(pDevExt->pIrpSyncCall && "Allocate Irp failed.\n");
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Query device information at the laod time:
|
|
// Subunit
|
|
// Unit Info
|
|
// Mode of operation
|
|
// NTSC or PAL
|
|
// Speed
|
|
//
|
|
Status =
|
|
AVCTapeGetDevInfo(
|
|
pDevExt,
|
|
pAVReq
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
TRACE(TL_PNP_ERROR,("GetDevInfo failed %x\n", Status));
|
|
ASSERT(NT_SUCCESS(Status) && "AVCTapeGetDevInfo failed");
|
|
goto AbortLoading;
|
|
}
|
|
|
|
|
|
//
|
|
// Get device's output plug handles and states
|
|
//
|
|
|
|
if(pDevExt->pDevOutPlugs->NumPlugs) {
|
|
NTSTATUS StatusPlug;
|
|
|
|
TRACE(TL_61883_WARNING,("%d oPCR(s); MaxDataRate:%d (%s)\n",
|
|
pDevExt->pDevOutPlugs->NumPlugs,
|
|
pDevExt->pDevOutPlugs->MaxDataRate,
|
|
(pDevExt->pDevOutPlugs->MaxDataRate == CMP_SPEED_S100) ? "S100" :
|
|
(pDevExt->pDevOutPlugs->MaxDataRate == CMP_SPEED_S200) ? "S200" :
|
|
(pDevExt->pDevOutPlugs->MaxDataRate == CMP_SPEED_S400) ? "S400" : "Sxxx"
|
|
));
|
|
|
|
for (i = 0; i < pDevExt->pDevOutPlugs->NumPlugs; i++) {
|
|
if(NT_SUCCESS(
|
|
StatusPlug = AVCDevGetDevPlug(
|
|
pDevExt,
|
|
CMP_PlugOut,
|
|
i,
|
|
&pDevExt->pDevOutPlugs->DevPlug[i].hPlug
|
|
))) {
|
|
|
|
if(NT_SUCCESS(
|
|
AVCDevGetPlugState(
|
|
pDevExt,
|
|
pDevExt->pDevOutPlugs->DevPlug[i].hPlug,
|
|
&pDevExt->pDevOutPlugs->DevPlug[i].PlugState
|
|
))) {
|
|
} else {
|
|
//
|
|
// This is an error if we were told to this many number of plugs;
|
|
// Set default plug states.
|
|
//
|
|
pDevExt->pDevOutPlugs->DevPlug[i].PlugState.DataRate = CMP_SPEED_S100;
|
|
pDevExt->pDevOutPlugs->DevPlug[i].PlugState.Payload = PCR_PAYLOAD_MPEG2TS_DEF;
|
|
pDevExt->pDevOutPlugs->DevPlug[i].PlugState.BC_Connections = 0;
|
|
pDevExt->pDevOutPlugs->DevPlug[i].PlugState.PP_Connections = 0;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// If there is a plug, we should be able to get its handle!
|
|
//
|
|
TRACE(TL_61883_ERROR,("GetDevPlug oPlug[%d] failed %x\n", i, StatusPlug));
|
|
ASSERT(NT_SUCCESS(StatusPlug) && "Failed to get oPCR handle from 61883!");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
TRACE(TL_61883_WARNING,("Has no oPCR\n"));
|
|
}
|
|
|
|
//
|
|
// Get device's input plug handles and states
|
|
//
|
|
if(pDevExt->pDevInPlugs->NumPlugs) {
|
|
NTSTATUS StatusPlug;
|
|
|
|
TRACE(TL_61883_WARNING,("%d iPCR(s); MaxDataRate:%d (%s)\n",
|
|
pDevExt->pDevInPlugs->NumPlugs,
|
|
pDevExt->pDevInPlugs->MaxDataRate,
|
|
(pDevExt->pDevInPlugs->MaxDataRate == CMP_SPEED_S100) ? "S100" :
|
|
(pDevExt->pDevInPlugs->MaxDataRate == CMP_SPEED_S200) ? "S200" :
|
|
(pDevExt->pDevInPlugs->MaxDataRate == CMP_SPEED_S400) ? "S400" : "Sxxx"
|
|
));
|
|
|
|
for (i = 0; i < pDevExt->pDevInPlugs->NumPlugs; i++) {
|
|
if(NT_SUCCESS(
|
|
StatusPlug = AVCDevGetDevPlug(
|
|
pDevExt,
|
|
CMP_PlugIn,
|
|
i,
|
|
&pDevExt->pDevInPlugs->DevPlug[i].hPlug
|
|
))) {
|
|
|
|
if(NT_SUCCESS(
|
|
AVCDevGetPlugState(
|
|
pDevExt,
|
|
pDevExt->pDevInPlugs->DevPlug[i].hPlug,
|
|
&pDevExt->pDevInPlugs->DevPlug[i].PlugState
|
|
))) {
|
|
} else {
|
|
//
|
|
// This is an error if we were told to this many number of plugs;
|
|
// Set default plug states.
|
|
//
|
|
pDevExt->pDevInPlugs->DevPlug[i].PlugState.DataRate = CMP_SPEED_S200;
|
|
pDevExt->pDevInPlugs->DevPlug[i].PlugState.Payload = PCR_PAYLOAD_MPEG2TS_DEF;
|
|
pDevExt->pDevInPlugs->DevPlug[i].PlugState.BC_Connections = 0;
|
|
pDevExt->pDevInPlugs->DevPlug[i].PlugState.PP_Connections = 0;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// If there is a plug, we should be able to get its handle!
|
|
//
|
|
TRACE(TL_61883_ERROR,("GetDevPlug iPlug[%d] failed %x\n", i, StatusPlug));
|
|
ASSERT(NT_SUCCESS(StatusPlug) && "Failed to get iPCR handle from 61883!");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
TRACE(TL_61883_WARNING,("Has no iPCR\n"));
|
|
}
|
|
|
|
|
|
#ifdef SUPPORT_LOCAL_PLUGS
|
|
// Create a local output plug.
|
|
pDevExt->OPCR.oPCR.OnLine = 0; // We are not online so we cannot be programmed.
|
|
pDevExt->OPCR.oPCR.BCCCounter = 0;
|
|
pDevExt->OPCR.oPCR.PPCCounter = 0;
|
|
pDevExt->OPCR.oPCR.Channel = 0;
|
|
|
|
// Default to MPEG2TS data since MPEg2TS device, like D-VHS, can initialize connection.
|
|
if(pDevExt->pDevOutPlugs->NumPlugs) {
|
|
//
|
|
// Set PC's oPCR to match device's oPCR[0]
|
|
//
|
|
pDevExt->OPCR.oPCR.DataRate =
|
|
#if 0
|
|
// Be conservative and use this to match its oPCR[0]'s setting..
|
|
pDevExt->pDevOutPlugs->DevPlug[0].PlugState.DataRate; // oPCR's data rate <= MPR's MaxDataRate
|
|
#else
|
|
// Be aggreessive in conserving BWU, use MaxDataRate.
|
|
pDevExt->pDevOutPlugs->MaxDataRate; // Use MPR's MaxDataRate?
|
|
#endif
|
|
pDevExt->OPCR.oPCR.OverheadID = PCR_OVERHEAD_ID_MPEG2TS_DEF; // Default since we do not get this as a plug state
|
|
pDevExt->OPCR.oPCR.Payload = pDevExt->pDevOutPlugs->DevPlug[0].PlugState.Payload;
|
|
|
|
} else {
|
|
pDevExt->OPCR.oPCR.DataRate = CMP_SPEED_S200; // Default of D-VHS
|
|
pDevExt->OPCR.oPCR.OverheadID = PCR_OVERHEAD_ID_MPEG2TS_DEF; // This is just default
|
|
pDevExt->OPCR.oPCR.Payload = PCR_PAYLOAD_MPEG2TS_DEF; // Default
|
|
}
|
|
|
|
if(!AVCTapeCreateLocalPlug(
|
|
pDevExt,
|
|
&pDevExt->AVReq,
|
|
CMP_PlugOut,
|
|
&pDevExt->OPCR,
|
|
&pDevExt->OutputPCRLocalNum,
|
|
&pDevExt->hOutputPCRLocal)) {
|
|
TRACE(TL_PNP_ERROR,("Create PC oPCR failed!\n"));
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto AbortLoading;
|
|
}
|
|
|
|
// Create a local input plug.
|
|
pDevExt->IPCR.iPCR.OnLine = 0; // We are not online so we cannot be programmed.
|
|
pDevExt->IPCR.iPCR.BCCCounter = 0;
|
|
pDevExt->IPCR.iPCR.PPCCounter = 0;
|
|
pDevExt->IPCR.iPCR.Channel = 0;
|
|
|
|
if(!AVCTapeCreateLocalPlug(
|
|
pDevExt,
|
|
&pDevExt->AVReq,
|
|
CMP_PlugIn,
|
|
&pDevExt->IPCR,
|
|
&pDevExt->InputPCRLocalNum,
|
|
&pDevExt->hInputPCRLocal)) {
|
|
|
|
TRACE(TL_PNP_ERROR,("Create PC iPCR failed!\n"));
|
|
|
|
// Delete oPCR created
|
|
if(!AVCTapeDeleteLocalPlug(
|
|
pDevExt,
|
|
&pDevExt->AVReq,
|
|
&pDevExt->OutputPCRLocalNum,
|
|
&pDevExt->hOutputPCRLocal)) {
|
|
TRACE(TL_PNP_ERROR,("Delete PC oPCR failed!\n"));
|
|
}
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto AbortLoading;
|
|
}
|
|
#endif
|
|
|
|
#ifdef SUPPORT_NEW_AVC // Initialize device
|
|
|
|
//
|
|
// Get plug handle of this device;
|
|
// BUGBUG: For now, assume there is one pair of input and output plugs
|
|
//
|
|
pDevExt->hPlugLocalIn = AVCTapeGetPlugHandle(pDevExt, 0, KSPIN_DATAFLOW_IN);
|
|
pDevExt->hPlugLocalOut = AVCTapeGetPlugHandle(pDevExt, 0, KSPIN_DATAFLOW_OUT);
|
|
|
|
|
|
//
|
|
// Get Pin information for connection purpose
|
|
//
|
|
Status = AVCTapeGetPinInfo(pDevExt);
|
|
if(!NT_SUCCESS(Status)) {
|
|
TRACE(TL_PNP_ERROR,("GetPinInfo failed %x\n", Status));
|
|
ASSERT(NT_SUCCESS(Status) && "AVCTapeGetPinInfo failed");
|
|
goto AbortLoading;
|
|
}
|
|
#endif
|
|
//
|
|
// Can customize the FormatInfoTable here!
|
|
//
|
|
switch(pDevExt->VideoFormatIndex) {
|
|
case AVCSTRM_FORMAT_SDDV_NTSC:
|
|
case AVCSTRM_FORMAT_SDDV_PAL:
|
|
case AVCSTRM_FORMAT_HDDV_NTSC:
|
|
case AVCSTRM_FORMAT_HDDV_PAL:
|
|
case AVCSTRM_FORMAT_SDLDV_NTSC:
|
|
case AVCSTRM_FORMAT_SDLDV_PAL:
|
|
pDevExt->NumOfPins = DV_STREAM_COUNT;
|
|
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].cipHdr1.DBS = CIP_DBS_SDDV;
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].cipHdr1.FN = CIP_FN_DV;
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].cipHdr1.QPC = CIP_QPC_DV;
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].cipHdr1.SPH = CIP_SPH_DV;
|
|
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].cipHdr2.FMT = CIP_FMT_DV;
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].cipHdr2.STYPE = CIP_STYPE_DV;
|
|
break;
|
|
|
|
case AVCSTRM_FORMAT_MPEG2TS:
|
|
pDevExt->NumOfPins = MPEG_STREAM_COUNT;
|
|
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].cipHdr1.DBS = CIP_DBS_MPEG;
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].cipHdr1.FN = CIP_FN_MPEG;
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].cipHdr1.QPC = CIP_QPC_MPEG;
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].cipHdr1.SPH = CIP_SPH_MPEG;
|
|
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].cipHdr2.FMT = CIP_FMT_MPEG;
|
|
// AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].cipHdr2.F5060_OR_TSF = CIP_60_FIELDS;
|
|
break;
|
|
default:
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
goto AbortLoading;
|
|
break;
|
|
}
|
|
|
|
switch(pDevExt->VideoFormatIndex) {
|
|
case AVCSTRM_FORMAT_SDDV_NTSC:
|
|
case AVCSTRM_FORMAT_HDDV_NTSC:
|
|
case AVCSTRM_FORMAT_SDLDV_NTSC:
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].cipHdr2.F5060_OR_TSF = CIP_60_FIELDS;
|
|
break;
|
|
case AVCSTRM_FORMAT_SDDV_PAL:
|
|
case AVCSTRM_FORMAT_HDDV_PAL:
|
|
case AVCSTRM_FORMAT_SDLDV_PAL:
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].cipHdr2.F5060_OR_TSF = CIP_50_FIELDS;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Note: Must do ExAllocatePool after DVIniDevExtStruct() since ->pStreamInfoObject is initialized.
|
|
// Since the format that this driver support is known when this driver is known,'
|
|
// the stream information table need to be custonmized. Make a copy and customized it.
|
|
//
|
|
|
|
//
|
|
// Set the size of the stream inforamtion structure that we returned in SRB_GET_STREAM_INFO
|
|
//
|
|
|
|
pDevExt->pStreamInfoObject = (STREAM_INFO_AND_OBJ *)
|
|
ExAllocatePool(NonPagedPool, sizeof(STREAM_INFO_AND_OBJ) * pDevExt->NumOfPins);
|
|
|
|
if(!pDevExt->pStreamInfoObject) {
|
|
ASSERT(pDevExt->pStreamInfoObject && "STATUS_INSUFFICIENT_RESOURCES");
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto AbortLoading;
|
|
}
|
|
|
|
pConfigInfo->StreamDescriptorSize =
|
|
(pDevExt->NumOfPins * sizeof(HW_STREAM_INFORMATION)) + // number of stream descriptors
|
|
sizeof(HW_STREAM_HEADER); // and 1 stream header
|
|
|
|
TRACE(TL_PNP_TRACE,("pStreamInfoObject:%x; StreamDescriptorSize:%d\n", pDevExt->pStreamInfoObject, pConfigInfo->StreamDescriptorSize ));
|
|
|
|
// Make a copy of the default stream information
|
|
for(i = 0; i < pDevExt->NumOfPins; i++ ) {
|
|
if(pDevExt->VideoFormatIndex == AVCSTRM_FORMAT_MPEG2TS)
|
|
pDevExt->pStreamInfoObject[i] = MPEGStreams[i];
|
|
else
|
|
pDevExt->pStreamInfoObject[i] = DVStreams[i];
|
|
}
|
|
|
|
switch(pDevExt->VideoFormatIndex) {
|
|
case AVCSTRM_FORMAT_SDDV_NTSC:
|
|
case AVCSTRM_FORMAT_SDDV_PAL:
|
|
// Set AUDIO AUX to reflect: NTSC/PAL, consumer DV or DVCPRO
|
|
if(pDevExt->bDVCPro) {
|
|
// Note: there is no DVInfo in VideoInfoHeader but there is for the iAV streams.
|
|
DvcrPALiavStream.DVVideoInfo.dwDVAAuxSrc = PAL_DVAAuxSrc_DVCPRO;
|
|
DvcrNTSCiavStream.DVVideoInfo.dwDVAAuxSrc = NTSC_DVAAuxSrc_DVCPRO;
|
|
} else {
|
|
DvcrPALiavStream.DVVideoInfo.dwDVAAuxSrc = PAL_DVAAuxSrc;
|
|
DvcrNTSCiavStream.DVVideoInfo.dwDVAAuxSrc = NTSC_DVAAuxSrc;
|
|
}
|
|
}
|
|
|
|
TRACE(TL_PNP_WARNING,("#### %s:%s:%s PhyDO %x, BusDO %x, DevExt %x, FrmSz %d; StrmIf %d\n",
|
|
pDevExt->ulDevType == ED_DEVTYPE_VCR ? "DVCR" : pDevExt->ulDevType == ED_DEVTYPE_CAMERA ? "Camera" : "Tuner?",
|
|
pDevExt->VideoFormatIndex == AVCSTRM_FORMAT_SDDV_NTSC ? "SD:NTSC" : pDevExt->VideoFormatIndex == AVCSTRM_FORMAT_SDDV_PAL ? "PAL" : "MPEG_TS?",
|
|
(pDevExt->ulDevType == ED_DEVTYPE_VCR && pDevExt->pDevInPlugs->NumPlugs > 0) ? "CanRec" : "NotRec",
|
|
pDevExt->pPhysicalDeviceObject,
|
|
pDevExt->pBusDeviceObject,
|
|
pDevExt,
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].FrameSize,
|
|
pConfigInfo->StreamDescriptorSize
|
|
));
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
AbortLoading:
|
|
|
|
DvFreeTextualString(pDevExt, &pDevExt->UnitIDs);
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AVCTapeInitializeCompleted(
|
|
IN PDVCR_EXTENSION pDevExt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This where we perform the necessary initialization tasks.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
|
|
#ifdef SUPPORT_ACCESS_DEVICE_INTERFACE
|
|
//
|
|
// Access to the device's interface section
|
|
//
|
|
DVAccessDeviceInterface(pDevExt, NUMBER_OF_DV_CATEGORIES, DVCategories);
|
|
#endif
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
AVCTapeGetStreamInfo(
|
|
IN PDVCR_EXTENSION pDevExt,
|
|
IN ULONG ulBytesToTransfer,
|
|
IN PHW_STREAM_HEADER pStreamHeader,
|
|
IN PHW_STREAM_INFORMATION pStreamInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the information of all streams that are supported by the driver
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
//
|
|
// Make sure we have enough space to return our stream informations
|
|
//
|
|
if(ulBytesToTransfer < sizeof (HW_STREAM_HEADER) + sizeof(HW_STREAM_INFORMATION) * pDevExt->NumOfPins ) {
|
|
TRACE(TL_PNP_ERROR,("GetStrmInfo: ulBytesToTransfer %d ?= %d\n",
|
|
ulBytesToTransfer, sizeof(HW_STREAM_HEADER) + sizeof(HW_STREAM_INFORMATION) * pDevExt->NumOfPins ));
|
|
ASSERT(ulBytesToTransfer >= sizeof(HW_STREAM_HEADER) + sizeof(HW_STREAM_INFORMATION) * pDevExt->NumOfPins );
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Initialize stream header:
|
|
// Device properties
|
|
// Streams
|
|
//
|
|
|
|
RtlZeroMemory(pStreamHeader, sizeof(HW_STREAM_HEADER));
|
|
|
|
pStreamHeader->NumberOfStreams = pDevExt->NumOfPins;
|
|
pStreamHeader->SizeOfHwStreamInformation = sizeof(HW_STREAM_INFORMATION);
|
|
|
|
pStreamHeader->NumDevPropArrayEntries = NUMBER_VIDEO_DEVICE_PROPERTIES;
|
|
pStreamHeader->DevicePropertiesArray = (PKSPROPERTY_SET) VideoDeviceProperties;
|
|
|
|
pStreamHeader->NumDevEventArrayEntries = NUMBER_VIDEO_DEVICE_EVENTS;
|
|
pStreamHeader->DeviceEventsArray = (PKSEVENT_SET) VideoDeviceEvents;
|
|
|
|
|
|
TRACE(TL_PNP_TRACE,("GetStreamInfo: StreamPropEntries %d, DevicePropEntries %d\n",
|
|
pStreamHeader->NumberOfStreams, pStreamHeader->NumDevPropArrayEntries));
|
|
|
|
|
|
//
|
|
// Initialize the stream structure.
|
|
//
|
|
ASSERT(pDevExt->pStreamInfoObject);
|
|
for( i = 0; i < pDevExt->NumOfPins; i++ )
|
|
*pStreamInfo++ = pDevExt->pStreamInfoObject[i].hwStreamInfo;
|
|
|
|
//
|
|
//
|
|
// store a pointer to the topology for the device
|
|
//
|
|
if(pDevExt->VideoFormatIndex == AVCSTRM_FORMAT_MPEG2TS)
|
|
pStreamHeader->Topology = &MPEG2TSTopology;
|
|
else
|
|
pStreamHeader->Topology = &DVTopology;
|
|
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AVCTapeVerifyDataFormat(
|
|
IN ULONG NumOfPins,
|
|
PKSDATAFORMAT pKSDataFormatToVerify,
|
|
ULONG StreamNumber,
|
|
ULONG ulSupportedFrameSize,
|
|
STREAM_INFO_AND_OBJ * pStreamInfoObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
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 KS_DATAFORMAT_VIDEOINFOHEADER structure.
|
|
StreamNumber - index of the stream being queried / opened.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the format is supported
|
|
FALSE if the format cannot be suppored
|
|
|
|
--*/
|
|
{
|
|
PKSDATAFORMAT *pAvailableFormats;
|
|
int NumberOfFormatArrayEntries;
|
|
int j;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Make sure the stream index is valid
|
|
//
|
|
if(StreamNumber >= NumOfPins) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// How many formats does this data range support?
|
|
//
|
|
NumberOfFormatArrayEntries = pStreamInfoObject[StreamNumber].hwStreamInfo.NumberOfFormatArrayEntries;
|
|
|
|
//
|
|
// Get the pointer to the array of available formats
|
|
//
|
|
pAvailableFormats = pStreamInfoObject[StreamNumber].hwStreamInfo.StreamFormatsArray;
|
|
|
|
|
|
//
|
|
// Walk the array, searching for a match
|
|
//
|
|
for (j = 0; j < NumberOfFormatArrayEntries; j++, pAvailableFormats++) {
|
|
|
|
if (!DVCmpGUIDsAndFormatSize(
|
|
pKSDataFormatToVerify,
|
|
*pAvailableFormats,
|
|
FALSE /* CompareFormatSize */ )) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Additional verification test
|
|
//
|
|
if(IsEqualGUID (&pKSDataFormatToVerify->Specifier, &KSDATAFORMAT_SPECIFIER_VIDEOINFO)) {
|
|
// Make sure
|
|
if( ((PKS_DATAFORMAT_VIDEOINFOHEADER)pKSDataFormatToVerify)->VideoInfoHeader.bmiHeader.biSizeImage !=
|
|
ulSupportedFrameSize) {
|
|
TRACE(TL_STRM_TRACE,("VIDEOINFO: biSizeToVerify %d != Supported %d\n",
|
|
((PKS_DATAFORMAT_VIDEOINFOHEADER)pKSDataFormatToVerify)->VideoInfoHeader.bmiHeader.biSizeImage,
|
|
ulSupportedFrameSize
|
|
));
|
|
continue;
|
|
} else {
|
|
TRACE(TL_STRM_TRACE,("VIDOINFO: **** biSizeToVerify %d == Supported %d\n",
|
|
((PKS_DATAFORMAT_VIDEOINFOHEADER)pKSDataFormatToVerify)->VideoInfoHeader.bmiHeader.biSizeImage,
|
|
ulSupportedFrameSize
|
|
));
|
|
}
|
|
} else if (IsEqualGUID (&pKSDataFormatToVerify->Specifier, &KSDATAFORMAT_SPECIFIER_DVINFO)) {
|
|
|
|
// Test 50/60 bit
|
|
if((((PKS_DATARANGE_DVVIDEO) pKSDataFormatToVerify)->DVVideoInfo.dwDVAAuxSrc & MASK_AUX_50_60_BIT) !=
|
|
(((PKS_DATARANGE_DVVIDEO) *pAvailableFormats)->DVVideoInfo.dwDVAAuxSrc & MASK_AUX_50_60_BIT) ||
|
|
(((PKS_DATARANGE_DVVIDEO) pKSDataFormatToVerify)->DVVideoInfo.dwDVVAuxSrc & MASK_AUX_50_60_BIT) !=
|
|
(((PKS_DATARANGE_DVVIDEO) *pAvailableFormats)->DVVideoInfo.dwDVVAuxSrc & MASK_AUX_50_60_BIT) ) {
|
|
|
|
TRACE(TL_STRM_TRACE,("DVINFO VerifyFormat failed: ASrc: %x!=%x (MSDV);or VSrc: %x!=%x\n",
|
|
((PKS_DATARANGE_DVVIDEO) pKSDataFormatToVerify)->DVVideoInfo.dwDVAAuxSrc,
|
|
((PKS_DATARANGE_DVVIDEO) *pAvailableFormats)->DVVideoInfo.dwDVAAuxSrc,
|
|
((PKS_DATARANGE_DVVIDEO) pKSDataFormatToVerify)->DVVideoInfo.dwDVVAuxSrc,
|
|
((PKS_DATARANGE_DVVIDEO) *pAvailableFormats)->DVVideoInfo.dwDVVAuxSrc
|
|
));
|
|
|
|
continue;
|
|
}
|
|
|
|
TRACE(TL_STRM_TRACE,("DVINFO: dwDVAAuxCtl %x, Supported %x\n",
|
|
((PKS_DATARANGE_DVVIDEO) pKSDataFormatToVerify)->DVVideoInfo.dwDVAAuxSrc,
|
|
((PKS_DATARANGE_DVVIDEO) *pAvailableFormats)->DVVideoInfo.dwDVAAuxSrc
|
|
));
|
|
|
|
TRACE(TL_STRM_TRACE,("DVINFO: dwDVVAuxSrc %x, Supported %x\n",
|
|
((PKS_DATARANGE_DVVIDEO) pKSDataFormatToVerify)->DVVideoInfo.dwDVVAuxSrc,
|
|
((PKS_DATARANGE_DVVIDEO) *pAvailableFormats)->DVVideoInfo.dwDVVAuxSrc
|
|
));
|
|
|
|
}
|
|
else if (IsEqualGUID (&pKSDataFormatToVerify->SubFormat, &KSDATAFORMAT_TYPE_MPEG2_TRANSPORT) ) {
|
|
TRACE(TL_STRM_TRACE,("VerifyFormat: MPEG2 subformat\n"));
|
|
}
|
|
else if (IsEqualGUID (&pKSDataFormatToVerify->SubFormat, &KSDATAFORMAT_TYPE_MPEG2_TRANSPORT_STRIDE)
|
|
&& pKSDataFormatToVerify->FormatSize >= (sizeof(KSDATARANGE)+sizeof(MPEG2_TRANSPORT_STRIDE)) ) {
|
|
//
|
|
// Verify the STRIDE structure
|
|
//
|
|
if( ((KS_DATARANGE_MPEG2TS_STRIDE_AVC *) pKSDataFormatToVerify)->Stride.dwOffset != MPEG2TS_STRIDE_OFFSET
|
|
|| ((KS_DATARANGE_MPEG2TS_STRIDE_AVC *) pKSDataFormatToVerify)->Stride.dwPacketLength != MPEG2TS_STRIDE_PACKET_LEN
|
|
|| ((KS_DATARANGE_MPEG2TS_STRIDE_AVC *) pKSDataFormatToVerify)->Stride.dwStride != MPEG2TS_STRIDE_STRIDE_LEN
|
|
) {
|
|
TRACE(TL_STRM_ERROR,("VerifyDataFormat: Invalid STRIDE parameters: dwOffset:%d; dwPacketLength:%d; dwStride:%d\n",
|
|
((KS_DATARANGE_MPEG2TS_STRIDE_AVC *) pKSDataFormatToVerify)->Stride.dwOffset,
|
|
((KS_DATARANGE_MPEG2TS_STRIDE_AVC *) pKSDataFormatToVerify)->Stride.dwPacketLength,
|
|
((KS_DATARANGE_MPEG2TS_STRIDE_AVC *) pKSDataFormatToVerify)->Stride.dwStride
|
|
));
|
|
continue;
|
|
}
|
|
TRACE(TL_STRM_TRACE,("VerifyFormat: MPEG2 stride subformat\n"));
|
|
}
|
|
else {
|
|
continue;
|
|
}
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
AVCTapeGetDataIntersection(
|
|
IN ULONG NumOfPins,
|
|
IN ULONG ulStreamNumber,
|
|
IN PKSDATARANGE pDataRange,
|
|
OUT PVOID pDataFormatBuffer,
|
|
IN ULONG ulSizeOfDataFormatBuffer,
|
|
IN ULONG ulSupportedFrameSize,
|
|
OUT ULONG *pulActualBytesTransferred,
|
|
STREAM_INFO_AND_OBJ * pStreamInfoObject
|
|
#ifdef SUPPORT_NEW_AVC
|
|
,
|
|
HANDLE hPlugLocalOut,
|
|
HANDLE hPlugLocalIn
|
|
#endif
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called to get a DATAFORMAT from a DATARANGE.
|
|
|
|
--*/
|
|
{
|
|
BOOL bMatchFound = FALSE;
|
|
ULONG ulFormatSize;
|
|
ULONG j;
|
|
ULONG ulNumberOfFormatArrayEntries;
|
|
PKSDATAFORMAT *pAvailableFormats;
|
|
#ifdef SUPPORT_NEW_AVC
|
|
AVCPRECONNECTINFO * pPreConnectInfo;
|
|
AVCCONNECTINFO * pConnectInfo;
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
//
|
|
// Check that the stream number is valid
|
|
//
|
|
if(ulStreamNumber >= NumOfPins) {
|
|
TRACE(TL_STRM_ERROR,("FormatFromRange: ulStreamNumber %d >= NumOfPins %d\n", ulStreamNumber, NumOfPins));
|
|
ASSERT(ulStreamNumber < NumOfPins && "Invalid stream index");
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
|
|
// Number of format this stream supports
|
|
ulNumberOfFormatArrayEntries = pStreamInfoObject[ulStreamNumber].hwStreamInfo.NumberOfFormatArrayEntries;
|
|
|
|
//
|
|
// Get the pointer to the array of available formats
|
|
//
|
|
pAvailableFormats = pStreamInfoObject[ulStreamNumber].hwStreamInfo.StreamFormatsArray;
|
|
|
|
|
|
//
|
|
// Walk the formats supported by the stream searching for a match
|
|
// Note: DataIntersection is really enumerating supported MediaType only!
|
|
// SO matter compare format is NTSC or PAL, we need suceeded both;
|
|
// however, we will copy back only the format is currently supported (NTSC or PAL).
|
|
//
|
|
for(j = 0; j < ulNumberOfFormatArrayEntries; j++, pAvailableFormats++) {
|
|
|
|
if(!DVCmpGUIDsAndFormatSize(pDataRange, *pAvailableFormats, TRUE)) {
|
|
TRACE(TL_STRM_TRACE,("CmpGUIDsAndFormatSize failed! FormatSize:%d?=%d\n", pDataRange->FormatSize, (*pAvailableFormats)->FormatSize));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// SUBTYPE_DVSD has a fix sample size;
|
|
//
|
|
if( IsEqualGUID (&pDataRange->SubFormat, &KSDATAFORMAT_SUBTYPE_DVSD)
|
|
&& (*pAvailableFormats)->SampleSize != ulSupportedFrameSize) {
|
|
TRACE(TL_STRM_TRACE,("_SUBTYPE_DVSD: StrmNum %d, %d of %d formats, SizeToVerify %d *!=* SupportedSampleSize %d\n",
|
|
ulStreamNumber,
|
|
j+1, ulNumberOfFormatArrayEntries,
|
|
(*pAvailableFormats)->SampleSize,
|
|
ulSupportedFrameSize));
|
|
continue;
|
|
}
|
|
|
|
|
|
// -------------------------------------------------------------------
|
|
// Specifier FORMAT_VideoInfo for VIDEOINFOHEADER
|
|
// -------------------------------------------------------------------
|
|
|
|
if(IsEqualGUID (&pDataRange->Specifier, &KSDATAFORMAT_SPECIFIER_VIDEOINFO)) {
|
|
|
|
PKS_DATARANGE_VIDEO pDataRangeVideoToVerify = (PKS_DATARANGE_VIDEO) pDataRange;
|
|
PKS_DATARANGE_VIDEO pDataRangeVideo = (PKS_DATARANGE_VIDEO) *pAvailableFormats;
|
|
|
|
#if 0
|
|
//
|
|
// Check that the other fields match
|
|
//
|
|
if ((pDataRangeVideoToVerify->bFixedSizeSamples != pDataRangeVideo->bFixedSizeSamples)
|
|
|| (pDataRangeVideoToVerify->bTemporalCompression != pDataRangeVideo->bTemporalCompression)
|
|
|| (pDataRangeVideoToVerify->StreamDescriptionFlags != pDataRangeVideo->StreamDescriptionFlags)
|
|
|| (pDataRangeVideoToVerify->MemoryAllocationFlags != pDataRangeVideo->MemoryAllocationFlags)
|
|
#ifdef COMPARE_CONFIG_CAP
|
|
|| (RtlCompareMemory (&pDataRangeVideoToVerify->ConfigCaps,
|
|
&pDataRangeVideo->ConfigCaps,
|
|
sizeof (KS_VIDEO_STREAM_CONFIG_CAPS)) !=
|
|
sizeof (KS_VIDEO_STREAM_CONFIG_CAPS))
|
|
#endif
|
|
) {
|
|
|
|
TRACE(TL_STRM_TRACE,("FormatFromRange: *!=* bFixSizeSample (%d %d) (%d %d) (%d %d) (%x %x)\n",
|
|
pDataRangeVideoToVerify->bFixedSizeSamples, pDataRangeVideo->bFixedSizeSamples,
|
|
pDataRangeVideoToVerify->bTemporalCompression , pDataRangeVideo->bTemporalCompression,
|
|
pDataRangeVideoToVerify->StreamDescriptionFlags, pDataRangeVideo->StreamDescriptionFlags,
|
|
pDataRangeVideoToVerify->ConfigCaps.VideoStandard, pDataRangeVideo->ConfigCaps.VideoStandard
|
|
));
|
|
|
|
continue;
|
|
} else {
|
|
TRACE(TL_STRM_TRACE,("FormatFromRange: == bFixSizeSample (%d %d) (%d %d) (%d %d) (%x %x)\n",
|
|
pDataRangeVideoToVerify->bFixedSizeSamples, pDataRangeVideo->bFixedSizeSamples,
|
|
pDataRangeVideoToVerify->bTemporalCompression , pDataRangeVideo->bTemporalCompression,
|
|
pDataRangeVideoToVerify->StreamDescriptionFlags, pDataRangeVideo->StreamDescriptionFlags,
|
|
pDataRangeVideoToVerify->ConfigCaps.VideoStandard, pDataRangeVideo->ConfigCaps.VideoStandard
|
|
));
|
|
}
|
|
|
|
#endif
|
|
bMatchFound = TRUE;
|
|
ulFormatSize = sizeof (KSDATAFORMAT) +
|
|
KS_SIZE_VIDEOHEADER (&pDataRangeVideo->VideoInfoHeader);
|
|
|
|
if(ulSizeOfDataFormatBuffer == 0) {
|
|
|
|
// We actually have not returned this much data,
|
|
// this "size" will be used by Ksproxy to send down
|
|
// a buffer of that size in next query.
|
|
*pulActualBytesTransferred = ulFormatSize;
|
|
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
|
|
// Caller wants the full data format
|
|
if(ulSizeOfDataFormatBuffer < ulFormatSize) {
|
|
TRACE(TL_STRM_TRACE,("VIDEOINFO: StreamNum %d, SizeOfDataFormatBuffer %d < ulFormatSize %d\n",ulStreamNumber, ulSizeOfDataFormatBuffer, ulFormatSize));
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
// KS_DATAFORMAT_VIDEOINFOHEADER
|
|
// KSDATAFORMAT DataFormat;
|
|
// KS_VIDEOINFOHEADER VideoInfoHeader;
|
|
RtlCopyMemory(
|
|
&((PKS_DATAFORMAT_VIDEOINFOHEADER)pDataFormatBuffer)->DataFormat,
|
|
&pDataRangeVideo->DataRange,
|
|
sizeof (KSDATAFORMAT));
|
|
|
|
// This size is differnt from our data range size which also contains ConfigCap
|
|
((PKSDATAFORMAT)pDataFormatBuffer)->FormatSize = ulFormatSize;
|
|
*pulActualBytesTransferred = ulFormatSize;
|
|
|
|
RtlCopyMemory(
|
|
&((PKS_DATAFORMAT_VIDEOINFOHEADER) pDataFormatBuffer)->VideoInfoHeader,
|
|
&pDataRangeVideo->VideoInfoHeader,
|
|
KS_SIZE_VIDEOHEADER (&pDataRangeVideo->VideoInfoHeader));
|
|
|
|
TRACE(TL_STRM_TRACE,("FormatFromRange: Matched, StrmNum %d, FormatSize %d, CopySize %d; FormatBufferSize %d, biSizeImage.\n",
|
|
ulStreamNumber, (*pAvailableFormats)->FormatSize, ulFormatSize, ulSizeOfDataFormatBuffer,
|
|
((PKS_DATAFORMAT_VIDEOINFOHEADER) pDataFormatBuffer)->VideoInfoHeader.bmiHeader.biSizeImage));
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} else if (IsEqualGUID (&pDataRange->Specifier, &KSDATAFORMAT_SPECIFIER_DVINFO)) {
|
|
// -------------------------------------------------------------------
|
|
// Specifier FORMAT_DVInfo for KS_DATARANGE_DVVIDEO
|
|
// -------------------------------------------------------------------
|
|
|
|
// MATCH FOUND!
|
|
bMatchFound = TRUE;
|
|
|
|
ulFormatSize = sizeof(KS_DATARANGE_DVVIDEO);
|
|
|
|
if(ulSizeOfDataFormatBuffer == 0) {
|
|
// We actually have not returned this much data,
|
|
// this "size" will be used by Ksproxy to send down
|
|
// a buffer of that size in next query.
|
|
*pulActualBytesTransferred = ulFormatSize;
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
// Caller wants the full data format
|
|
if (ulSizeOfDataFormatBuffer < ulFormatSize) {
|
|
TRACE(TL_STRM_ERROR,("DVINFO: StreamNum %d, SizeOfDataFormatBuffer %d < ulFormatSize %d\n", ulStreamNumber, ulSizeOfDataFormatBuffer, ulFormatSize));
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
pDataFormatBuffer,
|
|
*pAvailableFormats,
|
|
(*pAvailableFormats)->FormatSize);
|
|
|
|
((PKSDATAFORMAT)pDataFormatBuffer)->FormatSize = ulFormatSize;
|
|
*pulActualBytesTransferred = ulFormatSize;
|
|
|
|
#ifdef SUPPORT_NEW_AVC // Data intersection; return hPlug if flag is set
|
|
pPreConnectInfo = &(((KS_DATARANGE_DV_AVC *) *pAvailableFormats)->ConnectInfo);
|
|
pConnectInfo = &(((KS_DATAFORMAT_DV_AVC *) pDataFormatBuffer)->ConnectInfo);
|
|
|
|
if(pPreConnectInfo->Flags & (KSPIN_FLAG_AVC_PCRONLY | KSPIN_FLAG_AVC_FIXEDPCR)) {
|
|
// Need to return the plug handle
|
|
pConnectInfo->hPlug = \
|
|
(pPreConnectInfo->DataFlow == KSPIN_DATAFLOW_OUT) ? hPlugLocalOut : hPlugLocalIn;
|
|
} else {
|
|
// Choose any that is available
|
|
// Set to 0 for now.
|
|
pConnectInfo->hPlug = NULL;
|
|
}
|
|
|
|
#if DBG
|
|
TRACE(TL_STRM_TRACE,("DVINFO: pPreConnectInfo:%x; pConnectInfo:%x\n", pPreConnectInfo, pConnectInfo));
|
|
if(TapeDebugLevel >= 2) {
|
|
ASSERT(FALSE && "Check ConnectInfo!");
|
|
}
|
|
#endif
|
|
#endif
|
|
TRACE(TL_STRM_TRACE,("FormatFromRange: Matched, StrmNum %d, FormatSize %d, CopySize %d; FormatBufferSize %d.\n",
|
|
ulStreamNumber, (*pAvailableFormats)->FormatSize, ulFormatSize, ulSizeOfDataFormatBuffer));
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} else if (IsEqualGUID (&pDataRange->SubFormat, &KSDATAFORMAT_TYPE_MPEG2_TRANSPORT_STRIDE) ){
|
|
|
|
|
|
// -------------------------------------------------------------------
|
|
// Compare subformat since it is unique
|
|
// Subformat STATIC_KSDATAFORMAT_TYPE_MPEG2_TRANSPORT_STRIDE
|
|
// -------------------------------------------------------------------
|
|
|
|
#if 0 // Not enforced.
|
|
// Only for a certain specifier
|
|
if(!IsEqualGUID (&pDataRange->Specifier, &KSDATAFORMAT_SPECIFIER_61883_4)) {
|
|
TRACE(TL_STRM_TRACE,("SubFormat KSDATAFORMAT_TYPE_MPEG2_TRANSPORT_STRIDE but Specifier is not STATIC_KSDATAFORMAT_SPECIFIER_61883_4\n"));
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
// Sample size must match!
|
|
if((*pAvailableFormats)->SampleSize != pDataRange->SampleSize) {
|
|
TRACE(TL_STRM_TRACE,("SampleSize(MPEG2_TRANSPORT_STRIDE): Availabel:%d != Range:%d\n", (*pAvailableFormats)->SampleSize, pDataRange->SampleSize));
|
|
continue;
|
|
}
|
|
|
|
// MATCH FOUND!
|
|
bMatchFound = TRUE;
|
|
|
|
#ifdef SUPPORT_NEW_AVC
|
|
ulFormatSize = sizeof(KS_DATARANGE_MPEG2TS_STRIDE_AVC);
|
|
#else
|
|
ulFormatSize = sizeof(KS_DATARANGE_MPEG2TS_STRIDE_AVC) - sizeof(AVCPRECONNECTINFO); // FormatSize; exclude AVCPRECONNECTINFO
|
|
#endif
|
|
if(ulSizeOfDataFormatBuffer == 0) {
|
|
// We actually have not returned this much data,
|
|
// this "size" will be used by Ksproxy to send down
|
|
// a buffer of that size in next query.
|
|
*pulActualBytesTransferred = ulFormatSize;
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
// Caller wants the full data format
|
|
if (ulSizeOfDataFormatBuffer < ulFormatSize) {
|
|
TRACE(TL_STRM_ERROR,("MPEG2_TRANSPORT_STRIDE: StreamNum %d, SizeOfDataFormatBuffer %d < ulFormatSize %d\n", ulStreamNumber, ulSizeOfDataFormatBuffer, ulFormatSize));
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
// Verify the STRIDE structure
|
|
//
|
|
if( ((KS_DATARANGE_MPEG2TS_STRIDE_AVC *) pDataRange)->Stride.dwOffset != MPEG2TS_STRIDE_OFFSET
|
|
|| ((KS_DATARANGE_MPEG2TS_STRIDE_AVC *) pDataRange)->Stride.dwPacketLength != MPEG2TS_STRIDE_PACKET_LEN
|
|
|| ((KS_DATARANGE_MPEG2TS_STRIDE_AVC *) pDataRange)->Stride.dwStride != MPEG2TS_STRIDE_STRIDE_LEN
|
|
) {
|
|
TRACE(TL_PNP_ERROR,("AVCTapeGetDataIntersection:Invalid STRIDE parameters: dwOffset:%d; dwPacketLength:%d; dwStride:%d\n",
|
|
((KS_DATARANGE_MPEG2TS_STRIDE_AVC *) pDataRange)->Stride.dwOffset,
|
|
((KS_DATARANGE_MPEG2TS_STRIDE_AVC *) pDataRange)->Stride.dwPacketLength,
|
|
((KS_DATARANGE_MPEG2TS_STRIDE_AVC *) pDataRange)->Stride.dwStride
|
|
));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
RtlCopyMemory(pDataFormatBuffer, *pAvailableFormats, (*pAvailableFormats)->FormatSize);
|
|
((PKSDATAFORMAT)pDataFormatBuffer)->FormatSize = ulFormatSize;
|
|
*pulActualBytesTransferred = ulFormatSize;
|
|
|
|
#ifdef SUPPORT_NEW_AVC // Data intersection; return hPlug if flag is set
|
|
|
|
pPreConnectInfo = &(((KS_DATARANGE_MPEG2TS_AVC *) *pAvailableFormats)->ConnectInfo);
|
|
pConnectInfo = &(((KS_DATAFORMAT_MPEG2TS_AVC *) pDataFormatBuffer)->ConnectInfo);
|
|
|
|
|
|
if(pPreConnectInfo->Flags & (KSPIN_FLAG_AVC_PCRONLY | KSPIN_FLAG_AVC_FIXEDPCR)) {
|
|
// Need to return the plug handle
|
|
pConnectInfo->hPlug = \
|
|
(pPreConnectInfo->DataFlow == KSPIN_DATAFLOW_OUT) ? hPlugLocalOut : hPlugLocalIn;
|
|
} else {
|
|
// Choose any that is available
|
|
// Set to 0 for now.
|
|
pConnectInfo->hPlug = NULL;
|
|
}
|
|
#if DBG
|
|
TRACE(TL_STRM_TRACE,("MPEG2TS: pPreConnectInfo:%x; pConnectInfo:%x\n", pPreConnectInfo, pConnectInfo));
|
|
ASSERT(FALSE && "Check ConnectInfo!");
|
|
#endif
|
|
#endif
|
|
|
|
TRACE(TL_STRM_TRACE,("FormatFromRange:(MPEG2TS_STRIDE) Matched, StrmNum %d, FormatSize %d, CopySize %d; FormatBufferSize %d.\n",
|
|
ulStreamNumber, (*pAvailableFormats)->FormatSize, ulFormatSize, ulSizeOfDataFormatBuffer));
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} else if (IsEqualGUID (&pDataRange->SubFormat, &KSDATAFORMAT_TYPE_MPEG2_TRANSPORT)) {
|
|
|
|
// -------------------------------------------------------------------
|
|
// Compare subformat since it is unique
|
|
// Subformat STATIC_KSDATAFORMAT_TYPE_MPEG2_TRANSPORT
|
|
// -------------------------------------------------------------------
|
|
|
|
// Sample size must match!
|
|
if((*pAvailableFormats)->SampleSize != pDataRange->SampleSize) {
|
|
TRACE(TL_STRM_TRACE,("SampleSize(MPEG2_TRANSPORT): Availabel:%d != Range:%d\n", (*pAvailableFormats)->SampleSize, pDataRange->SampleSize));
|
|
continue;
|
|
}
|
|
|
|
// MATCH FOUND!
|
|
bMatchFound = TRUE;
|
|
|
|
#ifdef SUPPORT_NEW_AVC
|
|
ulFormatSize = sizeof(KS_DATARANGE_MPEG2TS_AVC);
|
|
#else
|
|
ulFormatSize = sizeof(KS_DATARANGE_MPEG2TS_AVC) - sizeof(AVCPRECONNECTINFO); // FormatSize; exclude AVCPRECONNECTINFO
|
|
#endif
|
|
if(ulSizeOfDataFormatBuffer == 0) {
|
|
// We actually have not returned this much data,
|
|
// this "size" will be used by Ksproxy to send down
|
|
// a buffer of that size in next query.
|
|
*pulActualBytesTransferred = ulFormatSize;
|
|
return STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
// Caller wants the full data format
|
|
if (ulSizeOfDataFormatBuffer < ulFormatSize) {
|
|
TRACE(TL_STRM_ERROR,("MPEG2_TRANSPORT: StreamNum %d, SizeOfDataFormatBuffer %d < ulFormatSize %d\n", ulStreamNumber, ulSizeOfDataFormatBuffer, ulFormatSize));
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
RtlCopyMemory(pDataFormatBuffer, *pAvailableFormats, (*pAvailableFormats)->FormatSize);
|
|
((PKSDATAFORMAT)pDataFormatBuffer)->FormatSize = ulFormatSize;
|
|
*pulActualBytesTransferred = ulFormatSize;
|
|
|
|
#ifdef SUPPORT_NEW_AVC // Data intersection; return hPlug if flag is set
|
|
|
|
pPreConnectInfo = &(((KS_DATARANGE_MPEG2TS_AVC *) *pAvailableFormats)->ConnectInfo);
|
|
pConnectInfo = &(((KS_DATAFORMAT_MPEG2TS_AVC *) pDataFormatBuffer)->ConnectInfo);
|
|
|
|
|
|
if(pPreConnectInfo->Flags & (KSPIN_FLAG_AVC_PCRONLY | KSPIN_FLAG_AVC_FIXEDPCR)) {
|
|
// Need to return the plug handle
|
|
pConnectInfo->hPlug = \
|
|
(pPreConnectInfo->DataFlow == KSPIN_DATAFLOW_OUT) ? hPlugLocalOut : hPlugLocalIn;
|
|
} else {
|
|
// Choose any that is available
|
|
// Set to 0 for now.
|
|
pConnectInfo->hPlug = NULL;
|
|
}
|
|
#if DBG
|
|
TRACE(TL_STRM_TRACE,("MPEG2TS: pPreConnectInfo:%x; pConnectInfo:%x\n", pPreConnectInfo, pConnectInfo));
|
|
ASSERT(FALSE && "Check ConnectInfo!");
|
|
#endif
|
|
#endif
|
|
|
|
TRACE(TL_STRM_TRACE,("FormatFromRange: (MPEG2TS) Matched, StrmNum %d, FormatSize %d, CopySize %d; FormatBufferSize %d.\n",
|
|
ulStreamNumber, (*pAvailableFormats)->FormatSize, ulFormatSize, ulSizeOfDataFormatBuffer));
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
} // End of loop on all formats for this stream
|
|
|
|
if(!bMatchFound) {
|
|
|
|
TRACE(TL_STRM_TRACE,("FormatFromRange: No Match! StrmNum %d, pDataRange %x\n", ulStreamNumber, pDataRange));
|
|
}
|
|
|
|
return STATUS_NO_MATCH;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
AVCTapeIniStrmExt(
|
|
PHW_STREAM_OBJECT pStrmObject,
|
|
PSTREAMEX pStrmExt,
|
|
PDVCR_EXTENSION pDevExt,
|
|
PSTREAM_INFO_AND_OBJ pStream
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize stream extension strcuture.
|
|
|
|
--*/
|
|
{
|
|
|
|
PAGED_CODE();
|
|
|
|
RtlZeroMemory( pStrmExt, sizeof(STREAMEX) );
|
|
|
|
pStrmExt->bEOStream = TRUE; // Stream has not started yet!
|
|
|
|
pStrmExt->pStrmObject = pStrmObject;
|
|
pStrmExt->StreamState = KSSTATE_STOP;
|
|
pStrmExt->pDevExt = pDevExt;
|
|
|
|
pStrmExt->hMyClock = 0;
|
|
pStrmExt->hMasterClock = 0;
|
|
pStrmExt->hClock = 0;
|
|
|
|
|
|
//
|
|
// Aplly to both IN/OUT data flow
|
|
//
|
|
//
|
|
// Init isoch resources
|
|
//
|
|
pStrmExt->CurrentStreamTime = 0;
|
|
|
|
pStrmExt->cntSRBReceived = 0; // Total number of SRB_READ/WRITE_DATA
|
|
pStrmExt->cntDataSubmitted = 0; // Number of pending data buffer
|
|
|
|
pStrmExt->cntSRBCancelled = 0; // number of SRB_READ/WRITE_DATA cancelled
|
|
|
|
|
|
pStrmExt->FramesProcessed = 0;
|
|
pStrmExt->PictureNumber = 0;
|
|
pStrmExt->FramesDropped = 0;
|
|
|
|
//
|
|
// Subcode data that can be extract from a DV frame
|
|
//
|
|
|
|
pStrmExt->AbsTrackNumber = 0;
|
|
pStrmExt->bATNUpdated = FALSE;
|
|
|
|
pStrmExt->Timecode[0] = 0;
|
|
pStrmExt->Timecode[1] = 0;
|
|
pStrmExt->Timecode[2] = 0;
|
|
pStrmExt->Timecode[3] = 0;
|
|
pStrmExt->bTimecodeUpdated = FALSE;
|
|
|
|
|
|
//
|
|
// Work item variables use to cancel all SRBs
|
|
//
|
|
pStrmExt->lCancelStateWorkItem = 0;
|
|
pStrmExt->AbortInProgress = FALSE;
|
|
|
|
#ifdef USE_WDM110
|
|
pStrmExt->pIoWorkItem = NULL;
|
|
#endif
|
|
|
|
//
|
|
// Cache the pointer
|
|
// What in DVStreams[] are READONLY
|
|
//
|
|
pStrmExt->pStrmInfo = &pStream->hwStreamInfo;
|
|
|
|
pStrmObject->ReceiveDataPacket = (PVOID) pStream->hwStreamObject.ReceiveDataPacket;
|
|
pStrmObject->ReceiveControlPacket = (PVOID) pStream->hwStreamObject.ReceiveControlPacket;
|
|
pStrmObject->Dma = pStream->hwStreamObject.Dma;
|
|
pStrmObject->Pio = pStream->hwStreamObject.Pio;
|
|
pStrmObject->StreamHeaderWorkspace = pStream->hwStreamObject.StreamHeaderWorkspace;
|
|
pStrmObject->StreamHeaderMediaSpecific = pStream->hwStreamObject.StreamHeaderMediaSpecific;
|
|
pStrmObject->HwClockObject = pStream->hwStreamObject.HwClockObject;
|
|
pStrmObject->Allocator = pStream->hwStreamObject.Allocator;
|
|
pStrmObject->HwEventRoutine = pStream->hwStreamObject.HwEventRoutine;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
AVCTapeOpenStream(
|
|
IN PHW_STREAM_OBJECT pStrmObject,
|
|
IN PKSDATAFORMAT pOpenFormat,
|
|
IN PAV_61883_REQUEST pAVReq
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Verify the OpenFormat and then allocate PC resource needed for this stream.
|
|
The isoch resource, if needed, is allocated when streaming is transition to PAUSE state.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PSTREAMEX pStrmExt;
|
|
PDVCR_EXTENSION pDevExt;
|
|
ULONG idxStreamNumber;
|
|
KSPIN_DATAFLOW DataFlow;
|
|
PIRP pIrp = NULL;
|
|
FMT_INDEX VideoFormatIndexLast; // Last format index; used to detect change.
|
|
PAVC_STREAM_REQUEST_BLOCK pAVCStrmReq;
|
|
ULONG i, j;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
pDevExt = (PDVCR_EXTENSION) pStrmObject->HwDeviceExtension;
|
|
pStrmExt = (PSTREAMEX) pStrmObject->HwStreamExtension;
|
|
idxStreamNumber = pStrmObject->StreamNumber;
|
|
|
|
TRACE(TL_STRM_TRACE,("OpenStream: pStrmObject %x, pOpenFormat %x, cntOpen %d, idxStream %d\n", pStrmObject, pOpenFormat, pDevExt->cndStrmOpen, idxStreamNumber));
|
|
|
|
//
|
|
// When nonone else has open a stream (or is opening ?)
|
|
//
|
|
if(pDevExt->cndStrmOpen > 0) {
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
TRACE(TL_STRM_WARNING,("OpenStream: %d stream open already; failed hr %x\n", pDevExt->cndStrmOpen, Status));
|
|
return Status;
|
|
}
|
|
|
|
pIrp = IoAllocateIrp(pDevExt->pBusDeviceObject->StackSize, FALSE);
|
|
if(!pIrp) {
|
|
ASSERT(pIrp && "IoAllocateIrp() failed!");
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// If a user switch from Camera to VCR mode very quickly (passing the OFF position),
|
|
// the driver may not be relaoded to detect correct mode of operation.
|
|
// It is safe to redetect here.
|
|
// Note: MSDV does return all the stream info for both input and output pin format.
|
|
//
|
|
DVGetDevModeOfOperation(pDevExt);
|
|
|
|
|
|
//
|
|
// WARNING: !! we advertise both input and output pin regardless of its mode of operation,
|
|
// but Camera does not support input pin so open should failed!
|
|
// If a VCR does not have input pin should fail as well.
|
|
//
|
|
// Ignore checking for ED_DEVTYOPE_UNKNOWN (most likely a hardware decoder box)
|
|
//
|
|
if((pDevExt->ulDevType == ED_DEVTYPE_CAMERA ||
|
|
(pDevExt->ulDevType == ED_DEVTYPE_VCR && pDevExt->pDevInPlugs->NumPlugs == 0))
|
|
&& idxStreamNumber == 2) {
|
|
|
|
IoFreeIrp(pIrp);
|
|
TRACE(TL_STRM_WARNING,("OpenStream:Camera mode or VCR with 0 input pin cannot take external in.\n"));
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
ASSERT(idxStreamNumber < pDevExt->NumOfPins);
|
|
ASSERT(pDevExt->paStrmExt[idxStreamNumber] == NULL); // Not yet open!
|
|
|
|
//
|
|
// Data flow
|
|
//
|
|
DataFlow= pDevExt->pStreamInfoObject[idxStreamNumber].hwStreamInfo.DataFlow;
|
|
|
|
|
|
//
|
|
// Initialize the stream extension structure
|
|
//
|
|
AVCTapeIniStrmExt(
|
|
pStrmObject,
|
|
pStrmExt,
|
|
pDevExt,
|
|
&pDevExt->pStreamInfoObject[idxStreamNumber]
|
|
);
|
|
|
|
//
|
|
// Sony's NTSC can play PAL tape and its plug will change its supported format accordingly.
|
|
//
|
|
// Query video format (NTSC/PAL) supported.
|
|
// Compare with its default (set at load time or last opensteam),
|
|
// if difference, change our internal video format table.
|
|
//
|
|
if(pDevExt->ulDevType != ED_DEVTYPE_CAMERA) {
|
|
VideoFormatIndexLast = pDevExt->VideoFormatIndex;
|
|
if(!DVGetDevSignalFormat(
|
|
pDevExt,
|
|
DataFlow,
|
|
pStrmExt
|
|
)) {
|
|
IoFreeIrp(pIrp);
|
|
// If querying its format has failed, we cannot open this stream.
|
|
TRACE(TL_STRM_WARNING,("OpenStream:Camera mode cannot take external in.\n"));
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto AbortOpenStream;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Check the video data format is okay.
|
|
//
|
|
if(!AVCTapeVerifyDataFormat(
|
|
pDevExt->NumOfPins,
|
|
pOpenFormat,
|
|
idxStreamNumber,
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].FrameSize,
|
|
pDevExt->pStreamInfoObject
|
|
) ) {
|
|
IoFreeIrp(pIrp);
|
|
TRACE(TL_STRM_ERROR,("OpenStream: AdapterVerifyFormat failed.\n"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
//
|
|
// This event guard againt work item completion
|
|
//
|
|
|
|
KeInitializeEvent(&pStrmExt->hCancelDoneEvent, NotificationEvent, TRUE);
|
|
|
|
|
|
//
|
|
// Alloccate synchronization structures for flow control and queue management
|
|
//
|
|
|
|
pStrmExt->hMutexFlow = (KMUTEX *) ExAllocatePool(NonPagedPool, sizeof(KMUTEX));
|
|
if(!pStrmExt->hMutexFlow) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto AbortOpenStream;
|
|
}
|
|
KeInitializeMutex( pStrmExt->hMutexFlow, 0); // Level 0 and in Signal state
|
|
|
|
pStrmExt->hMutexReq = (KMUTEX *) ExAllocatePool(NonPagedPool, sizeof(KMUTEX));
|
|
if(!pStrmExt->hMutexReq) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto AbortOpenStream;
|
|
}
|
|
KeInitializeMutex(pStrmExt->hMutexReq, 0);
|
|
|
|
pStrmExt->DataListLock = (KSPIN_LOCK *) ExAllocatePool(NonPagedPool, sizeof(KSPIN_LOCK));
|
|
if(!pStrmExt->DataListLock) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto AbortOpenStream;
|
|
}
|
|
KeInitializeSpinLock(pStrmExt->DataListLock);
|
|
|
|
|
|
//
|
|
// Request AVCStrm to open a stream
|
|
//
|
|
|
|
pStrmExt->pIrpReq = IoAllocateIrp(pDevExt->pBusDeviceObject->StackSize, FALSE);
|
|
if(!pStrmExt->pIrpReq) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto AbortOpenStream;
|
|
}
|
|
|
|
pStrmExt->pIrpAbort = IoAllocateIrp(pDevExt->pBusDeviceObject->StackSize, FALSE);
|
|
if(!pStrmExt->pIrpAbort) {
|
|
IoFreeIrp(pStrmExt->pIrpReq); pStrmExt->pIrpReq = NULL;
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto AbortOpenStream;
|
|
}
|
|
|
|
|
|
//
|
|
// Pre-allocate list of detached (free) and attached (busy) list for tracking
|
|
// data request sending down to lower driver for processing.
|
|
//
|
|
InitializeListHead(&pStrmExt->DataDetachedListHead); pStrmExt->cntDataDetached = 0;
|
|
InitializeListHead(&pStrmExt->DataAttachedListHead); pStrmExt->cntDataAttached = 0;
|
|
|
|
for (i=0; i < MAX_DATA_REQUESTS; i++) {
|
|
pStrmExt->AsyncReq[i].pIrp = IoAllocateIrp(pDevExt->pBusDeviceObject->StackSize, FALSE);
|
|
if(!pStrmExt->AsyncReq[i].pIrp) {
|
|
// Free resource allocated so far.
|
|
for (j=0; j < i; j++) {
|
|
if(pStrmExt->AsyncReq[j].pIrp) {
|
|
IoFreeIrp(pStrmExt->AsyncReq[j].pIrp); pStrmExt->AsyncReq[j].pIrp = NULL;
|
|
}
|
|
RemoveEntryList(&pStrmExt->AsyncReq[j].ListEntry); pStrmExt->cntDataDetached--;
|
|
}
|
|
IoFreeIrp(pStrmExt->pIrpAbort); pStrmExt->pIrpAbort = NULL;
|
|
IoFreeIrp(pStrmExt->pIrpReq); pStrmExt->pIrpReq = NULL;
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto AbortOpenStream;
|
|
}
|
|
|
|
InsertTailList(&pStrmExt->DataDetachedListHead, &pStrmExt->AsyncReq[i].ListEntry); pStrmExt->cntDataDetached++;
|
|
}
|
|
|
|
// Synchronous calls share the same AV request packet in the stream extension..
|
|
EnterAVCStrm(pStrmExt->hMutexReq);
|
|
|
|
pAVCStrmReq = &pStrmExt->AVCStrmReq;
|
|
RtlZeroMemory(pAVCStrmReq, sizeof(AVC_STREAM_REQUEST_BLOCK));
|
|
INIT_AVCSTRM_HEADER(pAVCStrmReq, AVCSTRM_OPEN);
|
|
#if 1
|
|
if(pDevExt->VideoFormatIndex == AVCSTRM_FORMAT_MPEG2TS) {
|
|
// Data Rate
|
|
// AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].AvgTimePerFrame = ?
|
|
if(DataFlow == KSPIN_DATAFLOW_IN) {
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].OptionFlags = 0;
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].FrameSize = BUFFER_SIZE_MPEG2TS_SPH;
|
|
} else {
|
|
if(IsEqualGUID (&pOpenFormat->SubFormat, &KSDATAFORMAT_TYPE_MPEG2_TRANSPORT_STRIDE)) {
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].OptionFlags = 0;
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].FrameSize = BUFFER_SIZE_MPEG2TS_SPH;
|
|
} else {
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].OptionFlags = AVCSTRM_FORMAT_OPTION_STRIP_SPH;
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].FrameSize = BUFFER_SIZE_MPEG2TS;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
pAVCStrmReq->CommandData.OpenStruct.AVCFormatInfo = &AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex];
|
|
pAVCStrmReq->CommandData.OpenStruct.AVCStreamContext = 0; // will return the AV stream context
|
|
pAVCStrmReq->CommandData.OpenStruct.DataFlow = DataFlow;
|
|
#ifdef SUPPORT_LOCAL_PLUGS
|
|
if(DataFlow == KSPIN_DATAFLOW_OUT)
|
|
pAVCStrmReq->CommandData.OpenStruct.hPlugLocal = pDevExt->hInputPCRLocal; // Remote(oPCR)->Local(iPCR)
|
|
else
|
|
pAVCStrmReq->CommandData.OpenStruct.hPlugLocal = pDevExt->hOutputPCRLocal; // Remote(iPCR)<-Local(oPCR)
|
|
#else
|
|
pAVCStrmReq->CommandData.OpenStruct.hPlugLocal = 0; // Not supported; use whatever 61883 supply.
|
|
#endif
|
|
|
|
Status =
|
|
AVCStrmReqSubmitIrpSynch(
|
|
pDevExt->pBusDeviceObject,
|
|
pStrmExt->pIrpReq,
|
|
pAVCStrmReq
|
|
);
|
|
|
|
// Expect SUCCESS or anything else is failure! (including _PENDING) since this is a Sync call.
|
|
if(STATUS_SUCCESS != Status) {
|
|
TRACE(TL_STRM_ERROR,("AVCSTRM_OPEN: failed %x; pAVCStrmReq:%x\n", Status, pAVCStrmReq));
|
|
ASSERT(NT_SUCCESS(Status) && "AVCSTGRM_OPEN failed!\n");
|
|
IoFreeIrp(pStrmExt->pIrpReq); pStrmExt->pIrpReq = NULL;
|
|
LeaveAVCStrm(pStrmExt->hMutexReq);
|
|
goto OpenStreamDone; // Failed to open!
|
|
}
|
|
|
|
//
|
|
// Save the context, which is used for subsequent call to AVCStrm filter driver
|
|
//
|
|
pStrmExt->AVCStreamContext = pAVCStrmReq->CommandData.OpenStruct.AVCStreamContext;
|
|
TRACE(TL_STRM_TRACE,("AVCSTRM_OPEN: suceeded %x; pAVCStrmReq:%x; AVCStreamContext:%x\n", Status, pAVCStrmReq, pStrmExt->AVCStreamContext));
|
|
|
|
|
|
//
|
|
// Format specific tasks
|
|
//
|
|
switch(pDevExt->VideoFormatIndex) {
|
|
// For DV input pin, setup a timer DPC to periodically fired to singal clock event.
|
|
case AVCSTRM_FORMAT_MPEG2TS:
|
|
break;
|
|
|
|
case AVCSTRM_FORMAT_SDDV_NTSC: // 61883-2
|
|
case AVCSTRM_FORMAT_SDDV_PAL: // 61883-2
|
|
case AVCSTRM_FORMAT_HDDV_NTSC: // 61883-3
|
|
case AVCSTRM_FORMAT_HDDV_PAL: // 61883-3
|
|
case AVCSTRM_FORMAT_SDLDV_NTSC: // 61883-5
|
|
case AVCSTRM_FORMAT_SDLDV_PAL: // 61883-5
|
|
#ifdef SUPPORT_LOCAL_PLUGS
|
|
if(DataFlow == KSPIN_DATAFLOW_IN) {
|
|
// Remote(iPCR)<-Local(oPCR)
|
|
// The default was S200 for MPEG2TS data; set it to DV.
|
|
pDevExt->OPCR.oPCR.DataRate = CMP_SPEED_S100;
|
|
pDevExt->OPCR.oPCR.OverheadID = PCR_OVERHEAD_ID_SDDV_DEF;
|
|
pDevExt->OPCR.oPCR.Payload = PCR_PAYLOAD_SDDV_DEF;
|
|
if(AVCTapeSetLocalPlug(
|
|
pDevExt,
|
|
&pDevExt->AVReq,
|
|
&pDevExt->hOutputPCRLocal,
|
|
&pDevExt->OPCR)) {
|
|
TRACE(TL_STRM_ERROR|TL_61883_ERROR,("Failed to set oPCR\n"));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
KeInitializeDpc(
|
|
&pStrmExt->DPCTimer,
|
|
AVCTapeSignalClockEvent,
|
|
pStrmExt
|
|
);
|
|
KeInitializeTimer(
|
|
&pStrmExt->Timer
|
|
);
|
|
break;
|
|
default:
|
|
// Not supported!
|
|
break;
|
|
}
|
|
|
|
|
|
LeaveAVCStrm(pStrmExt->hMutexReq);
|
|
|
|
//
|
|
// Cache it and reference when pDevExt is all we have, such as BusReset and SurprieseRemoval
|
|
//
|
|
pDevExt->idxStreamNumber = idxStreamNumber; // index of current active stream; work only if there is only one active stream at any time.
|
|
pDevExt->paStrmExt[idxStreamNumber] = pStrmExt;
|
|
|
|
//
|
|
// In the future, a DV can be unplug and plug back in,
|
|
// and restore its state if the application is not yet closed.
|
|
//
|
|
pDevExt->bDevRemoved = FALSE;
|
|
|
|
//
|
|
// No one else can open another stream (inout or output) unitil this is release.
|
|
// This is done to avoid cyclic graph.
|
|
//
|
|
pDevExt->cndStrmOpen++;
|
|
ASSERT(pDevExt->cndStrmOpen == 1);
|
|
|
|
OpenStreamDone:
|
|
|
|
TRACE(TL_STRM_WARNING,("OpenStream: %d stream open, idx %d, Status %x, pStrmExt %x, Context:%x; pDevExt %x\n",
|
|
pDevExt->cndStrmOpen, pDevExt->idxStreamNumber, Status, pStrmExt, pStrmExt->AVCStreamContext, pDevExt));
|
|
TRACE(TL_STRM_TRACE,("OpenStream: Status %x, idxStream %d, pDevExt %x, pStrmExt %x, Contextg:%x\n",
|
|
Status, idxStreamNumber, pDevExt, pStrmExt, pStrmExt->AVCStreamContext));
|
|
|
|
return Status;
|
|
|
|
AbortOpenStream:
|
|
|
|
if(pStrmExt->DataListLock) {
|
|
ExFreePool(pStrmExt->DataListLock); pStrmExt->DataListLock = NULL;
|
|
}
|
|
|
|
if(pStrmExt->hMutexFlow) {
|
|
ExFreePool(pStrmExt->hMutexFlow); pStrmExt->hMutexFlow = NULL;
|
|
}
|
|
|
|
if(pStrmExt->hMutexReq) {
|
|
ExFreePool(pStrmExt->hMutexReq); pStrmExt->hMutexReq = NULL;
|
|
}
|
|
|
|
TRACE(TL_STRM_ERROR,("OpenStream failed %x, idxStream %d, pDevExt %x, pStrmExt %x\n",
|
|
Status, idxStreamNumber, pDevExt, pStrmExt));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AVCTapeCloseStream(
|
|
IN PHW_STREAM_OBJECT pStrmObject,
|
|
IN PKSDATAFORMAT pOpenFormat,
|
|
IN PAV_61883_REQUEST pAVReq
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when an CloseStream Srb request is received
|
|
|
|
--*/
|
|
|
|
{
|
|
PSTREAMEX pStrmExt;
|
|
PDVCR_EXTENSION pDevExt;
|
|
ULONG idxStreamNumber;
|
|
NTSTATUS Status;
|
|
PAVC_STREAM_REQUEST_BLOCK pAVCStrmReq;
|
|
ULONG i;
|
|
PDRIVER_REQUEST pDriverReq;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
pDevExt = (PDVCR_EXTENSION) pStrmObject->HwDeviceExtension;
|
|
pStrmExt = (PSTREAMEX) pStrmObject->HwStreamExtension;
|
|
idxStreamNumber = pStrmObject->StreamNumber;
|
|
|
|
|
|
TRACE(TL_STRM_TRACE,("CloseStream: >> pStrmExt %x, pDevExt %x\n", pStrmExt, pDevExt));
|
|
|
|
|
|
//
|
|
// If the stream isn't open, just return
|
|
//
|
|
if(pStrmExt == NULL) {
|
|
ASSERT(pStrmExt && "CloseStream but pStrmExt is NULL!");
|
|
return STATUS_SUCCESS; // ????
|
|
}
|
|
|
|
//
|
|
// Wait until the pending work item is completed.
|
|
//
|
|
KeWaitForSingleObject( &pStrmExt->hCancelDoneEvent, Executive, KernelMode, FALSE, 0 );
|
|
|
|
//
|
|
// Request AVCStrm to close a stream
|
|
//
|
|
EnterAVCStrm(pStrmExt->hMutexReq);
|
|
|
|
#if 0
|
|
// For DV input pin, setup a timer DPC to periodically fired to singal clock event.
|
|
if(pDevExt->VideoFormatIndex != AVCSTRM_FORMAT_MPEG2TS) {
|
|
// Cancel timer
|
|
TRACE(TL_STRM_TRACE,("*** CancelTimer *********************************************...\n"));
|
|
KeCancelTimer(
|
|
&pStrmExt->Timer
|
|
);
|
|
}
|
|
#endif
|
|
|
|
pAVCStrmReq = &pStrmExt->AVCStrmReq;
|
|
RtlZeroMemory(pAVCStrmReq, sizeof(AVC_STREAM_REQUEST_BLOCK));
|
|
INIT_AVCSTRM_HEADER(pAVCStrmReq, AVCSTRM_CLOSE);
|
|
|
|
pAVCStrmReq->AVCStreamContext = pStrmExt->AVCStreamContext;
|
|
|
|
Status =
|
|
AVCStrmReqSubmitIrpSynch(
|
|
pDevExt->pBusDeviceObject,
|
|
pStrmExt->pIrpReq,
|
|
pAVCStrmReq
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
TRACE(TL_STRM_ERROR,("AVCSTRM_CLOSE: failed %x; pAVCStrmReq:%x\n", Status, pAVCStrmReq));
|
|
ASSERT(NT_SUCCESS(Status) && "AVCSTGRM_CLOSE failed!\n");
|
|
}
|
|
else {
|
|
// Save the context, which is used for subsequent call to AVCStrm.sys
|
|
TRACE(TL_STRM_TRACE,("AVCSTRM_CLOSE: suceeded %x; pAVCStrmReq:%x\n", Status, pAVCStrmReq));
|
|
pStrmExt->AVCStreamContext = 0;
|
|
}
|
|
|
|
// Free system resources
|
|
if(pStrmExt->pIrpReq) {
|
|
IoFreeIrp(pStrmExt->pIrpReq); pStrmExt->pIrpReq = NULL;
|
|
}
|
|
|
|
if(pStrmExt->pIrpAbort) {
|
|
IoFreeIrp(pStrmExt->pIrpAbort); pStrmExt->pIrpAbort = NULL;
|
|
}
|
|
|
|
#if 0
|
|
for (i=0; i < MAX_DATA_REQUESTS; i++) {
|
|
if(pStrmExt->AsyncReq[i].pIrp) {
|
|
IoFreeIrp(pStrmExt->AsyncReq[i].pIrp); pStrmExt->AsyncReq[i].pIrp = NULL;
|
|
}
|
|
}
|
|
#else
|
|
//
|
|
// Free IRPs preallocated. The entire data structure is part of the stream extension so
|
|
// it will be freed by the StreamClass.
|
|
//
|
|
ASSERT(pStrmExt->cntDataAttached == 0);
|
|
ASSERT(pStrmExt->cntDataDetached >= MAX_DATA_REQUESTS);
|
|
while (!IsListEmpty(&pStrmExt->DataDetachedListHead)) {
|
|
pDriverReq = (PDRIVER_REQUEST) RemoveHeadList(&pStrmExt->DataDetachedListHead); pStrmExt->cntDataDetached--;
|
|
IoFreeIrp(pDriverReq->pIrp); pDriverReq->pIrp = NULL;
|
|
}
|
|
#endif
|
|
|
|
LeaveAVCStrm(pStrmExt->hMutexReq);
|
|
|
|
//
|
|
// Not valid after this call.
|
|
//
|
|
for (i=0; i<pDevExt->NumOfPins; i++) {
|
|
//
|
|
// Find what we cache and remove it.
|
|
//
|
|
if(pStrmExt == pDevExt->paStrmExt[i]) {
|
|
pDevExt->paStrmExt[i] = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free synchronization structures
|
|
//
|
|
|
|
if(pStrmExt->DataListLock) {
|
|
ExFreePool(pStrmExt->DataListLock); pStrmExt->DataListLock = NULL;
|
|
}
|
|
|
|
if(pStrmExt->hMutexFlow) {
|
|
ExFreePool(pStrmExt->hMutexFlow); pStrmExt->hMutexFlow = NULL;
|
|
}
|
|
|
|
if(pStrmExt->hMutexReq) {
|
|
ExFreePool(pStrmExt->hMutexReq); pStrmExt->hMutexReq = NULL;
|
|
}
|
|
|
|
// Release this count so other can open.
|
|
pDevExt->cndStrmOpen--;
|
|
ASSERT(pDevExt->cndStrmOpen == 0);
|
|
|
|
TRACE(TL_STRM_TRACE,("CloseStream: completed; %d stream;\n", pDevExt->cndStrmOpen));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DVChangePower(
|
|
PDVCR_EXTENSION pDevExt,
|
|
PAV_61883_REQUEST pAVReq,
|
|
DEVICE_POWER_STATE NewPowerState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process changing this device's power state.
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
//
|
|
// D0: Device is on and can be streaming.
|
|
// D1,D2: not supported.
|
|
// D3: Device is off and can not streaming. The context is lost.
|
|
// Power can be removed from the device.
|
|
// When power is back on, we will get a bus reset.
|
|
//
|
|
|
|
TRACE(TL_PNP_TRACE,("ChangePower: PowrSt: %d->%d; (d0:[1:On],D3[4:off])\n", pDevExt->PowerState, NewPowerState));
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
if(pDevExt->PowerState == NewPowerState) {
|
|
TRACE(TL_STRM_WARNING,("ChangePower: no change; do nothing!\n"));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
switch (NewPowerState) {
|
|
case PowerDeviceD3: // Power OFF
|
|
// We are at D0 and ask to go to D3: save state, stop streaming and Sleep
|
|
if( pDevExt->PowerState == PowerDeviceD0) {
|
|
// For a supported power state change
|
|
for (i=0; i<pDevExt->NumOfPins; i++) {
|
|
if(pDevExt->paStrmExt[i]) {
|
|
if(pDevExt->paStrmExt[i]->bIsochIsActive) {
|
|
// Stop isoch but do not change the streaming state
|
|
TRACE(TL_PNP_WARNING,("ChangePower: Stop isoch but not change stream state:%d\n", pDevExt->paStrmExt[i]->StreamState));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
TRACE(TL_PNP_WARNING,("pDevExt->paStrmExt[i].StreamState:Intermieate power state; do nothing;\n"));
|
|
}
|
|
break;
|
|
|
|
case PowerDeviceD0: // Powering ON (waking up)
|
|
if( pDevExt->PowerState == PowerDeviceD3) {
|
|
// For a supported power state change
|
|
for (i=0; i<pDevExt->NumOfPins; i++) {
|
|
if(pDevExt->paStrmExt[i]) {
|
|
if(!pDevExt->paStrmExt[i]->bIsochIsActive) {
|
|
TRACE(TL_PNP_ERROR,("ChangePower: StrmSt:%d; Start isoch\n", pDevExt->paStrmExt[i]->StreamState));
|
|
// Start isoch depending on streaming state for DATAFLOW_IN/OUT
|
|
if(pDevExt->paStrmExt[i]->pStrmInfo->DataFlow == KSPIN_DATAFLOW_IN) {
|
|
if(pDevExt->paStrmExt[i]->StreamState == KSSTATE_PAUSE ||
|
|
pDevExt->paStrmExt[i]->StreamState == KSSTATE_RUN) {
|
|
}
|
|
}
|
|
else if(pDevExt->paStrmExt[i]->pStrmInfo->DataFlow == KSPIN_DATAFLOW_OUT) {
|
|
if(pDevExt->paStrmExt[i]->StreamState == KSSTATE_RUN) {
|
|
}
|
|
}
|
|
} // IsochActive
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
TRACE(TL_PNP_WARNING,("Intermieate power state; do nothing;\n"));
|
|
}
|
|
break;
|
|
|
|
// These state are not supported.
|
|
case PowerDeviceD1:
|
|
case PowerDeviceD2:
|
|
default:
|
|
TRACE(TL_PNP_WARNING,("ChangePower: Not supported PowerState %d\n", DevicePowerState));
|
|
Status = STATUS_SUCCESS; // STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
|
|
if(Status == STATUS_SUCCESS)
|
|
pDevExt->PowerState = NewPowerState;
|
|
else
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AVCTapeSurpriseRemoval(
|
|
PDVCR_EXTENSION pDevExt,
|
|
PAV_61883_REQUEST pAVReq
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Response to SRB_SURPRISE_REMOVAL.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
PKSEVENT_ENTRY pEvent = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// ONLY place this flag is set to TRUE.
|
|
// Block incoming read although there might still in the process of being attached
|
|
//
|
|
pDevExt->bDevRemoved = TRUE;
|
|
|
|
// Signal
|
|
if(pDevExt->PowerState != PowerDeviceD3) {
|
|
pDevExt->PowerState = PowerDeviceD3; // It is as good as power is off.
|
|
}
|
|
|
|
//
|
|
// Now Stop the stream and clean up
|
|
//
|
|
|
|
for(i=0; i < pDevExt->NumOfPins; i++) {
|
|
|
|
if(pDevExt->paStrmExt[i] != NULL) {
|
|
|
|
TRACE(TL_PNP_WARNING,("#SURPRISE_REMOVAL# StrmNum %d, pStrmExt %x\n", i, pDevExt->paStrmExt[i]));
|
|
|
|
// Signal this event so SRB can complete.
|
|
if(pDevExt->paStrmExt[i]->pStrmInfo->DataFlow == KSPIN_DATAFLOW_IN ) {
|
|
//
|
|
// Imply EOStream!
|
|
//
|
|
|
|
if(!pDevExt->paStrmExt[i]->bEOStream)
|
|
pDevExt->paStrmExt[i]->bEOStream = TRUE;
|
|
//
|
|
// Signal EOStream
|
|
//
|
|
StreamClassStreamNotification(
|
|
SignalMultipleStreamEvents,
|
|
pDevExt->paStrmExt[i]->pStrmObject,
|
|
(GUID *)&KSEVENTSETID_Connection_Local,
|
|
KSEVENT_CONNECTION_ENDOFSTREAM
|
|
);
|
|
}
|
|
|
|
//
|
|
// Start a work item to abort streaming
|
|
//
|
|
AVCTapeCreateAbortWorkItem(pDevExt, pDevExt->paStrmExt[i]);
|
|
|
|
//
|
|
// Wait until the pending work item is completed.
|
|
//
|
|
TRACE(TL_PNP_WARNING,("SupriseRemoval: Wait for CancelDoneEvent <entering>; lCancelStateWorkItem:%d\n", pDevExt->paStrmExt[i]->lCancelStateWorkItem));
|
|
KeWaitForSingleObject( &pDevExt->paStrmExt[i]->hCancelDoneEvent, Executive, KernelMode, FALSE, 0 );
|
|
TRACE(TL_PNP_WARNING,("SupriseRemoval: Wait for CancelDoneEvent; Attached:%d <exited>...\n", pDevExt->paStrmExt[i]->cntDataAttached));
|
|
ASSERT(pDevExt->paStrmExt[i]->cntDataAttached == 0); // No more attach after abort stream!
|
|
}
|
|
}
|
|
|
|
|
|
// Signal KSEvent that device is removed.
|
|
// After this SRb, there will be no more Set/Get property Srb into this driver.
|
|
// By notifying the COM I/F, it will wither signal application that device is removed and
|
|
// return ERROR_DEVICE_REMOVED error code for subsequent calls.
|
|
|
|
pEvent =
|
|
StreamClassGetNextEvent(
|
|
(PVOID) pDevExt,
|
|
0,
|
|
(GUID *)&KSEVENTSETID_EXTDEV_Command,
|
|
KSEVENT_EXTDEV_NOTIFY_REMOVAL,
|
|
pEvent);
|
|
|
|
if(pEvent) {
|
|
//
|
|
// signal the event here
|
|
//
|
|
if(pEvent->EventItem->EventId == KSEVENT_EXTDEV_NOTIFY_REMOVAL) {
|
|
StreamClassDeviceNotification(
|
|
SignalDeviceEvent,
|
|
pDevExt,
|
|
pEvent
|
|
);
|
|
TRACE(TL_PNP_WARNING,("SurpriseRemoval: signal KSEVENT_EXTDEV_NOTIFY_REMOVAL, id %x.\n", pEvent->EventItem->EventId));
|
|
} else {
|
|
TRACE(TL_PNP_TRACE,("SurpriseRemoval: pEvent:%x; Id:%d not matched!\n", pEvent, pEvent->EventItem->EventId));
|
|
}
|
|
} else {
|
|
TRACE(TL_PNP_TRACE,("SurpriseRemoval: KSEVENT_EXTDEV_NOTIFY_REMOVAL event not enabled\n"));
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
// Return code is basically return in pSrb->Status.
|
|
NTSTATUS
|
|
AVCTapeProcessPnPBusReset(
|
|
PDVCR_EXTENSION pDevExt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process a bus reset.
|
|
|
|
Arguments:
|
|
|
|
Srb - Pointer to stream request block
|
|
|
|
Return Value:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
{
|
|
#ifdef MSDVDV_SUPPORT_BUSRESET_EVENT
|
|
PKSEVENT_ENTRY pEvent;
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
TRACE(TL_PNP_TRACE,("ProcessPnPBusReset: >>\n"));
|
|
|
|
#ifdef MSDVDV_SUPPORT_BUSRESET_EVENT
|
|
//
|
|
// Signal (if enabled) busreset event to let upper layer know that a busreset has occurred.
|
|
//
|
|
pEvent = NULL;
|
|
pEvent =
|
|
StreamClassGetNextEvent(
|
|
(PVOID) pDevExt,
|
|
0,
|
|
(GUID *)&KSEVENTSETID_EXTDEV_Command,
|
|
KSEVENT_EXTDEV_COMMAND_BUSRESET,
|
|
pEvent
|
|
);
|
|
|
|
if(pEvent) {
|
|
//
|
|
// signal the event here
|
|
//
|
|
if(pEvent->EventItem->EventId == KSEVENT_EXTDEV_COMMAND_BUSRESET) {
|
|
StreamClassDeviceNotification(
|
|
SignalDeviceEvent,
|
|
pDevExt,
|
|
pEvent
|
|
);
|
|
|
|
TRACE(TL_PNP_TRACE,("ProcessPnPBusReset: Signal BUSRESET; EventId %d.\n", pEvent->EventItem->EventId));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Reset pending count and AVC command that is in Interim
|
|
//
|
|
DVAVCCmdResetAfterBusReset(pDevExt);
|
|
|
|
|
|
//
|
|
// Can we return anything other than SUCCESS ?
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AVCTapeUninitialize(
|
|
IN PDVCR_EXTENSION pDevExt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This where we perform the necessary initialization tasks.
|
|
|
|
Arguments:
|
|
|
|
Srb - Pointer to stream request block
|
|
|
|
Return Value:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
TRACE(TL_PNP_TRACE,("UnInitialize: pDevExt=%x\n", pDevExt));
|
|
|
|
//
|
|
// Clear all pending AVC command entries.
|
|
//
|
|
DVAVCCmdResetAfterBusReset(pDevExt);
|
|
|
|
|
|
//
|
|
// Free textual string
|
|
//
|
|
DvFreeTextualString(pDevExt, &pDevExt->UnitIDs);
|
|
|
|
|
|
#ifdef SUPPORT_LOCAL_PLUGS
|
|
|
|
// Delete the local output plug.
|
|
if(pDevExt->hOutputPCRLocal) {
|
|
if(!AVCTapeDeleteLocalPlug(
|
|
pDevExt,
|
|
&pDevExt->AVReq,
|
|
&pDevExt->OutputPCRLocalNum,
|
|
&pDevExt->hOutputPCRLocal)) {
|
|
TRACE(TL_PNP_ERROR,("Failed to delete a local oPCR!\n"));
|
|
}
|
|
}
|
|
|
|
// Delete the local input plug.
|
|
if(pDevExt->hInputPCRLocal) {
|
|
if(!AVCTapeDeleteLocalPlug(
|
|
pDevExt,
|
|
&pDevExt->AVReq,
|
|
&pDevExt->InputPCRLocalNum,
|
|
&pDevExt->hInputPCRLocal)) {
|
|
TRACE(TL_PNP_ERROR,("Failed to delete a local iPCR!\n"));
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
// Free preallocate resource
|
|
if(pDevExt->pIrpSyncCall) {
|
|
IoFreeIrp(pDevExt->pIrpSyncCall); pDevExt->pIrpSyncCall = NULL;
|
|
}
|
|
|
|
// Free stream information allocated
|
|
if(pDevExt->pStreamInfoObject) {
|
|
ExFreePool(pDevExt->pStreamInfoObject);
|
|
pDevExt->pStreamInfoObject = NULL;
|
|
}
|
|
|
|
TRACE(TL_PNP_TRACE,("UnInitialize: done!\n"));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
//*****************************************************************************
|
|
//*****************************************************************************
|
|
// S T R E A M S R B
|
|
//*****************************************************************************
|
|
//*****************************************************************************
|
|
#if DBG
|
|
ULONG DbgLastIdx = 0;
|
|
#endif
|
|
|
|
NTSTATUS
|
|
AVCTapeReqReadDataCR(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP pIrpReq,
|
|
IN PDRIVER_REQUEST pDriverReq
|
|
)
|
|
{
|
|
PHW_STREAM_REQUEST_BLOCK pSrb;
|
|
PSTREAMEX pStrmExt;
|
|
KIRQL oldIrql;
|
|
|
|
ASSERT(pDriverReq);
|
|
pSrb = pDriverReq->Context1;
|
|
pStrmExt = pDriverReq->Context2;
|
|
|
|
if(pSrb == NULL || pStrmExt == NULL) {
|
|
TRACE(TL_STRM_ERROR|TL_CIP_ERROR,("ReqReadDataCR: Context are all NULL!\n"));
|
|
return STATUS_MORE_PROCESSING_REQUIRED; // Will reuse this irp
|
|
}
|
|
|
|
|
|
|
|
KeAcquireSpinLock(pStrmExt->DataListLock, &oldIrql);
|
|
|
|
// Count frame procesed
|
|
pStrmExt->FramesProcessed++;
|
|
pStrmExt->cntDataSubmitted--;
|
|
|
|
#if 1
|
|
// Retrieve current stream time
|
|
if(pStrmExt->hMasterClock) {
|
|
pStrmExt->CurrentStreamTime = pSrb->CommandData.DataBufferArray->PresentationTime.Time;
|
|
#if 0
|
|
AVCTapeSignalClockEvent(pStrmExt);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#if DBG
|
|
//
|
|
// Check data request completion is in sequence
|
|
//
|
|
if(pStrmExt->FramesProcessed != pDriverReq->cntDataRequestReceived) {
|
|
TRACE(TL_STRM_WARNING,("** OOSeq: Next:%d != Actual:%d **\n",
|
|
(DWORD) pStrmExt->FramesProcessed, (DWORD) pDriverReq->cntDataRequestReceived));
|
|
// ASSERT(pStrmExt->FramesProcessed == pDriverReq->cntDataRequestReceived);
|
|
}
|
|
#endif
|
|
|
|
if(!NT_SUCCESS(pIrpReq->IoStatus.Status)) {
|
|
TRACE(TL_STRM_TRACE|TL_CIP_TRACE,("ReadDataReq failed; St:%x; DataUsed:%d\n", pIrpReq->IoStatus.Status,
|
|
pSrb->CommandData.DataBufferArray->DataUsed));
|
|
// Only acceptable status is cancel.
|
|
ASSERT(pIrpReq->IoStatus.Status == STATUS_CANCELLED && "ReadDataReq failed\n");
|
|
} else {
|
|
TRACE(TL_STRM_INFO,("ReadDataReq pSrb:%x; St:%x; DataUsed:%d; Flag:%x\n", pIrpReq->IoStatus.Status,
|
|
pSrb->CommandData.DataBufferArray->DataUsed, pSrb->CommandData.DataBufferArray->OptionsFlags));
|
|
}
|
|
|
|
ASSERT(pIrpReq->IoStatus.Status != STATUS_PENDING);
|
|
|
|
pSrb->Status = pIrpReq->IoStatus.Status;
|
|
|
|
// Reset them so if this is completed here before the IRP's IoCallDriver is returned,
|
|
// it will not try to complete again.
|
|
pDriverReq->Context1 = NULL;
|
|
pDriverReq->Context2 = NULL;
|
|
|
|
// Done; recycle.
|
|
RemoveEntryList(&pDriverReq->ListEntry); pStrmExt->cntDataAttached--;
|
|
InsertTailList(&pStrmExt->DataDetachedListHead, &pDriverReq->ListEntry); pStrmExt->cntDataDetached++;
|
|
|
|
KeReleaseSpinLock(pStrmExt->DataListLock, oldIrql);
|
|
|
|
//
|
|
// Signal the graph manager that we are completed.
|
|
//
|
|
if(pSrb->CommandData.DataBufferArray->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_ENDOFSTREAM) {
|
|
|
|
StreamClassStreamNotification(
|
|
SignalMultipleStreamEvents,
|
|
pStrmExt->pStrmObject,
|
|
&KSEVENTSETID_Connection,
|
|
KSEVENT_CONNECTION_ENDOFSTREAM
|
|
);
|
|
}
|
|
|
|
// Finally, send the srb back up ...
|
|
StreamClassStreamNotification(
|
|
StreamRequestComplete,
|
|
pSrb->StreamObject,
|
|
pSrb
|
|
);
|
|
|
|
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED; // Will reuse this irp
|
|
} // AVCStrmReqIrpSynchCR
|
|
|
|
|
|
NTSTATUS
|
|
AVCTapeGetStreamState(
|
|
PSTREAMEX pStrmExt,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
PKSSTATE pStreamState,
|
|
PULONG pulActualBytesTransferred
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Gets the current state of the requested stream
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PAVC_STREAM_REQUEST_BLOCK pAVCStrmReq;
|
|
|
|
PAGED_CODE();
|
|
|
|
if(!pStrmExt) {
|
|
TRACE(TL_STRM_ERROR,("GetStreamState: pStrmExt:%x; STATUS_UNSUCCESSFUL\n", pStrmExt));
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Request AVCStrm to get current stream state
|
|
//
|
|
EnterAVCStrm(pStrmExt->hMutexReq);
|
|
|
|
pAVCStrmReq = &pStrmExt->AVCStrmReq;
|
|
RtlZeroMemory(pAVCStrmReq, sizeof(AVC_STREAM_REQUEST_BLOCK));
|
|
INIT_AVCSTRM_HEADER(pAVCStrmReq, AVCSTRM_GET_STATE);
|
|
pAVCStrmReq->AVCStreamContext = pStrmExt->AVCStreamContext;
|
|
|
|
Status =
|
|
AVCStrmReqSubmitIrpSynch(
|
|
DeviceObject,
|
|
pStrmExt->pIrpReq,
|
|
pAVCStrmReq
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
TRACE(TL_STRM_ERROR,("AVCSTRM_GET_STATE: failed %x; pAVCStrmReq:%x\n", Status, pAVCStrmReq));
|
|
ASSERT(NT_SUCCESS(Status) && "AVCSTRM_GET_STATE failed!\n");
|
|
}
|
|
else {
|
|
// Save the context, which is used for subsequent call to AVCStrm.sys
|
|
TRACE(TL_STRM_WARNING,("AVCSTRM_GET_STATE: Status:%x; pAVCStrmReq:%x; KSSTATE:%d\n", Status, pAVCStrmReq, pAVCStrmReq->CommandData.StreamState));
|
|
*pStreamState = pAVCStrmReq->CommandData.StreamState;
|
|
*pulActualBytesTransferred = sizeof (KSSTATE);
|
|
|
|
// A very odd rule:
|
|
// When transitioning from stop to pause, DShow tries to preroll
|
|
// the graph. Capture sources can't preroll, and indicate this
|
|
// by returning VFW_S_CANT_CUE in user mode. To indicate this
|
|
// condition from drivers, they must return ERROR_NO_DATA_DETECTED
|
|
if( *pStreamState == KSSTATE_PAUSE
|
|
&& pStrmExt->pStrmInfo->DataFlow == KSPIN_DATAFLOW_OUT
|
|
)
|
|
Status = STATUS_NO_DATA_DETECTED;
|
|
else
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
LeaveAVCStrm(pStrmExt->hMutexReq);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
AVCTapeSetStreamState(
|
|
PSTREAMEX pStrmExt,
|
|
PDVCR_EXTENSION pDevExt,
|
|
PAV_61883_REQUEST pAVReq,
|
|
KSSTATE StreamState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the stream state via the SRB.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAVC_STREAM_REQUEST_BLOCK pAVCStrmReq;
|
|
NTSTATUS Status;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
ASSERT(pStrmExt);
|
|
if(pStrmExt == NULL) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
TRACE(TL_STRM_TRACE,("Set State %d -> %d; PowerSt:%d (1/On;4/Off]); AD [%d,%d]\n", \
|
|
pStrmExt->StreamState, StreamState, pDevExt->PowerState,
|
|
pStrmExt->cntDataAttached,
|
|
pStrmExt->cntDataDetached
|
|
));
|
|
|
|
#if DBG
|
|
if(StreamState == KSSTATE_RUN) {
|
|
ASSERT(pDevExt->PowerState == PowerDeviceD0 && "Cannot set to RUN while power is off!");
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Request AVCStrm to set to a new stream state
|
|
//
|
|
EnterAVCStrm(pStrmExt->hMutexReq);
|
|
|
|
pAVCStrmReq = &pStrmExt->AVCStrmReq;
|
|
RtlZeroMemory(pAVCStrmReq, sizeof(AVC_STREAM_REQUEST_BLOCK));
|
|
INIT_AVCSTRM_HEADER(pAVCStrmReq, AVCSTRM_SET_STATE);
|
|
pAVCStrmReq->AVCStreamContext = pStrmExt->AVCStreamContext;
|
|
pAVCStrmReq->CommandData.StreamState = StreamState;
|
|
|
|
Status =
|
|
AVCStrmReqSubmitIrpSynch(
|
|
pDevExt->pBusDeviceObject,
|
|
pStrmExt->pIrpReq,
|
|
pAVCStrmReq
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
TRACE(TL_STRM_ERROR,("AVCSTRM_SET_STATE: failed %x; pAVCStrmReq:%x\n", Status, pAVCStrmReq));
|
|
ASSERT(NT_SUCCESS(Status) && "AVCSTRM_SET_STATE failed!\n");
|
|
}
|
|
else {
|
|
// Save the context, which is used for subsequent call to AVCStrm.sys
|
|
TRACE(TL_STRM_TRACE,("AVCSTRM_SET_STATE: Status:%x; pAVCStrmReq:%x, new KSSTATE:%d\n", Status, pAVCStrmReq, pAVCStrmReq->CommandData.StreamState));
|
|
|
|
// Reset the abort state
|
|
if(pStrmExt->StreamState == KSSTATE_STOP && StreamState == KSSTATE_ACQUIRE)
|
|
pStrmExt->AbortInProgress = FALSE;
|
|
|
|
|
|
// Reaction due to state change
|
|
switch(StreamState) {
|
|
case KSSTATE_STOP:
|
|
TRACE(TL_STRM_TRACE,("SrbRcv:%d, Processed:%d; Pending:%d\n", (DWORD) pStrmExt->cntSRBReceived, (DWORD) pStrmExt->FramesProcessed, (DWORD) pStrmExt->cntDataSubmitted));
|
|
// Reset it
|
|
pStrmExt->cntSRBReceived = pStrmExt->FramesProcessed = pStrmExt->cntDataSubmitted = 0;
|
|
pStrmExt->CurrentStreamTime = 0;
|
|
break;
|
|
|
|
case KSSTATE_PAUSE:
|
|
// For DV input pin, setup a timer DPC to periodically fired to singal clock event.
|
|
if(pStrmExt->hMasterClock && pDevExt->VideoFormatIndex != AVCSTRM_FORMAT_MPEG2TS && pStrmExt->StreamState == KSSTATE_RUN) {
|
|
// Cancel timer
|
|
#if 1
|
|
TRACE(TL_STRM_TRACE,("*** (RUN->PAUSE) CancelTimer *********************************************...\n"));
|
|
KeCancelTimer(
|
|
&pStrmExt->Timer
|
|
);
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case KSSTATE_RUN:
|
|
// For DV input pin, setup a timer DPC to periodically fired to singal clock event.
|
|
if(pStrmExt->hMasterClock &&
|
|
pDevExt->VideoFormatIndex != AVCSTRM_FORMAT_MPEG2TS) {
|
|
LARGE_INTEGER DueTime;
|
|
#define CLOCK_INTERVAL 20 // Unit=MilliSeconds
|
|
|
|
#if 0
|
|
// For DV input pin, setup a timer DPC to periodically fired to singal clock event.
|
|
KeInitializeDpc(
|
|
&pStrmExt->DPCTimer,
|
|
AVCTapeSignalClockEvent,
|
|
pStrmExt
|
|
);
|
|
KeInitializeTimer(
|
|
&pStrmExt->Timer
|
|
);
|
|
#endif
|
|
|
|
DueTime = RtlConvertLongToLargeInteger(-CLOCK_INTERVAL * 10000);
|
|
TRACE(TL_STRM_TRACE,("*** ScheduleTimer (RUN) *****************************************...\n"));
|
|
KeSetTimerEx(
|
|
&pStrmExt->Timer,
|
|
DueTime,
|
|
CLOCK_INTERVAL, // Repeat every 40 MilliSecond
|
|
&pStrmExt->DPCTimer
|
|
);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Cache the current state
|
|
pStrmExt->StreamState = StreamState;
|
|
}
|
|
|
|
LeaveAVCStrm(pStrmExt->hMutexReq);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
DVStreamGetConnectionProperty (
|
|
PDVCR_EXTENSION pDevExt,
|
|
PSTREAM_PROPERTY_DESCRIPTOR pSPD,
|
|
PULONG pulActualBytesTransferred
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handles KS_PROPERTY_CONNECTION* request. For now, only ALLOCATORFRAMING and
|
|
CONNECTION_STATE are supported.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
switch (pSPD->Property->Id) {
|
|
|
|
case KSPROPERTY_CONNECTION_ALLOCATORFRAMING:
|
|
if (pDevExt != NULL && pDevExt->cndStrmOpen) {
|
|
PKSALLOCATOR_FRAMING pFraming = (PKSALLOCATOR_FRAMING) pSPD->PropertyInfo;
|
|
|
|
pFraming->RequirementsFlags =
|
|
KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY |
|
|
KSALLOCATOR_REQUIREMENTF_INPLACE_MODIFIER |
|
|
KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY;
|
|
pFraming->PoolType = NonPagedPool;
|
|
|
|
pFraming->Frames = \
|
|
pDevExt->paStrmExt[pDevExt->idxStreamNumber]->pStrmInfo->DataFlow == KSPIN_DATAFLOW_OUT ? \
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].NumOfRcvBuffers : \
|
|
AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].NumOfXmtBuffers;
|
|
|
|
// Note: we'll allocate the biggest frame. We need to make sure when we're
|
|
// passing the frame back up we also set the number of bytes in the frame.
|
|
pFraming->FrameSize = AVCStrmFormatInfoTable[pDevExt->VideoFormatIndex].FrameSize;
|
|
pFraming->FileAlignment = 0; // FILE_LONG_ALIGNMENT;
|
|
pFraming->Reserved = 0;
|
|
*pulActualBytesTransferred = sizeof (KSALLOCATOR_FRAMING);
|
|
|
|
TRACE(TL_STRM_TRACE,("*** AllocFraming: cntStrmOpen:%d; VdoFmtIdx:%d; Frames %d; size:%d\n", \
|
|
pDevExt->cndStrmOpen, pDevExt->VideoFormatIndex, pFraming->Frames, pFraming->FrameSize));
|
|
} else {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
*pulActualBytesTransferred = 0;
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
ASSERT(pSPD->Property->Id == KSPROPERTY_CONNECTION_ALLOCATORFRAMING);
|
|
break;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DVGetDroppedFramesProperty(
|
|
PDVCR_EXTENSION pDevExt,
|
|
PSTREAMEX pStrmExt,
|
|
PSTREAM_PROPERTY_DESCRIPTOR pSPD,
|
|
PULONG pulBytesTransferred
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Return the dropped frame information while captureing.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
switch (pSPD->Property->Id) {
|
|
|
|
case KSPROPERTY_DROPPEDFRAMES_CURRENT:
|
|
{
|
|
|
|
PKSPROPERTY_DROPPEDFRAMES_CURRENT_S pDroppedFrames =
|
|
(PKSPROPERTY_DROPPEDFRAMES_CURRENT_S) pSPD->PropertyInfo;
|
|
|
|
pDroppedFrames->AverageFrameSize = AVCStrmFormatInfoTable[pStrmExt->pDevExt->VideoFormatIndex].FrameSize;
|
|
|
|
if(pStrmExt->pStrmInfo->DataFlow == KSPIN_DATAFLOW_IN) {
|
|
#if 0
|
|
// pStrmExt->PictureNumber is not returned since it might be greater than number of SRBs returned.
|
|
// pStrmExt->CurrentStreamTime >= pDroppedFrames->PictureNumber * (ulAvgTimePerFrame)
|
|
// CurrentStreamTime will be ahead if there is repeat frame and the data source
|
|
// cannot keep up with the constant data transfer of 29.97 (or 25) FPS; therefore,
|
|
// repeat frame might have inserted and the last data in the last SRB is transferred.
|
|
// To resolve this, an application can query PictureNumber and CurrentStreamTime and
|
|
// does a read ahead of their delta to "catch up".
|
|
pDroppedFrames->PictureNumber = pStrmExt->FramesProcessed + pStrmExt->FramesDropped;
|
|
#else
|
|
// This is the picture number that MSDV is actually sending, and in a slow harddisk case,
|
|
// it will be greater than (FramesProcessed + FramesDropped) considering repeat frame.
|
|
pDroppedFrames->PictureNumber = pStrmExt->PictureNumber;
|
|
#endif
|
|
} else {
|
|
pDroppedFrames->PictureNumber = pStrmExt->PictureNumber;
|
|
}
|
|
pDroppedFrames->DropCount = pStrmExt->FramesDropped; // For transmit, this value includes both dropped and repeated.
|
|
|
|
TRACE(TL_STRM_TRACE,("*DroppedFP: Pic#(%d), Drp(%d)\n", (LONG) pDroppedFrames->PictureNumber, (LONG) pDroppedFrames->DropCount));
|
|
|
|
*pulBytesTransferred = sizeof (KSPROPERTY_DROPPEDFRAMES_CURRENT_S);
|
|
Status = STATUS_SUCCESS;
|
|
|
|
}
|
|
break;
|
|
|
|
default:
|
|
*pulBytesTransferred = 0;
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
ASSERT(pSPD->Property->Id == KSPROPERTY_DROPPEDFRAMES_CURRENT);
|
|
break;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DVGetStreamProperty(
|
|
PHW_STREAM_REQUEST_BLOCK pSrb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine to process property request
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PSTREAM_PROPERTY_DESCRIPTOR pSPD = pSrb->CommandData.PropertyInfo;
|
|
|
|
PAGED_CODE();
|
|
|
|
if(IsEqualGUID (&KSPROPSETID_Connection, &pSPD->Property->Set)) {
|
|
|
|
Status =
|
|
DVStreamGetConnectionProperty (
|
|
pSrb->HwDeviceExtension,
|
|
pSrb->CommandData.PropertyInfo,
|
|
&pSrb->ActualBytesTransferred
|
|
);
|
|
}
|
|
else if (IsEqualGUID (&PROPSETID_VIDCAP_DROPPEDFRAMES, &pSPD->Property->Set)) {
|
|
|
|
Status =
|
|
DVGetDroppedFramesProperty (
|
|
pSrb->HwDeviceExtension,
|
|
(PSTREAMEX) pSrb->StreamObject->HwStreamExtension,
|
|
pSrb->CommandData.PropertyInfo,
|
|
&pSrb->ActualBytesTransferred
|
|
);
|
|
}
|
|
else {
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DVSetStreamProperty(
|
|
PHW_STREAM_REQUEST_BLOCK pSrb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine to process set property request
|
|
|
|
--*/
|
|
|
|
{
|
|
PSTREAM_PROPERTY_DESCRIPTOR pSPD = pSrb->CommandData.PropertyInfo;
|
|
|
|
PAGED_CODE();
|
|
|
|
TRACE(TL_STRM_TRACE,("SetStreamProperty: entered ...\n"));
|
|
|
|
return STATUS_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
|
void
|
|
DVCancelSrbWorkItemRoutine(
|
|
#ifdef USE_WDM110 // Win2000 code base
|
|
// Extra parameter if using WDM10
|
|
PDEVICE_OBJECT DeviceObject,
|
|
#endif
|
|
PSTREAMEX pStrmExt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This work item routine will stop streaming and cancel all SRBs.
|
|
|
|
--*/
|
|
{
|
|
PAVC_STREAM_REQUEST_BLOCK pAVCStrmReq;
|
|
NTSTATUS Status;
|
|
NTSTATUS StatusWait;
|
|
|
|
PAGED_CODE();
|
|
|
|
TRACE(TL_STRM_WARNING,("CancelWorkItem: StreamState:%d; lCancel:%d\n", pStrmExt->StreamState, pStrmExt->lCancelStateWorkItem));
|
|
ASSERT(pStrmExt->lCancelStateWorkItem == 1);
|
|
#ifdef USE_WDM110 // Win2000 code base
|
|
ASSERT(pStrmExt->pIoWorkItem);
|
|
#endif
|
|
|
|
// Synchronize
|
|
// streaming state, and
|
|
// incoming streaming data SRBs
|
|
StatusWait =
|
|
KeWaitForMutexObject(pStrmExt->hMutexFlow, Executive, KernelMode, FALSE, NULL);
|
|
ASSERT(StatusWait == STATUS_SUCCESS);
|
|
|
|
//
|
|
// We get here usually as a result of a thread was terminated and it needs to cancel irps.
|
|
// We therefore abort streaming.
|
|
//
|
|
pAVCStrmReq = &pStrmExt->AVCStrmReqAbort;
|
|
|
|
RtlZeroMemory(pAVCStrmReq, sizeof(AVC_STREAM_REQUEST_BLOCK));
|
|
INIT_AVCSTRM_HEADER(pAVCStrmReq, AVCSTRM_ABORT_STREAMING);
|
|
pAVCStrmReq->AVCStreamContext = pStrmExt->AVCStreamContext;
|
|
|
|
Status =
|
|
AVCStrmReqSubmitIrpSynch(
|
|
pStrmExt->pDevExt->pBusDeviceObject,
|
|
pStrmExt->pIrpAbort,
|
|
pAVCStrmReq
|
|
);
|
|
|
|
#if DBG
|
|
if(Status != STATUS_SUCCESS) {
|
|
TRACE(TL_STRM_ERROR,("Abort streaming status:%x\n", Status));
|
|
ASSERT(Status == STATUS_SUCCESS && "Abort streaming failed\n");
|
|
}
|
|
#endif
|
|
|
|
KeReleaseMutex(pStrmExt->hMutexFlow, FALSE);
|
|
|
|
#ifdef USE_WDM110 // Win2000 code base
|
|
// Release work item and release the cancel token
|
|
IoFreeWorkItem(pStrmExt->pIoWorkItem); pStrmExt->pIoWorkItem = NULL;
|
|
#endif
|
|
pStrmExt->AbortInProgress = TRUE;
|
|
InterlockedExchange(&pStrmExt->lCancelStateWorkItem, 0);
|
|
KeSetEvent(&pStrmExt->hCancelDoneEvent, 0, FALSE);
|
|
}
|
|
|
|
VOID
|
|
AVCTapeCreateAbortWorkItem(
|
|
PDVCR_EXTENSION pDevExt,
|
|
PSTREAMEX pStrmExt
|
|
)
|
|
{
|
|
// Claim this token
|
|
if(InterlockedExchange(&pStrmExt->lCancelStateWorkItem, 1) == 1) {
|
|
TRACE(TL_STRM_WARNING,("Cancel work item is already issued.\n"));
|
|
return;
|
|
}
|
|
// Cancel is already in progress
|
|
if(pStrmExt->AbortInProgress) {
|
|
TRACE(TL_STRM_WARNING,("Cancel work item is already in progress.\n"));
|
|
return;
|
|
}
|
|
|
|
#ifdef USE_WDM110 // Win2000 code base
|
|
ASSERT(pStrmExt->pIoWorkItem == NULL); // Have not yet queued work item.
|
|
|
|
// We will queue work item to stop and cancel all SRBs
|
|
if(pStrmExt->pIoWorkItem = IoAllocateWorkItem(pDevExt->pBusDeviceObject)) {
|
|
|
|
// Set to non-signal
|
|
KeClearEvent(&pStrmExt->hCancelDoneEvent); // Before queuing; just in case it return the work item is completed.
|
|
|
|
IoQueueWorkItem(
|
|
pStrmExt->pIoWorkItem,
|
|
DVCancelSrbWorkItemRoutine,
|
|
DelayedWorkQueue, // CriticalWorkQueue
|
|
pStrmExt
|
|
);
|
|
|
|
#else // Win9x code base
|
|
ExInitializeWorkItem( &pStrmExt->IoWorkItem, DVCancelSrbWorkItemRoutine, pStrmExt);
|
|
if(TRUE) {
|
|
|
|
// Set to non-signal
|
|
KeClearEvent(&pStrmExt->hCancelDoneEvent); // Before queuing; just in case it return the work item is completed.
|
|
|
|
ExQueueWorkItem(
|
|
&pStrmExt->IoWorkItem,
|
|
DelayedWorkQueue // CriticalWorkQueue
|
|
);
|
|
#endif
|
|
|
|
TRACE(TL_STRM_WARNING,("CancelWorkItm queued\n"));
|
|
}
|
|
#ifdef USE_WDM110 // Win2000 code base
|
|
else {
|
|
InterlockedExchange(&pStrmExt->lCancelStateWorkItem, 0);
|
|
ASSERT(pStrmExt->pIoWorkItem && "IoAllocateWorkItem failed.\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
VOID
|
|
DVCRCancelOnePacket(
|
|
IN PHW_STREAM_REQUEST_BLOCK pSrbToCancel
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Search pending read lists for the SRB to be cancel. If found cancel it.
|
|
|
|
--*/
|
|
{
|
|
PDVCR_EXTENSION pDevExt;
|
|
PSTREAMEX pStrmExt;
|
|
|
|
|
|
pDevExt = (PDVCR_EXTENSION) pSrbToCancel->HwDeviceExtension;
|
|
|
|
// Cannot cancel device Srb.
|
|
if ((pSrbToCancel->Flags & SRB_HW_FLAGS_STREAM_REQUEST) != SRB_HW_FLAGS_STREAM_REQUEST) {
|
|
TRACE(TL_PNP_WARNING,("CancelOnePacket: Device SRB %x; cannot cancel!\n", pSrbToCancel));
|
|
ASSERT((pSrbToCancel->Flags & SRB_HW_FLAGS_STREAM_REQUEST) == SRB_HW_FLAGS_STREAM_REQUEST );
|
|
return;
|
|
}
|
|
|
|
// Can try to cancel a stream Srb and only if the stream extension still around.
|
|
pStrmExt = (PSTREAMEX) pSrbToCancel->StreamObject->HwStreamExtension;
|
|
if(pStrmExt == NULL) {
|
|
TRACE(TL_PNP_ERROR,("CancelOnePacket: pSrbTocancel %x but pStrmExt %x\n", pSrbToCancel, pStrmExt));
|
|
ASSERT(pStrmExt && "Stream SRB but stream extension is NULL\n");
|
|
return;
|
|
}
|
|
|
|
// We can only cancel SRB_READ/WRITE_DATA SRB
|
|
if((pSrbToCancel->Command != SRB_READ_DATA) && (pSrbToCancel->Command != SRB_WRITE_DATA)) {
|
|
TRACE(TL_PNP_ERROR,("CancelOnePacket: pSrbTocancel %x; Command:%d not SRB_READ,WRITE_DATA\n", pSrbToCancel, pSrbToCancel->Command));
|
|
ASSERT(pSrbToCancel->Command == SRB_READ_DATA || pSrbToCancel->Command == SRB_WRITE_DATA);
|
|
return;
|
|
}
|
|
|
|
TRACE(TL_STRM_TRACE,("CancelOnePacket: KSSt %d; Srb:%x;\n", pStrmExt->StreamState, pSrbToCancel));
|
|
|
|
|
|
// This is called at DispatchLevel.
|
|
// We will create a work item to do the cancelling (detaching buffers) at the passive level.
|
|
AVCTapeCreateAbortWorkItem(pDevExt, pStrmExt);
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
DVTimeoutHandler(
|
|
IN PHW_STREAM_REQUEST_BLOCK pSrb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when a packet has been in the minidriver too long.
|
|
It can only valid if we are it wa a streaming packet and in PAUSE state;
|
|
else we have a problem!
|
|
|
|
Arguments:
|
|
|
|
pSrb - Pointer to Stream request block
|
|
|
|
Return Value:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Note:
|
|
// Called from StreamClass at DisptchLevel
|
|
//
|
|
|
|
//
|
|
// We only expect stream SRB, but not device SRB.
|
|
//
|
|
|
|
if ( (pSrb->Flags & SRB_HW_FLAGS_STREAM_REQUEST) != SRB_HW_FLAGS_STREAM_REQUEST) {
|
|
TRACE(TL_PNP_WARNING,("TimeoutHandler: Device SRB %x timed out!\n", pSrb));
|
|
ASSERT((pSrb->Flags & SRB_HW_FLAGS_STREAM_REQUEST) == SRB_HW_FLAGS_STREAM_REQUEST );
|
|
return;
|
|
} else {
|
|
|
|
//
|
|
// pSrb->StreamObject (and pStrmExt) only valid if it is a stream SRB
|
|
//
|
|
PSTREAMEX pStrmExt;
|
|
|
|
pStrmExt = (PSTREAMEX) pSrb->StreamObject->HwStreamExtension;
|
|
ASSERT(pStrmExt);
|
|
|
|
if(!pStrmExt) {
|
|
TRACE(TL_PNP_ERROR,("TimeoutHandler: Stream SRB %x timeout with ppStrmExt %x\n", pSrb, pStrmExt));
|
|
ASSERT(pStrmExt);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Reset Timeout counter, or we are going to get this call immediately.
|
|
//
|
|
|
|
pSrb->TimeoutCounter = pSrb->TimeoutOriginal;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
AVCTapeEventHandler(
|
|
IN PHW_EVENT_DESCRIPTOR pEventDescriptor
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to enable/disable and possibly process events.
|
|
|
|
--*/
|
|
{
|
|
PKSEVENT_TIME_MARK pEventTime;
|
|
PSTREAMEX pStrmExt;
|
|
|
|
if(IsEqualGUID (&KSEVENTSETID_Clock, pEventDescriptor->EventEntry->EventSet->Set)) {
|
|
if(pEventDescriptor->EventEntry->EventItem->EventId == KSEVENT_CLOCK_POSITION_MARK) {
|
|
if(pEventDescriptor->Enable) {
|
|
// Note: According to the DDK, StreamClass queues pEventDescriptor->EventEntry, and dellaocate
|
|
// every other structures, including the pEventDescriptor->EventData.
|
|
if(pEventDescriptor->StreamObject) {
|
|
pStrmExt = (PSTREAMEX) pEventDescriptor->StreamObject->HwStreamExtension;
|
|
pEventTime = (PKSEVENT_TIME_MARK) pEventDescriptor->EventData;
|
|
// Cache the event data (Specified in the ExtraEntryData of KSEVENT_ITEM)
|
|
RtlCopyMemory((pEventDescriptor->EventEntry+1), pEventDescriptor->EventData, sizeof(KSEVENT_TIME_MARK));
|
|
TRACE(TL_CLK_TRACE,("CurrentStreamTime:%d, MarkTime:%d\n", (DWORD) pStrmExt->CurrentStreamTime, (DWORD) pEventTime->MarkTime));
|
|
}
|
|
} else {
|
|
// Disabled!
|
|
TRACE(TL_CLK_TRACE,("KSEVENT_CLOCK_POSITION_MARK disabled!\n"));
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
} else if(IsEqualGUID (&KSEVENTSETID_Connection, pEventDescriptor->EventEntry->EventSet->Set)) {
|
|
TRACE(TL_STRM_TRACE,("Connecytion event: pEventDescriptor:%x; id:%d\n", pEventDescriptor, pEventDescriptor->EventEntry->EventItem->EventId));
|
|
if(pEventDescriptor->EventEntry->EventItem->EventId == KSEVENT_CONNECTION_ENDOFSTREAM) {
|
|
if(pEventDescriptor->Enable) {
|
|
TRACE(TL_STRM_TRACE,("KSEVENT_CONNECTION_ENDOFSTREAM enabled!\n"));
|
|
} else {
|
|
TRACE(TL_STRM_TRACE,("KSEVENT_CONNECTION_ENDOFSTREAM disabled!\n"));
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
TRACE(TL_PNP_ERROR|TL_CLK_ERROR,("NOT_SUPPORTED event: pEventDescriptor:%x\n", pEventDescriptor));
|
|
ASSERT(FALSE);
|
|
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
VOID
|
|
AVCTapeSignalClockEvent(
|
|
IN PKDPC Dpc,
|
|
|
|
IN PSTREAMEX pStrmExt,
|
|
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when we are the clock provider and when our clock "tick".
|
|
Find a pending clock event, signal it if it has expired.
|
|
|
|
--*/
|
|
{
|
|
PKSEVENT_ENTRY pEvent, pLast;
|
|
|
|
pEvent = NULL;
|
|
pLast = NULL;
|
|
|
|
while((
|
|
pEvent = StreamClassGetNextEvent(
|
|
pStrmExt->pDevExt,
|
|
pStrmExt->pStrmObject,
|
|
(GUID *)&KSEVENTSETID_Clock,
|
|
KSEVENT_CLOCK_POSITION_MARK,
|
|
pLast ))
|
|
!= NULL ) {
|
|
|
|
#if 1
|
|
#define CLOCK_ADJUSTMENT 400000
|
|
if (((PKSEVENT_TIME_MARK)(pEvent +1))->MarkTime <= pStrmExt->CurrentStreamTime + CLOCK_ADJUSTMENT) {
|
|
#else
|
|
if (((PKSEVENT_TIME_MARK)(pEvent +1))->MarkTime <= pStrmExt->CurrentStreamTime) {
|
|
#endif
|
|
TRACE(TL_CLK_TRACE,("Clock event %x with id %d; Data:%x; tmMark:%d; tmCurrentStream:%d; Notify!\n",
|
|
pEvent, KSEVENT_CLOCK_POSITION_MARK, (PKSEVENT_TIME_MARK)(pEvent +1),
|
|
(DWORD) (((PKSEVENT_TIME_MARK)(pEvent +1))->MarkTime), (DWORD) pStrmExt->CurrentStreamTime));
|
|
ASSERT( ((PKSEVENT_TIME_MARK)(pEvent +1))->MarkTime != 0 );
|
|
|
|
//
|
|
// signal the event here
|
|
//
|
|
StreamClassStreamNotification(
|
|
SignalStreamEvent,
|
|
pStrmExt->pStrmObject,
|
|
pEvent
|
|
);
|
|
} else {
|
|
TRACE(TL_CLK_WARNING,("Still early! ClockEvent: MarkTime:%d, tmStream%d\n",
|
|
(DWORD) (((PKSEVENT_TIME_MARK)(pEvent +1))->MarkTime), (DWORD) pStrmExt->CurrentStreamTime));
|
|
|
|
}
|
|
pLast = pEvent;
|
|
}
|
|
|
|
#if DBG
|
|
if(pLast == NULL) {
|
|
TRACE(TL_CLK_WARNING,("No clock event in the queued! State:%d; tmCurrentStream:%d\n", pStrmExt->StreamState, (DWORD) pStrmExt->CurrentStreamTime));
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
VOID
|
|
AVCTapeStreamClockRtn(
|
|
IN PHW_TIME_CONTEXT TimeContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called whenever someone in the graph wants to know what time it is, and we are the Master Clock.
|
|
|
|
--*/
|
|
{
|
|
PDVCR_EXTENSION pDevExt;
|
|
PHW_STREAM_OBJECT pStrmObj;
|
|
PSTREAMEX pStrmExt;
|
|
|
|
// Call at dispatch level
|
|
|
|
pDevExt = (PDVCR_EXTENSION) TimeContext->HwDeviceExtension;
|
|
pStrmObj = TimeContext->HwStreamObject;
|
|
if(pStrmObj)
|
|
pStrmExt = pStrmObj->HwStreamExtension;
|
|
else
|
|
pStrmExt = 0;
|
|
|
|
if(!pDevExt || !pStrmExt) {
|
|
ASSERT(pDevExt && pStrmExt);
|
|
return;
|
|
}
|
|
|
|
|
|
switch (TimeContext->Function) {
|
|
|
|
case TIME_GET_STREAM_TIME:
|
|
|
|
//
|
|
// How long since the stream was first set into the run state?
|
|
//
|
|
ASSERT(pStrmExt->hMasterClock && "We are not master clock but we were qureied?");
|
|
TimeContext->Time = pStrmExt->CurrentStreamTime;
|
|
TimeContext->SystemTime = GetSystemTime();
|
|
|
|
TRACE(TL_STRM_WARNING|TL_CLK_TRACE,("State:%d; tmStream:%d tmSys:%d\n", pStrmExt->StreamState, (DWORD) TimeContext->Time, (DWORD) TimeContext->SystemTime ));
|
|
break;
|
|
|
|
default:
|
|
ASSERT(TimeContext->Function == TIME_GET_STREAM_TIME && "Unsupport clock func");
|
|
break;
|
|
} // switch TimeContext->Function
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AVCTapeOpenCloseMasterClock (
|
|
PSTREAMEX pStrmExt,
|
|
HANDLE hMasterClockHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
We can be a clock provider.
|
|
|
|
--*/
|
|
{
|
|
|
|
PAGED_CODE();
|
|
|
|
// Make sure the stream exist.
|
|
if(pStrmExt == NULL) {
|
|
TRACE(TL_STRM_ERROR|TL_CLK_ERROR,("OpenCloseMasterClock: stream is not yet running.\n"));
|
|
ASSERT(pStrmExt);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
TRACE(TL_CLK_WARNING,("OpenCloseMasterClock: pStrmExt %x; hMyClock:%x->%x\n",
|
|
pStrmExt, pStrmExt->hMyClock, hMasterClockHandle));
|
|
|
|
if(hMasterClockHandle) {
|
|
// Open master clock
|
|
ASSERT(pStrmExt->hMyClock == NULL && "OpenMasterClk while hMyClock is not NULL!");
|
|
pStrmExt->hMyClock = hMasterClockHandle;
|
|
} else {
|
|
// Close master clock
|
|
ASSERT(pStrmExt->hMyClock && "CloseMasterClk while hMyClock is NULL!");
|
|
pStrmExt->hMyClock = NULL;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AVCTapeIndicateMasterClock (
|
|
PSTREAMEX pStrmExt,
|
|
HANDLE hIndicateClockHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Compare the indicate clock handle with my clock handle.
|
|
If the same, we are the master clock; else, other device is
|
|
the master clock.
|
|
|
|
Note: either hMasterClock or hClock can be set.
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
// Make sure the stream exist.
|
|
if (pStrmExt == NULL) {
|
|
TRACE(TL_STRM_ERROR|TL_CLK_ERROR,("AVCTapeIndicateMasterClock: stream is not yet running.\n"));
|
|
ASSERT(pStrmExt);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
TRACE(TL_STRM_TRACE|TL_CLK_WARNING,("IndicateMasterClock[Enter]: pStrmExt:%x; hMyClk:%x; IndMClk:%x; pClk:%x, pMClk:%x\n",
|
|
pStrmExt, pStrmExt->hMyClock, hIndicateClockHandle, pStrmExt->hClock, pStrmExt->hMasterClock));
|
|
|
|
// it not null, set master clock accordingly.
|
|
if(hIndicateClockHandle == pStrmExt->hMyClock) {
|
|
pStrmExt->hMasterClock = hIndicateClockHandle;
|
|
pStrmExt->hClock = NULL;
|
|
} else {
|
|
pStrmExt->hMasterClock = NULL;
|
|
pStrmExt->hClock = hIndicateClockHandle;
|
|
}
|
|
|
|
TRACE(TL_STRM_TRACE|TL_CLK_TRACE,("IndicateMasterClk[Exit]: hMyClk:%x; IndMClk:%x; pClk:%x; pMClk:%x\n",
|
|
pStrmExt->hMyClock, hIndicateClockHandle, pStrmExt->hClock, pStrmExt->hMasterClock));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|