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.
1513 lines
45 KiB
1513 lines
45 KiB
/*++
|
|
|
|
Copyright (C) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
stream.c
|
|
|
|
Abstract
|
|
|
|
MS AVC streaming filter driver
|
|
|
|
Author:
|
|
|
|
Yee Wu 01/27/2000
|
|
|
|
Revision History:
|
|
Date Who What
|
|
----------- --------- ------------------------------------------------------------
|
|
01/27/2000 YJW created
|
|
--*/
|
|
|
|
|
|
#include "filter.h"
|
|
#include "ksmedia.h"
|
|
|
|
|
|
NTSTATUS
|
|
AVCStreamOpen(
|
|
IN PIRP pIrp, // The Irp from its client
|
|
IN struct DEVICE_EXTENSION * pDevExt,
|
|
IN OUT AVCSTRM_OPEN_STRUCT * pOpenStruct
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open a stream for a client based on the information in the OpenStruct.
|
|
|
|
Arguments:
|
|
|
|
Irp -
|
|
The irp client sent us.
|
|
|
|
pDevExt -
|
|
This driver's extension.
|
|
|
|
pOpenStruct-
|
|
Strcture contains information on how to open this stream.
|
|
The stream context allocated will be returned and this will be the context
|
|
to be passed for subsequent call.
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
STATUS_SUCCESS
|
|
STATUS_INVALID_PARAMETER
|
|
STATUS_INSUFFICIENT_RESOURCES
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG ulSizeAllocated;
|
|
PAVC_STREAM_EXTENSION pAVCStrmExt;
|
|
|
|
|
|
PAGED_CODE();
|
|
ENTER("AVCStreamOpen");
|
|
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
// Validate open structures.
|
|
if(pOpenStruct == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
if(pOpenStruct->AVCFormatInfo == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
// Validate open format.
|
|
if(STATUS_SUCCESS != AVCStrmValidateFormat(pOpenStruct->AVCFormatInfo)) {
|
|
TRACE(TL_STRM_ERROR,("StreamOpen: pAVCFormatInfo:%x; contain invalid data\n", pOpenStruct->AVCFormatInfo ));
|
|
ASSERT(FALSE && "AVCFormatInfo contain invalid parameter!");
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// If supported, open a stream based on this stream information.
|
|
// Allocate a contiguous data strcutre for a
|
|
ulSizeAllocated =
|
|
sizeof(AVC_STREAM_EXTENSION) +
|
|
sizeof(AVCSTRM_FORMAT_INFO) +
|
|
sizeof(AVC_STREAM_DATA_STRUCT);
|
|
|
|
pAVCStrmExt = (PAVC_STREAM_EXTENSION) ExAllocatePool(NonPagedPool, ulSizeAllocated);
|
|
if(NULL == pAVCStrmExt) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Initialize stream extension:
|
|
// Copy the stream format information which is continuation of the stream extension.
|
|
//
|
|
RtlZeroMemory(pAVCStrmExt, ulSizeAllocated);
|
|
pAVCStrmExt->SizeOfThisPacket = sizeof(AVC_STREAM_EXTENSION);
|
|
|
|
(PBYTE) pAVCStrmExt->pAVCStrmFormatInfo = ((PBYTE) pAVCStrmExt) + sizeof(AVC_STREAM_EXTENSION);
|
|
RtlCopyMemory(pAVCStrmExt->pAVCStrmFormatInfo, pOpenStruct->AVCFormatInfo, sizeof(AVCSTRM_FORMAT_INFO));
|
|
|
|
(PBYTE) pAVCStrmExt->pAVCStrmDataStruc = ((PBYTE) pAVCStrmExt->pAVCStrmFormatInfo) + sizeof(AVCSTRM_FORMAT_INFO);
|
|
pAVCStrmExt->pAVCStrmDataStruc->SizeOfThisPacket = sizeof(AVC_STREAM_DATA_STRUCT);
|
|
|
|
TRACE(TL_STRM_TRACE,("pAVCStrmExt:%x; pAVCStrmFormatInfo:%x; pAVCStrmDataStruc:%x\n", pAVCStrmExt, pAVCStrmExt->pAVCStrmFormatInfo, pAVCStrmExt->pAVCStrmDataStruc));
|
|
|
|
pAVCStrmExt->hPlugLocal = pOpenStruct->hPlugLocal;
|
|
pAVCStrmExt->DataFlow = pOpenStruct->DataFlow;
|
|
pAVCStrmExt->StreamState = KSSTATE_STOP;
|
|
pAVCStrmExt->IsochIsActive = FALSE;
|
|
|
|
// Mutext for serialize setting stream state and accepting data packet
|
|
KeInitializeMutex(&pAVCStrmExt->hMutexControl, 0);
|
|
|
|
// Allocate resource for the common Request structure
|
|
pAVCStrmExt->pIrpAVReq = IoAllocateIrp(pDevExt->physicalDevObj->StackSize, FALSE);
|
|
if(!pAVCStrmExt->pIrpAVReq) {
|
|
ExFreePool(pAVCStrmExt); pAVCStrmExt = NULL;
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
KeInitializeMutex(&pAVCStrmExt->hMutexAVReq, 0);
|
|
KeInitializeEvent(&pAVCStrmExt->hAbortDoneEvent, NotificationEvent, TRUE); // Signal!
|
|
pAVCStrmExt->pDevExt = pDevExt;
|
|
|
|
//
|
|
// Get target device's plug handle
|
|
//
|
|
if(!NT_SUCCESS(Status =
|
|
AVCStrmGetPlugHandle(
|
|
pDevExt->physicalDevObj,
|
|
pAVCStrmExt
|
|
))) {
|
|
IoFreeIrp(pAVCStrmExt->pIrpAVReq); pAVCStrmExt->pIrpAVReq = NULL;
|
|
ExFreePool(pAVCStrmExt); pAVCStrmExt = NULL;
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Set stream state related flags
|
|
//
|
|
pAVCStrmExt->b1stNewFrameFromPauseState = TRUE;
|
|
|
|
|
|
// Allocate PC resources
|
|
// Queues
|
|
//
|
|
if(!NT_SUCCESS(Status =
|
|
AVCStrmAllocateQueues(
|
|
pDevExt,
|
|
pAVCStrmExt,
|
|
pAVCStrmExt->DataFlow,
|
|
pAVCStrmExt->pAVCStrmDataStruc,
|
|
pAVCStrmExt->pAVCStrmFormatInfo
|
|
))) {
|
|
IoFreeIrp(pAVCStrmExt->pIrpAVReq); pAVCStrmExt->pIrpAVReq = NULL;
|
|
ExFreePool(pAVCStrmExt); pAVCStrmExt = NULL;
|
|
return Status;
|
|
}
|
|
|
|
// Return stream extension
|
|
pOpenStruct->AVCStreamContext = pAVCStrmExt;
|
|
TRACE(TL_STRM_TRACE,("Open: AVCStreamContext:%x\n", pOpenStruct->AVCStreamContext));
|
|
|
|
// Cache it. This stream extension will be the context that will be
|
|
// check when we are asked to provide service.
|
|
pDevExt->NumberOfStreams++; pDevExt->pAVCStrmExt[pDevExt->NextStreamIndex] = pAVCStrmExt;
|
|
pDevExt->NextStreamIndex = ((pDevExt->NextStreamIndex + 1) % MAX_STREAMS_PER_DEVICE);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AVCStreamClose(
|
|
IN PIRP pIrp, // The Irp from its client
|
|
IN struct DEVICE_EXTENSION * pDevExt,
|
|
IN PAVC_STREAM_EXTENSION pAVCStrmExt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Close a stream.
|
|
|
|
Arguments:
|
|
|
|
Irp -
|
|
The irp client sent us.
|
|
|
|
pDevExt -
|
|
This driver's extension.
|
|
|
|
pAVCStrmExt -
|
|
The stream context created when a stream is open.
|
|
|
|
pOpenStruct-
|
|
Strcture contains information on how to open this stream.
|
|
The stream context allocated will be returned and this will be the context
|
|
to be passed for subsequent call.
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
STATUS_SUCCESS
|
|
STATUS_INVALID_PARAMETER
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
BOOL Found;
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
ENTER("AVCStreamClose");
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Found = FALSE;
|
|
for (i=0; i < MAX_STREAMS_PER_DEVICE; i++) {
|
|
// Free stream extension
|
|
if(pDevExt->pAVCStrmExt[i] == pAVCStrmExt) {
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!Found) {
|
|
TRACE(TL_STRM_ERROR,("AVCStreamClose: pAVCStrmExt %x not found; pDevExt:%x\n", pAVCStrmExt, pDevExt));
|
|
ASSERT(Found && "pAVCStrmExt not found!\n");
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
// Stop stream if not already
|
|
if(pAVCStrmExt->StreamState != KSSTATE_STOP) {
|
|
// Stop isoch if necessary and then Cancel all pending IOs
|
|
AVCStrmCancelIO(pDevExt->physicalDevObj, pAVCStrmExt);
|
|
}
|
|
|
|
// Free queue allocated if they are not being used.
|
|
if(NT_SUCCESS(Status = AVCStrmFreeQueues(pAVCStrmExt->pAVCStrmDataStruc))) {
|
|
ExFreePool(pAVCStrmExt); pDevExt->pAVCStrmExt[i] = NULL; pDevExt->NumberOfStreams--;
|
|
} else {
|
|
TRACE(TL_STRM_ERROR,("*** StreamClose: AVCStrmExt is not freed!\n"));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AVCStreamControlGetState(
|
|
IN PIRP pIrp, // The Irp from its client
|
|
IN struct DEVICE_EXTENSION * pDevExt,
|
|
IN PAVC_STREAM_EXTENSION pAVCStrmExt,
|
|
OUT KSSTATE * pKSState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get current stream state
|
|
|
|
Arguments:
|
|
|
|
Irp -
|
|
The irp client sent us.
|
|
|
|
pDevExt -
|
|
This driver's extension.
|
|
|
|
pAVCStrmExt -
|
|
The stream context created when a stream is open.
|
|
|
|
pKSState -
|
|
Get current stream state and return.
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
STATUS_SUCCESS
|
|
STATUS_INVALID_PARAMETER
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PAGED_CODE();
|
|
ENTER("AVCStreamControlGetState");
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
*pKSState = pAVCStrmExt->StreamState;
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AVCStreamControlSetState(
|
|
IN PIRP pIrp, // The Irp from its client
|
|
IN struct DEVICE_EXTENSION * pDevExt,
|
|
IN PAVC_STREAM_EXTENSION pAVCStrmExt,
|
|
IN KSSTATE KSState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set to a new stream state
|
|
|
|
Arguments:
|
|
|
|
Irp -
|
|
The irp client sent us.
|
|
|
|
pDevExt -
|
|
This driver's extension.
|
|
|
|
pAVCStrmExt -
|
|
The stream context created when a stream is open.
|
|
|
|
pKSState -
|
|
Get current stream state.
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
STATUS_SUCCESS
|
|
STATUS_INVALID_PARAMETER
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PAGED_CODE();
|
|
ENTER("AVCStreamControlSetState");
|
|
|
|
TRACE(TL_STRM_WARNING,("Set stream state %d -> %d\n", pAVCStrmExt->StreamState, KSState));
|
|
if(pAVCStrmExt->StreamState == KSState)
|
|
return STATUS_SUCCESS;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
switch (KSState) {
|
|
case KSSTATE_STOP:
|
|
|
|
if(pAVCStrmExt->StreamState != KSSTATE_STOP) {
|
|
KeWaitForMutexObject(&pAVCStrmExt->hMutexControl, Executive, KernelMode, FALSE, NULL);
|
|
// Once this is set, data stream will reject SRB_WRITE/READ_DATA
|
|
pAVCStrmExt->StreamState = KSSTATE_STOP;
|
|
KeReleaseMutex(&pAVCStrmExt->hMutexControl, FALSE);
|
|
|
|
// Cancel all pending IOs
|
|
AVCStrmCancelIO(pDevExt->physicalDevObj, pAVCStrmExt);
|
|
|
|
// Breeak Isoch connection
|
|
AVCStrmBreakConnection(pDevExt->physicalDevObj, pAVCStrmExt);
|
|
}
|
|
break;
|
|
|
|
case KSSTATE_ACQUIRE:
|
|
|
|
// Get Isoch resource
|
|
if(pAVCStrmExt->StreamState == KSSTATE_STOP) {
|
|
//
|
|
// Reset values.for the case that the graph restart
|
|
//
|
|
pAVCStrmExt->pAVCStrmDataStruc->CurrentStreamTime = 0;
|
|
pAVCStrmExt->pAVCStrmDataStruc->FramesProcessed = 0;
|
|
pAVCStrmExt->pAVCStrmDataStruc->FramesDropped = 0;
|
|
pAVCStrmExt->pAVCStrmDataStruc->cntFrameCancelled = 0;
|
|
#if DBG
|
|
pAVCStrmExt->pAVCStrmDataStruc->FramesAttached = 0;
|
|
#endif
|
|
|
|
pAVCStrmExt->pAVCStrmDataStruc->cntDataReceived = 0;
|
|
// All the list should be initialized (count:0, and List is empty)
|
|
TRACE(TL_STRM_TRACE,("Set to ACQUIRE state: flow %d; AQD [%d:%d:%d]\n", pAVCStrmExt->DataFlow,
|
|
pAVCStrmExt->pAVCStrmDataStruc->cntDataAttached, pAVCStrmExt->pAVCStrmDataStruc->cntDataQueued, pAVCStrmExt->pAVCStrmDataStruc->cntDataDetached));
|
|
ASSERT(pAVCStrmExt->pAVCStrmDataStruc->cntDataAttached == 0 && IsListEmpty(&pAVCStrmExt->pAVCStrmDataStruc->DataAttachedListHead));
|
|
ASSERT(pAVCStrmExt->pAVCStrmDataStruc->cntDataQueued == 0 && IsListEmpty(&pAVCStrmExt->pAVCStrmDataStruc->DataQueuedListHead));
|
|
ASSERT(pAVCStrmExt->pAVCStrmDataStruc->cntDataDetached > 0 && !IsListEmpty(&pAVCStrmExt->pAVCStrmDataStruc->DataDetachedListHead));
|
|
// Cannot stream using previous stream data !!!
|
|
if(pAVCStrmExt->pAVCStrmDataStruc->cntDataAttached != 0 || // Stale data ??
|
|
pAVCStrmExt->pAVCStrmDataStruc->cntDataQueued != 0 || // NO data unil PAUSE ??
|
|
pAVCStrmExt->pAVCStrmDataStruc->cntDataDetached == 0) { // NO avaialble queue ?
|
|
TRACE(TL_STRM_ERROR,("Set to ACQUIRE State: queues not empty (stale data?); Failed!\n"));
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Make connection
|
|
//
|
|
Status =
|
|
AVCStrmMakeConnection(
|
|
pDevExt->physicalDevObj,
|
|
pAVCStrmExt
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
|
|
TRACE(TL_STRM_ERROR,("Acquire failed:%x\n", Status));
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
//
|
|
// Change to generic insufficient resource status.
|
|
//
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
//
|
|
// Note: even setting to this state failed, KSSTATE_PAUSE will still be called;
|
|
// Since hConnect is NULL, STATUS_INSUFFICIENT_RESOURCES will be returned.
|
|
//
|
|
}
|
|
else {
|
|
//
|
|
// Can verify connection by query the plug state
|
|
//
|
|
Status =
|
|
AVCStrmGetPlugState(
|
|
pDevExt->physicalDevObj,
|
|
pAVCStrmExt
|
|
);
|
|
if(NT_SUCCESS(Status)) {
|
|
ASSERT(pAVCStrmExt->RemotePlugState.BC_Connections == 1 || pAVCStrmExt->RemotePlugState.PP_Connections > 0);
|
|
}
|
|
else {
|
|
ASSERT(NT_SUCCESS(Status) && "Failed to get Plug State");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case KSSTATE_PAUSE:
|
|
|
|
if(pAVCStrmExt->hConnect == NULL) {
|
|
// Cannot stream without connection!
|
|
// failed to get hConnect at ACQUIRE state.
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
// The system time (1394 CycleTime) will reset when enter PAUSE state.
|
|
if(pAVCStrmExt->StreamState != KSSTATE_PAUSE) {
|
|
pAVCStrmExt->b1stNewFrameFromPauseState = TRUE;
|
|
pAVCStrmExt->pAVCStrmDataStruc->PictureNumber = 0;
|
|
}
|
|
|
|
|
|
if(pAVCStrmExt->StreamState == KSSTATE_ACQUIRE ||
|
|
pAVCStrmExt->StreamState == KSSTATE_STOP) {
|
|
|
|
}
|
|
else if (pAVCStrmExt->StreamState == KSSTATE_RUN) {
|
|
|
|
//
|
|
// Stop isoch transfer
|
|
//
|
|
AVCStrmStopIsoch(pDevExt->physicalDevObj, pAVCStrmExt);
|
|
}
|
|
break;
|
|
|
|
case KSSTATE_RUN:
|
|
|
|
// Even there is no attach data request,
|
|
// 61883 has its own buffers so isoch can start now.
|
|
Status =
|
|
AVCStrmStartIsoch(
|
|
pDevExt->physicalDevObj,
|
|
pAVCStrmExt
|
|
);
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
pAVCStrmExt->LastSystemTime = GetSystemTime();
|
|
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
if(NT_SUCCESS(Status))
|
|
pAVCStrmExt->StreamState = KSState;
|
|
|
|
return Status;
|
|
}
|
|
|
|
#if 0
|
|
NTSTATUS
|
|
AVCStreamControlGetProperty(
|
|
IN PIRP pIrp, // The Irp from its client
|
|
IN struct DEVICE_EXTENSION * pDevExt,
|
|
IN PAVC_STREAM_EXTENSION pAVCStrmExt,
|
|
IN PSTREAM_PROPERTY_DESCRIPTOR pSPD // BUGBUG StreamClass specific
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get control property
|
|
|
|
Arguments:
|
|
|
|
Irp -
|
|
The irp client sent us.
|
|
|
|
pDevExt -
|
|
This driver's extension.
|
|
|
|
pAVCStrmExt -
|
|
The stream context created when a stream is open.
|
|
|
|
pSPD -
|
|
Stream property descriptor
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
STATUS_SUCCESS
|
|
STATUS_INVALID_PARAMETER
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG ulActualBytesTransferred;
|
|
PAGED_CODE();
|
|
ENTER("AVCStreamControlGetProperty");
|
|
|
|
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
|
|
if(IsEqualGUID (&KSPROPSETID_Connection, &pSPD->Property->Set)) {
|
|
|
|
Status =
|
|
AVCStrmGetConnectionProperty(
|
|
pDevExt,
|
|
pAVCStrmExt,
|
|
pSPD,
|
|
&ulActualBytesTransferred
|
|
);
|
|
}
|
|
else if (IsEqualGUID (&PROPSETID_VIDCAP_DROPPEDFRAMES, &pSPD->Property->Set)) {
|
|
|
|
Status =
|
|
AVCStrmGetDroppedFramesProperty(
|
|
pDevExt,
|
|
pAVCStrmExt,
|
|
pSPD,
|
|
&ulActualBytesTransferred
|
|
);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AVCStreamControlSetProperty(
|
|
IN PIRP pIrp, // The Irp from its client
|
|
IN struct DEVICE_EXTENSION * pDevExt,
|
|
IN PAVC_STREAM_EXTENSION pAVCStrmExt,
|
|
IN PSTREAM_PROPERTY_DESCRIPTOR pSPD // BUGBUG StreamClass specific
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set control property
|
|
|
|
Arguments:
|
|
|
|
Irp -
|
|
The irp client sent us.
|
|
|
|
pDevExt -
|
|
This driver's extension.
|
|
|
|
pAVCStrmExt -
|
|
The stream context created when a stream is open.
|
|
|
|
pSPD -
|
|
Stream property descriptor
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
STATUS_SUCCESS
|
|
STATUS_INVALID_PARAMETER
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PAGED_CODE();
|
|
ENTER("AVCStreamControlSetProperty");
|
|
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
|
|
return Status;
|
|
}
|
|
#endif
|
|
|
|
NTSTATUS
|
|
AVCStreamRead(
|
|
IN PIRP pIrpUpper, // The Irp from its client
|
|
IN struct DEVICE_EXTENSION * pDevExt,
|
|
IN PAVC_STREAM_EXTENSION pAVCStrmExt,
|
|
IN AVCSTRM_BUFFER_STRUCT * pBufferStruct
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Submit a read buffer to be filled.
|
|
|
|
Arguments:
|
|
|
|
Irp -
|
|
The irp client sent us.
|
|
|
|
pDevExt -
|
|
This driver's extension.
|
|
|
|
pAVCStrmExt -
|
|
The stream context created when a stream is open.
|
|
|
|
BufferStruct -
|
|
Buffer structure
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
STATUS_SUCCESS
|
|
STATUS_INVALID_PARAMETER
|
|
|
|
--*/
|
|
{
|
|
PAVC_STREAM_DATA_STRUCT pDataStruc;
|
|
KIRQL oldIrql;
|
|
PIO_STACK_LOCATION NextIrpStack;
|
|
NTSTATUS Status;
|
|
PAVCSTRM_DATA_ENTRY pDataEntry;
|
|
|
|
|
|
PAGED_CODE();
|
|
ENTER("AVCStreamRead");
|
|
|
|
// Cancel data request if device is being removed.
|
|
if( pDevExt->state == STATE_REMOVING
|
|
|| pDevExt->state == STATE_REMOVED) {
|
|
TRACE(TL_STRM_WARNING,("Read: device is remvoved; cancel read/write request!!\n"));
|
|
Status = STATUS_DEVICE_REMOVED; goto DoneStreamRead;
|
|
}
|
|
|
|
// If we are in the abort state, we will reject incoming data request.
|
|
if(pAVCStrmExt->lAbortToken) {
|
|
TRACE(TL_STRM_WARNING,("Read: aborting a stream; stop receiving data reqest!!\n"));
|
|
Status = STATUS_CANCELLED; goto DoneStreamRead;
|
|
}
|
|
|
|
// Validate basic parameters
|
|
if(pAVCStrmExt->DataFlow != KSPIN_DATAFLOW_OUT) {
|
|
TRACE(TL_STRM_ERROR,("Read: invalid Wrong data flow (%d) direction!!\n", pAVCStrmExt->DataFlow));
|
|
Status = STATUS_INVALID_PARAMETER; goto DoneStreamRead;
|
|
}
|
|
pDataStruc = pAVCStrmExt->pAVCStrmDataStruc;
|
|
if(!pDataStruc) {
|
|
TRACE(TL_STRM_ERROR,("Read: invalid pDataStruc:%x\n", pDataStruc));
|
|
Status = STATUS_INVALID_PARAMETER; goto DoneStreamRead;
|
|
}
|
|
if(pBufferStruct->StreamHeader->FrameExtent < pDataStruc->FrameSize) {
|
|
TRACE(TL_STRM_ERROR,("Read: invalid buffer size:%d < FrameSize:%d\n", pBufferStruct->StreamHeader->FrameExtent, pDataStruc->FrameSize));
|
|
Status = STATUS_INVALID_PARAMETER; goto DoneStreamRead;
|
|
}
|
|
if(!pBufferStruct->FrameBuffer) {
|
|
TRACE(TL_STRM_ERROR,("Read: invalid FrameBuffer:%x\n", pBufferStruct->FrameBuffer));
|
|
Status = STATUS_INVALID_PARAMETER; goto DoneStreamRead;
|
|
}
|
|
|
|
// Only accept read requests when in either the Pause or Run state and is connected.
|
|
if( pAVCStrmExt->StreamState == KSSTATE_STOP ||
|
|
pAVCStrmExt->StreamState == KSSTATE_ACQUIRE ||
|
|
pAVCStrmExt->hConnect == NULL
|
|
) {
|
|
TRACE(TL_STRM_WARNING,("Read: StrmSt:%d and Connected:%x!!\n", pAVCStrmExt->StreamState, pAVCStrmExt->hConnect));
|
|
Status = STATUS_CANCELLED; goto DoneStreamRead;
|
|
}
|
|
|
|
|
|
KeAcquireSpinLock(&pDataStruc->DataListLock, &oldIrql);
|
|
if(IsListEmpty(&pDataStruc->DataDetachedListHead)) {
|
|
TRACE(TL_STRM_ERROR,("Read:no detached buffers!\n"));
|
|
ASSERT(!IsListEmpty(&pDataStruc->DataDetachedListHead));
|
|
KeReleaseSpinLock(&pDataStruc->DataListLock, oldIrql);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES; goto DoneStreamRead;
|
|
}
|
|
|
|
pDataEntry = (PAVCSTRM_DATA_ENTRY)
|
|
RemoveHeadList(&pDataStruc->DataDetachedListHead); InterlockedDecrement(&pDataStruc->cntDataDetached);
|
|
|
|
pDataStruc->cntDataReceived++;
|
|
|
|
//
|
|
// Format an attach frame request
|
|
//
|
|
AVCStrmFormatAttachFrame(
|
|
pAVCStrmExt->DataFlow,
|
|
pAVCStrmExt,
|
|
pAVCStrmExt->pAVCStrmFormatInfo->AVCStrmFormat,
|
|
&pDataEntry->AVReq,
|
|
pDataEntry,
|
|
pDataStruc->SourcePacketSize,
|
|
pDataStruc->FrameSize,
|
|
pIrpUpper,
|
|
pBufferStruct->StreamHeader,
|
|
pBufferStruct->FrameBuffer
|
|
);
|
|
|
|
// Client's clock information
|
|
pDataEntry->ClockProvider = pBufferStruct->ClockProvider;
|
|
pDataEntry->ClockHandle = pBufferStruct->ClockHandle;
|
|
|
|
// Add this to the attached list before it is completed since
|
|
// the completion callback can be called before the IRP completion rooutine!
|
|
InsertTailList(&pDataStruc->DataAttachedListHead, &pDataEntry->ListEntry); InterlockedIncrement(&pDataStruc->cntDataAttached);
|
|
KeReleaseSpinLock(&pDataStruc->DataListLock, oldIrql);
|
|
|
|
NextIrpStack = IoGetNextIrpStackLocation(pDataEntry->pIrpLower);
|
|
NextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
NextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_61883_CLASS;
|
|
NextIrpStack->Parameters.Others.Argument1 = &pDataEntry->AVReq;
|
|
|
|
IoSetCompletionRoutine(
|
|
pDataEntry->pIrpLower,
|
|
AVCStrmAttachFrameCR,
|
|
pDataEntry, // Context
|
|
TRUE, // Success
|
|
TRUE, // Error
|
|
TRUE // Cancel
|
|
);
|
|
|
|
pDataEntry->pIrpLower->IoStatus.Status = STATUS_SUCCESS; // Initialize it
|
|
|
|
if(!NT_SUCCESS(Status = IoCallDriver(
|
|
pDevExt->physicalDevObj,
|
|
pDataEntry->pIrpLower
|
|
))) {
|
|
|
|
//
|
|
// Completion routine should have take care of this.
|
|
//
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Check the flag in pDataEntry to know the status of the IRP.
|
|
//
|
|
|
|
KeAcquireSpinLock(&pDataStruc->DataListLock, &oldIrql);
|
|
|
|
ASSERT(IsStateSet(pDataEntry->State, DE_IRP_LOWER_ATTACHED_COMPLETED)); // Must be attached
|
|
|
|
if(IsStateSet(pDataEntry->State, DE_IRP_LOWER_CALLBACK_COMPLETED)) {
|
|
|
|
if(IsStateSet(pDataEntry->State, DE_IRP_UPPER_COMPLETED)) {
|
|
|
|
//
|
|
// How does this happen? It should be protected by spinlock! Assert() to understand!
|
|
//
|
|
TRACE(TL_STRM_ERROR,("Watch out! Read: pDataEntry:%x\n", pDataEntry));
|
|
|
|
ASSERT(!IsStateSet(pDataEntry->State, DE_IRP_UPPER_COMPLETED));
|
|
}
|
|
else {
|
|
|
|
IoCompleteRequest( pDataEntry->pIrpUpper, IO_NO_INCREMENT ); pDataEntry->State |= DE_IRP_UPPER_COMPLETED;
|
|
|
|
//
|
|
// Transfer from attach to detach list
|
|
//
|
|
RemoveEntryList(&pDataEntry->ListEntry); InterlockedDecrement(&pDataStruc->cntDataAttached);
|
|
#if DBG
|
|
if(pDataStruc->cntDataAttached < 0) {
|
|
TRACE(TL_STRM_ERROR,("Read: pDataStruc:%x; pDataEntry:%x\n", pDataStruc, pDataEntry));
|
|
ASSERT(pDataStruc->cntDataAttached >= 0);
|
|
}
|
|
#endif
|
|
InsertTailList(&pDataStruc->DataDetachedListHead, &pDataEntry->ListEntry); InterlockedIncrement(&pDataStruc->cntDataDetached);
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Normal case: IrpUpper will be pending until the callback routine is called or cancelled.
|
|
//
|
|
|
|
IoMarkIrpPending(pDataEntry->pIrpUpper); pDataEntry->State |= DE_IRP_UPPER_PENDING_COMPLETED;
|
|
|
|
Status = STATUS_PENDING; // This will be returned to IoCallDriver() from the client.
|
|
}
|
|
|
|
KeReleaseSpinLock(&pDataStruc->DataListLock, oldIrql);
|
|
|
|
|
|
EXIT("AVCStreamRead", Status);
|
|
|
|
//
|
|
// If the data was attached siccessful, we must return STATUS_PENDING
|
|
//
|
|
return Status;
|
|
|
|
DoneStreamRead:
|
|
|
|
// Note: pDataStruc and pDataEntry may not be valid!
|
|
pIrpUpper->IoStatus.Status = Status;
|
|
IoCompleteRequest( pIrpUpper, IO_NO_INCREMENT );
|
|
|
|
EXIT("AVCStreamRead", Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
#if DBG
|
|
typedef union {
|
|
CYCLE_TIME CycleTime;
|
|
ULONG ulCycleTime;
|
|
} U_CYCLE_TIME, * PU_CYCLE_TIME;
|
|
#endif
|
|
|
|
NTSTATUS
|
|
AVCStreamWrite(
|
|
IN PIRP pIrpUpper, // The Irp from its client
|
|
IN struct DEVICE_EXTENSION * pDevExt,
|
|
IN PAVC_STREAM_EXTENSION pAVCStrmExt,
|
|
IN AVCSTRM_BUFFER_STRUCT * pBufferStruct
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Submit a write buffer to be transmitted.
|
|
|
|
Arguments:
|
|
|
|
Irp -
|
|
The irp client sent us.
|
|
|
|
pDevExt -
|
|
This driver's extension.
|
|
|
|
pAVCStrmExt -
|
|
The stream context created when a stream is open.
|
|
|
|
BufferStruct -
|
|
Buffer structure
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
STATUS_SUCCESS
|
|
STATUS_INVALID_PARAMETER
|
|
|
|
--*/
|
|
{
|
|
PAVC_STREAM_DATA_STRUCT pDataStruc;
|
|
KIRQL oldIrql;
|
|
PIO_STACK_LOCATION NextIrpStack;
|
|
NTSTATUS Status;
|
|
PAVCSTRM_DATA_ENTRY pDataEntry;
|
|
|
|
PAGED_CODE();
|
|
ENTER("AVCStreamWrite");
|
|
|
|
// Cancel data request if device is being removed.
|
|
if( pDevExt->state == STATE_REMOVING
|
|
|| pDevExt->state == STATE_REMOVED) {
|
|
TRACE(TL_STRM_WARNING,("Write: device is remvoved; cancel read/write request!!\n"));
|
|
Status = STATUS_DEVICE_REMOVED; goto DoneStreamWrite;
|
|
}
|
|
|
|
// If we are in the abort state, we will reject incoming data request.
|
|
if(pAVCStrmExt->lAbortToken) {
|
|
TRACE(TL_STRM_WARNING,("Write: aborting a stream; stop receiving data reqest!!\n"));
|
|
Status = STATUS_CANCELLED; goto DoneStreamWrite;
|
|
}
|
|
|
|
// Validate basic parameters
|
|
if(pAVCStrmExt->DataFlow != KSPIN_DATAFLOW_IN) {
|
|
TRACE(TL_STRM_ERROR,("Write: invalid Wrong data flow (%d) direction!!\n", pAVCStrmExt->DataFlow));
|
|
Status = STATUS_INVALID_PARAMETER; goto DoneStreamWrite;
|
|
}
|
|
pDataStruc = pAVCStrmExt->pAVCStrmDataStruc;
|
|
if(!pDataStruc) {
|
|
TRACE(TL_STRM_ERROR,("Write: invalid pDataStruc:%x\n", pDataStruc));
|
|
Status = STATUS_INVALID_PARAMETER; goto DoneStreamWrite;
|
|
}
|
|
|
|
// The client should take care of END OF stream buffer;
|
|
// If we get this flag, we will ignore it for now.
|
|
if((pBufferStruct->StreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_ENDOFSTREAM)) {
|
|
TRACE(TL_STRM_TRACE,("Write: End of stream\n"));
|
|
|
|
// Wait until all transmit are completed.
|
|
AVCStrmWaitUntilAttachedAreCompleted(pAVCStrmExt);
|
|
|
|
Status = STATUS_SUCCESS; goto DoneStreamWrite;
|
|
}
|
|
|
|
// The client should take care of format change;
|
|
// If we get this flag, we will ignore it for now.
|
|
if((pBufferStruct->StreamHeader->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_TYPECHANGED)) {
|
|
TRACE(TL_STRM_WARNING,("Write: Format change reuqested\n"));
|
|
Status = STATUS_SUCCESS; goto DoneStreamWrite;
|
|
}
|
|
|
|
if(pBufferStruct->StreamHeader->FrameExtent < pDataStruc->FrameSize) {
|
|
TRACE(TL_STRM_ERROR,("Write: invalid buffer size:%d < FrameSize:%d\n", pBufferStruct->StreamHeader->FrameExtent, pDataStruc->FrameSize));
|
|
Status = STATUS_INVALID_PARAMETER; goto DoneStreamWrite;
|
|
}
|
|
if(!pBufferStruct->FrameBuffer) {
|
|
TRACE(TL_STRM_ERROR,("Write: invalid FrameBuffer:%x\n", pBufferStruct->FrameBuffer));
|
|
Status = STATUS_INVALID_PARAMETER; goto DoneStreamWrite;
|
|
}
|
|
|
|
// Only accept write requests when in either the Pause or Run state and is connected.
|
|
if( pAVCStrmExt->StreamState == KSSTATE_STOP ||
|
|
pAVCStrmExt->StreamState == KSSTATE_ACQUIRE ||
|
|
pAVCStrmExt->hConnect == NULL
|
|
) {
|
|
TRACE(TL_STRM_ERROR,("Write: StrmSt:%d or hConnect:%x!!\n", pAVCStrmExt->StreamState, pAVCStrmExt->hConnect));
|
|
Status = STATUS_CANCELLED; goto DoneStreamWrite;
|
|
}
|
|
|
|
#if DBG
|
|
#define MASK_LOWER_25BIT 0x01ffffff
|
|
if(pAVCStrmExt->pAVCStrmFormatInfo->AVCStrmFormat == AVCSTRM_FORMAT_MPEG2TS) {
|
|
U_CYCLE_TIME TimeStamp25Bits;
|
|
TimeStamp25Bits.ulCycleTime = *((PDWORD) pBufferStruct->FrameBuffer);
|
|
TimeStamp25Bits.ulCycleTime = bswap(TimeStamp25Bits.ulCycleTime);
|
|
TRACE(TL_CIP_TRACE,("\t%d \t%d \t%d \t%x \t%d \t%d\n",
|
|
(DWORD) pDataStruc->cntDataReceived,
|
|
pDataStruc->FrameSize,
|
|
pDataStruc->SourcePacketSize,
|
|
TimeStamp25Bits.ulCycleTime & MASK_LOWER_25BIT,
|
|
TimeStamp25Bits.CycleTime.CL_CycleCount,
|
|
TimeStamp25Bits.CycleTime.CL_CycleOffset));
|
|
}
|
|
#endif
|
|
|
|
KeAcquireSpinLock(&pDataStruc->DataListLock, &oldIrql);
|
|
if(IsListEmpty(&pDataStruc->DataDetachedListHead)) {
|
|
KeReleaseSpinLock(&pDataStruc->DataListLock, oldIrql);
|
|
TRACE(TL_STRM_ERROR,("Write:no detached buffers!\n"));
|
|
ASSERT(!IsListEmpty(&pDataStruc->DataDetachedListHead));
|
|
Status = STATUS_INSUFFICIENT_RESOURCES; goto DoneStreamWrite;
|
|
}
|
|
|
|
#if DBG
|
|
//
|
|
// For write operation, DataUsed <= FrameSize <= FrameExt
|
|
//
|
|
if(pBufferStruct->StreamHeader->DataUsed < pDataStruc->FrameSize) {
|
|
// Jut to detect if this ever happen.
|
|
TRACE(TL_PNP_ERROR,("**** Write: DataUsed:%d < FrameSize:%d; DataRcv:%d; AQD [%d:%d:%d]\n",
|
|
pBufferStruct->StreamHeader->DataUsed, pDataStruc->FrameSize,
|
|
(DWORD) pDataStruc->cntDataReceived,
|
|
pAVCStrmExt->pAVCStrmDataStruc->cntDataAttached,
|
|
pAVCStrmExt->pAVCStrmDataStruc->cntDataQueued,
|
|
pAVCStrmExt->pAVCStrmDataStruc->cntDataDetached
|
|
));
|
|
}
|
|
#endif
|
|
|
|
|
|
pDataEntry = (PAVCSTRM_DATA_ENTRY)
|
|
RemoveHeadList(&pDataStruc->DataDetachedListHead); InterlockedDecrement(&pDataStruc->cntDataDetached);
|
|
|
|
pDataStruc->cntDataReceived++;
|
|
|
|
//
|
|
// Format an attach frame request
|
|
//
|
|
AVCStrmFormatAttachFrame(
|
|
pAVCStrmExt->DataFlow,
|
|
pAVCStrmExt,
|
|
pAVCStrmExt->pAVCStrmFormatInfo->AVCStrmFormat,
|
|
&pDataEntry->AVReq,
|
|
pDataEntry,
|
|
pDataStruc->SourcePacketSize,
|
|
#if 0
|
|
pDataStruc->FrameSize,
|
|
#else
|
|
pBufferStruct->StreamHeader->DataUsed, // For write operation, DataUsed <= FrameSize <= FrameExt
|
|
#endif
|
|
pIrpUpper,
|
|
pBufferStruct->StreamHeader,
|
|
pBufferStruct->FrameBuffer
|
|
);
|
|
|
|
// Client's clock information
|
|
pDataEntry->ClockProvider = pBufferStruct->ClockProvider;
|
|
pDataEntry->ClockHandle = pBufferStruct->ClockHandle;
|
|
|
|
// Add this to the attached list before it is completed since
|
|
// the completion callback can be called before the IRP completion rooutine!
|
|
InsertTailList(&pDataStruc->DataAttachedListHead, &pDataEntry->ListEntry); InterlockedIncrement(&pDataStruc->cntDataAttached);
|
|
KeReleaseSpinLock(&pDataStruc->DataListLock, oldIrql);
|
|
|
|
NextIrpStack = IoGetNextIrpStackLocation(pDataEntry->pIrpLower);
|
|
NextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
NextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_61883_CLASS;
|
|
NextIrpStack->Parameters.Others.Argument1 = &pDataEntry->AVReq;
|
|
|
|
IoSetCompletionRoutine(
|
|
pDataEntry->pIrpLower,
|
|
AVCStrmAttachFrameCR,
|
|
pDataEntry,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
IoSetCancelRoutine(
|
|
pDataEntry->pIrpLower,
|
|
NULL
|
|
);
|
|
|
|
pDataEntry->pIrpLower->IoStatus.Status = STATUS_SUCCESS; // Initialize it
|
|
|
|
if(!NT_SUCCESS(Status = IoCallDriver(
|
|
pDevExt->physicalDevObj,
|
|
pDataEntry->pIrpLower
|
|
))) {
|
|
|
|
//
|
|
// Completion routine should have take care of this.
|
|
//
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Check the flag in pDataEntry to know the status of the IRP.
|
|
//
|
|
|
|
KeAcquireSpinLock(&pDataStruc->DataListLock, &oldIrql);
|
|
|
|
ASSERT(IsStateSet(pDataEntry->State, DE_IRP_LOWER_ATTACHED_COMPLETED)); // Must be attached
|
|
|
|
if(IsStateSet(pDataEntry->State, DE_IRP_LOWER_CALLBACK_COMPLETED)) {
|
|
|
|
if(IsStateSet(pDataEntry->State, DE_IRP_UPPER_COMPLETED)) {
|
|
|
|
//
|
|
// How does this happen? It should be protected by spinlock! Assert() to understand!
|
|
//
|
|
TRACE(TL_STRM_ERROR,("Watch out! Write: pDataEntry:%x\n", pDataEntry));
|
|
|
|
ASSERT(!IsStateSet(pDataEntry->State, DE_IRP_UPPER_COMPLETED));
|
|
}
|
|
else {
|
|
|
|
IoCompleteRequest( pDataEntry->pIrpUpper, IO_NO_INCREMENT ); pDataEntry->State |= DE_IRP_UPPER_COMPLETED;
|
|
|
|
//
|
|
// Transfer from attach to detach list
|
|
//
|
|
RemoveEntryList(&pDataEntry->ListEntry); InterlockedDecrement(&pDataStruc->cntDataAttached);
|
|
|
|
//
|
|
// Signal when there is no more data buffer attached.
|
|
//
|
|
if(pDataStruc->cntDataAttached == 0)
|
|
KeSetEvent(&pDataStruc->hNoAttachEvent, 0, FALSE);
|
|
|
|
#if DBG
|
|
if(pDataStruc->cntDataAttached < 0) {
|
|
TRACE(TL_STRM_ERROR,("Write: pDataStruc:%x; pDataEntry:%x\n", pDataStruc, pDataEntry));
|
|
ASSERT(pDataStruc->cntDataAttached >= 0);
|
|
}
|
|
#endif
|
|
InsertTailList(&pDataStruc->DataDetachedListHead, &pDataEntry->ListEntry); InterlockedIncrement(&pDataStruc->cntDataDetached);
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Normal case: IrpUpper will be pending until the callback routine is called or cancelled.
|
|
//
|
|
|
|
IoMarkIrpPending(pDataEntry->pIrpUpper); pDataEntry->State |= DE_IRP_UPPER_PENDING_COMPLETED;
|
|
|
|
Status = STATUS_PENDING; // This will be returned to IoCallDriver() from the client.
|
|
}
|
|
|
|
KeReleaseSpinLock(&pDataStruc->DataListLock, oldIrql);
|
|
|
|
EXIT("AVCStreamWrite", Status);
|
|
|
|
//
|
|
// If the data was attached siccessful, we must return STATUS_PENDING
|
|
//
|
|
return Status;
|
|
|
|
DoneStreamWrite:
|
|
|
|
// Note: pDataStruc and pDataEntry may not be valid!
|
|
pIrpUpper->IoStatus.Status = Status;
|
|
IoCompleteRequest( pIrpUpper, IO_NO_INCREMENT );
|
|
|
|
EXIT("AVCStreamWrite", Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AVCStreamAbortStreaming(
|
|
IN PIRP pIrp, // The Irp from its client
|
|
IN struct DEVICE_EXTENSION * pDevExt,
|
|
IN PAVC_STREAM_EXTENSION pAVCStrmExt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine could be called at DISPATCH_LEVEL so it will create a work item
|
|
to stop isoch and then cancel all pennding buffers.
|
|
To cancel each individual buffer, IoCancelIrp() should be used..
|
|
|
|
Arguments:
|
|
|
|
Irp -
|
|
The irp client sent us.
|
|
|
|
pDevExt -
|
|
This driver's extension.
|
|
|
|
pAVCStrmExt -
|
|
The stream context created when a stream is open.
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
STATUS_SUCCESS
|
|
STATUS_INVALID_PARAMETER
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PAGED_CODE();
|
|
ENTER("AVCStreamAbortStreaming");
|
|
|
|
TRACE(TL_STRM_WARNING,("AbortStreaming: Active:%d; State:%d\n", pAVCStrmExt->IsochIsActive, pAVCStrmExt->StreamState));
|
|
|
|
// Claim this token
|
|
if(InterlockedExchange(&pAVCStrmExt->lAbortToken, 1) == 1) {
|
|
TRACE(TL_STRM_WARNING,("AbortStreaming: One already issued.\n"));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
#ifdef USE_WDM110 // Win2000 code base
|
|
ASSERT(pAVCStrmExt->pIoWorkItem == NULL); // Have not yet queued work item.
|
|
|
|
// We will queue work item to stop and cancel all SRBs
|
|
if(pAVCStrmExt->pIoWorkItem = IoAllocateWorkItem(pDevExt->physicalDevObj)) {
|
|
|
|
// Set to non-signal
|
|
KeClearEvent(&pAVCStrmExt->hAbortDoneEvent); // Before queuing; just in case it return the work item is completed.
|
|
IoQueueWorkItem(
|
|
pAVCStrmExt->pIoWorkItem,
|
|
AVCStrmAbortStreamingWorkItemRoutine,
|
|
DelayedWorkQueue, // CriticalWorkQueue
|
|
pAVCStrmExt
|
|
);
|
|
|
|
#else // Win9x code base
|
|
ExInitializeWorkItem( &pAVCStrmExt->IoWorkItem, AVCStrmAbortStreamingWorkItemRoutine, pAVCStrmExt);
|
|
if(TRUE) {
|
|
|
|
// Set to non-signal
|
|
KeClearEvent(&pAVCStrmExt->hAbortDoneEvent); // Before queuing; just in case it return the work item is completed.
|
|
|
|
ExQueueWorkItem(
|
|
&pAVCStrmExt->IoWorkItem,
|
|
DelayedWorkQueue // CriticalWorkQueue
|
|
);
|
|
#endif
|
|
|
|
TRACE(TL_STRM_TRACE,("AbortStreaming: CancelWorkItm queued; Pic#:%d;Prc:%d;;Drop:%d; AQD [%d:%d:%d]\n",
|
|
(DWORD) pAVCStrmExt->pAVCStrmDataStruc->PictureNumber,
|
|
(DWORD) pAVCStrmExt->pAVCStrmDataStruc->FramesProcessed,
|
|
(DWORD) pAVCStrmExt->pAVCStrmDataStruc->FramesDropped,
|
|
pAVCStrmExt->pAVCStrmDataStruc->cntDataAttached,
|
|
pAVCStrmExt->pAVCStrmDataStruc->cntDataQueued,
|
|
pAVCStrmExt->pAVCStrmDataStruc->cntDataDetached
|
|
));
|
|
|
|
}
|
|
#ifdef USE_WDM110 // Win2000 code base
|
|
else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES; // Only reason IoAllocateWorkItem can fail.
|
|
InterlockedExchange(&pAVCStrmExt->lAbortToken, 0);
|
|
ASSERT(pAVCStrmExt->pIoWorkItem && "IoAllocateWorkItem failed.\n");
|
|
}
|
|
#endif
|
|
|
|
#define MAX_ABORT_WAIT 50000000 // max wait time (100nsec unit)
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
|
|
NTSTATUS StatusWait;
|
|
LARGE_INTEGER tmMaxWait;
|
|
|
|
tmMaxWait = RtlConvertLongToLargeInteger(-(MAX_ABORT_WAIT));
|
|
|
|
//
|
|
// Wait with timeout until the work item has completed.
|
|
//
|
|
StatusWait =
|
|
KeWaitForSingleObject(
|
|
&pAVCStrmExt->hAbortDoneEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
&tmMaxWait
|
|
);
|
|
|
|
TRACE(TL_STRM_ERROR,("**WorkItem completed! StatusWait:%x; pAVStrmExt:%x; AQD [%d:%d:%d]\n",
|
|
StatusWait, pAVCStrmExt,
|
|
pAVCStrmExt->pAVCStrmDataStruc->cntDataAttached,
|
|
pAVCStrmExt->pAVCStrmDataStruc->cntDataQueued,
|
|
pAVCStrmExt->pAVCStrmDataStruc->cntDataDetached
|
|
));
|
|
|
|
ASSERT(StatusWait == STATUS_SUCCESS);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AVCStreamSurpriseRemoval(
|
|
IN struct DEVICE_EXTENSION * pDevExt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when this device is being surprise removed
|
|
with IRP_MN_SURPRISE_REMOVAL. We need to clean up and cancel any
|
|
pending request before passing irp down to lower driver.
|
|
|
|
Arguments:
|
|
|
|
pDevExt -
|
|
This driver's extension.
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
STATUS_SUCCESS
|
|
STATUS_INVALID_PARAMETER
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG i;
|
|
|
|
for (i=0; i < pDevExt->NumberOfStreams; i++) {
|
|
if(pDevExt->pAVCStrmExt[i]) {
|
|
if(pDevExt->pAVCStrmExt[i]->lAbortToken == 1) {
|
|
PAVC_STREAM_EXTENSION pAVCStrmExt = pDevExt->pAVCStrmExt[i];
|
|
#if DBG
|
|
ULONGLONG tmStart = GetSystemTime();
|
|
#endif
|
|
KeWaitForSingleObject(
|
|
&pAVCStrmExt->hAbortDoneEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
TRACE(TL_PNP_WARNING,("** Waited %d for AbortStream to complete\n", (DWORD) (GetSystemTime() - tmStart) ));
|
|
}
|
|
|
|
//
|
|
// Since we are already removed, go ahead and break the connection.
|
|
//
|
|
AVCStrmBreakConnection(pDevExt->physicalDevObj, pDevExt->pAVCStrmExt[i]);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AVCStrmValidateStreamRequest(
|
|
struct DEVICE_EXTENSION *pDevExt,
|
|
PAVC_STREAM_REQUEST_BLOCK pAVCStrmReqBlk
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Validate the StreamIndex of an AVC Stream Extension according to a AVC Stream function.
|
|
|
|
Arguments:
|
|
|
|
pDevExt -
|
|
This driver's extension.
|
|
|
|
pAVCStrmReqBlk -
|
|
AVC Stream reuqest block.
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
STATUS_SUCCESS
|
|
STATUS_INVALID_PARAMETER
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
ENTER("AVCStrmValidateStreamRequest");
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
// Validate pointer
|
|
if(!pAVCStrmReqBlk)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
// Validate block size
|
|
if(pAVCStrmReqBlk->SizeOfThisBlock != sizeof(AVC_STREAM_REQUEST_BLOCK))
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
#if 0
|
|
// Validate version supported
|
|
if( pAVCStrmReqBlk->Version != '15TN'
|
|
&& pAVCStrmReqBlk->Version != ' 8XD'
|
|
)
|
|
return STATUS_INVALID_PARAMETER;
|
|
#endif
|
|
|
|
if(pAVCStrmReqBlk->Function == AVCSTRM_OPEN) {
|
|
if(pDevExt->NumberOfStreams >= MAX_STREAMS_PER_DEVICE) {
|
|
ASSERT(pDevExt->NumberOfStreams < MAX_STREAMS_PER_DEVICE && "AVCStreamOpen: Too many stream open!\n");
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
} else {
|
|
if(pAVCStrmReqBlk->AVCStreamContext == NULL) {
|
|
ASSERT(pAVCStrmReqBlk->AVCStreamContext != NULL && "Invalid pAVCStrmExt\n");
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// To be more robust, we may need to make sure this is
|
|
// one of the cached stream extension created by us.
|
|
// ......
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AvcStrm_IoControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
struct DEVICE_EXTENSION *pDevExt;
|
|
PIO_STACK_LOCATION irpSp;
|
|
BOOLEAN passIrpDown = TRUE;
|
|
NTSTATUS Status;
|
|
PAVC_STREAM_REQUEST_BLOCK pAvcStrmIrb;
|
|
|
|
PAGED_CODE();
|
|
ENTER("AvcStrm_IoControl");
|
|
|
|
|
|
Status = STATUS_SUCCESS;
|
|
pDevExt = DeviceObject->DeviceExtension;
|
|
ASSERT(pDevExt->signature == DEVICE_EXTENSION_SIGNATURE);
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
pAvcStrmIrb = irpSp->Parameters.Others.Argument1;
|
|
|
|
// Validate the stream context
|
|
if(!NT_SUCCESS(Status =
|
|
AVCStrmValidateStreamRequest(
|
|
pDevExt,
|
|
pAvcStrmIrb))) {
|
|
|
|
goto DoneIoControl;
|
|
}
|
|
|
|
switch(pAvcStrmIrb->Function) {
|
|
case AVCSTRM_OPEN:
|
|
Status = AVCStreamOpen(
|
|
Irp,
|
|
pDevExt,
|
|
&pAvcStrmIrb->CommandData.OpenStruct
|
|
);
|
|
break;
|
|
case AVCSTRM_CLOSE:
|
|
Status = AVCStreamClose(
|
|
Irp,
|
|
pDevExt,
|
|
(PAVC_STREAM_EXTENSION) pAvcStrmIrb->AVCStreamContext
|
|
);
|
|
break;
|
|
|
|
|
|
case AVCSTRM_GET_STATE:
|
|
Status = AVCStreamControlGetState(
|
|
Irp,
|
|
pDevExt,
|
|
(PAVC_STREAM_EXTENSION) pAvcStrmIrb->AVCStreamContext,
|
|
&pAvcStrmIrb->CommandData.StreamState
|
|
);
|
|
break;
|
|
case AVCSTRM_SET_STATE:
|
|
Status = AVCStreamControlSetState(
|
|
Irp,
|
|
pDevExt,
|
|
(PAVC_STREAM_EXTENSION) pAvcStrmIrb->AVCStreamContext,
|
|
pAvcStrmIrb->CommandData.StreamState
|
|
);
|
|
break;
|
|
|
|
#if 0 // Later...
|
|
case AVCSTRM_GET_PROPERTY:
|
|
Status = AVCStreamControlGetProperty(
|
|
Irp,
|
|
pDevExt,
|
|
(PAVC_STREAM_EXTENSION) pAvcStrmIrb->AVCStreamContext,
|
|
pAvcStrmIrb->CommandData.PropertyDescriptor
|
|
);
|
|
break;
|
|
case AVCSTRM_SET_PROPERTY:
|
|
Status = AVCStreamControlSetProperty(
|
|
Irp,
|
|
pDevExt,
|
|
(PAVC_STREAM_EXTENSION) pAvcStrmIrb->AVCStreamContext,
|
|
pAvcStrmIrb->CommandData.PropertyDescriptor
|
|
);
|
|
break;
|
|
#endif
|
|
|
|
case AVCSTRM_READ:
|
|
// Mutex with Cancel or setting to stop state.
|
|
KeWaitForMutexObject(&((PAVC_STREAM_EXTENSION) pAvcStrmIrb->AVCStreamContext)->hMutexControl, Executive, KernelMode, FALSE, NULL);
|
|
Status = AVCStreamRead(
|
|
Irp,
|
|
pDevExt,
|
|
(PAVC_STREAM_EXTENSION) pAvcStrmIrb->AVCStreamContext,
|
|
&pAvcStrmIrb->CommandData.BufferStruct
|
|
);
|
|
KeReleaseMutex(&((PAVC_STREAM_EXTENSION) pAvcStrmIrb->AVCStreamContext)->hMutexControl, FALSE);
|
|
return Status;
|
|
break;
|
|
case AVCSTRM_WRITE:
|
|
KeWaitForMutexObject(&((PAVC_STREAM_EXTENSION) pAvcStrmIrb->AVCStreamContext)->hMutexControl, Executive, KernelMode, FALSE, NULL);
|
|
Status = AVCStreamWrite(
|
|
Irp,
|
|
pDevExt,
|
|
(PAVC_STREAM_EXTENSION) pAvcStrmIrb->AVCStreamContext,
|
|
&pAvcStrmIrb->CommandData.BufferStruct
|
|
);
|
|
KeReleaseMutex(&((PAVC_STREAM_EXTENSION) pAvcStrmIrb->AVCStreamContext)->hMutexControl, FALSE);
|
|
return Status;
|
|
break;
|
|
|
|
|
|
case AVCSTRM_ABORT_STREAMING:
|
|
Status = AVCStreamAbortStreaming(
|
|
Irp,
|
|
pDevExt,
|
|
(PAVC_STREAM_EXTENSION) pAvcStrmIrb->AVCStreamContext
|
|
);
|
|
break;
|
|
|
|
|
|
default:
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
DoneIoControl:
|
|
|
|
#if DBG
|
|
if(!NT_SUCCESS(Status)) {
|
|
TRACE(TL_PNP_WARNING,("Av_IoControl return Status:%x\n", Status));
|
|
}
|
|
#endif
|
|
|
|
if (Status == STATUS_PENDING) {
|
|
TRACE(TL_PNP_TRACE,("Av_IoControl: returning STATUS_PENDING."));
|
|
IoMarkIrpPending(Irp);
|
|
} else {
|
|
Irp->IoStatus.Status = Status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
return Status;
|
|
}
|