windows-nt/Source/XPSP1/NT/drivers/wdm/capture/mini/avcstrm/stream.c
2020-09-26 16:20:57 +08:00

1513 lines
44 KiB
C

/*++
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;
}