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