2686 lines
81 KiB
C
2686 lines
81 KiB
C
//===========================================================================
|
|
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
|
|
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
|
|
// PURPOSE.
|
|
//
|
|
// Copyright (c) 1996 - 2000 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
//===========================================================================
|
|
/*++
|
|
|
|
Module Name:
|
|
|
|
dcampkt.c
|
|
|
|
Abstract:
|
|
|
|
Stream class based WDM driver for 1934 Desktop Camera.
|
|
This file contains code to handle the stream class packets.
|
|
|
|
Author:
|
|
|
|
Shaun Pierce 25-May-96
|
|
|
|
Modified:
|
|
|
|
Yee J. Wu 15-Oct-97
|
|
|
|
Environment:
|
|
|
|
Kernel mode only
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
|
|
#include "strmini.h"
|
|
#include "ksmedia.h"
|
|
#include "1394.h"
|
|
#include "wdm.h" // for DbgBreakPoint() defined in dbg.h
|
|
#include "dbg.h"
|
|
#include "dcamdef.h"
|
|
#include "dcampkt.h"
|
|
#include "strmdata.h" // stream format and data ranges; static data
|
|
#include "capprop.h" // Video and camera property function prototype
|
|
|
|
#define WAIT_FOR_SLOW_DEVICE
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, DCamProcessPnpIrp)
|
|
#pragma alloc_text(PAGE, DCamGetStreamInfo)
|
|
#pragma alloc_text(PAGE, DCamFreeIsochResource)
|
|
#pragma alloc_text(PAGE, InitializeStreamExtension)
|
|
#pragma alloc_text(PAGE, DCamOpenStream)
|
|
#pragma alloc_text(PAGE, DCamCloseStream)
|
|
#pragma alloc_text(PAGE, AdapterCompareGUIDsAndFormatSize)
|
|
#pragma alloc_text(PAGE, AdapterVerifyFormat)
|
|
#pragma alloc_text(PAGE, AdapterFormatFromRange)
|
|
#pragma alloc_text(PAGE, VideoGetProperty)
|
|
#pragma alloc_text(PAGE, VideoGetState)
|
|
#pragma alloc_text(PAGE, VideoStreamGetConnectionProperty)
|
|
#pragma alloc_text(PAGE, VideoStreamGetDroppedFramesProperty)
|
|
#pragma alloc_text(PAGE, VideoIndicateMasterClock)
|
|
#pragma alloc_text(PAGE, DCamReceivePacket)
|
|
#pragma alloc_text(PAGE, DCamChangePower)
|
|
#endif
|
|
|
|
void
|
|
tmGetStreamTime(
|
|
IN PHW_STREAM_REQUEST_BLOCK Srb,
|
|
PSTREAMEX pStrmEx,
|
|
ULONGLONG * ptmStream)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Query the current time used to timestamp the frame or calculating the dropped frame.
|
|
This is used in IsochCallback so must be paged in always.
|
|
|
|
Arguments:
|
|
|
|
Srb - Pointer to Stream request block
|
|
|
|
Return Value:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
{
|
|
|
|
HW_TIME_CONTEXT TimeContext;
|
|
|
|
TimeContext.HwDeviceExtension = (PVOID) Srb->HwDeviceExtension;
|
|
TimeContext.HwStreamObject = Srb->StreamObject;
|
|
TimeContext.Function = TIME_GET_STREAM_TIME;
|
|
TimeContext.Time = 0;
|
|
TimeContext.SystemTime = 0;
|
|
|
|
StreamClassQueryMasterClockSync(
|
|
pStrmEx->hMasterClock,
|
|
&TimeContext);
|
|
|
|
*ptmStream = TimeContext.Time;
|
|
}
|
|
|
|
BOOL
|
|
DCamAllocateIrbAndIrp(
|
|
PIRB * ppIrb,
|
|
PIRP * ppIrp,
|
|
CCHAR StackSize
|
|
)
|
|
{
|
|
|
|
// Allocate Irb and Irp
|
|
*ppIrb = ExAllocatePoolWithTag(NonPagedPool, sizeof(IRB), 'macd');
|
|
if(!*ppIrb) {
|
|
return FALSE;
|
|
}
|
|
|
|
*ppIrp = IoAllocateIrp(StackSize, FALSE);
|
|
if(!*ppIrp) {
|
|
ExFreePool(*ppIrb);
|
|
*ppIrb = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
// Initialize IRB
|
|
RtlZeroMemory(*ppIrb, sizeof(IRB));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DCamAllocateIrbIrpAndContext(
|
|
PDCAM_IO_CONTEXT * ppDCamIoContext,
|
|
PIRB * ppIrb,
|
|
PIRP * ppIrp,
|
|
CCHAR StackSize
|
|
)
|
|
{
|
|
|
|
// Allocate DCamIoContext
|
|
*ppDCamIoContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(DCAM_IO_CONTEXT), 'macd');
|
|
if(!*ppDCamIoContext) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Allocate Irb and Irp
|
|
*ppIrb = ExAllocatePoolWithTag(NonPagedPool, sizeof(IRB), 'macd');
|
|
if(!*ppIrb) {
|
|
ExFreePool(*ppDCamIoContext);
|
|
*ppDCamIoContext = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
*ppIrp = IoAllocateIrp(StackSize, FALSE);
|
|
if(!*ppIrp) {
|
|
ExFreePool(*ppDCamIoContext);
|
|
*ppDCamIoContext = NULL;
|
|
ExFreePool(*ppIrb);
|
|
*ppIrb = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
// Initialize this context
|
|
RtlZeroMemory(*ppDCamIoContext, sizeof(DCAM_IO_CONTEXT));
|
|
(*ppDCamIoContext)->dwSize = sizeof(DCAM_IO_CONTEXT);
|
|
(*ppDCamIoContext)->pIrb = *ppIrb;
|
|
|
|
// Initialize IRB
|
|
RtlZeroMemory(*ppIrb, sizeof(IRB));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
DCamFreeIrbIrpAndContext(
|
|
PDCAM_IO_CONTEXT pDCamIoContext,
|
|
PIRB pIrb,
|
|
PIRP pIrp
|
|
)
|
|
{
|
|
if(pIrp)
|
|
IoFreeIrp(pIrp);
|
|
if(pIrb)
|
|
ExFreePool(pIrb);
|
|
if(pDCamIoContext)
|
|
ExFreePool(pDCamIoContext);
|
|
}
|
|
|
|
|
|
BOOL
|
|
DCamIsoEnable(
|
|
PIRB pIrb,
|
|
PDCAM_EXTENSION pDevExt,
|
|
BOOL Enable
|
|
)
|
|
/*
|
|
Start or start isoch transmission by setting the ISOEnable bit.
|
|
TRUE: Start transmission;
|
|
FALSE: Stop transmission.
|
|
*/
|
|
{
|
|
BOOL EnableVerify;
|
|
DCamRegArea RegArea;
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER stableTime;
|
|
LONG lRetries = MAX_READ_REG_RETRIES;
|
|
|
|
do {
|
|
RegArea.AsULONG = (Enable ? START_ISOCH_TRANSMISSION : STOP_ISOCH_TRANSMISSION);
|
|
Status = DCamWriteRegister(pIrb, pDevExt, FIELDOFFSET(CAMERA_REGISTER_MAP, IsoEnable), RegArea.AsULONG);
|
|
EnableVerify = DCamDeviceInUse(pIrb, pDevExt);
|
|
if(!NT_SUCCESS(Status) || EnableVerify != Enable) {
|
|
ERROR_LOG(("\'DCAmIsoEnable: St:%x; Enable:%d vs EnableVerify:%d\n", Status, Enable, EnableVerify));
|
|
if(lRetries >= 1) {
|
|
stableTime.LowPart = DCAM_REG_STABLE_DELAY;
|
|
stableTime.HighPart = -1;
|
|
KeDelayExecutionThread(KernelMode, TRUE, &stableTime);
|
|
ERROR_LOG(("\'DCamIsoEnable: delayed and try again...\n"))
|
|
}
|
|
}
|
|
} while (--lRetries > 0 && (!NT_SUCCESS(Status) || (EnableVerify != Enable)) );
|
|
|
|
return (EnableVerify == Enable);
|
|
}
|
|
|
|
void
|
|
DCamProcessPnpIrp(
|
|
IN PHW_STREAM_REQUEST_BLOCK Srb,
|
|
PIO_STACK_LOCATION IrpStack,
|
|
PDCAM_EXTENSION pDevExt
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process PnP Irp.
|
|
|
|
Arguments:
|
|
|
|
Srb - Pointer to Stream request block
|
|
|
|
Return Value:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status, StatusWait;
|
|
PIRB pIrb;
|
|
PIRP pIrp;
|
|
PSTREAMEX pStrmEx;
|
|
|
|
PAGED_CODE();
|
|
|
|
switch (IrpStack->MinorFunction) {
|
|
#if 1
|
|
case IRP_MN_QUERY_POWER:
|
|
pStrmEx = (PSTREAMEX) pDevExt->pStrmEx;
|
|
ERROR_LOG(("IRP_MN_QUERY_POWER: pStrmEx:%x\n", pStrmEx));
|
|
if(!pStrmEx) {
|
|
Srb->Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
if(pStrmEx->KSState == KSSTATE_PAUSE || pStrmEx->KSState == KSSTATE_RUN) {
|
|
ERROR_LOG(("Does not support hibernation while streaming!\n"));
|
|
Srb->Status = STATUS_NOT_SUPPORTED;
|
|
} else {
|
|
ERROR_LOG(("OK to hibernation if not streaming\n"));
|
|
Srb->Status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
#endif
|
|
case IRP_MN_BUS_RESET:
|
|
//
|
|
// We will realocate the resource (bandwith and channel) in IRQL PASSIVE level.
|
|
//
|
|
Srb->Status = STATUS_SUCCESS;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// The generation count is updated in the bus reset callback notification only.
|
|
// Continue iff the generation count has been updated.
|
|
// Else, we are assuming another bus reset has occurred,
|
|
// and we will pass to us later.
|
|
//
|
|
if(pDevExt->CurrentGeneration != *((PULONG) &IrpStack->Parameters.Others.Argument4)) {
|
|
ERROR_LOG(("DCamProcessPnpIrp: Generation count old (%d) != new (%d); STOP!\n",
|
|
pDevExt->CurrentGeneration, *((PULONG) &IrpStack->Parameters.Others.Argument4)) );
|
|
break;
|
|
}
|
|
|
|
pStrmEx = (PSTREAMEX) pDevExt->pStrmEx;
|
|
DbgMsg2(("\'%d:%s) SonyDCamProcessPnpIrp: pDevExt %x; pStrmEx %x; CurGen %d\n",
|
|
pDevExt->idxDev, pDevExt->pchVendorName, pDevExt, pStrmEx, pDevExt->CurrentGeneration));
|
|
|
|
//
|
|
// If the stream was open (pStrmEx != NULL && pStrmEx->pVideoInfoHeader != NULL);
|
|
// We need to ask controller to allocate bandwidth and channel.
|
|
//
|
|
if(pStrmEx &&
|
|
pStrmEx->pVideoInfoHeader != NULL) {
|
|
|
|
DbgMsg2(("\'%d:%s) DCamProcessPnpIrp: Stream was open so re-allocate resource.\n", pDevExt->idxDev, pDevExt->pchVendorName));
|
|
|
|
|
|
// Allocate Irb
|
|
pIrb = ExAllocatePoolWithTag(NonPagedPool, sizeof(IRB), 'macd');
|
|
if(!pIrb) {
|
|
ERROR_LOG(("\'DCamProcessPnpIrp: allocate IRB failed; insufficient resource.\n"));
|
|
Srb->Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
} else {
|
|
ULONG ulChannel;
|
|
HANDLE hResource;
|
|
|
|
pIrp = IoAllocateIrp(pDevExt->BusDeviceObject->StackSize, FALSE);
|
|
if(!pIrp) {
|
|
ExFreePool(pIrb);
|
|
Srb->Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
RtlZeroMemory(pIrb, sizeof(IRB));
|
|
|
|
//
|
|
// Bus reset will free the bandwidth but not the bandwidth structure allocated by the lower driver
|
|
//
|
|
if (pDevExt->hBandwidth) {
|
|
|
|
DbgMsg2(("\'DCamProcessPnpIrp: Attempt to free ->hBandwidth\n"));
|
|
|
|
pIrb->FunctionNumber = REQUEST_ISOCH_FREE_BANDWIDTH;
|
|
pIrb->Flags = 0;
|
|
pIrb->u.IsochFreeBandwidth.hBandwidth = pDevExt->hBandwidth;
|
|
Status = DCamSubmitIrpSynch(pDevExt, pIrp, pIrb);
|
|
|
|
if (Status) {
|
|
ERROR_LOG(("DCamProcessPnpIrp: Error %x while trying to free Isoch bandwidth\n", Status));
|
|
ASSERT(Status == STATUS_SUCCESS || Status == STATUS_INVALID_GENERATION);
|
|
}
|
|
|
|
pDevExt->hBandwidth = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Before we assign the new hResource, we wait for it attaching buffer to complete.
|
|
// For buffer that completed with previous hResource,
|
|
// It will complete with error ?
|
|
//
|
|
|
|
StatusWait = KeWaitForSingleObject( &pStrmEx->hMutex, Executive, KernelMode, FALSE, 0 );
|
|
|
|
|
|
//
|
|
// Reallocate bandwidth and channel, and resource if necessary.
|
|
// IF THIS FAIL, we are consider illegally streaming, and need to STOP streaming.
|
|
//
|
|
|
|
ulChannel = pDevExt->IsochChannel;
|
|
hResource = pDevExt->hResource;
|
|
|
|
Status = DCamAllocateIsochResource(pDevExt, pIrb, FALSE);
|
|
|
|
if(Status) {
|
|
|
|
ERROR_LOG(("\'%d:%s) DCamProcessPnpIrp: Re-AllocateIsochResource failed! Status=%x; Treat as device removed.\n\n",
|
|
pDevExt->idxDev, pDevExt->pchVendorName, Status));
|
|
ASSERT(Status == STATUS_SUCCESS);
|
|
|
|
//
|
|
// No resource so let's treat this situation as
|
|
// Device has been removed because there is no
|
|
// way to restart this.
|
|
// This will stop future SRB_READ until stream is STOP and RUN again.
|
|
//
|
|
|
|
pDevExt->bDevRemoved = TRUE;
|
|
Srb->Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
//
|
|
// Stop tranmission so it will not send data to the old channel,
|
|
// which might be "owned" by other device.
|
|
//
|
|
|
|
if(pStrmEx->KSState == KSSTATE_RUN) {
|
|
// Disable EnableISO
|
|
DCamIsoEnable(pIrb, pDevExt, FALSE);
|
|
}
|
|
|
|
KeReleaseMutex(&pStrmEx->hMutex, FALSE);
|
|
ExFreePool(pIrb);
|
|
IoFreeIrp(pIrp);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If channel number change due to bus reset, we must
|
|
// - continue to blocking incoming SRB_READ (with mutex)
|
|
// - if RUN state, stop transmission
|
|
// - detach all pending buffer(s)
|
|
// - free "stale" isoch resource
|
|
// - if RUN state, program device to use the new channel
|
|
// - if RUN state, restart transmission
|
|
//
|
|
|
|
if(pDevExt->IsochChannel != ISOCH_ANY_CHANNEL &&
|
|
ulChannel != pDevExt->IsochChannel) {
|
|
|
|
//
|
|
// Stop tranmission so it will not send data to the old channel,
|
|
// which might be "owned" by other device.
|
|
//
|
|
|
|
if(pStrmEx->KSState == KSSTATE_RUN) {
|
|
// Disable EnableISO
|
|
DCamIsoEnable(pIrb, pDevExt, FALSE);
|
|
}
|
|
|
|
|
|
//
|
|
// Detach pending packets using the hOldRources and reattached using the new hResource
|
|
// Note: incoming SRB_READ is block right now.
|
|
// free old resource after all pending reads are detached.
|
|
//
|
|
|
|
if(pDevExt->PendingReadCount > 0) {
|
|
|
|
Status = DCamReSubmitPacket(hResource, pDevExt, pStrmEx, pDevExt->PendingReadCount);
|
|
}
|
|
|
|
|
|
//
|
|
// Free "stale" isoch resource
|
|
//
|
|
|
|
if(pDevExt->hResource != hResource) {
|
|
|
|
DbgMsg2(("DCamReSubmitPacket: Attempt to free hStaleResource %x\n", hResource));
|
|
pIrb->FunctionNumber = REQUEST_ISOCH_FREE_RESOURCES;
|
|
pIrb->Flags = 0;
|
|
pIrb->u.IsochFreeResources.hResource = hResource;
|
|
Status = DCamSubmitIrpSynch(pDevExt, pIrp, pIrb);
|
|
if (Status) {
|
|
ERROR_LOG(("\'DCamFreeIsochResource: Error %x while trying to free Isoch resources\n\n", Status));
|
|
ASSERT(Status == STATUS_SUCCESS);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Getting ready to accept callback
|
|
//
|
|
pDevExt->bStopIsochCallback = FALSE;
|
|
|
|
//
|
|
// Restore to its initial Streaming state
|
|
// mainly, programming device.
|
|
//
|
|
|
|
DCamSetKSStateInitialize(pDevExt);
|
|
}
|
|
|
|
KeReleaseMutex(&pStrmEx->hMutex, FALSE);
|
|
ExFreePool(pIrb);
|
|
IoFreeIrp(pIrp);
|
|
}
|
|
}
|
|
|
|
if(Status == STATUS_SUCCESS) {
|
|
|
|
//
|
|
// Set to last saved configuration
|
|
//
|
|
|
|
SetCurrentDevicePropertyValues(pDevExt, (PIRB) Srb->SRBExtension);
|
|
}
|
|
|
|
DbgMsg2(("\'DCamProcessPnpIrp, IRP_MN_BUS_RESET: Done, Status %x\n", Status));
|
|
|
|
break;
|
|
|
|
case IRP_MN_QUERY_CAPABILITIES:
|
|
ERROR_LOG(("\'SonyDCamProcessPnpIrp: IRP_MN_QUERY_CAPABILITIES: Srb->Status = STATUS_NOT_IMPLEMENTED.\n"));
|
|
default:
|
|
Srb->Status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
DCamChangePower(
|
|
IN PHW_STREAM_REQUEST_BLOCK pSrb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process chnaging this device's power state.
|
|
|
|
Arguments:
|
|
|
|
Srb - Pointer to Stream request block
|
|
|
|
Return Value:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
{
|
|
PDCAM_EXTENSION pDevExt;
|
|
PSTREAMEX pStrmEx;
|
|
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(pSrb->Irp);
|
|
DEVICE_POWER_STATE DevicePowerState = pSrb->CommandData.DeviceState;
|
|
|
|
PAGED_CODE();
|
|
|
|
pDevExt = (PDCAM_EXTENSION) pSrb->HwDeviceExtension;
|
|
DbgMsg2(("\'DCamChangePower: pSrb=%x; pDevExt=%x\n", pSrb, pDevExt));
|
|
|
|
ASSERT(pDevExt != NULL);
|
|
if(!pDevExt) {
|
|
pSrb->Status = STATUS_INVALID_PARAMETER;
|
|
ERROR_LOG(("DCamChangePower: pDevExt is NULL!\n"));
|
|
return;
|
|
}
|
|
|
|
pStrmEx = (PSTREAMEX) pDevExt->pStrmEx;
|
|
if (pStrmEx ==NULL) {
|
|
pSrb->Status = STATUS_SUCCESS;
|
|
pDevExt->CurrentPowerState = DevicePowerState;
|
|
DbgMsg2(("DCamChangePower: pStrmEx is NULL => Stream is not open. That is Ok!!\n"));
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// We can honor power state change:
|
|
//
|
|
// D0: device is on and running
|
|
// D1,D2: not implemented.
|
|
// D3: device is off and not running. Device context is lost.
|
|
// Power can be removed from the device.
|
|
// when power is back on, we will get a bus reset.
|
|
//
|
|
// (0) Remove DontSuspendIfStreamsAreRunning from INF
|
|
// save current state.
|
|
// (1) ->D3, to PAUSE/STOP state (depends on if pending buffers can be kept by its lower driver)
|
|
// (2) ->D0, to restore saved state
|
|
//
|
|
// We can do the above but we do not know at this point
|
|
// how our client application react
|
|
//
|
|
if(IrpStack->MinorFunction == IRP_MN_SET_POWER) {
|
|
DbgMsg2(("DCamChangePower: changin power state from %d to %d.\n", pDevExt->CurrentPowerState, DevicePowerState));
|
|
|
|
pSrb->Status = STATUS_SUCCESS;
|
|
|
|
if(pDevExt->CurrentPowerState != DevicePowerState) {
|
|
|
|
switch (DevicePowerState) {
|
|
case PowerDeviceD3: // D0->D3: save state, stop streaming and Sleep
|
|
if( pDevExt->CurrentPowerState == PowerDeviceD0 ) {
|
|
DbgMsg1(("DCamChangePower: Switching from D0 to D3; Save current state.\n"));
|
|
// Save current state to be restored when awake
|
|
pStrmEx->KSSavedState = pStrmEx->KSState;
|
|
}
|
|
break;
|
|
|
|
case PowerDeviceD0: // to Wakeup, restore state and running
|
|
if( pDevExt->CurrentPowerState == PowerDeviceD3 ) {
|
|
DbgMsg1(("DCamChangePower: Switching from D3 to D0; restore state.\n"));
|
|
pStrmEx->KSState = pStrmEx->KSSavedState;
|
|
}
|
|
break;
|
|
|
|
// These state are not defined and noe used.
|
|
case PowerDeviceD1:
|
|
case PowerDeviceD2:
|
|
default:
|
|
ERROR_LOG(("DCamChangePower: Invalid PowerState %d\n", DevicePowerState));
|
|
pSrb->Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(pSrb->Status == STATUS_SUCCESS)
|
|
pDevExt->CurrentPowerState = DevicePowerState;
|
|
|
|
} else {
|
|
|
|
pSrb->Status = STATUS_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
DCamGetStreamInfo(
|
|
IN PHW_STREAM_REQUEST_BLOCK Srb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the information of all streams that are supported by the driver
|
|
|
|
Arguments:
|
|
|
|
Srb - Pointer to Stream request block
|
|
|
|
Return Value:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// pick up the pointer to the stream information data structure
|
|
//
|
|
PIRB pIrb;
|
|
PHW_STREAM_HEADER StreamHeader = &(Srb->CommandData.StreamBuffer->StreamHeader);
|
|
PDCAM_EXTENSION pDevExt = (PDCAM_EXTENSION) Srb->HwDeviceExtension;
|
|
PHW_STREAM_INFORMATION StreamInfo = &(Srb->CommandData.StreamBuffer->StreamInfo);
|
|
|
|
PAGED_CODE();
|
|
|
|
pIrb = (PIRB) Srb->SRBExtension;
|
|
|
|
//
|
|
// set number of streams
|
|
//
|
|
|
|
ASSERT (Srb->NumberOfBytesToTransfer >=
|
|
sizeof (HW_STREAM_HEADER) +
|
|
sizeof (HW_STREAM_INFORMATION));
|
|
|
|
//
|
|
// initialize stream header
|
|
//
|
|
|
|
RtlZeroMemory(StreamHeader,
|
|
sizeof (HW_STREAM_HEADER) +
|
|
sizeof (HW_STREAM_INFORMATION));
|
|
|
|
//
|
|
// initialize the number of streams supported
|
|
//
|
|
|
|
StreamHeader->NumberOfStreams = 1;
|
|
StreamHeader->SizeOfHwStreamInformation = sizeof(HW_STREAM_INFORMATION);
|
|
|
|
//
|
|
// set the device property info
|
|
//
|
|
|
|
StreamHeader->NumDevPropArrayEntries = pDevExt->ulPropSetSupported;
|
|
StreamHeader->DevicePropertiesArray = &pDevExt->VideoProcAmpSet;
|
|
|
|
|
|
//
|
|
// Initialize the stream structure.
|
|
//
|
|
// Number of instances field indicates the number of concurrent streams
|
|
// of this type the device can support.
|
|
//
|
|
|
|
StreamInfo->NumberOfPossibleInstances = 1;
|
|
|
|
//
|
|
// indicates the direction of data flow for this stream, relative to
|
|
// the driver
|
|
//
|
|
|
|
StreamInfo->DataFlow = KSPIN_DATAFLOW_OUT;
|
|
|
|
//
|
|
// dataAccessible - Indicates whether the data is "seen" by the host
|
|
// processor.
|
|
//
|
|
|
|
StreamInfo->DataAccessible = TRUE;
|
|
|
|
//
|
|
// Return number of formats and the table.
|
|
// These information is collected dynamically.
|
|
//
|
|
StreamInfo->NumberOfFormatArrayEntries = pDevExt->ModeSupported;
|
|
StreamInfo->StreamFormatsArray = &pDevExt->DCamStrmModes[0];
|
|
|
|
|
|
//
|
|
// set the property information for the video stream
|
|
//
|
|
|
|
|
|
StreamInfo->NumStreamPropArrayEntries = NUMBER_VIDEO_STREAM_PROPERTIES;
|
|
StreamInfo->StreamPropertiesArray = (PKSPROPERTY_SET) VideoStreamProperties;
|
|
|
|
//
|
|
// set the pin name and category
|
|
//
|
|
|
|
StreamInfo->Name = (GUID *) &PINNAME_VIDEO_CAPTURE;
|
|
StreamInfo->Category = (GUID *) &PINNAME_VIDEO_CAPTURE;
|
|
|
|
|
|
//
|
|
// store a pointer to the topology for the device
|
|
//
|
|
|
|
Srb->CommandData.StreamBuffer->StreamHeader.Topology = &Topology;
|
|
|
|
|
|
//
|
|
// indicate success
|
|
//
|
|
|
|
Srb->Status = STATUS_SUCCESS;
|
|
|
|
DbgMsg2(("\'DCamGetStreamInfo: NumFormat %d, StreamFormatArray %x\n",
|
|
StreamInfo->NumberOfFormatArrayEntries, StreamInfo->StreamFormatsArray));
|
|
|
|
}
|
|
|
|
#define TIME_ROUNDING 1000 // Give it some rounding error of 100microsec
|
|
#define TIME_0750FPS (1333333+TIME_ROUNDING) // 1/7.50 * 10,000,000 (unit=100ns)
|
|
#define TIME_1500FPS (666666+TIME_ROUNDING) // 1/15.0 * 10,000,000 (unit=100ns) do not round to 666667
|
|
#define TIME_3000FPS (333333+TIME_ROUNDING) // 1/30.0 * 10,000,000 (unit=100ns)
|
|
|
|
NTSTATUS
|
|
DCamAllocateIsochResource(
|
|
PDCAM_EXTENSION pDevExt,
|
|
PIRB Irb,
|
|
BOOL bAllocateResource
|
|
)
|
|
{
|
|
PIRP Irp;
|
|
CCHAR StackSize;
|
|
ULONG ModeIndex;
|
|
PSTREAMEX pStrmEx;
|
|
DWORD dwAvgTimePerFrame, dwCompression;
|
|
ULONG fulSpeed;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
|
|
ASSERT(pDevExt);
|
|
pStrmEx = (PSTREAMEX) pDevExt->pStrmEx;
|
|
ASSERT(pStrmEx);
|
|
|
|
|
|
DbgMsg2(("\'DCamAllocateIsochResource: enter; pStrmEx %x; pVideoInfo %x\n", pStrmEx, pStrmEx->pVideoInfoHeader));
|
|
//
|
|
// Now if they're on a YUV4:2:2 format, we've gotta check what
|
|
// resolution they want it at, since we support this format
|
|
// but in two different resolutions (modes on the camera).
|
|
//
|
|
|
|
// This is the INDEX to the frame rate and resource allocation; see IsochInfoTable.
|
|
// 0 : reserved
|
|
// 1 : 3.75
|
|
// 2 : 7.5
|
|
// 3 : 15 (DEFAULT_FRAME_RATE)
|
|
// 4 : 30
|
|
// 5 : 60 (Not supported for Mode 1 & 3)
|
|
dwAvgTimePerFrame = (DWORD) pStrmEx->pVideoInfoHeader->AvgTimePerFrame;
|
|
dwCompression = (DWORD) pStrmEx->pVideoInfoHeader->bmiHeader.biCompression;
|
|
|
|
|
|
|
|
// Determine the Frame rate
|
|
if (dwAvgTimePerFrame > TIME_0750FPS)
|
|
pDevExt->FrameRate = 1; // 3.75FPS
|
|
else if (dwAvgTimePerFrame > TIME_1500FPS)
|
|
pDevExt->FrameRate = 2; // 7.5FPS
|
|
else if (dwAvgTimePerFrame > TIME_3000FPS)
|
|
pDevExt->FrameRate = 3; // 15 FPS
|
|
else
|
|
pDevExt->FrameRate = 4; // 30 FPS
|
|
|
|
|
|
DbgMsg2(("\'DCamAllocateIsochResource: FrameRate: %d FPS\n", (1 << (pDevExt->FrameRate-1)) * 15 / 4));
|
|
|
|
// Determine the Video Mode
|
|
switch(dwCompression) {
|
|
#ifdef SUPPORT_YUV444
|
|
case FOURCC_Y444: // Mode 0
|
|
ModeIndex = VMODE0_YUV444;
|
|
break;
|
|
#endif
|
|
case FOURCC_UYVY: // Mode 1 or 3
|
|
if (pStrmEx->pVideoInfoHeader->bmiHeader.biWidth == 640 &&
|
|
(pStrmEx->pVideoInfoHeader->bmiHeader.biHeight == 480 ||
|
|
pStrmEx->pVideoInfoHeader->bmiHeader.biHeight == -480)) {
|
|
ModeIndex = VMODE3_YUV422;
|
|
// Max frame rate is 15
|
|
if(pDevExt->FrameRate > 3)
|
|
pDevExt->FrameRate = 3;
|
|
} else
|
|
ModeIndex = VMODE1_YUV422;
|
|
break;
|
|
#ifdef SUPPORT_YUV411
|
|
case FOURCC_Y411: // Mode 2
|
|
ModeIndex = VMODE2_YUV411;
|
|
break;
|
|
#endif
|
|
|
|
#ifdef SUPPORT_RGB24
|
|
case KS_BI_RGB: // = 0
|
|
ModeIndex = VMODE4_RGB24;
|
|
// Max frame rate is 15
|
|
if(pDevExt->FrameRate > 3)
|
|
pDevExt->FrameRate = 3;
|
|
break;
|
|
#endif
|
|
|
|
#ifdef SUPPORT_YUV800
|
|
case FOURCC_Y800:
|
|
ModeIndex = VMODE5_YUV800;
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
Status = STATUS_NOT_IMPLEMENTED;;
|
|
return Status;;
|
|
}
|
|
|
|
|
|
DbgMsg1(("\'DCamAllocateIsochResource: ModeIndex=%d, AvgTimePerFrame=%d, FrameRate=%d\n",
|
|
ModeIndex, dwAvgTimePerFrame, pDevExt->FrameRate));
|
|
|
|
//
|
|
// Get an Irp so we can send some allocation commands down
|
|
//
|
|
|
|
StackSize = pDevExt->BusDeviceObject->StackSize;
|
|
Irp = IoAllocateIrp(StackSize, FALSE);
|
|
|
|
if (!Irp) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Calculate the index to use to reference the ISOCH table
|
|
//
|
|
pStrmEx->idxIsochTable = ModeIndex * NUM_POSSIBLE_RATES + pDevExt->FrameRate;
|
|
|
|
ASSERT(pStrmEx->pVideoInfoHeader->bmiHeader.biSizeImage == IsochInfoTable[pStrmEx->idxIsochTable].CompletePictureSize);
|
|
DbgMsg2(("\'DCamAllocateIsochResource: ModeIndex=%d, idxIsochTable=%d, biSizeImage=%d, CompletePictureSize=%d\n",
|
|
ModeIndex, pStrmEx->idxIsochTable, pStrmEx->pVideoInfoHeader->bmiHeader.biSizeImage, IsochInfoTable[pStrmEx->idxIsochTable].CompletePictureSize));
|
|
|
|
//
|
|
// 0. Determine the MAX_SPEED and not use the speed defined in the static table.
|
|
//
|
|
Irb->FunctionNumber = REQUEST_GET_SPEED_BETWEEN_DEVICES;
|
|
Irb->Flags = 0;
|
|
Irb->u.GetMaxSpeedBetweenDevices.fulFlags = USE_LOCAL_NODE;
|
|
Irb->u.GetMaxSpeedBetweenDevices.ulNumberOfDestinations = 0;
|
|
Status = DCamSubmitIrpSynch(pDevExt, Irp, Irb);
|
|
if(Status) {
|
|
ERROR_LOG(("\'DCamAllocateIsochResource: Error %x while trying to get maximun speed between devices.\n", Status));
|
|
|
|
IoFreeIrp(Irp);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
fulSpeed = Irb->u.GetMaxSpeedBetweenDevices.fulSpeed;
|
|
|
|
//
|
|
// The max speed between devices should be within supported speed range, and
|
|
// must be equal or greater than the required speed for the chosen format.
|
|
//
|
|
if(
|
|
( fulSpeed != SPEED_FLAGS_100
|
|
&& fulSpeed != SPEED_FLAGS_200
|
|
&& fulSpeed != SPEED_FLAGS_400
|
|
)
|
|
|| fulSpeed < IsochInfoTable[pStrmEx->idxIsochTable].SpeedRequired
|
|
) {
|
|
|
|
ASSERT(fulSpeed == SPEED_FLAGS_100 || fulSpeed == SPEED_FLAGS_200 || fulSpeed == SPEED_FLAGS_400);
|
|
ASSERT(fulSpeed >= IsochInfoTable[pStrmEx->idxIsochTable].SpeedRequired);
|
|
|
|
IoFreeIrp(Irp);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
pDevExt->SpeedCode = fulSpeed >> 1; // Safe for S100, 200 and 400 (is checked above).
|
|
DbgMsg2(("\'GetMaxSpeedBetweenDevices.fulSpeed=%x; SpeedCode:%x\n", fulSpeed, pDevExt->SpeedCode));
|
|
|
|
|
|
//
|
|
// 1. Allocate CHANNEL
|
|
// First try to re-allocate the same channel
|
|
// If it is used, try to get any channel. 1394DCam can only be on channel 0..15
|
|
//
|
|
Irb->FunctionNumber = REQUEST_ISOCH_ALLOCATE_CHANNEL;
|
|
Irb->Flags = 0;
|
|
|
|
//
|
|
// ULONG nRequestedChannel; // Need a specific channel
|
|
// ULONG Channel; // Returned channel
|
|
// LARGE_INTEGER ChannelsAvailable; // Channels available
|
|
// Instead of hardcoded '0'; use -1 to ask the bus driver to get the next available channel for us.
|
|
// -1 (any channel) or an existing channel
|
|
Irb->u.IsochAllocateChannel.nRequestedChannel = pDevExt->IsochChannel;
|
|
Status = DCamSubmitIrpSynch(pDevExt, Irp, Irb);
|
|
if(Status) {
|
|
|
|
//
|
|
// Due to channel change,
|
|
// all Pending read will be either resubmitted,
|
|
// or cancelled (if out of resource).
|
|
//
|
|
pDevExt->bStopIsochCallback = TRUE; // Set back to FALSE after pending buffer are attached.
|
|
|
|
|
|
//
|
|
// If this is an initial request and no channel available,
|
|
// free all resource and abort.
|
|
//
|
|
if(pDevExt->IsochChannel == ISOCH_ANY_CHANNEL)
|
|
goto NoResource_abort;
|
|
|
|
DbgMsg1(("DCamAllocateIsochResource: last allocated channel %d is not available; pending count %d.\n",
|
|
pDevExt->IsochChannel, pDevExt->PendingReadCount));
|
|
|
|
// Try gettting any channel.
|
|
Irb->FunctionNumber = REQUEST_ISOCH_ALLOCATE_CHANNEL;
|
|
Irb->Flags = 0;
|
|
Irb->u.IsochAllocateChannel.nRequestedChannel = ISOCH_ANY_CHANNEL;
|
|
Status = DCamSubmitIrpSynch(pDevExt, Irp, Irb);
|
|
if(Status) {
|
|
ERROR_LOG(("DCamAllocateIsochResource: allocate any channel failed, status %x!\n", Status));
|
|
goto NoResource_abort;
|
|
}
|
|
|
|
//
|
|
// Channel changed, we MUST reallocate resource.
|
|
// The "stale" resrouce will be free later when
|
|
// pending packet are detached.
|
|
//
|
|
|
|
bAllocateResource = TRUE;
|
|
}
|
|
|
|
DbgMsg1(("**IsochAlloc: Channel(Old) %d, requested %d, got %d, HiLo(0x%x:%x), PendingRead %d\n",
|
|
pDevExt->IsochChannel,
|
|
Irb->u.IsochAllocateChannel.nRequestedChannel,
|
|
Irb->u.IsochAllocateChannel.Channel,
|
|
Irb->u.IsochAllocateChannel.ChannelsAvailable.u.HighPart,
|
|
Irb->u.IsochAllocateChannel.ChannelsAvailable.u.LowPart,
|
|
pDevExt->PendingReadCount));
|
|
|
|
// New channel
|
|
pDevExt->IsochChannel = Irb->u.IsochAllocateChannel.Channel; // Used in allocating iso. resource and reallocation
|
|
|
|
|
|
//
|
|
// 2. Allocate BANDWIDTH
|
|
//
|
|
Irb->FunctionNumber = REQUEST_ISOCH_ALLOCATE_BANDWIDTH;
|
|
Irb->Flags = 0;
|
|
Irb->u.IsochAllocateBandwidth.nMaxBytesPerFrameRequested = IsochInfoTable[pStrmEx->idxIsochTable].QuadletPayloadPerPacket << 2;
|
|
Irb->u.IsochAllocateBandwidth.fulSpeed = fulSpeed;
|
|
Irb->u.IsochAllocateBandwidth.hBandwidth = 0;
|
|
Status = DCamSubmitIrpSynch(pDevExt, Irp, Irb);
|
|
|
|
if(Status) {
|
|
ERROR_LOG(("DCamAllocateIsochResource: Error %x while trying to allocate Isoch bandwidth\n", Status));
|
|
goto NoResource_abort;
|
|
}
|
|
|
|
pDevExt->hBandwidth = Irb->u.IsochAllocateBandwidth.hBandwidth;
|
|
DbgMsg2(("**IsochAlloc: nMaxBytesPerFrameRequested %d, fulSpeed %d; hBandWidth 0x%x\n",
|
|
IsochInfoTable[pStrmEx->idxIsochTable].QuadletPayloadPerPacket << 2, fulSpeed, pDevExt->hBandwidth));
|
|
|
|
|
|
|
|
//
|
|
// 3. Allocate RESOURCES
|
|
// Note: after a bus reset, we need not free and re-allocate this resoruce again.
|
|
//
|
|
if(bAllocateResource) {
|
|
Irb->FunctionNumber = REQUEST_ISOCH_ALLOCATE_RESOURCES;
|
|
Irb->Flags = 0;
|
|
Irb->u.IsochAllocateResources.fulSpeed = fulSpeed;
|
|
Irb->u.IsochAllocateResources.nChannel = pDevExt->IsochChannel;
|
|
Irb->u.IsochAllocateResources.nMaxBytesPerFrame = IsochInfoTable[pStrmEx->idxIsochTable].QuadletPayloadPerPacket << 2;
|
|
// For slower frame rate use smaller quadlets
|
|
// smaller frame size will use more packet to fill the same amount of data
|
|
// this is why smaller frame rate actually demand more resource !!
|
|
Irb->u.IsochAllocateResources.nNumberOfBuffers = MAX_BUFFERS_SUPPLIED + 1; // "+1" as a "safety"
|
|
Irb->u.IsochAllocateResources.nMaxBufferSize = IsochInfoTable[pStrmEx->idxIsochTable].CompletePictureSize;
|
|
if (pDevExt->HostControllerInfomation.HostCapabilities & HOST_INFO_SUPPORTS_RETURNING_ISO_HDR) {
|
|
Irb->u.IsochAllocateResources.nQuadletsToStrip = 1;
|
|
Irb->u.IsochAllocateResources.fulFlags = RESOURCE_USED_IN_LISTENING | RESOURCE_STRIP_ADDITIONAL_QUADLETS;
|
|
|
|
} else {
|
|
Irb->u.IsochAllocateResources.nQuadletsToStrip = 0;
|
|
Irb->u.IsochAllocateResources.fulFlags = RESOURCE_USED_IN_LISTENING;
|
|
}
|
|
|
|
Irb->u.IsochAllocateResources.hResource = 0;
|
|
DbgMsg2(("\'DCamAllocateIsochResource: fullSpeed(%d), nMaxBytesPerFrame(%d), nMaxBufferSize(%d)\n",
|
|
Irb->u.IsochAllocateResources.fulSpeed,
|
|
Irb->u.IsochAllocateResources.nMaxBytesPerFrame,
|
|
Irb->u.IsochAllocateResources.nMaxBufferSize));
|
|
Status = DCamSubmitIrpSynch(pDevExt, Irp, Irb);
|
|
|
|
if(Status) {
|
|
ERROR_LOG(("DCamAllocateIsochResource: Error %x while trying to allocate Isoch resources\n", Status));
|
|
goto NoResource_abort;
|
|
}
|
|
|
|
|
|
pDevExt->hResource = Irb->u.IsochAllocateResources.hResource;
|
|
|
|
}
|
|
|
|
pDevExt->CurrentModeIndex = ModeIndex;
|
|
DbgMsg2(("**IsochAlloc: hResource = %x\n", pDevExt->hResource));
|
|
|
|
IoFreeIrp(Irp);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
|
|
|
|
NoResource_abort:
|
|
|
|
// Free bandwidth
|
|
if(pDevExt->hBandwidth != NULL) {
|
|
|
|
Irb->FunctionNumber = REQUEST_ISOCH_FREE_BANDWIDTH;
|
|
Irb->Flags = 0;
|
|
Irb->u.IsochFreeBandwidth.hBandwidth = pDevExt->hBandwidth;
|
|
Status = DCamSubmitIrpSynch(pDevExt, Irp, Irb);
|
|
pDevExt->hBandwidth = NULL;
|
|
if(Status) {
|
|
ERROR_LOG(("DCamAllocateIsochResource: Error %x while trying to free Isoch bandwidth\n", Status));
|
|
}
|
|
}
|
|
|
|
// Free channel
|
|
if (pDevExt->IsochChannel != ISOCH_ANY_CHANNEL) {
|
|
|
|
Irb->FunctionNumber = REQUEST_ISOCH_FREE_CHANNEL;
|
|
Irb->Flags = 0;
|
|
Irb->u.IsochFreeChannel.nChannel = pDevExt->IsochChannel;
|
|
Status = DCamSubmitIrpSynch(pDevExt, Irp, Irb);
|
|
pDevExt->IsochChannel = ISOCH_ANY_CHANNEL; // Reset it.
|
|
|
|
if(Status) {
|
|
ERROR_LOG(("DCamAllocateIsochResource: Error %x while trying to free Isoch channel\n", Status));
|
|
}
|
|
}
|
|
|
|
|
|
IoFreeIrp(Irp);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DCamFreeIsochResource (
|
|
PDCAM_EXTENSION pDevExt,
|
|
PIRB Irb,
|
|
BOOL bFreeResource
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free resource allocated in DCamAllocateIsochResource().
|
|
|
|
Arguments:
|
|
|
|
Srb - Pointer to Stream request block
|
|
|
|
Return Value:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
{
|
|
PIRP Irp;
|
|
CCHAR StackSize;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
DbgMsg2(("\'DCamFreeIsochResource: enter; DevExt=%x, Irb=%x\n", pDevExt, Irb));
|
|
|
|
ASSERT(pDevExt);
|
|
ASSERT(Irb);
|
|
|
|
|
|
if(Irb == 0 ||
|
|
pDevExt == 0) {
|
|
DbgMsg2(("\'DCamFreeIsochResource: ABORTED!\n"));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
//
|
|
// Get an Irp so we can send some free commands down
|
|
//
|
|
StackSize = pDevExt->BusDeviceObject->StackSize;
|
|
Irp = IoAllocateIrp(StackSize, FALSE);
|
|
|
|
if (!Irp) {
|
|
ERROR_LOG(("DCamFreeIsochResource: Error %x while trying to allocate an Irp\n\n", Status));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
|
|
//
|
|
// 1. Free Resource
|
|
//
|
|
if (pDevExt->hResource && bFreeResource) {
|
|
|
|
DbgMsg2(("\'DCamFreeIsochResource: Attempt to free ->hResource\n"));
|
|
|
|
Irb->FunctionNumber = REQUEST_ISOCH_FREE_RESOURCES;
|
|
Irb->Flags = 0;
|
|
Irb->u.IsochFreeResources.hResource = pDevExt->hResource;
|
|
Status = DCamSubmitIrpSynch(pDevExt, Irp, Irb);
|
|
|
|
pDevExt->hResource = NULL;
|
|
if (Status) {
|
|
|
|
ERROR_LOG(("DCamFreeIsochResource: Error %x while trying to free Isoch resources\n\n", Status));
|
|
}
|
|
}
|
|
|
|
//
|
|
// 2. Free Channel
|
|
//
|
|
if (pDevExt->IsochChannel != ISOCH_ANY_CHANNEL) {
|
|
|
|
DbgMsg2(("\'DCamFreeIsochResource: Attempt to free ->IsochChannel\n"));
|
|
|
|
Irb->FunctionNumber = REQUEST_ISOCH_FREE_CHANNEL;
|
|
Irb->Flags = 0;
|
|
Irb->u.IsochFreeChannel.nChannel = pDevExt->IsochChannel;
|
|
Status = DCamSubmitIrpSynch(pDevExt, Irp, Irb);
|
|
|
|
pDevExt->IsochChannel = ISOCH_ANY_CHANNEL;
|
|
|
|
if(Status) {
|
|
|
|
ERROR_LOG(("DCamFreeIsochResource: Error %x while trying to free Isoch channel\n\n", Status));
|
|
}
|
|
}
|
|
|
|
//
|
|
// 3. Free Bandwidth
|
|
//
|
|
if (pDevExt->hBandwidth) {
|
|
|
|
DbgMsg2(("\'DCamFreeIsochResource: Attempt to free ->hBandwidth\n"));
|
|
|
|
Irb->FunctionNumber = REQUEST_ISOCH_FREE_BANDWIDTH;
|
|
Irb->Flags = 0;
|
|
Irb->u.IsochFreeBandwidth.hBandwidth = pDevExt->hBandwidth;
|
|
Status = DCamSubmitIrpSynch(pDevExt, Irp, Irb);
|
|
|
|
pDevExt->hBandwidth = NULL;
|
|
|
|
if (Status) {
|
|
|
|
ERROR_LOG(("DCamFreeIsochResource: Error %x while trying to free Isoch bandwidth\n", Status));
|
|
}
|
|
}
|
|
|
|
DbgMsg2(("\'DCamFreeIsochResource: hResource = %x\n", pDevExt->hResource));
|
|
|
|
|
|
IoFreeIrp(Irp);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
InitializeStreamExtension(
|
|
PDCAM_EXTENSION pDevExt,
|
|
PHW_STREAM_OBJECT pStreamObject,
|
|
PSTREAMEX pStrmEx
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
pStrmEx->hMasterClock = 0;
|
|
pStrmEx->FrameInfo.ExtendedHeaderSize = sizeof(KS_FRAME_INFO);
|
|
pStrmEx->FrameInfo.PictureNumber = 0;
|
|
pStrmEx->FrameInfo.DropCount = 0;
|
|
pStrmEx->FrameInfo.dwFrameFlags = 0;
|
|
pStrmEx->FirstFrameTime = 0;
|
|
pStrmEx->pVideoInfoHeader = 0;
|
|
pStrmEx->KSState = KSSTATE_STOP;
|
|
pStrmEx->KSSavedState = KSSTATE_STOP;
|
|
|
|
|
|
KeInitializeMutex( &pStrmEx->hMutex, 0); // Level 0 and in Signal state
|
|
|
|
}
|
|
|
|
BOOL
|
|
DCamDeviceInUse(
|
|
PIRB pIrb,
|
|
PDCAM_EXTENSION pDevExt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
See if this device is in used.
|
|
We check ISO_ENABLE since this is the only register
|
|
in a 1394DCam that we can set/get and 99%+ of time
|
|
this bit is set by its owner.
|
|
|
|
Arguments:
|
|
|
|
pIrb - Pointer to IEEE 1394 Request Block definition (IRB)
|
|
pDevExt - this device extension
|
|
|
|
Return Value:
|
|
|
|
TRUE: Iso_enable != 0
|
|
FALSE: iso_enable == 0
|
|
|
|
--*/
|
|
|
|
{
|
|
DCamRegArea RegArea;
|
|
NTSTATUS status;
|
|
LONG lRetries = MAX_READ_REG_RETRIES;
|
|
|
|
|
|
// If a device is removed, it is not available.
|
|
if(pDevExt->bDevRemoved)
|
|
return TRUE;
|
|
|
|
do {
|
|
RegArea.AsULONG = 0;
|
|
status = DCamReadRegister(pIrb, pDevExt, FIELDOFFSET(CAMERA_REGISTER_MAP, IsoEnable), &(RegArea.AsULONG));
|
|
#if DBG
|
|
if(!NT_SUCCESS(status))
|
|
ERROR_LOG(("**** DCamDeviceInUse: Status %x, ISO_ENABLE %x\n", status, RegArea.AsULONG));
|
|
#endif
|
|
} while (--lRetries > 0 && !NT_SUCCESS(status));
|
|
|
|
if(NT_SUCCESS(status))
|
|
return ((RegArea.AsULONG & ISO_ENABLE_BIT) == ISO_ENABLE_BIT);
|
|
|
|
// failed to query the device.
|
|
return TRUE; // Assume it is in use.
|
|
}
|
|
|
|
|
|
VOID
|
|
DCamOpenStream(
|
|
IN PHW_STREAM_REQUEST_BLOCK pSrb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when an OpenStream Srb request is received
|
|
|
|
Arguments:
|
|
|
|
pSrb - Pointer to Stream request block
|
|
|
|
Return Value:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PIRB Irb;
|
|
ULONG nSize;
|
|
PDCAM_EXTENSION pDevExt;
|
|
PSTREAMEX pStrmEx;
|
|
PKS_DATAFORMAT_VIDEOINFOHEADER pKSDataFormat =
|
|
(PKS_DATAFORMAT_VIDEOINFOHEADER) pSrb->CommandData.OpenFormat;
|
|
PKS_VIDEOINFOHEADER pVideoInfoHdrRequested =
|
|
&pKSDataFormat->VideoInfoHeader;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
Irb = (PIRB) pSrb->SRBExtension;
|
|
pDevExt = (PDCAM_EXTENSION) pSrb->HwDeviceExtension;
|
|
pStrmEx = (PSTREAMEX)pSrb->StreamObject->HwStreamExtension;
|
|
|
|
DbgMsg2(("\'DCamOpenStream: >>> !!! pDevEx %x; pStrmEx %x !!!\n", pDevExt, pStrmEx));
|
|
|
|
|
|
//
|
|
// Cache the stream extension.
|
|
//
|
|
|
|
pDevExt->pStrmEx = pStrmEx;
|
|
|
|
|
|
//
|
|
// default to success
|
|
//
|
|
|
|
pSrb->Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// determine which stream number is being opened. This number indicates
|
|
// the offset into the array of streaminfo structures that was filled out
|
|
// in the AdapterStreamInfo call.
|
|
//
|
|
// So:
|
|
// 0 - Video data from camera
|
|
//
|
|
|
|
switch (pSrb->StreamObject->StreamNumber) {
|
|
|
|
case 0:
|
|
|
|
//
|
|
// Make sure that this device is not in used
|
|
//
|
|
if(DCamDeviceInUse(Irb, pDevExt)) {
|
|
ERROR_LOG(("Device is in used! Open Stream fail!!\n"));
|
|
pDevExt->pStrmEx = NULL;
|
|
pSrb->Status = STATUS_UNSUCCESSFUL;
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Figure out what format they're trying to open first
|
|
//
|
|
|
|
if (!AdapterVerifyFormat (pDevExt->ModeSupported, pDevExt->DCamStrmModes, pKSDataFormat, pSrb->StreamObject->StreamNumber)) {
|
|
pDevExt->pStrmEx = NULL;
|
|
ERROR_LOG(("DCamOpenStream: AdapterVerifyFormat failed.\n"));
|
|
pSrb->Status = STATUS_INVALID_PARAMETER;
|
|
return;
|
|
}
|
|
|
|
InitializeStreamExtension(pDevExt, pSrb->StreamObject, pStrmEx);
|
|
|
|
// It should already been freed by DCamCloseStream()
|
|
ASSERT(pStrmEx->pVideoInfoHeader == NULL);
|
|
ASSERT(pVideoInfoHdrRequested != (PKS_VIDEOINFOHEADER) 0);
|
|
|
|
// Use this instead of sizeof(KS_VIDEOINFOHEADER) to handle variable size structure
|
|
nSize = KS_SIZE_VIDEOHEADER (pVideoInfoHdrRequested);
|
|
|
|
pStrmEx->pVideoInfoHeader = ExAllocatePoolWithTag(NonPagedPool, nSize, 'macd');
|
|
if (pStrmEx->pVideoInfoHeader == NULL) {
|
|
|
|
ERROR_LOG(("DCamOpenStream: ExAllocatePool (->pVideoInfoHeader) failed!\n"));
|
|
ASSERT(pStrmEx->pVideoInfoHeader != NULL);
|
|
|
|
pDevExt->pStrmEx = NULL;
|
|
|
|
pSrb->Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
return;
|
|
}
|
|
|
|
// Copy the VIDEOINFOHEADER requested to our storage
|
|
RtlCopyMemory(
|
|
pStrmEx->pVideoInfoHeader,
|
|
pVideoInfoHdrRequested,
|
|
nSize);
|
|
|
|
DbgMsg3(("\'DCamOpenStream: Copied biSizeImage=%d Duration=%ld (100ns)\n",
|
|
pStrmEx->pVideoInfoHeader->bmiHeader.biSizeImage, (DWORD) pStrmEx->pVideoInfoHeader->AvgTimePerFrame));
|
|
|
|
// Allocate ISOCH resource
|
|
pSrb->Status = DCamAllocateIsochResource(pDevExt, pSrb->SRBExtension, TRUE);
|
|
|
|
if (pSrb->Status) {
|
|
|
|
ERROR_LOG(("DCamOpenStream: !!!! Allocate ISOCH resource failed. CanNOT STREAM!!!!!\n"));
|
|
|
|
ExFreePool(pStrmEx->pVideoInfoHeader);
|
|
pStrmEx->pVideoInfoHeader = NULL;
|
|
pDevExt->pStrmEx = NULL;
|
|
pSrb->Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
return;
|
|
}
|
|
|
|
pSrb->StreamObject->ReceiveDataPacket = (PVOID) DCamReceiveDataPacket;
|
|
pSrb->StreamObject->ReceiveControlPacket = (PVOID) DCamReceiveCtrlPacket;
|
|
|
|
// If bus reset failed and user close the stream and reopen the stream successfully,
|
|
// This must be reset !!
|
|
if(pDevExt->bDevRemoved || pDevExt->bStopIsochCallback) {
|
|
DbgMsg1(("Stream Open successful, reset bDevRemoved and bStopCallback!!\n"));
|
|
pDevExt->bStopIsochCallback = FALSE;
|
|
pDevExt->bDevRemoved = FALSE;
|
|
}
|
|
|
|
//
|
|
// initialize the stream extension data handling information
|
|
//
|
|
|
|
break;
|
|
|
|
default:
|
|
ERROR_LOG(("DCamOpenStream: Hit a non-support pSrb->StreamObject->StreamNumber (%d).\n", pSrb->StreamObject->StreamNumber));
|
|
ASSERT(FALSE);
|
|
pDevExt->pStrmEx = NULL;
|
|
pSrb->Status = STATUS_INVALID_PARAMETER;
|
|
return;
|
|
}
|
|
|
|
|
|
pSrb->StreamObject->HwClockObject.ClockSupportFlags = 0;
|
|
|
|
// We don't use DMA.
|
|
pSrb->StreamObject->Dma = FALSE;
|
|
pSrb->StreamObject->StreamHeaderMediaSpecific = sizeof(KS_FRAME_INFO);
|
|
|
|
//
|
|
// The PIO flag must be set when the mini driver will be accessing the data
|
|
// buffers passed in using logical addressing. We are not going to touch these
|
|
// buffer at all.
|
|
//
|
|
pSrb->StreamObject->Pio = FALSE;
|
|
|
|
|
|
//
|
|
// Set to last saved configuration
|
|
//
|
|
SetCurrentDevicePropertyValues(pDevExt, (PIRB) pSrb->SRBExtension);
|
|
|
|
|
|
DbgMsg1((" #OPEN_STREAM#: %s DCam, Status %x, pDevExt %x, pStrmEx %x, IsochDescriptorList is at %x\n",
|
|
pDevExt->pchVendorName, pSrb->Status, pDevExt, pDevExt->pStrmEx, &pDevExt->IsochDescriptorList));
|
|
|
|
ASSERT(pSrb->Status == STATUS_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
DCamCloseStream(
|
|
IN PHW_STREAM_REQUEST_BLOCK pSrb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when an CloseStream Srb request is received. We get this when calling user
|
|
application do a CloseHandle() on the pin connection handle. This can happen after
|
|
HwUninitialize().
|
|
|
|
Arguments:
|
|
|
|
pSrb - Pointer to Stream request block
|
|
|
|
Return Value:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
{
|
|
PDCAM_EXTENSION pDevExt;
|
|
PSTREAMEX pStrmEx;
|
|
PIRB pIrb;
|
|
|
|
PAGED_CODE();
|
|
|
|
pSrb->Status = STATUS_SUCCESS;
|
|
|
|
pDevExt = (PDCAM_EXTENSION) pSrb->HwDeviceExtension;
|
|
ASSERT(pDevExt);
|
|
if(!pDevExt) {
|
|
StreamClassDeviceNotification(DeviceRequestComplete, pSrb->HwDeviceExtension, pSrb);
|
|
return;
|
|
}
|
|
|
|
pStrmEx = (PSTREAMEX)pDevExt->pStrmEx;
|
|
ASSERT(pStrmEx);
|
|
if(!pStrmEx ) {
|
|
StreamClassDeviceNotification(DeviceRequestComplete, pSrb->HwDeviceExtension, pSrb);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// pDevExt->Irb might have been freed in HwUninitialize()
|
|
// due to Surprise removal; so we must use this:
|
|
//
|
|
pIrb = (PIRB) pSrb->SRBExtension;
|
|
|
|
|
|
//
|
|
// If it is still in use (setting it to stop failed?),
|
|
// we will diable ISO_ENABLE so other application can use it.
|
|
//
|
|
|
|
if(!pDevExt->bDevRemoved &&
|
|
DCamDeviceInUse(pIrb, pDevExt)) {
|
|
|
|
DbgMsg1(("DCamCloseStream: Is still in use! Disable it!\n"));
|
|
// Disable EnableISO
|
|
DCamIsoEnable(pIrb, pDevExt, FALSE);
|
|
}
|
|
|
|
|
|
//
|
|
// Save current state and free resource alllocaed in OpenStream()
|
|
//
|
|
DCamSetPropertyValuesToRegistry(pDevExt);
|
|
|
|
|
|
//
|
|
// Free Isoch resource and master clock
|
|
//
|
|
|
|
DCamFreeIsochResource (pDevExt, pIrb, TRUE);
|
|
if(pStrmEx->pVideoInfoHeader) {
|
|
ExFreePool(pStrmEx->pVideoInfoHeader);
|
|
pStrmEx->pVideoInfoHeader = NULL;
|
|
}
|
|
|
|
pStrmEx->hMasterClock = 0;
|
|
|
|
|
|
//
|
|
// If there are pening read, cancel them all.
|
|
//
|
|
if(pDevExt->PendingReadCount > 0) {
|
|
|
|
DCamCancelAllPackets(
|
|
pSrb,
|
|
pDevExt,
|
|
&pDevExt->PendingReadCount
|
|
);
|
|
|
|
pDevExt->pStrmEx = 0;
|
|
|
|
return; // SRB completed in CancelAllPackets
|
|
}
|
|
|
|
pDevExt->pStrmEx = 0;
|
|
|
|
StreamClassDeviceNotification(DeviceRequestComplete, pSrb->HwDeviceExtension, pSrb);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
DCamTimeoutHandler(
|
|
IN PHW_STREAM_REQUEST_BLOCK pSrb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when a packet has been in the minidriver too long (Srb->TimeoutCounter == 0).
|
|
We will cancel the SRB if we are in the RUN state; else set ->TimeoutCounter and return.
|
|
We assume the cancel SRB is serialized and in the same order as it is read. So this timeout is
|
|
applying to the head of the queue.
|
|
|
|
Arguments:
|
|
|
|
pSrb - Pointer to Stream request block that has timeout.
|
|
|
|
Return Value:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
|
|
{
|
|
PDCAM_EXTENSION pDevExt;
|
|
PSTREAMEX pStrmEx;
|
|
|
|
// Called from StreamClass at DisptchLevel
|
|
|
|
|
|
//
|
|
// We only expect stream SRB, but not device SRB.
|
|
//
|
|
|
|
if ( (pSrb->Flags & SRB_HW_FLAGS_STREAM_REQUEST) != SRB_HW_FLAGS_STREAM_REQUEST) {
|
|
ERROR_LOG(("DCamTimeoutHandler: Device SRB %x (cmd:%x) timed out!\n", pSrb, pSrb->Command));
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// StreamSRB only valid if we have a stream extension
|
|
//
|
|
|
|
pDevExt = (PDCAM_EXTENSION) pSrb->HwDeviceExtension;
|
|
ASSERT(pDevExt);
|
|
pStrmEx = (PSTREAMEX) pDevExt->pStrmEx;
|
|
|
|
if(!pStrmEx) {
|
|
ERROR_LOG(("DCamTimeoutHandler: Stream SRB %x timeout with pDevExt %x, pStrmEx %x\n", pSrb, pDevExt, pStrmEx));
|
|
ASSERT(pStrmEx);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Cancel IRP only if in RUN state, BUT...
|
|
// Note: if we are TIMEOUT and in RUN state, something is terribley wrong.
|
|
// but I guess that can happen when it is being suspended;
|
|
// so we will extend the time out for all states.
|
|
//
|
|
|
|
DbgMsg2(("\'DCamTimeoutHandler: pSrb %x, %s state, PendingReadCount %d.\n",
|
|
pSrb,
|
|
pStrmEx->KSState == KSSTATE_RUN ? "RUN" :
|
|
pStrmEx->KSState == KSSTATE_PAUSE ? "PAUSE":
|
|
pStrmEx->KSState == KSSTATE_STOP ? "STOP": "Unknown",
|
|
pDevExt->PendingReadCount));
|
|
|
|
// ASSERT(pStrmEx->KSState == KSSTATE_PAUSE);
|
|
|
|
|
|
//
|
|
// Reset Timeout counter, or we are going to get this call immediately.
|
|
//
|
|
|
|
pSrb->TimeoutCounter = pSrb->TimeoutOriginal;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
DCamStartListenCR(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP pIrp,
|
|
IN PDCAM_IO_CONTEXT pDCamIoContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns more processing required so the IO Manager will leave us alone
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to driver object created by system.
|
|
|
|
pIrp - Irp that just completed
|
|
|
|
pDCamIoContext - Context
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDCAM_EXTENSION pDevExt;
|
|
NTSTATUS Status;
|
|
PIRB pIrb;
|
|
PIO_STACK_LOCATION NextIrpStack;
|
|
|
|
#ifdef WAIT_FOR_SLOW_DEVICE
|
|
KeStallExecutionProcessor(5000); // 5 msec
|
|
#endif
|
|
|
|
DbgMsg2(("\'DCamStartListenCR: pIrp->IoStatus.Status=%x\n", pIrp->IoStatus.Status));
|
|
|
|
if(STATUS_SUCCESS != pIrp->IoStatus.Status) {
|
|
|
|
pDevExt = pDCamIoContext->pDevExt;
|
|
pIrb = pDCamIoContext->pIrb;
|
|
|
|
if(pDevExt->lRetries > 0) {
|
|
|
|
pDevExt->lRetries--;
|
|
DbgMsg1(("DCamStartListenCR: Try DCAM_RUNSTATE_SET_REQUEST_ISOCH_LISTEN again!\n"));
|
|
|
|
pIrb->FunctionNumber = REQUEST_ISOCH_LISTEN;
|
|
pIrb->Flags = 0;
|
|
pIrb->u.IsochListen.hResource = pDevExt->hResource;
|
|
pIrb->u.IsochListen.fulFlags = 0;
|
|
|
|
NextIrpStack = IoGetNextIrpStackLocation(pIrp);
|
|
NextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
NextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_1394_CLASS;
|
|
NextIrpStack->Parameters.Others.Argument1 = pIrb;
|
|
|
|
IoSetCompletionRoutine(
|
|
pIrp,
|
|
DCamStartListenCR,
|
|
pDCamIoContext,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
Status =
|
|
IoCallDriver(
|
|
pDevExt->BusDeviceObject,
|
|
pIrp);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
} else {
|
|
ERROR_LOG(("Start Listening has failed Status=%x; try again in next read.\n", pIrp->IoStatus.Status));
|
|
pDCamIoContext->pDevExt->bNeedToListen = TRUE;
|
|
}
|
|
}
|
|
|
|
DCamFreeIrbIrpAndContext(pDCamIoContext, pDCamIoContext->pIrb, pIrp);
|
|
|
|
// No StreamClassDeviceNotification() here since
|
|
// this is local initiated Irb (as part of AttachBufferCR().
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
** AdapterCompareGUIDsAndFormatSize()
|
|
**
|
|
** Checks for a match on the three GUIDs and FormatSize
|
|
**
|
|
** Arguments:
|
|
**
|
|
** IN DataRange1
|
|
** IN DataRange2
|
|
**
|
|
** Returns:
|
|
**
|
|
** TRUE if all elements match
|
|
** FALSE if any are different
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
|
|
BOOL
|
|
AdapterCompareGUIDsAndFormatSize(
|
|
IN PKSDATARANGE DataRange1,
|
|
IN PKSDATARANGE DataRange2)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
return (
|
|
IsEqualGUID (
|
|
&DataRange1->MajorFormat,
|
|
&DataRange2->MajorFormat) &&
|
|
IsEqualGUID (
|
|
&DataRange1->SubFormat,
|
|
&DataRange2->SubFormat) &&
|
|
IsEqualGUID (
|
|
&DataRange1->Specifier,
|
|
&DataRange2->Specifier) &&
|
|
(DataRange1->FormatSize == DataRange2->FormatSize));
|
|
}
|
|
|
|
/*
|
|
** AdapterVerifyFormat()
|
|
**
|
|
** Checks the validity of a format request by walking through the
|
|
** array of supported PKSDATARANGEs for a given stream.
|
|
**
|
|
** Arguments:
|
|
**
|
|
** pKSDataFormatVideoToVerify - pointer of a KS_DATAFORMAT_VIDEOINFOHEADER structure.
|
|
** StreamNumber - index of the stream being queried / opened.
|
|
**
|
|
** Returns:
|
|
**
|
|
** TRUE if the format is supported
|
|
** FALSE if the format cannot be suppored
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
|
|
BOOL
|
|
AdapterVerifyFormat(
|
|
ULONG VideoModesSupported,
|
|
PKSDATAFORMAT *pDCamStrmModesSupported,
|
|
PKS_DATAFORMAT_VIDEOINFOHEADER pDataFormatVideoToVerify,
|
|
int StreamNumber)
|
|
{
|
|
PKS_VIDEOINFOHEADER pVideoInfoHdrToVerify = &pDataFormatVideoToVerify->VideoInfoHeader;
|
|
PKSDATAFORMAT *paDataFormatsVideoAvail; // an array of PKSDATAFORMAT (not PKS_DATARANGE_VIDEO !!)
|
|
PKS_DATARANGE_VIDEO pDataRangeVideo;
|
|
KS_VIDEO_STREAM_CONFIG_CAPS *pConfigCaps;
|
|
PKS_BITMAPINFOHEADER pbmiHeader,
|
|
pbmiHeaderToVerify;
|
|
int j;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Make sure the stream index is valid
|
|
// We only has one capure pin/stream (index 0).
|
|
//
|
|
if (StreamNumber >= 1) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Get the pointer to the array of available formats
|
|
//
|
|
paDataFormatsVideoAvail = &pDCamStrmModesSupported[0]; // &pDevExt->DCamStrmModes[0];
|
|
|
|
|
|
//
|
|
// Walk the array, searching for a match
|
|
//
|
|
for (j = 0; j < (LONG) VideoModesSupported; j++, paDataFormatsVideoAvail++) {
|
|
|
|
pDataRangeVideo = (PKS_DATARANGE_VIDEO) *paDataFormatsVideoAvail;
|
|
|
|
//
|
|
// Check for matching size, Major Type, Sub Type, and Specifier
|
|
//
|
|
|
|
//
|
|
// Check for matching size, Major Type, Sub Type, and Specifier
|
|
//
|
|
|
|
if (!IsEqualGUID (&pDataRangeVideo->DataRange.MajorFormat,
|
|
&pDataFormatVideoToVerify->DataFormat.MajorFormat)) {
|
|
DbgMsg2(("\'%d) AdapterVerifyFormat: MajorFormat mismatch!\n", j));
|
|
continue;
|
|
}
|
|
|
|
if (!IsEqualGUID (&pDataRangeVideo->DataRange.SubFormat,
|
|
&pDataFormatVideoToVerify->DataFormat.SubFormat)) {
|
|
DbgMsg2(("\'%d) AdapterVerifyFormat: SubFormat mismatch!\n", j));
|
|
continue;
|
|
}
|
|
|
|
if (!IsEqualGUID (&pDataRangeVideo->DataRange.Specifier,
|
|
&pDataFormatVideoToVerify->DataFormat.Specifier)) {
|
|
DbgMsg2(("\'%d) AdapterVerifyFormat: Specifier mismatch!\n", j));
|
|
continue;
|
|
}
|
|
|
|
if(pDataFormatVideoToVerify->DataFormat.FormatSize <
|
|
sizeof(KS_DATAFORMAT_VIDEOINFOHEADER))
|
|
continue;
|
|
|
|
//
|
|
// Only if we get here, we are certain that we are dealing with video info.
|
|
//
|
|
|
|
// We do not support scaling or cropping so the dimension
|
|
// (biWidth, biHeight, biBitCount and biCompression)
|
|
// must match.
|
|
//
|
|
pbmiHeader = &pDataRangeVideo->VideoInfoHeader.bmiHeader;
|
|
pbmiHeaderToVerify = &pDataFormatVideoToVerify->VideoInfoHeader.bmiHeader;
|
|
|
|
if(pbmiHeader->biWidth != pbmiHeaderToVerify->biWidth ||
|
|
pbmiHeader->biHeight != pbmiHeaderToVerify->biHeight ||
|
|
pbmiHeader->biBitCount != pbmiHeaderToVerify->biBitCount ||
|
|
pbmiHeader->biCompression != pbmiHeaderToVerify->biCompression
|
|
) {
|
|
|
|
DbgMsg2(("AdapterVerifyFormat: Supported: %dx%dx%d [%x] != ToVerify: %dx%dx%d [%x]\n",
|
|
pbmiHeader->biWidth, pbmiHeader->biHeight, pbmiHeader->biBitCount, pbmiHeader->biCompression,
|
|
pbmiHeaderToVerify->biWidth, pbmiHeaderToVerify->biHeight, pbmiHeaderToVerify->biBitCount, pbmiHeaderToVerify->biCompression));
|
|
continue;
|
|
}
|
|
|
|
// biSizeImage must be to be BIG ENOUGH
|
|
if(pbmiHeaderToVerify->biSizeImage < pbmiHeader->biSizeImage) {
|
|
|
|
DbgMsg2(("AdapterVerifyFormat: biSizeImageToVerify %d < required %x\n",
|
|
pbmiHeaderToVerify->biSizeImage, pbmiHeader->biSizeImage));
|
|
continue;
|
|
}
|
|
|
|
// Frame rate needs to be within range
|
|
pConfigCaps = &pDataRangeVideo->ConfigCaps;
|
|
if(pDataFormatVideoToVerify->VideoInfoHeader.AvgTimePerFrame > pConfigCaps->MaxFrameInterval &&
|
|
pDataFormatVideoToVerify->VideoInfoHeader.AvgTimePerFrame < pConfigCaps->MinFrameInterval) {
|
|
|
|
DbgMsg2(("\'format index %d) AdapterVerifyFormat: Frame rate %ld is not within range(%ld, %ld)!\n",
|
|
j, pDataFormatVideoToVerify->VideoInfoHeader.AvgTimePerFrame,
|
|
pConfigCaps->MaxFrameInterval, pConfigCaps->MinFrameInterval));
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// The format passed all of the tests, so we support it
|
|
//
|
|
|
|
DbgMsg2(("\'(format idx %d) AdapterVerifyFormat: Verify!! Width=%d, Height=%d, biBitCount=%d, biSizeImage=%d\n", j,
|
|
pbmiHeaderToVerify->biWidth, pbmiHeaderToVerify->biHeight, pbmiHeaderToVerify->biBitCount,pbmiHeaderToVerify->biSizeImage));
|
|
DbgMsg2(("AdapterVerifyFormat: AvgTimePerFrame = %ld\n", pDataFormatVideoToVerify->VideoInfoHeader.AvgTimePerFrame));
|
|
DbgMsg2(("AdapterVerifyFormat: (Max %ld\n", pConfigCaps->MaxFrameInterval));
|
|
DbgMsg2(("AdapterVerifyFormat: Min %ld)\n", pConfigCaps->MinFrameInterval));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// The format requested didn't match any of our listed ranges,
|
|
// so refuse the connection.
|
|
//
|
|
DbgMsg2(("AdapterVerifyFormat: This format is not supported!\n"));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
** AdapterFormatFromRange()
|
|
**
|
|
** Examine the given data format with many key fields and
|
|
** return a complete data format that can be used to open a stream.
|
|
**
|
|
** Arguments:
|
|
**
|
|
** IN PHW_STREAM_REQUEST_BLOCK Srb
|
|
**
|
|
** Returns:
|
|
**
|
|
** TRUE if the format is supported
|
|
** FALSE if the format cannot be suppored
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
BOOL
|
|
AdapterFormatFromRange(
|
|
IN PHW_STREAM_REQUEST_BLOCK Srb)
|
|
{
|
|
PDCAM_EXTENSION pDevExt = (PDCAM_EXTENSION) Srb->HwDeviceExtension;
|
|
PSTREAM_DATA_INTERSECT_INFO IntersectInfo;
|
|
PKSDATARANGE DataRange,
|
|
*pAvailableFormats; // KSDATARANGE == KSDATAFORMAT
|
|
PKS_DATARANGE_VIDEO DataRangeVideoToVerify,
|
|
DataRangeVideo;
|
|
PKS_BITMAPINFOHEADER pbmiHeader,
|
|
pbmiHeaderToVerify;
|
|
ULONG FormatSize;
|
|
BOOL MatchFound = FALSE;
|
|
ULONG j;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
Srb->Status = STATUS_SUCCESS;
|
|
IntersectInfo = Srb->CommandData.IntersectInfo;
|
|
DataRange = IntersectInfo->DataRange;
|
|
DbgMsg2(("IntersectIfo->DataFormatBuffer=%x, size=%d\n", IntersectInfo->DataFormatBuffer, IntersectInfo->SizeOfDataFormatBuffer));
|
|
|
|
|
|
//
|
|
// Check that the stream number is valid
|
|
// We support only one capture pin/stream (index 0)
|
|
//
|
|
|
|
if (IntersectInfo->StreamNumber >= 1) {
|
|
|
|
Srb->Status = STATUS_NOT_IMPLEMENTED;
|
|
ERROR_LOG(("\'AdapterFormatFromRange: StreamNumber(=%d) is not implemented.\n", IntersectInfo->StreamNumber));
|
|
ASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// Get the pointer to the array of available formats
|
|
//
|
|
|
|
pAvailableFormats = &pDevExt->DCamStrmModes[0];
|
|
|
|
|
|
//
|
|
// Walk the formats supported by the stream searching for a match
|
|
// of the three GUIDs which together define a DATARANGE
|
|
//
|
|
|
|
DataRangeVideoToVerify = (PKS_DATARANGE_VIDEO) DataRange;
|
|
|
|
for (j = 0; j < pDevExt->ModeSupported; j++, pAvailableFormats++) {
|
|
|
|
DataRangeVideo = (PKS_DATARANGE_VIDEO) *pAvailableFormats;
|
|
|
|
//
|
|
// STREAM_DATA_INTERSECT_INFO
|
|
// [IN] ULONG StreamNumber;
|
|
// [IN] PKSDATARANGE DataRange;
|
|
// [OUT] PVOID DataFormatBuffer; // == PKS_DATAFORMAT_VIDEOINFOHEADER
|
|
// [OUT] ULONG SizeOfDataFormatBuffer;
|
|
//
|
|
|
|
//
|
|
// KS_DATAFORMAT_VIDEOINFOHEADER:
|
|
// fields marked with 'm' must match;
|
|
// marked with 'r' must within range;
|
|
// marked with 'f' is filled by us
|
|
//
|
|
// KSDATAFORMAT == KSDATARANGE
|
|
// m ULONG FormatSize;
|
|
// ULONG Flags;
|
|
// ULONG SampleSize;
|
|
// ULONG Reserved;
|
|
// m GUID MajorFormat;
|
|
// m GUID SubFormat;
|
|
// m GUID Specifier;.
|
|
// m BOOL bFixedSizeSamples; // all samples same size?
|
|
// m BOOL bTemporalCompression; // all I frames?
|
|
// m DWORD StreamDescriptionFlags; // KS_VIDEO_DESC_*
|
|
// m DWORD MemoryAllocationFlags; // KS_VIDEO_ALLOC_*
|
|
// m KS_VIDEO_STREAM_CONFIG_CAPS ConfigCaps;
|
|
// KS_VIDEOINFOHEADER
|
|
// RECT rcSource; // The bit we really want to use
|
|
// RECT rcTarget; // Where the video should go
|
|
// DWORD dwBitRate; // Approximate bit data rate
|
|
// DWORD dwBitErrorRate; // Bit error rate for this stream
|
|
// r/f REFERENCE_TIME AvgTimePerFrame; // Average time per frame (100ns units)
|
|
// KS_BITMAPINFOHEADER bmiHeader;
|
|
// DWORD biSize;
|
|
// m LONG biWidth;
|
|
// m LONG biHeight;
|
|
// WORD biPlanes;
|
|
// m WORD biBitCount;
|
|
// m DWORD biCompression;
|
|
// f DWORD biSizeImage;
|
|
// LONG biXPelsPerMeter;
|
|
// LONG biYPelsPerMeter;
|
|
// DWORD biClrUsed;
|
|
// DWORD biClrImportant;
|
|
//
|
|
|
|
// Verify that it is a VIDEO format/range.
|
|
if (!AdapterCompareGUIDsAndFormatSize((PKSDATARANGE)DataRangeVideoToVerify, (PKSDATARANGE)DataRangeVideo)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// It is valid video format/range; now check that the other fields match
|
|
//
|
|
if ((DataRangeVideoToVerify->bFixedSizeSamples != DataRangeVideo->bFixedSizeSamples) ||
|
|
(DataRangeVideoToVerify->bTemporalCompression != DataRangeVideo->bTemporalCompression) ||
|
|
(DataRangeVideoToVerify->StreamDescriptionFlags != DataRangeVideo->StreamDescriptionFlags) ||
|
|
(DataRangeVideoToVerify->MemoryAllocationFlags != DataRangeVideo->MemoryAllocationFlags) ||
|
|
(RtlCompareMemory (&DataRangeVideoToVerify->ConfigCaps, &DataRangeVideo->ConfigCaps, sizeof(KS_VIDEO_STREAM_CONFIG_CAPS)) != sizeof(KS_VIDEO_STREAM_CONFIG_CAPS))) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// We do not support scaling or cropping so the dimension
|
|
// (biWidth, biHeight, biBitCount and biCompression)
|
|
// must match, and we will filled in the biSizeImage and others.
|
|
//
|
|
pbmiHeader = &DataRangeVideo->VideoInfoHeader.bmiHeader;
|
|
pbmiHeaderToVerify = &DataRangeVideoToVerify->VideoInfoHeader.bmiHeader;
|
|
|
|
if(pbmiHeader->biWidth != pbmiHeaderToVerify->biWidth ||
|
|
abs(pbmiHeader->biHeight) != abs(pbmiHeaderToVerify->biHeight) ||
|
|
pbmiHeader->biBitCount != pbmiHeaderToVerify->biBitCount ||
|
|
pbmiHeader->biCompression != pbmiHeaderToVerify->biCompression
|
|
) {
|
|
|
|
DbgMsg1(("AdapterFormatFromRange: Supported: %dx%dx%d [%x] != ToVerify: %dx%dx%d [%x]\n",
|
|
pbmiHeader->biWidth, pbmiHeader->biHeight, pbmiHeader->biBitCount, pbmiHeader->biCompression,
|
|
pbmiHeaderToVerify->biWidth, pbmiHeaderToVerify->biHeight, pbmiHeaderToVerify->biBitCount, pbmiHeaderToVerify->biCompression));
|
|
continue;
|
|
}
|
|
|
|
|
|
// MATCH FOUND!
|
|
MatchFound = TRUE;
|
|
|
|
|
|
|
|
// KS_DATAFORMAT_VIDEOINFOHEADER
|
|
// KSDATAFORMAT DataFormat;
|
|
// KS_VIDEOINFOHEADER VideoInfoHeader;
|
|
FormatSize = sizeof (KSDATAFORMAT) + KS_SIZE_VIDEOHEADER (&DataRangeVideo->VideoInfoHeader);
|
|
|
|
//
|
|
// 1st query: Srb->ActualBytesTransferred = FormatSize
|
|
//
|
|
|
|
if(IntersectInfo->SizeOfDataFormatBuffer == 0) {
|
|
|
|
Srb->Status = STATUS_BUFFER_OVERFLOW;
|
|
// We actually have not returned this much data,
|
|
// this "size" will be used by Ksproxy to send down
|
|
// a buffer of that size in next query.
|
|
Srb->ActualBytesTransferred = FormatSize;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// 2nd time: pass back the format information
|
|
//
|
|
|
|
if (IntersectInfo->SizeOfDataFormatBuffer < FormatSize) {
|
|
Srb->Status = STATUS_BUFFER_TOO_SMALL;
|
|
DbgMsg2(("IntersectInfo->SizeOfDataFormatBuffer=%d, FormatSize=%d\n", IntersectInfo->SizeOfDataFormatBuffer, FormatSize));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// A match is found, Copy from our supported/matched data range and set frame rate:
|
|
// KS_DATAFORMAT_VIDEOINFOHEADER
|
|
// KSDATAFORMAT DataFormat;
|
|
// KS_VIDEOINFOHEADER VideoInfoHeader;
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
&((PKS_DATAFORMAT_VIDEOINFOHEADER)IntersectInfo->DataFormatBuffer)->DataFormat,
|
|
&DataRangeVideo->DataRange,
|
|
sizeof (KSDATAFORMAT));
|
|
|
|
RtlCopyMemory(
|
|
&((PKS_DATAFORMAT_VIDEOINFOHEADER) IntersectInfo->DataFormatBuffer)->VideoInfoHeader, // KS_VIDEOINFOHEADER
|
|
&DataRangeVideo->VideoInfoHeader, // KS_VIDEOINFOHEADER
|
|
KS_SIZE_VIDEOHEADER (&DataRangeVideo->VideoInfoHeader)); // Use KS_SIZE_VIDEOHEADER() since this is variable size
|
|
|
|
//
|
|
// Special atttention to these two fields: biSizeImage and AvgTimePerFrame.
|
|
// We do not scale or stretch so biSizeImage is fixed.
|
|
// However, AvgTimePerFrame (FrameRate) can/need to be within (ConfigCaps.MinFrameInterval, ConfigCaps.MaxFrameInterval)
|
|
//
|
|
|
|
if (DataRangeVideoToVerify->VideoInfoHeader.AvgTimePerFrame > DataRangeVideoToVerify->ConfigCaps.MaxFrameInterval ||
|
|
DataRangeVideoToVerify->VideoInfoHeader.AvgTimePerFrame < DataRangeVideoToVerify->ConfigCaps.MinFrameInterval) {
|
|
|
|
((PKS_DATAFORMAT_VIDEOINFOHEADER) IntersectInfo->DataFormatBuffer)->VideoInfoHeader.AvgTimePerFrame =
|
|
DataRangeVideo->VideoInfoHeader.AvgTimePerFrame;
|
|
DbgMsg2(("AdapterFormatFromRange: out of range; so set it to default (%ld)\n",DataRangeVideo->VideoInfoHeader.AvgTimePerFrame));
|
|
} else {
|
|
|
|
((PKS_DATAFORMAT_VIDEOINFOHEADER) IntersectInfo->DataFormatBuffer)->VideoInfoHeader.AvgTimePerFrame =
|
|
DataRangeVideoToVerify->VideoInfoHeader.AvgTimePerFrame;
|
|
}
|
|
|
|
((PKSDATAFORMAT)IntersectInfo->DataFormatBuffer)->FormatSize = FormatSize;
|
|
Srb->ActualBytesTransferred = FormatSize;
|
|
|
|
DbgMsg2(("AdapterFormatFromRange: match found: [%x], %dx%dx%d=%d, AvgTimePerFrame %ld\n",
|
|
pbmiHeader->biCompression, pbmiHeader->biWidth, pbmiHeader->biHeight, pbmiHeader->biBitCount, pbmiHeader->biSizeImage,
|
|
((PKS_DATAFORMAT_VIDEOINFOHEADER) IntersectInfo->DataFormatBuffer)->VideoInfoHeader.AvgTimePerFrame));
|
|
break;
|
|
|
|
} // End of loop on all formats for this stream
|
|
|
|
if(!MatchFound) {
|
|
|
|
DbgMsg2(("AdapterFormatFromRange: No match !!\n"));
|
|
Srb->Status = STATUS_NO_MATCH;
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
DCamBuildFormatTable(
|
|
PDCAM_EXTENSION pDevExt,
|
|
PIRB pIrb
|
|
)
|
|
/*
|
|
Description:
|
|
|
|
Query Video format and mode supported by the camera.
|
|
|
|
Return:
|
|
|
|
TRUE: support at least one mode
|
|
FALSE: failed to read mode register or do not support any mode.
|
|
*/
|
|
{
|
|
// Initialize
|
|
pDevExt->ModeSupported = 0;
|
|
|
|
if(DCamGetVideoMode(pDevExt, pIrb)) {
|
|
|
|
#ifdef SUPPORT_YUV444
|
|
// Mode0: 160x120 (4:4:4)
|
|
if(pDevExt->DCamVModeInq0.VMode.Mode0 == 1 && pDevExt->DecoderDCamVModeInq0.VMode.Mode0 == 1) {
|
|
pDevExt->DCamStrmModes[pDevExt->ModeSupported] = (PKSDATAFORMAT) &DCAM_StreamMode_0;
|
|
pDevExt->ModeSupported++;
|
|
}
|
|
#endif
|
|
// Mode1: 320x240 (4:2:2)
|
|
if(pDevExt->DCamVModeInq0.VMode.Mode1 == 1 && pDevExt->DecoderDCamVModeInq0.VMode.Mode1 == 1) {
|
|
pDevExt->DCamStrmModes[pDevExt->ModeSupported] = (PKSDATAFORMAT) &DCAM_StreamMode_1;
|
|
pDevExt->ModeSupported++;
|
|
}
|
|
|
|
#ifdef SUPPORT_YUV411
|
|
// Mode2: 640x480 (4:1:1)
|
|
if(pDevExt->DCamVModeInq0.VMode.Mode2 == 1 && pDevExt->DecoderDCamVModeInq0.VMode.Mode2 == 1) {
|
|
pDevExt->DCamStrmModes[pDevExt->ModeSupported] = (PKSDATAFORMAT) &DCAM_StreamMode_2;
|
|
pDevExt->ModeSupported++;
|
|
}
|
|
#endif
|
|
|
|
// Mode3: 640x480 (4:2:2)
|
|
if(pDevExt->DCamVModeInq0.VMode.Mode3 == 1 && pDevExt->DecoderDCamVModeInq0.VMode.Mode3 == 1) {
|
|
pDevExt->DCamStrmModes[pDevExt->ModeSupported] = (PKSDATAFORMAT) &DCAM_StreamMode_3;
|
|
pDevExt->ModeSupported++;
|
|
}
|
|
|
|
#ifdef SUPPORT_RGB24
|
|
// Mode4: 640x480 (RGB24)
|
|
if(pDevExt->DCamVModeInq0.VMode.Mode4 == 1 && pDevExt->DecoderDCamVModeInq0.VMode.Mode4 == 1) {
|
|
pDevExt->DCamStrmModes[pDevExt->ModeSupported] = (PKSDATAFORMAT) &DCAM_StreamMode_4;
|
|
pDevExt->ModeSupported++;
|
|
}
|
|
#endif
|
|
|
|
#ifdef SUPPORT_YUV800
|
|
// Mode5: 640x480 (Y800)
|
|
if(pDevExt->DCamVModeInq0.VMode.Mode5 == 1 && pDevExt->DecoderDCamVModeInq0.VMode.Mode5 == 1) {
|
|
pDevExt->DCamStrmModes[pDevExt->ModeSupported] = (PKSDATAFORMAT) &DCAM_StreamMode_5;
|
|
pDevExt->ModeSupported++;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
DbgMsg1(("\'Support %d modes; ->DCamStrmModes[]:%x\n", pDevExt->ModeSupported, &pDevExt->DCamStrmModes[0]));
|
|
ASSERT(pDevExt->ModeSupported > 0);
|
|
|
|
return (pDevExt->ModeSupported > 0);
|
|
}
|
|
|
|
/*
|
|
** VideoGetProperty()
|
|
**
|
|
** Routine to process video property requests
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Srb - pointer to the stream request block for properties
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
|
|
VOID
|
|
VideoGetProperty(
|
|
PHW_STREAM_REQUEST_BLOCK Srb)
|
|
{
|
|
PSTREAM_PROPERTY_DESCRIPTOR pSPD = Srb->CommandData.PropertyInfo;
|
|
|
|
|
|
// preset to success
|
|
|
|
Srb->Status = STATUS_SUCCESS;
|
|
|
|
if (IsEqualGUID (&KSPROPSETID_Connection, &pSPD->Property->Set)) {
|
|
VideoStreamGetConnectionProperty (Srb);
|
|
} else if (IsEqualGUID (&PROPSETID_VIDCAP_DROPPEDFRAMES, &pSPD->Property->Set)) {
|
|
VideoStreamGetDroppedFramesProperty (Srb);
|
|
} else {
|
|
Srb->Status = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
** VideoGetState()
|
|
**
|
|
** Gets the current state of the requested stream
|
|
**
|
|
** Arguments:
|
|
**
|
|
** Srb - pointer to the stream request block for properties
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
|
|
VOID
|
|
VideoGetState(
|
|
PHW_STREAM_REQUEST_BLOCK Srb)
|
|
{
|
|
PDCAM_EXTENSION pDevExt = (PDCAM_EXTENSION) Srb->HwDeviceExtension;
|
|
PSTREAMEX pStrmEx = pDevExt->pStrmEx;
|
|
|
|
PAGED_CODE();
|
|
|
|
DbgMsg2(("\'%d:%s) VideoGetState: KSSTATE=%s.\n",
|
|
pDevExt->idxDev, pDevExt->pchVendorName,
|
|
pStrmEx->KSState == KSSTATE_STOP ? "STOP" :
|
|
pStrmEx->KSState == KSSTATE_PAUSE ? "PAUSE" :
|
|
pStrmEx->KSState == KSSTATE_RUN ? "RUN" : "ACQUIRE"));
|
|
|
|
Srb->CommandData.StreamState = pStrmEx->KSState;
|
|
Srb->ActualBytesTransferred = sizeof (KSSTATE);
|
|
|
|
// A very odd rule:
|
|
// When transitioning from stop to pause, DShow tries to preroll
|
|
// the graph. Capture sources can't preroll, and indicate this
|
|
// by returning VFW_S_CANT_CUE in user mode. To indicate this
|
|
// condition from drivers, they must return ERROR_NO_DATA_DETECTED
|
|
|
|
Srb->Status = STATUS_SUCCESS;
|
|
|
|
|
|
if (pStrmEx->KSState == KSSTATE_PAUSE) {
|
|
Srb->Status = STATUS_NO_DATA_DETECTED;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
VideoStreamGetConnectionProperty (
|
|
PHW_STREAM_REQUEST_BLOCK Srb)
|
|
{
|
|
PDCAM_EXTENSION pDevExt = (PDCAM_EXTENSION) Srb->HwDeviceExtension;
|
|
PSTREAM_PROPERTY_DESCRIPTOR pSPD = Srb->CommandData.PropertyInfo;
|
|
ULONG Id = pSPD->Property->Id; // index of the property
|
|
PSTREAMEX pStrmEx = (PSTREAMEX) pDevExt->pStrmEx;
|
|
ASSERT(pStrmEx == (PSTREAMEX)Srb->StreamObject->HwStreamExtension);
|
|
|
|
PAGED_CODE();
|
|
|
|
switch (Id) {
|
|
|
|
case KSPROPERTY_CONNECTION_ALLOCATORFRAMING:
|
|
|
|
if (pStrmEx->pVideoInfoHeader) {
|
|
|
|
PKSALLOCATOR_FRAMING Framing =
|
|
(PKSALLOCATOR_FRAMING) pSPD->PropertyInfo;
|
|
Framing->RequirementsFlags =
|
|
KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY |
|
|
KSALLOCATOR_REQUIREMENTF_INPLACE_MODIFIER |
|
|
KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY;
|
|
Framing->PoolType = PagedPool;
|
|
Framing->Frames = MAX_BUFFERS_SUPPLIED;
|
|
Framing->FrameSize = pStrmEx->pVideoInfoHeader->bmiHeader.biSizeImage;
|
|
Framing->FileAlignment = FILE_BYTE_ALIGNMENT; // 0: Basically no alignment by spec
|
|
Framing->Reserved = 0;
|
|
Srb->ActualBytesTransferred = sizeof (KSALLOCATOR_FRAMING);
|
|
Srb->Status = STATUS_SUCCESS;
|
|
DbgMsg2(("\'VideoStreamGetConnectionProperty: status=0x%x, Alignment %d, Frame %d, FrameSize %d\n",
|
|
Srb->Status, Framing->FileAlignment+1, Framing->Frames, Framing->FrameSize));
|
|
|
|
} else {
|
|
|
|
Srb->Status = STATUS_INVALID_PARAMETER;
|
|
DbgMsg2(("\'VideoStreamGetConnectionProperty: status=0x\n",Srb->Status));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ERROR_LOG(("VideoStreamGetConnectionProperty: Unsupported property id=%d\n",Id));
|
|
ASSERT(FALSE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** VideoStreamGetConnectionProperty()
|
|
**
|
|
** Gets the current state of the requested stream
|
|
**
|
|
** Arguments:
|
|
**
|
|
** pSrb - pointer to the stream request block for properties
|
|
**
|
|
** Returns:
|
|
**
|
|
** Side Effects: none
|
|
*/
|
|
|
|
VOID
|
|
VideoStreamGetDroppedFramesProperty(
|
|
PHW_STREAM_REQUEST_BLOCK Srb
|
|
)
|
|
{
|
|
PSTREAMEX pStrmEx = (PSTREAMEX)Srb->StreamObject->HwStreamExtension;
|
|
PSTREAM_PROPERTY_DESCRIPTOR pSPD = Srb->CommandData.PropertyInfo;
|
|
ULONG Id = pSPD->Property->Id; // index of the property
|
|
ULONGLONG tmStream;
|
|
|
|
PAGED_CODE();
|
|
|
|
switch (Id) {
|
|
|
|
case KSPROPERTY_DROPPEDFRAMES_CURRENT:
|
|
{
|
|
|
|
PKSPROPERTY_DROPPEDFRAMES_CURRENT_S pDroppedFrames =
|
|
(PKSPROPERTY_DROPPEDFRAMES_CURRENT_S) pSPD->PropertyInfo;
|
|
|
|
if (pStrmEx->hMasterClock) {
|
|
|
|
tmGetStreamTime(Srb, pStrmEx, &tmStream);
|
|
|
|
if (tmStream < pStrmEx->FirstFrameTime) {
|
|
DbgMsg2(("\'*DroppedFP: Tm(%dms) < 1stFrameTm(%d)\n",
|
|
(LONG) tmStream/10000, (LONG)pStrmEx->FirstFrameTime));
|
|
pDroppedFrames->DropCount = 0;
|
|
} else {
|
|
pDroppedFrames->DropCount = (tmStream - pStrmEx->FirstFrameTime)
|
|
/ pStrmEx->pVideoInfoHeader->AvgTimePerFrame + 1 - pStrmEx->FrameCaptured;
|
|
}
|
|
|
|
if (pDroppedFrames->DropCount < 0)
|
|
pDroppedFrames->DropCount = 0;
|
|
|
|
} else {
|
|
pDroppedFrames->DropCount = 0;
|
|
}
|
|
|
|
// Update our drop frame here. "pDroppedFrames->DropCount" is return when a frame is returned.
|
|
if (pDroppedFrames->DropCount > pStrmEx->FrameInfo.DropCount) {
|
|
pStrmEx->FrameInfo.DropCount = pDroppedFrames->DropCount;
|
|
//pStrmEx->bDiscontinue = TRUE;
|
|
} else {
|
|
pDroppedFrames->DropCount = pStrmEx->FrameInfo.DropCount;
|
|
}
|
|
|
|
pDroppedFrames->AverageFrameSize = pStrmEx->pVideoInfoHeader->bmiHeader.biSizeImage;
|
|
pDroppedFrames->PictureNumber = pStrmEx->FrameCaptured + pDroppedFrames->DropCount;
|
|
|
|
// Correction if no picture has been successfully capture in the IsochCallback.
|
|
if (pDroppedFrames->PictureNumber < pDroppedFrames->DropCount)
|
|
pDroppedFrames->PictureNumber = pDroppedFrames->DropCount;
|
|
|
|
DbgMsg2(("\'*DroppedFP: tm(%d); Pic#(%d)=?Cap(%d)+Drp(%d)\n",
|
|
(ULONG) tmStream/10000,
|
|
(LONG) pDroppedFrames->PictureNumber,
|
|
(LONG) pStrmEx->FrameCaptured,
|
|
(LONG) pDroppedFrames->DropCount));
|
|
|
|
Srb->ActualBytesTransferred = sizeof (KSPROPERTY_DROPPEDFRAMES_CURRENT_S);
|
|
Srb->Status = STATUS_SUCCESS;
|
|
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ERROR_LOG(("VideoStreamGetDroppedFramesProperty: Unsupported property id=%d\n",Id));
|
|
ASSERT(FALSE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
VideoIndicateMasterClock(
|
|
PHW_STREAM_REQUEST_BLOCK Srb)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Assign a master clock for this stream.
|
|
|
|
Arguments:
|
|
|
|
pSrb - Pointer to Stream request block
|
|
|
|
Return Value:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
PDCAM_EXTENSION pDevExt = (PDCAM_EXTENSION) Srb->HwDeviceExtension;
|
|
PSTREAMEX pStrmEx = (PSTREAMEX) pDevExt->pStrmEx;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(pStrmEx == (PSTREAMEX)Srb->StreamObject->HwStreamExtension);
|
|
|
|
pStrmEx->hMasterClock = Srb->CommandData.MasterClockHandle;
|
|
|
|
DbgMsg2(("\'%d:%s)VideoIndicateMasterClock: hMasterClock = 0x%x\n", pDevExt->idxDev, pDevExt->pchVendorName, pStrmEx->hMasterClock));
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
DCamReceivePacket(
|
|
IN PHW_STREAM_REQUEST_BLOCK pSrb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is where most of the interesting Stream requests come to us
|
|
|
|
Arguments:
|
|
|
|
pSrb - Pointer to Stream request block
|
|
|
|
Return Value:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION IrpStack;
|
|
PDCAM_EXTENSION pDevExt = (PDCAM_EXTENSION) pSrb->HwDeviceExtension;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
pSrb->Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Switch on the command within the Srb itself
|
|
//
|
|
|
|
switch (pSrb->Command) {
|
|
|
|
case SRB_INITIALIZE_DEVICE: // Per device
|
|
|
|
pSrb->Status = DCamHwInitialize(pSrb);
|
|
break;
|
|
|
|
case SRB_INITIALIZATION_COMPLETE:
|
|
|
|
pSrb->Status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
case SRB_GET_STREAM_INFO: // Per Device
|
|
|
|
//
|
|
// this is a request for the driver to enumerate requested streams
|
|
//
|
|
DCamGetStreamInfo(pSrb);
|
|
break;
|
|
|
|
case SRB_OPEN_STREAM: // Per stream
|
|
|
|
DCamOpenStream(pSrb);
|
|
break;
|
|
|
|
case SRB_CLOSE_STREAM: // Per Stream
|
|
DbgMsg1((" #CLOSE_STREAM# (%d) camera: pSrb %x, pDevExt %x, pStrmEx %x, PendingRead %d\n",
|
|
pDevExt->idxDev, pSrb, pDevExt, pDevExt->pStrmEx, pDevExt->PendingReadCount));
|
|
DCamCloseStream(pSrb);
|
|
return; // SRB will finish asynchronously in its IoCompletionRoutine if there are pending reads to cancel.
|
|
|
|
case SRB_SURPRISE_REMOVAL:
|
|
|
|
DbgMsg1((" #SURPRISE_REMOVAL# (%d) camera: pSrb %x, pDevExt %x, pStrmEx %x, PendingRead %d\n",
|
|
pDevExt->idxDev, pSrb, pDevExt, pDevExt->pStrmEx, pDevExt->PendingReadCount));
|
|
DCamSurpriseRemoval(pSrb);
|
|
return; // SRB will finish asynchronously in its IoCompletionRoutine.
|
|
|
|
case SRB_UNKNOWN_DEVICE_COMMAND:
|
|
|
|
//
|
|
// We might be interested in unknown commands if they pertain
|
|
// to bus resets. We will reallocate resource (bandwidth and
|
|
// channel) if this device is streaming.
|
|
//
|
|
IrpStack = IoGetCurrentIrpStackLocation(pSrb->Irp);
|
|
|
|
if (IrpStack->MajorFunction == IRP_MJ_PNP)
|
|
DCamProcessPnpIrp(pSrb, IrpStack, pDevExt);
|
|
else
|
|
pSrb->Status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
|
|
case SRB_UNINITIALIZE_DEVICE: // Per device
|
|
|
|
DbgMsg1((" #UNINITIALIZE_DEVICE# (%d) %s camera : pSrb %x, pDevExt %x, pStrmEx %x\n",
|
|
pDevExt->idxDev, pDevExt->pchVendorName, pSrb, pDevExt, pDevExt->pStrmEx));
|
|
pSrb->Status = DCamHwUnInitialize(pSrb);
|
|
break;
|
|
|
|
case SRB_GET_DATA_INTERSECTION:
|
|
|
|
//
|
|
// Return a format, given a range
|
|
//
|
|
AdapterFormatFromRange(pSrb);
|
|
StreamClassDeviceNotification(DeviceRequestComplete, pSrb->HwDeviceExtension, pSrb);
|
|
return;
|
|
|
|
case SRB_CHANGE_POWER_STATE:
|
|
|
|
DCamChangePower(pSrb);
|
|
break;
|
|
|
|
// VideoProcAmp and CameraControl requests
|
|
case SRB_GET_DEVICE_PROPERTY:
|
|
|
|
AdapterGetProperty(pSrb);
|
|
break;
|
|
|
|
case SRB_SET_DEVICE_PROPERTY:
|
|
|
|
AdapterSetProperty(pSrb);
|
|
break;
|
|
|
|
case SRB_PAGING_OUT_DRIVER:
|
|
|
|
// Once we register bus reset, we can be called at any time;
|
|
// So we cannot page out.
|
|
pSrb->Status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
DbgMsg1(("DCamReceivePacket: entry with unknown and unsupported SRB command 0x%x\n", pSrb->Command));
|
|
//
|
|
// this is a request that we do not understand. Indicate invalid
|
|
// command and complete the request
|
|
//
|
|
|
|
pSrb->Status = STATUS_NOT_IMPLEMENTED;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// NOTE:
|
|
//
|
|
// all of the commands that we do, or do not understand can all be completed
|
|
// synchronously at this point, so we can use a common callback routine here.
|
|
// If any of the above commands require asynchronous processing, this will
|
|
// have to change
|
|
//
|
|
|
|
#if DBG
|
|
if (pSrb->Status != STATUS_SUCCESS &&
|
|
pSrb->Status != STATUS_NOT_IMPLEMENTED) {
|
|
DbgMsg1(("pSrb->Command(0x%x) does not return STATUS_SUCCESS or NOT_IMPLEMENTED but 0x%x\n", pSrb->Command, pSrb->Status));
|
|
}
|
|
#endif
|
|
|
|
StreamClassDeviceNotification(DeviceRequestComplete, pSrb->HwDeviceExtension, pSrb);
|
|
|
|
}
|
|
|