/*++ Copyright (C) 1999 Microsoft Corporation Module Name: avcutil.c Abstract MS AVC streaming utility functions Author: Yee Wu 03/17/2000 Revision History: Date Who What ----------- --------- ------------------------------------------------------------ 03/17/2000 YJW created --*/ #include "filter.h" #include "ksmedia.h" // KSPROERTY_DROPPEDFRAMES_CURRENT /************************************ * Synchronous IOCall to lower driver ************************************/ NTSTATUS IrpSynchCR( IN PDEVICE_OBJECT DeviceObject, IN PIRP pIrp, IN PKEVENT Event ) { ENTER("IrpSynchCR"); KeSetEvent(Event, 0, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } // IrpSynchCR NTSTATUS SubmitIrpSynch( IN PDEVICE_OBJECT DeviceObject, IN PIRP pIrp, IN PAV_61883_REQUEST pAVReq ) { NTSTATUS Status; KEVENT Event; PIO_STACK_LOCATION NextIrpStack; ENTER("SubmitIrpSynch"); Status = STATUS_SUCCESS;; NextIrpStack = IoGetNextIrpStackLocation(pIrp); NextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; NextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_61883_CLASS; NextIrpStack->Parameters.Others.Argument1 = pAVReq; KeInitializeEvent(&Event, NotificationEvent, FALSE); IoSetCompletionRoutine( pIrp, IrpSynchCR, &Event, TRUE, TRUE, TRUE ); Status = IoCallDriver( DeviceObject, pIrp ); if (Status == STATUS_PENDING) { TRACE(TL_PNP_INFO,("Irp is pending...\n")); if(KeGetCurrentIrql() < DISPATCH_LEVEL) { KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL ); TRACE(TL_PNP_TRACE,("Irp returned; IoStatus.Status %x\n", pIrp->IoStatus.Status)); Status = pIrp->IoStatus.Status; // Final status } else { ASSERT(FALSE && "Pending but in DISPATCH_LEVEL!"); return Status; } } EXIT("SubmitIrpSynch", Status); return Status; } // SubmitIrpSynchAV /**************************** * Control utility functions ****************************/ NTSTATUS AVCStrmGetPlugHandle( IN PDEVICE_OBJECT DeviceObject, IN PAVC_STREAM_EXTENSION pAVCStrmExt ) { NTSTATUS Status; PAV_61883_REQUEST pAVReq; PAGED_CODE(); ENTER("AVCStrmGetPlugHandle"); Status = STATUS_SUCCESS; // Claim ownership of hMutexAVReqIsoch KeWaitForMutexObject(&pAVCStrmExt->hMutexAVReq, Executive, KernelMode, FALSE, NULL); pAVReq = &pAVCStrmExt->AVReq; RtlZeroMemory(pAVReq, sizeof(AV_61883_REQUEST)); INIT_61883_HEADER(pAVReq, Av61883_GetPlugHandle); pAVReq->GetPlugHandle.PlugNum = 0; pAVReq->GetPlugHandle.hPlug = 0; pAVReq->GetPlugHandle.Type = pAVCStrmExt->DataFlow == KSPIN_DATAFLOW_OUT ? CMP_PlugOut : CMP_PlugIn; Status = SubmitIrpSynch(DeviceObject, pAVCStrmExt->pIrpAVReq, pAVReq); if(!NT_SUCCESS(Status)) { TRACE(TL_61883_ERROR,("GetPlugHandle: Failed:%x\n", Status)); ASSERT(NT_SUCCESS(Status)); pAVCStrmExt->hPlugRemote = NULL; } else { TRACE(TL_61883_TRACE,("GetPlugHandle:hPlug:%x\n", pAVReq->GetPlugHandle.hPlug)); pAVCStrmExt->hPlugRemote = pAVReq->GetPlugHandle.hPlug; } KeReleaseMutex(&pAVCStrmExt->hMutexAVReq, FALSE); EXIT("AVCStrmGetPlugHandle", Status); return Status; } NTSTATUS AVCStrmGetPlugState( IN PDEVICE_OBJECT DeviceObject, IN PAVC_STREAM_EXTENSION pAVCStrmExt ) /*++ Routine Description: Ask 61883.sys for the plug state. Arguments: Return Value: Nothing --*/ { NTSTATUS Status; PAV_61883_REQUEST pAVReq; PAGED_CODE(); ENTER("AVCStrmGetPlugState"); Status = STATUS_SUCCESS; // // Check only requirement: hConnect // if(pAVCStrmExt->hPlugRemote == NULL) { TRACE(TL_STRM_ERROR,("GetPlugState: hPlugRemote is NULL.\n")); ASSERT(pAVCStrmExt->hPlugRemote != NULL); return STATUS_UNSUCCESSFUL; } // Claim ownership of hMutexAVReqIsoch KeWaitForMutexObject(&pAVCStrmExt->hMutexAVReq, Executive, KernelMode, FALSE, NULL); pAVReq = &pAVCStrmExt->AVReq; RtlZeroMemory(pAVReq, sizeof(AV_61883_REQUEST)); INIT_61883_HEADER(pAVReq, Av61883_GetPlugState); pAVReq->GetPlugState.hPlug = pAVCStrmExt->hPlugRemote; Status = SubmitIrpSynch( DeviceObject, pAVCStrmExt->pIrpAVReq, pAVReq ); if(!NT_SUCCESS(Status)) { TRACE(TL_61883_ERROR,("GetPlugState Failed %x\n", Status)); } else { // Cache plug state (note: these are dynamic values) pAVCStrmExt->RemotePlugState = pAVReq->GetPlugState; TRACE(TL_61883_TRACE,("GetPlugState: ST %x; State %x; DRate %d; Payld %d; BCCnt %d; PPCnt %d\n", pAVReq->Flags , pAVReq->GetPlugState.State, pAVReq->GetPlugState.DataRate, pAVReq->GetPlugState.Payload, pAVReq->GetPlugState.BC_Connections, pAVReq->GetPlugState.PP_Connections )); } KeReleaseMutex(&pAVCStrmExt->hMutexAVReq, FALSE); EXIT("AVCStrmGetPlugState", Status); return Status; } NTSTATUS AVCStrmMakeConnection( IN PDEVICE_OBJECT DeviceObject, IN PAVC_STREAM_EXTENSION pAVCStrmExt ) /*++ Routine Description: Make an isoch connection. --*/ { NTSTATUS Status; PAV_61883_REQUEST pAVReq; PAVCSTRM_FORMAT_INFO pAVCStrmFormatInfo; PAGED_CODE(); ENTER("AVCStrmMakeConnection"); // Claim ownership of hMutexAVReqIsoch KeWaitForMutexObject(&pAVCStrmExt->hMutexAVReq, Executive, KernelMode, FALSE, NULL); TRACE(TL_61883_TRACE,("MakeConnect: State:%d; hConnect:%x\n", pAVCStrmExt->StreamState, pAVCStrmExt->hConnect)); if(pAVCStrmExt->hConnect) { KeReleaseMutex(&pAVCStrmExt->hMutexAVReq, FALSE); return STATUS_SUCCESS; } Status = STATUS_SUCCESS; pAVCStrmFormatInfo = pAVCStrmExt->pAVCStrmFormatInfo; pAVReq = &pAVCStrmExt->AVReq; INIT_61883_HEADER(pAVReq, Av61883_Connect); pAVReq->Connect.Type = CMP_PointToPoint; // !! // see which way we the data will flow... if(pAVCStrmExt->DataFlow == KSPIN_DATAFLOW_OUT) { // Remote(oPCR)->Local(iPCR) pAVReq->Connect.hOutputPlug = pAVCStrmExt->hPlugRemote; pAVReq->Connect.hInputPlug = pAVCStrmExt->hPlugLocal; // Other parameters !! } else { // Remote(iPCR)<-Local(oPCR) pAVReq->Connect.hOutputPlug = pAVCStrmExt->hPlugLocal; pAVReq->Connect.hInputPlug = pAVCStrmExt->hPlugRemote; pAVReq->Connect.Format.FMT = (UCHAR) pAVCStrmFormatInfo->cipHdr2.FMT; // From AV/C in/outpug plug signal format status cmd // 00 for NTSC, 80 for PAL; set the 50/60 bit // From AV/C in/outpug plug signal format status cmd pAVReq->Connect.Format.FDF_hi = ((UCHAR) pAVCStrmFormatInfo->cipHdr2.F5060_OR_TSF << 7) | ((UCHAR) pAVCStrmFormatInfo->cipHdr2.STYPE << 2) | ((UCHAR) pAVCStrmFormatInfo->cipHdr2.RSV); // // 16bit SYT field = 4BitCycleCount:12BitCycleOffset; // Will be set by 61883 // pAVReq->Connect.Format.FDF_mid = 0; pAVReq->Connect.Format.FDF_lo = 0; // // Constants depend on the A/V data format (in or out plug format) // pAVReq->Connect.Format.bHeader = (BOOL) pAVCStrmFormatInfo->cipHdr1.SPH; pAVReq->Connect.Format.Padding = (UCHAR) pAVCStrmFormatInfo->cipHdr1.QPC; pAVReq->Connect.Format.BlockSize = (UCHAR) pAVCStrmFormatInfo->cipHdr1.DBS; pAVReq->Connect.Format.Fraction = (UCHAR) pAVCStrmFormatInfo->cipHdr1.FN; } pAVReq->Connect.Format.BlockPeriod = pAVCStrmFormatInfo->BlockPeriod; TRACE(TL_61883_TRACE,("Connect:hOutPlg:%x<->hInPlug:%x; cipQuad2[%.2x:%.2x:%.2x:%.2x]; BlkSz %d; SrcPkt %d; AvgTm %d, BlkPrd %d\n", pAVReq->Connect.hOutputPlug, pAVReq->Connect.hInputPlug, pAVReq->Connect.Format.FMT, pAVReq->Connect.Format.FDF_hi, pAVReq->Connect.Format.FDF_mid, pAVReq->Connect.Format.FDF_lo, pAVReq->Connect.Format.BlockSize, pAVCStrmFormatInfo->SrcPacketsPerFrame, pAVCStrmFormatInfo->AvgTimePerFrame, pAVReq->Connect.Format.BlockPeriod )); Status = SubmitIrpSynch( DeviceObject, pAVCStrmExt->pIrpAVReq, pAVReq ); if(!NT_SUCCESS(Status)) { TRACE(TL_61883_ERROR,("Connect Failed = 0x%x\n", Status)); pAVCStrmExt->hConnect = NULL; } else { TRACE(TL_61883_TRACE,("hConnect = 0x%x\n", pAVReq->Connect.hConnect)); pAVCStrmExt->hConnect = pAVReq->Connect.hConnect; } KeReleaseMutex(&pAVCStrmExt->hMutexAVReq, FALSE); EXIT("AVCStrmMakeConnection", Status); return Status; } NTSTATUS AVCStrmBreakConnection( IN PDEVICE_OBJECT DeviceObject, IN PAVC_STREAM_EXTENSION pAVCStrmExt ) /*++ Routine Description: Break the isoch connection. --*/ { NTSTATUS Status; PAV_61883_REQUEST pAVReq; #if DBG PAVC_STREAM_DATA_STRUCT pDataStruc; #endif PAGED_CODE(); ENTER("AVCStrmBreakConnection"); // Claim ownership of hMutexAVReqIsoch KeWaitForMutexObject(&pAVCStrmExt->hMutexAVReq, Executive, KernelMode, FALSE, NULL); TRACE(TL_STRM_TRACE,("BreakConnect: State:%d; hConnect:%x\n", pAVCStrmExt->StreamState, pAVCStrmExt->hConnect)); if(!pAVCStrmExt->hConnect) { KeReleaseMutex(&pAVCStrmExt->hMutexAVReq, FALSE); return STATUS_SUCCESS; } Status = STATUS_SUCCESS; #if DBG pDataStruc = pAVCStrmExt->pAVCStrmDataStruc; #endif pAVReq = &pAVCStrmExt->AVReq; RtlZeroMemory(pAVReq, sizeof(AV_61883_REQUEST)); INIT_61883_HEADER(pAVReq, Av61883_Disconnect); pAVReq->Disconnect.hConnect = pAVCStrmExt->hConnect; Status = SubmitIrpSynch( DeviceObject, pAVCStrmExt->pIrpAVReq, pAVReq ); // This could be caused that the connection was not P2P, and // it tried to disconnect. if(!NT_SUCCESS(Status) || Status == STATUS_NO_SUCH_DEVICE) { TRACE(TL_61883_ERROR,("Disconnect Failed:%x; AvReq->ST %x\n", Status, pAVReq->Flags )); } else { TRACE(TL_61883_TRACE,("Disconnect suceeded; ST %x; AvReq->ST %x\n", Status, pAVReq->Flags )); } TRACE(TL_STRM_WARNING,("*** DisConn St:%x; Stat: DataRcved:%d; [Pic# =? Prcs:Drp:Cncl] [%d ?=%d+%d+%d]\n", Status, (DWORD) pDataStruc->cntDataReceived, (DWORD) pDataStruc->PictureNumber, (DWORD) pDataStruc->FramesProcessed, (DWORD) pDataStruc->FramesDropped, (DWORD) pDataStruc->cntFrameCancelled )); // We will not have another chance to reconnect it so we assume it is disconnected. pAVCStrmExt->hConnect = NULL; KeReleaseMutex(&pAVCStrmExt->hMutexAVReq, FALSE); EXIT("AVCStrmBreakConnection", Status); return Status; } NTSTATUS AVCStrmStartIsoch( IN PDEVICE_OBJECT DeviceObject, IN PAVC_STREAM_EXTENSION pAVCStrmExt ) /*++ Routine Description: Start streaming. --*/ { NTSTATUS Status; PAVC_STREAM_DATA_STRUCT pDataStruc; PAGED_CODE(); ENTER("AVCStrmStartIsoch"); // Claim ownership of hMutexAVReqIsoch KeWaitForMutexObject(&pAVCStrmExt->hMutexAVReq, Executive, KernelMode, FALSE, NULL); if(pAVCStrmExt->IsochIsActive) { TRACE(TL_STRM_WARNING,("Isoch already active!")); KeReleaseMutex(&pAVCStrmExt->hMutexAVReq, FALSE); return STATUS_SUCCESS; } if(!pAVCStrmExt->hConnect) { ASSERT(pAVCStrmExt->hConnect && "Cannot start isoch while graph is not connected!\n"); KeReleaseMutex(&pAVCStrmExt->hMutexAVReq, FALSE); return STATUS_INVALID_PARAMETER; } Status = STATUS_SUCCESS; pDataStruc = pAVCStrmExt->pAVCStrmDataStruc; TRACE(TL_61883_TRACE,("StartIsoch: flow %d; AQD [%d:%d:%d]\n", pAVCStrmExt->DataFlow, pDataStruc->cntDataAttached, pDataStruc->cntDataQueued, pDataStruc->cntDataDetached)); RtlZeroMemory(&pAVCStrmExt->AVReq, sizeof(AV_61883_REQUEST)); if(pAVCStrmExt->DataFlow == KSPIN_DATAFLOW_OUT) { INIT_61883_HEADER(&pAVCStrmExt->AVReq, Av61883_Listen); pAVCStrmExt->AVReq.Listen.hConnect = pAVCStrmExt->hConnect; } else { INIT_61883_HEADER(&pAVCStrmExt->AVReq, Av61883_Talk); pAVCStrmExt->AVReq.Talk.hConnect = pAVCStrmExt->hConnect; if(pAVCStrmExt->pAVCStrmFormatInfo->AVCStrmFormat == AVCSTRM_FORMAT_MPEG2TS) pAVCStrmExt->AVReq.Flags = CIP_TALK_DOUBLE_BUFFER | CIP_TALK_USE_SPH_TIMESTAMP; } Status = SubmitIrpSynch( DeviceObject, pAVCStrmExt->pIrpAVReq, &pAVCStrmExt->AVReq ); if (NT_SUCCESS(Status)) { pAVCStrmExt->IsochIsActive = TRUE; TRACE(TL_61883_TRACE,("Av61883_%s; Status %x; Streaming...\n", (pAVCStrmExt->DataFlow == KSPIN_DATAFLOW_OUT ? "Listen" : "Talk"), Status)); } else { TRACE(TL_61883_ERROR,("Av61883_%s; failed %x\n", (pAVCStrmExt->DataFlow == KSPIN_DATAFLOW_OUT ? "Listen" : "Talk"), Status)); ASSERT(NT_SUCCESS(Status) && "Start isoch failed!"); } KeReleaseMutex(&pAVCStrmExt->hMutexAVReq, FALSE); EXIT("AVCStrmStartIsoch", Status); return Status; } // // This wait is based on testing transmitting MPEG2TS data with up to 32 date request. // Each data request has 256 MPEG2TS data packets. There is a slow motion mode, // and it may take longer for video to be transmitted in the slow motion mode. // #define MAX_ATTACH_WAIT 50000000 // max wait time in seconds VOID AVCStrmWaitUntilAttachedAreCompleted( IN PAVC_STREAM_EXTENSION pAVCStrmExt ) { KIRQL oldIrql; PAVC_STREAM_DATA_STRUCT pDataStruc; pDataStruc = pAVCStrmExt->pAVCStrmDataStruc; // // Wait until attached data to complete transmission before aborting (cancel) them // KeAcquireSpinLock(&pDataStruc->DataListLock, &oldIrql); if( pAVCStrmExt->DataFlow == KSPIN_DATAFLOW_IN && pDataStruc->cntDataAttached > 0 ) { LARGE_INTEGER tmMaxWait; NTSTATUS StatusWait; #if DBG ULONGLONG tmStart; #endif TRACE(TL_STRM_TRACE,("StopIsoch: MaxWait %d (msec) for %d data buffer to finished transmitting!\n", MAX_ATTACH_WAIT/10000, pDataStruc->cntDataAttached)); // // This event will be signalled when all attach buffers are returned. // It is protected by Spinlock for common data pDataStruc->cntDataAttached. // KeClearEvent(&pDataStruc->hNoAttachEvent); #if DBG tmStart = GetSystemTime(); #endif KeReleaseSpinLock(&pDataStruc->DataListLock, oldIrql); tmMaxWait = RtlConvertLongToLargeInteger(-(MAX_ATTACH_WAIT)); StatusWait = KeWaitForSingleObject( &pDataStruc->hNoAttachEvent, Executive, KernelMode, FALSE, &tmMaxWait ); if(StatusWait == STATUS_TIMEOUT) { TRACE(TL_STRM_ERROR,("TIMEOUT (%d msec) on hNoAttachEvent! DataRcv:%d; AQD [%d:%d:%d]\n", (DWORD) (GetSystemTime()-tmStart)/10000, (DWORD) pDataStruc->cntDataReceived, pAVCStrmExt->pAVCStrmDataStruc->cntDataAttached, pAVCStrmExt->pAVCStrmDataStruc->cntDataQueued, pAVCStrmExt->pAVCStrmDataStruc->cntDataDetached )); } else { TRACE(TL_STRM_WARNING,("Status:%x; (%d msec) on hNoAttachEvent. DataRcv:%d; AQD [%d:%d:%d]\n", StatusWait, (DWORD) (GetSystemTime()-tmStart)/10000, (DWORD) pDataStruc->cntDataReceived, pAVCStrmExt->pAVCStrmDataStruc->cntDataAttached, pAVCStrmExt->pAVCStrmDataStruc->cntDataQueued, pAVCStrmExt->pAVCStrmDataStruc->cntDataDetached )); } } else { KeReleaseSpinLock(&pDataStruc->DataListLock, oldIrql); } } NTSTATUS AVCStrmStopIsoch( IN PDEVICE_OBJECT DeviceObject, IN PAVC_STREAM_EXTENSION pAVCStrmExt ) /*++ Routine Description: Stop streaming. --*/ { NTSTATUS Status; PAVC_STREAM_DATA_STRUCT pDataStruc; PAGED_CODE(); ENTER("AVCStrmStopIsoch"); // Claim ownership of hMutexAVReqIsoch KeWaitForMutexObject(&pAVCStrmExt->hMutexAVReq, Executive, KernelMode, FALSE, NULL); if(!pAVCStrmExt->IsochIsActive) { TRACE(TL_STRM_WARNING|TL_61883_WARNING,("Isoch already not active!")); KeReleaseMutex(&pAVCStrmExt->hMutexAVReq, FALSE); return STATUS_SUCCESS; } if(!pAVCStrmExt->hConnect) { ASSERT(pAVCStrmExt->hConnect && "Cannot stop isoch while graph is not connected!\n"); KeReleaseMutex(&pAVCStrmExt->hMutexAVReq, FALSE); return STATUS_INVALID_PARAMETER; } Status = STATUS_SUCCESS; pDataStruc = pAVCStrmExt->pAVCStrmDataStruc; TRACE(TL_STRM_TRACE,("IsochSTOP; flow %d; AQD [%d:%d:%d]\n", pAVCStrmExt->DataFlow, pDataStruc->cntDataAttached, pDataStruc->cntDataQueued, pDataStruc->cntDataDetached)); RtlZeroMemory(&pAVCStrmExt->AVReq, sizeof(AV_61883_REQUEST)); INIT_61883_HEADER(&pAVCStrmExt->AVReq, Av61883_Stop); pAVCStrmExt->AVReq.Listen.hConnect = pAVCStrmExt->hConnect; Status = SubmitIrpSynch( DeviceObject, pAVCStrmExt->pIrpAVReq, &pAVCStrmExt->AVReq ); if (NT_SUCCESS(Status) || Status == STATUS_NO_SUCH_DEVICE) { TRACE(TL_61883_TRACE,("Av61883_%s; Status %x; Stopped...\n", (pAVCStrmExt->DataFlow == KSPIN_DATAFLOW_OUT ? "Listen" : "Talk"), Status)); } else { TRACE(TL_61883_ERROR,("Av61883_%s; failed %x\n", (pAVCStrmExt->DataFlow == KSPIN_DATAFLOW_OUT ? "Listen" : "Talk"), Status)); ASSERT(NT_SUCCESS(Status) && "Stop isoch failed!"); } // Assume isoch is stopped regardless of the return status. pAVCStrmExt->IsochIsActive = FALSE; KeReleaseMutex(&pAVCStrmExt->hMutexAVReq, FALSE); EXIT("AVCStrmStopIsoch", Status); return Status; } /****************************** * Streaming utility funcrtions *******************************/ // // GetSystemTime in 100 nS units // ULONGLONG GetSystemTime() { LARGE_INTEGER rate, ticks; ticks = KeQueryPerformanceCounter(&rate); return (KSCONVERT_PERFORMANCE_TIME(rate.QuadPart, ticks)); } /// // The "signature" of the header section of Seq0 of incoming source packets: // // "Blue" book, Part2, 11.4 (page 50); Figure 66, table 36 (page 111) // // ID0 = {SCT2,SCT1,SCT0,RSV,Seq3,Seq2,Seq1,Seq0} // // SCT2-0 = {0,0,0} = Header Section Type // RSV = {1} // Seq3-0 = {1,1,1,1} for NoInfo or {0,0,0,} for Sequence 0 // // ID1 = {DSeq3-0, 0, RSV, RSV, RSV} // DSeq3-0 = {0, 0, 0, 0} = Beginning of a DV frame // // ID2 = {DBN7,DBN6,DBN5,DBN4,DBN3,DBN2,DBN1,DBN0} // DBB7-0 = {0,0,0,0,0,0,0,0,0} = Beginning of a DV frame // #define DIF_BLK_ID0_SCT_MASK 0xe0 // 11100000b; Section Type (SCT)2-0 are all 0's for the Header section #define DIF_BLK_ID1_DSEQ_MASK 0xf0 // 11110000b; DIF Sequence Number(DSEQ)3-0 are all 0's #define DIF_BLK_ID2_DBN_MASK 0xff // 11111111b; Data Block Number (DBN)7-0 are all 0's #define DIF_HEADER_DSF 0x80 // 10000000b; DSF=0; 10 DIF Sequences (525-60) // DSF=1; 12 DIF Sequences (625-50) #define DIF_HEADER_TFn 0x80 // 10000000b; TFn=0; DIF bloick of area N are transmitted in the current DIF sequence. // TFn=1; DIF bloick of area N are NOT transmitted in the current DIF sequence. ULONG AVCStrmDVReadFrameValidate( IN PCIP_VALIDATE_INFO pInfo ) /*++ Routine Description: Used to validate the header section of a frame. so 61883 will start filling data for a DVFrame. Note: This routine apply only to DV ONLY. Return 0 verified 1: invallid --*/ { if(pInfo->Packet) { // // Detect header 0 signature. // if( (pInfo->Packet[0] & DIF_BLK_ID0_SCT_MASK) == 0 && (pInfo->Packet[1] & DIF_BLK_ID1_DSEQ_MASK) == 0 && (pInfo->Packet[2] & DIF_BLK_ID2_DBN_MASK) == 0 ) { // Check TF1, TF2, and TF3: 1: not transmitted; 0:transmitted // TF1:Audio; TF2:Video; TF3:Subcode; they all need to be 0 to be valid. if((pInfo->Packet[5] & 0x80) || (pInfo->Packet[6] & 0x80) || (pInfo->Packet[7] & 0x80) ) { TRACE(TL_CIP_TRACE,("inv src pkts; [%x %x %d %x], [%x %x %x %x]\n", pInfo->Packet[0], pInfo->Packet[1], pInfo->Packet[2], pInfo->Packet[3], pInfo->Packet[4], pInfo->Packet[5], pInfo->Packet[6], pInfo->Packet[7] )); // Valid header but DIF block for this area is not transmitted. // Some DV (such as DVCPro) may wait untill its "mecha and servo" to be stable to make these valid. // This should happen if a graph is in run state before a tape is played (and stablized). return 1; } return 0; } else { return 1; } } else { TRACE(TL_CIP_ERROR,("Validate: invalid SrcPktSeq; Packet %x\n", pInfo->Packet)); return 1; } } // DVReadFrameValidate NTSTATUS AVCStrmProcessReadComplete( PAVCSTRM_DATA_ENTRY pDataEntry, PAVC_STREAM_EXTENSION pAVCStrmExt, PAVC_STREAM_DATA_STRUCT pDataStruc ) /*++ Routine Description: Process the data read completion. --*/ { PKSSTREAM_HEADER pStrmHeader; LONGLONG LastPictureNumber; NTSTATUS Status = STATUS_SUCCESS; pStrmHeader = pDataEntry->StreamHeader; ASSERT(pStrmHeader->Size >= sizeof(KSSTREAM_HEADER)); // Check CIP_STATUS from 61883 // CIP_STATUS_CORRUPT_FRAME (0x00000001) if(pDataEntry->Frame->Status & CIP_STATUS_CORRUPT_FRAME) { TRACE(TL_STRM_WARNING|TL_CIP_WARNING,("CIP_STATUS_CORRUPT_FRAME\n")); pStrmHeader->OptionsFlags = 0; Status = STATUS_SUCCESS; // Success but no data ! pStrmHeader->DataUsed = 0; pDataStruc->PictureNumber++; pDataStruc->FramesProcessed++; } else // CIP_STATUS_SUCCESS (0x00000000) // CIP_STATUS_FIRST_FRAME (0x00000002) if(pDataEntry->Frame->Status == CIP_STATUS_SUCCESS || pDataEntry->Frame->Status & CIP_STATUS_FIRST_FRAME) { // Only increment FramesProcessed if it is a valid frame; pDataStruc->FramesProcessed++; Status = STATUS_SUCCESS; pStrmHeader->OptionsFlags = KSSTREAM_HEADER_OPTIONSF_SPLICEPOINT; #ifdef NT51_61883 pStrmHeader->DataUsed = pDataEntry->Frame->CompletedBytes; #else pStrmHeader->DataUsed = pAVCStrmExt->pAVCStrmDataStruc->FrameSize; #endif // This subunit driver is a Master clock if (pDataEntry->ClockProvider) { #ifdef NT51_61883 ULONG ulDeltaCycleCounts; // If not the first frame. we will calculate the drop frame information. if(pAVCStrmExt->b1stNewFrameFromPauseState) { // Default number of packets for a DV frame if(pDataStruc->FramesProcessed > 1) // PAUSE->RUN->PAUSE->RUN case; no increase for the 1st frame. pDataStruc->CurrentStreamTime += pAVCStrmExt->pAVCStrmFormatInfo->AvgTimePerFrame; pAVCStrmExt->b1stNewFrameFromPauseState = FALSE; } else { ULONG ulCycleCount16bits; // Calculate skipped 1394 cycle from the returned CycleTime VALIDATE_CYCLE_COUNTS(pDataEntry->Frame->Timestamp); ulCycleCount16bits = CALCULATE_CYCLE_COUNTS(pDataEntry->Frame->Timestamp); ulDeltaCycleCounts = CALCULATE_DELTA_CYCLE_COUNT(pAVCStrmExt->CycleCount16bits, ulCycleCount16bits); // Adjust to max allowable gap to the max elapsed time of the CycleTime returned by OHCI 1394. if(ulDeltaCycleCounts > MAX_CYCLES) ulDeltaCycleCounts = MAX_CYCLES; // There are two cases for drop frames: (1) Starve of buffer; (2) no data // If there is starving, status will be CIP_STATUS_FIRST_FRAME. if(pDataEntry->Frame->Status & CIP_STATUS_FIRST_FRAME) { // Convert packets (cycles) to time in 100 nanosecond unit; (one cycle = 1250 * 100 nsec) // We could use the skip frame, but CycleCount is more accurate. pDataStruc->CurrentStreamTime += ulDeltaCycleCounts * TIME_PER_CYCLE; // Use cycle count to be precise. } else { // Ignore all "drop frames" in the "no data" case if(ulDeltaCycleCounts * TIME_PER_CYCLE > pAVCStrmExt->pAVCStrmFormatInfo->AvgTimePerFrame) // There might be some frame(s) skipped due to no data or tape stopped playing, we skip this skipped data. pDataStruc->CurrentStreamTime += pAVCStrmExt->pAVCStrmFormatInfo->AvgTimePerFrame; else pDataStruc->CurrentStreamTime += ulDeltaCycleCounts * TIME_PER_CYCLE; // Use cycle count to be precise. } } // StreamTime start with 0; pStrmHeader->PresentationTime.Time = pDataStruc->CurrentStreamTime; // Use to adjust the queried stream time pAVCStrmExt->LastSystemTime = GetSystemTime(); // Cache current CycleCount pAVCStrmExt->CycleCount16bits = CALCULATE_CYCLE_COUNTS(pDataEntry->Frame->Timestamp); #else // NT51_61883 // This is the old way when 61883 was not returning the correct CycleTime. // This is the old way when 61883 was not returning the correct CycleTime. pStrmHeader->PresentationTime.Time = pDataStruc->CurrentStreamTime; pAVCStrmExt->LastSystemTime = GetSystemTime(); // Use to adjust the queried stream time pDataStruc->CurrentStreamTime += pAVCStrmExt->pAVCStrmFormatInfo->AvgTimePerFrame; #endif // NT51_61883 // no Clock so "free flowing!" } else { pStrmHeader->PresentationTime.Time = 0; } // Put in Timestamp info depending on clock provider pStrmHeader->PresentationTime.Numerator = 1; pStrmHeader->PresentationTime.Denominator = 1; // Only if there is a clock, presentation time and drop frames information are set. // Acoording to DDK: // The PictureNumber member count represents the idealized count of the current picture, // which is calculated in one of two ways: // ("Other" clock) Measure the time since the stream was started and divide by the frame duration. // (MasterClock) Add together the count of frames captured and the count of frame dropped. // // Here, we know the current stream time, and the picture number is calculated from that. // if(pDataEntry->ClockProvider) { pStrmHeader->Duration = pAVCStrmExt->pAVCStrmFormatInfo->AvgTimePerFrame; pStrmHeader->OptionsFlags |= (KSSTREAM_HEADER_OPTIONSF_TIMEVALID | // pStrmHeader->PresentationTime.Time is valid KSSTREAM_HEADER_OPTIONSF_DURATIONVALID); if(pDataEntry->Frame->Status & CIP_STATUS_FIRST_FRAME) pStrmHeader->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_DATADISCONTINUITY; // Calculate picture number and dropped frame; // For NTSC, it could be 267 or 266 packet time per frame. Since integer calculation will round, // we will add a packet time (TIME_PER_CYCLE = 125 us = 1250 100nsec) to that.This is only used for calculation. LastPictureNumber = pDataStruc->PictureNumber; pDataStruc->PictureNumber = 1 + // Picture number start with 1.but PresetationTime start with 0. (pStrmHeader->PresentationTime.Time + TIME_PER_CYCLE) * (LONGLONG) GET_AVG_TIME_PER_FRAME_DENOM(pAVCStrmExt->pAVCStrmFormatInfo->AVCStrmFormat) / (LONGLONG) GET_AVG_TIME_PER_FRAME_NUM(pAVCStrmExt->pAVCStrmFormatInfo->AVCStrmFormat); if(pDataStruc->PictureNumber > LastPictureNumber+1) { pStrmHeader->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_DATADISCONTINUITY; // If there is a skipped frame, set the discontinuity flag TRACE(TL_CIP_WARNING,("Discontinuity: LastPic#:%d; Pic#%d; PresTime:%d;\n", (DWORD) LastPictureNumber, (DWORD) pDataStruc->PictureNumber, (DWORD) pStrmHeader->PresentationTime.Time)); } if(pDataStruc->PictureNumber <= LastPictureNumber) { TRACE(TL_STRM_TRACE|TL_CIP_TRACE,("Same pic #:%d; LastPic:%d; tmPres:%d; OptionFlags:%x\n", (DWORD) pDataStruc->PictureNumber, (DWORD) LastPictureNumber, (DWORD) pStrmHeader->PresentationTime.Time, pStrmHeader->OptionsFlags)); pDataStruc->PictureNumber = LastPictureNumber + 1; // Picture number must progress !!!! } pDataStruc->FramesDropped = pDataStruc->PictureNumber - pDataStruc->FramesProcessed; // no Clock so "free flowing!" } else { pStrmHeader->Duration = 0; // No clock so not valid. pDataStruc->PictureNumber++; TRACE(TL_STRM_TRACE,("No clock: PicNum:%d\n", (DWORD) pDataStruc->PictureNumber)); } } else { // 61883 has not defined this yet! pStrmHeader->OptionsFlags = 0; Status = STATUS_SUCCESS; pStrmHeader->DataUsed = 0; pDataStruc->PictureNumber++; pDataStruc->FramesProcessed++; TRACE(TL_STRM_ERROR|TL_CIP_ERROR,("Unexpected Frame->Status %x\n", pDataEntry->Frame->Status)); ASSERT(FALSE && "Unknown pDataEntry->Frame->Status"); } #if 0 // For VidOnly which uses VideoInfoHeader and has // an extended frame information (KS_FRAME_INFO) appended to KSSTREAM_HEADER if(pStrmHeader->Size >= (sizeof(KSSTREAM_HEADER) + sizeof(PKS_FRAME_INFO)) ) { pFrameInfo = (PKS_FRAME_INFO) (pStrmHeader + 1); pFrameInfo->ExtendedHeaderSize = sizeof(KS_FRAME_INFO); pFrameInfo->PictureNumber = pDataStruc->PictureNumber; pFrameInfo->DropCount = pDataStruc->FramesDropped; pFrameInfo->dwFrameFlags = KS_VIDEO_FLAG_FRAME | // Complete frame KS_VIDEO_FLAG_I_FRAME; // Every DV frame is an I frame } #endif #if DBG // Validate that the data is return in the right sequence if(pDataEntry->FrameNumber != pDataStruc->FramesProcessed) { TRACE(TL_STRM_ERROR|TL_CIP_ERROR,("ProcessRead: OOSequence %d != %d\n", (DWORD) pDataEntry->FrameNumber, (DWORD) pDataStruc->FramesProcessed)); }; #endif return Status; } ULONG AVCStrmCompleteRead( PCIP_NOTIFY_INFO pInfo ) /*++ Routine Description: 61883 has completed receiving data and callback to us to complete. --*/ { PAVCSTRM_DATA_ENTRY pDataEntry; PAVC_STREAM_EXTENSION pAVCStrmExt; PAVC_STREAM_DATA_STRUCT pDataStruc; KIRQL oldIrql; // Callback and in DISPATCH_LEVEL // The called might have acquired spinlock as well! TRACE(TL_STRM_INFO,("CompleteRead: pInfo:%x\n", pInfo)); pDataEntry = pInfo->Context; if(!pDataEntry) { ASSERT(pDataEntry && "Context is NULL!\n"); return 1; } pAVCStrmExt = pDataEntry->pAVCStrmExt; if(!pAVCStrmExt) { ASSERT(pAVCStrmExt && "pAVCStrmExt is NULL\n"); return 1; } pDataStruc = pAVCStrmExt->pAVCStrmDataStruc; if(!pDataStruc) { ASSERT(pDataStruc && "pDataStruc is NULL\n"); return 1; } KeAcquireSpinLock(&pDataStruc->DataListLock, &oldIrql); #if DBG // It is possible that a buffer is completed before it is return from IoCallDriver to attach this buffer. if(!IsStateSet(pDataEntry->State, DE_IRP_LOWER_ATTACHED_COMPLETED)) { TRACE(TL_STRM_WARNING,("AVCStrmCompleteRead: pDataEntry:%x not yet attached but completed.\n", pDataEntry)); // // This irp will be completed from its IoCallDriver to attach this frame. // } #endif // Can the cancel routione is ahead of us? Error condition. if(pDataStruc->cntDataAttached <= 0) { TRACE(TL_STRM_ERROR|TL_CIP_ERROR,("AVCStrmCompleteRead:pAVCStrmExt:%x, pDataEntry:%x, AQD[%d:%d:%d]\n", pAVCStrmExt, pDataEntry, pDataStruc->cntDataAttached, pDataStruc->cntDataQueued,pDataStruc->cntDataDetached)); KeReleaseSpinLock(&pDataStruc->DataListLock, oldIrql); return 1; } // // Process this completion based on the return status from 61883 // pDataEntry->pIrpUpper->IoStatus.Status = AVCStrmProcessReadComplete( pDataEntry, pAVCStrmExt, pDataStruc ); // // There are two possible ways to complete the data request: // // (A) Normal case: attach data request (pIrpLower), attached completed, notify callback (here), and completion (pIrpUpper) // (B) Rare/stress case: attach data request (pIrpLower), notify callback (here), attach complete (pIrpLower), and complete (pIrpUpper) // pDataEntry->State |= DE_IRP_LOWER_CALLBACK_COMPLETED; // // Case (A): If DE_IRP_LOWER_CALLBACK_COMPLETED is set and pIrpUpper is marked pending, complete the UpperIrp. // if(IsStateSet(pDataEntry->State, DE_IRP_LOWER_ATTACHED_COMPLETED)) { if(IsStateSet(pDataEntry->State, DE_IRP_UPPER_PENDING_COMPLETED)) { // // This is the normal case: attached, IoMarkPending, then complete in the callback routine. // 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|TL_CIP_ERROR,("pDataStruc:%x; pDataEntry:%x\n", pDataStruc, pDataEntry)); ASSERT(pDataStruc->cntDataAttached >= 0); } #endif InsertTailList(&pDataStruc->DataDetachedListHead, &pDataEntry->ListEntry); InterlockedIncrement(&pDataStruc->cntDataDetached); // // pDataEntry should not be referenced after this. // } else { TRACE(TL_STRM_TRACE,("Watch out! pDataEntry:%x in between attach complete and IoMarkIrpPending!\n", pDataEntry)); // // Case (B): Complete IrpUpper when return to IoCallDriver(IrpLower) // Note: The IrpLower has not called IoMarkIrpPending(). (Protected with spinlock) // } } else { // // Case (B): Complete IrpUpper when return to IoCallDriver(IrpLower) // } KeReleaseSpinLock(&pDataStruc->DataListLock, oldIrql); return 0; } // AVCStrmCompleteRead #if DBG PAVCSTRM_DATA_ENTRY pLastDataEntry; LONGLONG LastFrameNumber; #endif ULONG AVCStrmCompleteWrite( PCIP_NOTIFY_INFO pInfo ) /*++ Routine Description: 61883 has completed receiving data and callback to us to complete. --*/ { PAVCSTRM_DATA_ENTRY pDataEntry; PAVC_STREAM_EXTENSION pAVCStrmExt; PAVC_STREAM_DATA_STRUCT pDataStruc; NTSTATUS irpStatus; KIRQL oldIrql; // Callback and in DISPATCH_LEVEL // The called might have acquired spinlock as well! TRACE(TL_STRM_INFO,("CompleteWrite: pInfo:%x\n", pInfo)); pDataEntry = pInfo->Context; if(!pDataEntry) { ASSERT(pDataEntry && "Context is NULL!\n"); return 1; } pAVCStrmExt = pDataEntry->pAVCStrmExt; if(!pAVCStrmExt) { ASSERT(pAVCStrmExt && "pAVCStrmExt is NULL\n"); return 1; } pDataStruc = pAVCStrmExt->pAVCStrmDataStruc; if(!pDataStruc) { ASSERT(pDataStruc && "pDataStruc is NULL\n"); return 1; } #if 0 // Must complete it! // If isoch is not active, then we are in the process of stopping; Let this be cancellable. if(!pAVCStrmExt->IsochIsActive) { TRACE(TL_STRM_ERROR,("AVCStrmCompleteRead: IsochActive:%d; pDataEntry:%x\n", pAVCStrmExt->IsochIsActive, pDataEntry)); ASSERT(pAVCStrmExt->IsochIsActive); return 1; } #endif irpStatus = STATUS_SUCCESS; KeAcquireSpinLock(&pDataStruc->DataListLock, &oldIrql); #if DBG // It is possible that a buffer is completed before it is return from IoCallDriver to attach this buffer. if(!IsStateSet(pDataEntry->State, DE_IRP_LOWER_ATTACHED_COMPLETED)) { TRACE(TL_STRM_WARNING,("CompleteWrite: pDataEntry:%x not yet attached but completed.\n", pDataEntry)); // // This irp will be completed from its IoCallDriver to attach this frame. // } #endif // Can the cancel routione is ahead of us? Error condition. if(pDataStruc->cntDataAttached <= 0) { TRACE(TL_STRM_ERROR|TL_CIP_ERROR,("AVCStrmCompleteWrite:pAVCStrmExt:%x, pDataEntry:%x, AQD[%d:%d:%d]\n", pAVCStrmExt, pDataEntry, pDataStruc->cntDataAttached, pDataStruc->cntDataQueued,pDataStruc->cntDataDetached)); KeReleaseSpinLock(&pDataStruc->DataListLock, oldIrql); return 1; } // // Process according to status for the frame // if(pDataEntry->Frame->Status & CIP_STATUS_CORRUPT_FRAME) { pDataStruc->FramesProcessed++; TRACE(TL_CIP_ERROR,("CIP_STATUS_CORRUPT_FRAME; pIrpUpper:%x; pIrpLower:%x\n", pDataEntry->pIrpUpper, pDataEntry->pIrpLower)); } else if(pDataEntry->Frame->Status == CIP_STATUS_SUCCESS || pDataEntry->Frame->Status & CIP_STATUS_FIRST_FRAME) { #if DBG if(pDataEntry->Frame->Status & CIP_STATUS_FIRST_FRAME) TRACE(TL_CIP_TRACE,("CIP_STATUS_FIRST_FRAME; pIrpUpper:%x; pIrpLower:%x\n", pDataEntry->pIrpUpper, pDataEntry->pIrpLower)); #endif pDataStruc->FramesProcessed++; } else { pDataStruc->FramesProcessed++; TRACE(TL_CIP_ERROR,("Unknown Status:%x\n", pDataEntry->Frame->Status)); } pDataStruc->PictureNumber++; #if DBG // // Validate that the data is return in the right sequence // if(pDataEntry->FrameNumber != pDataStruc->FramesProcessed) { TRACE(TL_STRM_WARNING|TL_CIP_WARNING,("CompleteWrite: OOSequence FrameNum:%d != FrameProcessed:%d; pIrpUpper:%x; pIrpLower:%x; Last(%d:%x,%x)\n", (DWORD) pDataEntry->FrameNumber, (DWORD) pDataStruc->FramesProcessed, pDataEntry->pIrpUpper, pDataEntry->pIrpLower, (DWORD) pLastDataEntry->FrameNumber, pLastDataEntry->pIrpUpper, pLastDataEntry->pIrpLower )); // ASSERT(pDataEntry->FrameNumber == pDataStruc->FramesProcessed); }; pLastDataEntry = pDataEntry; LastFrameNumber = pDataEntry->FrameNumber; #endif // // There are two possible ways to complete the data request: // // (A) Normal case: attach data request (pIrpLower), attached completed, notify callback (here), and completion (pIrpUpper) // (B) Rare/stress case: attach data request (pIrpLower), notify callback (here), attach complete (pIrpLower), and complete (pIrpUpper) // pDataEntry->pIrpUpper->IoStatus.Status = irpStatus; pDataEntry->State |= DE_IRP_LOWER_CALLBACK_COMPLETED; // // Case (A): If DE_IRP_LOWER_CALLBACK_COMPLETED is set and pIrpUpper is marked pending, complete the UpperIrp. // if(IsStateSet(pDataEntry->State, DE_IRP_LOWER_ATTACHED_COMPLETED)) { if(IsStateSet(pDataEntry->State, DE_IRP_UPPER_PENDING_COMPLETED)) { // // This is the normal case: attached, IoMarkPending, then complete in the callback routine. // 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|TL_CIP_ERROR,("pDataStruc:%x; pDataEntry:%x\n", pDataStruc, pDataEntry)); ASSERT(pDataStruc->cntDataAttached >= 0); } #endif InsertTailList(&pDataStruc->DataDetachedListHead, &pDataEntry->ListEntry); InterlockedIncrement(&pDataStruc->cntDataDetached); // // pDataEntry should not be referenced after this. // } else { TRACE(TL_STRM_TRACE,("Watch out! pDataEntry:%x in between attach complete and IoMarkIrpPending!\n", pDataEntry)); // // Case (B): Complete IrpUpper when return to IoCallDriver(IrpLower); // Note: The IrpLower has not called IoMarkIrpPending(). (Protected with spinlock) // } } else { // // Case (B): Complete IrpUpper when return to IoCallDriver(IrpLower) // } KeReleaseSpinLock(&pDataStruc->DataListLock, oldIrql); return 0; } // AVCStrmCompleteWrite NTSTATUS AVCStrmAttachFrameCR( IN PDEVICE_OBJECT DeviceObject, IN PIRP pIrp, IN PAVCSTRM_DATA_ENTRY pDataEntry ) /*++ Routine Description: Completion routine for attaching a data request to 61883. --*/ { PAVC_STREAM_EXTENSION pAVCStrmExt; PAVC_STREAM_DATA_STRUCT pDataStruc; KIRQL oldIrql; PAGED_CODE(); pAVCStrmExt = pDataEntry->pAVCStrmExt; pDataStruc = pAVCStrmExt->pAVCStrmDataStruc; KeAcquireSpinLock(&pDataStruc->DataListLock, &oldIrql); // // Check for possible attaching data request error // if(!NT_SUCCESS(pIrp->IoStatus.Status)) { TRACE(TL_STRM_ERROR|TL_CIP_ERROR,("AttachFrameCR: pDataEntry:%x; pIrp->IoStatus.Status:%x (Error!)\n", pDataEntry, pIrp->IoStatus.Status)); ASSERT(NT_SUCCESS(pIrp->IoStatus.Status)); pDataEntry->State |= DE_IRP_ERROR; // // If attach data request has failed, we complete the pIrpUpper with this error. // pDataEntry->pIrpUpper->IoStatus.Status = pIrp->IoStatus.Status; // or should we cancel (STATUS_CANCELLED) it? 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 completion event when all attached are completed. // if(pAVCStrmExt->DataFlow != KSPIN_DATAFLOW_IN && pDataStruc->cntDataAttached == 0) KeSetEvent(&pDataStruc->hNoAttachEvent, 0, FALSE); ASSERT(pDataStruc->cntDataAttached >= 0); InsertTailList(&pDataStruc->DataDetachedListHead, &pDataEntry->ListEntry); InterlockedIncrement(&pDataStruc->cntDataDetached); // // No additional processing when return to IoCallDriver() with the error pIrp->IoStatus.Status. // KeReleaseSpinLock(&pDataStruc->DataListLock, oldIrql); return STATUS_MORE_PROCESSING_REQUIRED; } #if DBG // // Validate that the data is attached in the right sequence // pDataStruc->FramesAttached++; if(pDataEntry->FrameNumber != pDataStruc->FramesAttached) { TRACE(TL_STRM_WARNING|TL_CIP_WARNING,("Attached completed OOSequence FrameNum:%d != FrameAttached:%d; pIrpUpper:%x; pIrpLower:%x; Last(%d:%x,%x)\n", (DWORD) pDataEntry->FrameNumber, (DWORD) pDataStruc->FramesAttached )); // ASSERT(pDataEntry->FrameNumber == pDataStruc->FramesAttached); }; #endif // // Attached data reuqest to 61883 is completed (note: however, we do not know if callback is called.) // pDataEntry->State |= DE_IRP_LOWER_ATTACHED_COMPLETED; KeReleaseSpinLock(&pDataStruc->DataListLock, oldIrql); return STATUS_MORE_PROCESSING_REQUIRED; } VOID AVCStrmFormatAttachFrame( IN KSPIN_DATAFLOW DataFlow, IN PAVC_STREAM_EXTENSION pAVCStrmExt, IN AVCSTRM_FORMAT AVCStrmFormat, IN PAV_61883_REQUEST pAVReq, IN PAVCSTRM_DATA_ENTRY pDataEntry, IN ULONG ulSourcePacketSize, // Packet length in bytes IN ULONG ulFrameSize, // Buffer size; may contain one or multiple source packets IN PIRP pIrpUpper, IN PKSSTREAM_HEADER StreamHeader, IN PVOID FrameBuffer ) /*++ Routine Description: Format an attach frame request. --*/ { InitializeListHead(&pDataEntry->ListEntry); // A DataEntry must be previously completed! ASSERT(IsStateSet(pDataEntry->State, DE_IRP_UPPER_COMPLETED) && "Reusing a data entry that was not completed!"); pDataEntry->State = DE_PREPARED; // Initial state of a resued DataEntry (start over!) pDataEntry->pAVCStrmExt = pAVCStrmExt; pDataEntry->pIrpUpper = pIrpUpper; pDataEntry->StreamHeader = StreamHeader; pDataEntry->FrameBuffer = FrameBuffer; ASSERT(pDataEntry->FrameBuffer != NULL); pDataEntry->Frame->pNext = NULL; pDataEntry->Frame->Status = 0; pDataEntry->Frame->Packet = (PUCHAR) FrameBuffer; #if DBG pDataEntry->FrameNumber = pAVCStrmExt->pAVCStrmDataStruc->cntDataReceived; #endif pDataEntry->Frame->Flags = 0; if(DataFlow == KSPIN_DATAFLOW_OUT) { // DV needs validation to determine the header section as the start of a DV frame if(AVCStrmFormat == AVCSTRM_FORMAT_SDDV_NTSC || AVCStrmFormat == AVCSTRM_FORMAT_SDDV_PAL || AVCStrmFormat == AVCSTRM_FORMAT_HDDV_NTSC || AVCStrmFormat == AVCSTRM_FORMAT_HDDV_PAL || AVCStrmFormat == AVCSTRM_FORMAT_SDLDV_NTSC || AVCStrmFormat == AVCSTRM_FORMAT_SDLDV_PAL ) { pDataEntry->Frame->pfnValidate = AVCStrmDVReadFrameValidate; // use to validate the 1st source packet #ifdef NT51_61883 // // Set CIP_USE_SOURCE_HEADER_TIMESTAMP to get 25 bit CycleTime from source packet header // (13CycleCount:12CycleOffset) // Do not set this to get 16 bit CycleTime from isoch packet (3 SecondCount:13CycleCount) // pDataEntry->Frame->Flags |= ( CIP_VALIDATE_FIRST_SOURCE | CIP_RESET_FRAME_ON_DISCONTINUITY); // Reset buffer pointer when encounter discontinuity #endif } else { // MPEG2 specific flags pDataEntry->Frame->pfnValidate = NULL; if(pAVCStrmExt->pAVCStrmFormatInfo->OptionFlags & AVCSTRM_FORMAT_OPTION_STRIP_SPH) pDataEntry->Frame->Flags |= CIP_STRIP_SOURCE_HEADER; } pDataEntry->Frame->ValidateContext = pDataEntry; pDataEntry->Frame->pfnNotify = AVCStrmCompleteRead; } else { // DV needs validation to determine the header section as the start of a DV frame if(AVCStrmFormat == AVCSTRM_FORMAT_SDDV_NTSC || AVCStrmFormat == AVCSTRM_FORMAT_SDDV_PAL || AVCStrmFormat == AVCSTRM_FORMAT_HDDV_NTSC || AVCStrmFormat == AVCSTRM_FORMAT_HDDV_PAL || AVCStrmFormat == AVCSTRM_FORMAT_SDLDV_NTSC || AVCStrmFormat == AVCSTRM_FORMAT_SDLDV_PAL ) { pDataEntry->Frame->Flags |= CIP_DV_STYLE_SYT; } else { // MPEG2 specific flag } pDataEntry->Frame->pfnValidate = NULL; pDataEntry->Frame->ValidateContext = NULL; pDataEntry->Frame->pfnNotify = AVCStrmCompleteWrite; } pDataEntry->Frame->NotifyContext = pDataEntry; // // Av61883_AttachFrames // RtlZeroMemory(pAVReq, sizeof(AV_61883_REQUEST)); INIT_61883_HEADER(pAVReq, Av61883_AttachFrame); pAVReq->AttachFrame.hConnect = pAVCStrmExt->hConnect; pAVReq->AttachFrame.FrameLength = ulFrameSize; pAVReq->AttachFrame.SourceLength = ulSourcePacketSize; pAVReq->AttachFrame.Frame = pDataEntry->Frame; TRACE(TL_STRM_TRACE,("DataFlow:%d; pDataEntry:%x; pIrpUp:%x; hConnect:%x; FrameSz:%d; SrcPktSz:%d; Frame:%x;\n pfnVldt:(%x, %x); pfnNtfy:(%x, %x) \n", DataFlow, pDataEntry, pIrpUpper, pAVCStrmExt->hConnect, ulFrameSize, ulSourcePacketSize, pDataEntry->Frame, pAVReq->AttachFrame.Frame->pfnValidate, pAVReq->AttachFrame.Frame->ValidateContext, pAVReq->AttachFrame.Frame->pfnNotify, pAVReq->AttachFrame.Frame->NotifyContext)); ASSERT(pAVCStrmExt->hConnect); } NTSTATUS AVCStrmCancelOnePacketCR( IN PDEVICE_OBJECT DeviceObject, IN PIRP pIrpLower, IN PAVCSTRM_DATA_ENTRY pDataEntry ) /*++ Routine Description: Completion routine for detach an isoch descriptor associate with a pending IO. Will cancel the pending IO here if detaching descriptor has suceeded. --*/ { PAVC_STREAM_EXTENSION pAVCStrmExt; PAVC_STREAM_DATA_STRUCT pDataStruc; KIRQL oldIrql; ENTER("AVCStrmCancelOnePacketCR"); if(!pDataEntry) { ASSERT(pDataEntry); return STATUS_MORE_PROCESSING_REQUIRED; } pAVCStrmExt = pDataEntry->pAVCStrmExt; ASSERT(pAVCStrmExt); pDataStruc = pAVCStrmExt->pAVCStrmDataStruc; ASSERT(pDataStruc); KeAcquireSpinLock(&pDataStruc->DataListLock, &oldIrql); if(!NT_SUCCESS(pIrpLower->IoStatus.Status)) { TRACE(TL_STRM_ERROR|TL_CIP_ERROR,("CancelOnePacketCR: pIrpLower->IoStatus.Status %x (Error!)\n", pIrpLower->IoStatus.Status)); ASSERT(pIrpLower->IoStatus.Status != STATUS_NOT_FOUND); // Catch lost packet! pDataEntry->State |= DE_IRP_ERROR; // // Even though there is an error, but we have a valid DataEntry. // Go ahead complete and cancel it. // } #ifdef NT51_61883 // // Special case for MPEG2TS data since a data buffer contains multiple data packets // (188*N or 192*N) in one data buffer. The first cancelled buffer may contain valid // data packet that an application will need to completely present a video frame. // So instead of cancelling it, it will be completed. // if( pAVCStrmExt->pAVCStrmFormatInfo->AVCStrmFormat == AVCSTRM_FORMAT_MPEG2TS && pDataEntry->Frame->CompletedBytes) { pDataEntry->pIrpUpper->IoStatus.Status = AVCStrmProcessReadComplete( pDataEntry, pAVCStrmExt, pDataStruc ); // // CompletedBytes should be multiple of 188 or 192 bytes // ASSERT(pDataEntry->Frame->CompletedBytes % \ ((pAVCStrmExt->pAVCStrmFormatInfo->OptionFlags & AVCSTRM_FORMAT_OPTION_STRIP_SPH) ? 188 : 192) == 0); TRACE(TL_PNP_ERROR,("pDataEntry:%x; Cancelled buffer (MPEG2TS) has %d bytes; Status:%x\n", pDataEntry, pDataEntry->Frame->CompletedBytes, pIrpLower->IoStatus.Status, pDataEntry->pIrpUpper->IoStatus.Status)); } else { pDataStruc->cntFrameCancelled++; pDataEntry->pIrpUpper->IoStatus.Status = STATUS_CANCELLED; TRACE(TL_CIP_TRACE,("pDataEntry:%x; Cancelled buffer (MPEG2TS) has %d bytes; Status:%x\n", pDataEntry, pDataEntry->Frame->CompletedBytes, pIrpLower->IoStatus.Status, pDataEntry->pIrpUpper->IoStatus.Status)); } #else pDataStruc->cntFrameCancelled++; pDataEntry->pIrpUpper->IoStatus.Status = STATUS_CANCELLED; #endif IoCompleteRequest(pDataEntry->pIrpUpper, IO_NO_INCREMENT); pDataEntry->State |= DE_IRP_UPPER_COMPLETED; pDataEntry->State |= DE_IRP_CANCELLED; pDataEntry->pIrpUpper = NULL; // No more access of this! // // Note: pDataEntry is already dequed from DataAttachList // InsertTailList(&pDataStruc->DataDetachedListHead, &pDataEntry->ListEntry); InterlockedIncrement(&pDataStruc->cntDataDetached); KeReleaseSpinLock(&pDataStruc->DataListLock, oldIrql); EXIT("AVCStrmCancelOnePacketCR", STATUS_MORE_PROCESSING_REQUIRED); return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS AVCStrmCancelIO( IN PDEVICE_OBJECT DeviceObject, IN PAVC_STREAM_EXTENSION pAVCStrmExt ) /*++ Routine Description: Cancel all pending IOs --*/ { NTSTATUS Status; PAVC_STREAM_DATA_STRUCT pDataStruc; KIRQL oldIrql; PAVCSTRM_DATA_ENTRY pDataEntry; PAV_61883_REQUEST pAVReq; PIO_STACK_LOCATION NextIrpStack; PAGED_CODE(); ENTER("AVCStrmCancelIO"); Status = STATUS_SUCCESS; if(pAVCStrmExt->IsochIsActive) { TRACE(TL_STRM_WARNING,("Isoch is active while trying to cancel IO!\n")); // Try stop isoch and continue if success! Status = AVCStrmStopIsoch(DeviceObject, pAVCStrmExt); if(!NT_SUCCESS(Status) && Status != STATUS_NO_SUCH_DEVICE) { TRACE(TL_STRM_ERROR,("Isoch stop failed! Cannnot cancelIO while isoch active.\n")); return Status; } } // // Guard againt data attach completion // KeWaitForMutexObject(&pAVCStrmExt->hMutexControl, Executive, KernelMode, FALSE, NULL); // // Cancel all pending IOs // pDataStruc = pAVCStrmExt->pAVCStrmDataStruc; TRACE(TL_STRM_WARNING,("CancelIO Starting: pDataStruc:%x; AQD [%d:%d:%d]\n", pDataStruc, pDataStruc->cntDataAttached, pDataStruc->cntDataQueued, pDataStruc->cntDataDetached)); KeAcquireSpinLock(&pDataStruc->DataListLock, &oldIrql); while (!IsListEmpty(&pDataStruc->DataAttachedListHead)) { pDataEntry = (PAVCSTRM_DATA_ENTRY) \ RemoveHeadList(&pDataStruc->DataAttachedListHead); InterlockedDecrement(&pDataStruc->cntDataAttached); #if DBG if(!IsStateSet(pDataEntry->State, DE_IRP_LOWER_ATTACHED_COMPLETED)) { TRACE(TL_STRM_ERROR|TL_CIP_ERROR,("CancelIO: pDataEntry:%x\n", pDataEntry)); // Must be already attached in order to cancel it. ASSERT(IsStateSet(pDataEntry->State, DE_IRP_LOWER_ATTACHED_COMPLETED)); } #endif KeReleaseSpinLock(&pDataStruc->DataListLock, oldIrql); // Issue 61883 request to cancel this attach pAVReq = &pDataEntry->AVReq; RtlZeroMemory(pAVReq, sizeof(AV_61883_REQUEST)); INIT_61883_HEADER(pAVReq, Av61883_CancelFrame); pAVReq->CancelFrame.hConnect = pAVCStrmExt->hConnect; pAVReq->CancelFrame.Frame = pDataEntry->Frame; TRACE(TL_STRM_TRACE,("Canceling AttachList: pAvReq %x; pDataEntry:%x\n", pAVReq, pDataEntry)); NextIrpStack = IoGetNextIrpStackLocation(pDataEntry->pIrpLower); NextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; NextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_61883_CLASS; NextIrpStack->Parameters.Others.Argument1 = pAVReq; IoSetCompletionRoutine( pDataEntry->pIrpLower, AVCStrmCancelOnePacketCR, pDataEntry, TRUE, TRUE, TRUE ); Status = IoCallDriver( DeviceObject, pDataEntry->pIrpLower ); ASSERT(Status == STATUS_PENDING || Status == STATUS_SUCCESS || Status == STATUS_NO_SUCH_DEVICE); KeAcquireSpinLock(&pDataStruc->DataListLock, &oldIrql); } // while KeReleaseSpinLock(&pDataStruc->DataListLock, oldIrql); TRACE(TL_61883_TRACE,("CancelIO complete: pDataStruc:%x; AQD [%d:%d:%d]\n", pDataStruc, pDataStruc->cntDataAttached, pDataStruc->cntDataQueued, pDataStruc->cntDataDetached)); // // Guard against data attach completion // KeReleaseMutex(&pAVCStrmExt->hMutexControl, FALSE); EXIT("AVCStrmCancelIO", Status); return Status; } NTSTATUS AVCStrmValidateFormat( PAVCSTRM_FORMAT_INFO pAVCFormatInfo ) /*++ Routine Description: Validate AVC format information. --*/ { NTSTATUS Status; PAGED_CODE(); Status = STATUS_SUCCESS; if(pAVCFormatInfo->SizeOfThisBlock != sizeof(AVCSTRM_FORMAT_INFO)) { TRACE(TL_STRM_ERROR,("pAVCFormatInfo:%x; SizeOfThisBlock:%d != %d\n", pAVCFormatInfo, pAVCFormatInfo->SizeOfThisBlock, sizeof(AVCSTRM_FORMAT_INFO))); ASSERT((pAVCFormatInfo->SizeOfThisBlock == sizeof(AVCSTRM_FORMAT_INFO)) && "Invalid format info parameter!"); return STATUS_INVALID_PARAMETER; } TRACE(TL_STRM_TRACE|TL_CIP_TRACE,("ValidateFormat: pAVCFormatInfo:%x; idx:%d; SrcPkt:%d; RcvBuf:%d; XmtBuf:%d; Strip:%d; AvgTm:%d; BlkPeriod:%d\n", pAVCFormatInfo, pAVCFormatInfo->AVCStrmFormat, pAVCFormatInfo->SrcPacketsPerFrame, pAVCFormatInfo->NumOfRcvBuffers, pAVCFormatInfo->NumOfXmtBuffers, pAVCFormatInfo->OptionFlags, pAVCFormatInfo->AvgTimePerFrame, pAVCFormatInfo->BlockPeriod )); TRACE(TL_STRM_TRACE|TL_CIP_TRACE,("ValidateFormat: cip1(DBS:%d, FN:%x); cip2(FMT:%x, 50_60:%x, STYPE:%x, SYT:%x)\n", pAVCFormatInfo->cipHdr1.DBS, pAVCFormatInfo->cipHdr1.FN, pAVCFormatInfo->cipHdr2.FMT, pAVCFormatInfo->cipHdr2.F5060_OR_TSF, pAVCFormatInfo->cipHdr2.STYPE, pAVCFormatInfo->cipHdr2.SYT )); if(pAVCFormatInfo->SrcPacketsPerFrame == 0 || (pAVCFormatInfo->NumOfRcvBuffers == 0 && pAVCFormatInfo->NumOfXmtBuffers == 0) || // pAVCFormatInfo->AvgTimePerFrame == 0 || pAVCFormatInfo->BlockPeriod == 0 || pAVCFormatInfo->cipHdr1.DBS == 0 ) { TRACE(TL_STRM_ERROR,("ValidateFormat: Invalid parametert!\n")); return STATUS_INVALID_PARAMETER; } return Status; } NTSTATUS AVCStrmAllocateQueues( IN struct DEVICE_EXTENSION * pDevExt, IN PAVC_STREAM_EXTENSION pAVCStrmExt, IN KSPIN_DATAFLOW DataFlow, IN PAVC_STREAM_DATA_STRUCT pDataStruc, PAVCSTRM_FORMAT_INFO pAVCStrmFormatInfo ) /*++ Routine Description: Preallocated all nodes for the data queuding. --*/ { ULONG ulNumberOfNodes; ULONG ulSizeOfOneNode; // Might combine multiple structures ULONG ulSizeAllocated; PBYTE pMemoryBlock; PAVCSTRM_DATA_ENTRY pDataEntry; ULONG i; PCIP_HDR1 pCipHdr1; PAGED_CODE(); ENTER("AVCStrmAllocateQueues"); // // Pre-allcoate PC resource // ulNumberOfNodes = DataFlow == KSPIN_DATAFLOW_OUT ? \ pAVCStrmFormatInfo->NumOfRcvBuffers : pAVCStrmFormatInfo->NumOfXmtBuffers; ASSERT(ulNumberOfNodes > 0); ulSizeOfOneNode = sizeof(AVCSTRM_DATA_ENTRY) + sizeof(struct _CIP_FRAME); ulSizeAllocated = ulNumberOfNodes * ulSizeOfOneNode; pMemoryBlock = ExAllocatePool(NonPagedPool, ulSizeAllocated); if(!pMemoryBlock) { return STATUS_NO_MEMORY; } RtlZeroMemory(pMemoryBlock, ulSizeAllocated); // Initialize data IO structure InitializeListHead(&pDataStruc->DataAttachedListHead); InitializeListHead(&pDataStruc->DataQueuedListHead); InitializeListHead(&pDataStruc->DataDetachedListHead); KeInitializeSpinLock(&pDataStruc->DataListLock); KeInitializeEvent(&pDataStruc->hNoAttachEvent, NotificationEvent, FALSE); // Cache it for freeing purpose; pDataStruc->pMemoryBlock = pMemoryBlock; pDataEntry = (PAVCSTRM_DATA_ENTRY) pMemoryBlock; for (i=0; i < ulNumberOfNodes; i++) { ((PBYTE) pDataEntry->Frame) = ((PBYTE) pDataEntry) + sizeof(AVCSTRM_DATA_ENTRY); pDataEntry->pIrpLower = IoAllocateIrp(pDevExt->physicalDevObj->StackSize, FALSE); if(!pDataEntry->pIrpLower) { while(!IsListEmpty(&pDataStruc->DataDetachedListHead)) { pDataEntry = (PAVCSTRM_DATA_ENTRY) \ RemoveHeadList(&pDataStruc->DataDetachedListHead); InterlockedDecrement(&pDataStruc->cntDataDetached); if(pDataEntry->pIrpLower) { IoFreeIrp(pDataEntry->pIrpLower); pDataEntry->pIrpLower = NULL; } } ExFreePool(pMemoryBlock); pMemoryBlock = NULL; return STATUS_INSUFFICIENT_RESOURCES; } pDataEntry->State = DE_IRP_UPPER_COMPLETED; // Inital state InsertTailList(&pDataStruc->DataDetachedListHead, &pDataEntry->ListEntry); InterlockedIncrement(&pDataStruc->cntDataDetached); ((PBYTE) pDataEntry) += ulSizeOfOneNode; } pCipHdr1 = &pAVCStrmFormatInfo->cipHdr1; // Calculate source packet size (if strip header, 4 bytes less). pDataStruc->SourcePacketSize = \ pCipHdr1->DBS * 4 * (1 << pCipHdr1->FN) - \ ((pAVCStrmFormatInfo->OptionFlags & AVCSTRM_FORMAT_OPTION_STRIP_SPH) ? 4 : 0); pDataStruc->FrameSize = \ pDataStruc->SourcePacketSize * pAVCStrmFormatInfo->SrcPacketsPerFrame; TRACE(TL_STRM_TRACE,("DBS:%d; FN:%d; SrcPktSz:%d; SrcPktPerFrame:%d; FrameSize:%d\n", pCipHdr1->DBS, pCipHdr1->FN, pDataStruc->SourcePacketSize, pAVCStrmFormatInfo->SrcPacketsPerFrame, pDataStruc->FrameSize )); TRACE(TL_STRM_TRACE,("pDataStruc:%x; A(%d,%x); Q(%d,%x); D(%d,%x)\n", pDataStruc, pDataStruc->cntDataAttached, &pDataStruc->DataAttachedListHead, pDataStruc->cntDataQueued, &pDataStruc->DataQueuedListHead, pDataStruc->cntDataDetached, &pDataStruc->DataDetachedListHead )); return STATUS_SUCCESS; } NTSTATUS AVCStrmFreeQueues( IN PAVC_STREAM_DATA_STRUCT pDataStruc ) /*++ Routine Description: Free nodes preallocated. --*/ { PAVCSTRM_DATA_ENTRY pDataEntry; PAGED_CODE(); ENTER("AVCStrmFreeQueues"); while(!IsListEmpty(&pDataStruc->DataAttachedListHead)) { pDataEntry = (PAVCSTRM_DATA_ENTRY) \ RemoveHeadList(&pDataStruc->DataAttachedListHead); InterlockedDecrement(&pDataStruc->cntDataAttached); if(pDataEntry->pIrpLower) { IoFreeIrp(pDataEntry->pIrpLower); pDataEntry->pIrpLower = NULL; } } if(pDataStruc->cntDataAttached == 0) { ExFreePool(pDataStruc->pMemoryBlock); pDataStruc->pMemoryBlock = NULL; return STATUS_SUCCESS; } else { TRACE(TL_STRM_ERROR,("FreeQueue: pDataStruc:%x, cntDataAttached:%x\n", pDataStruc, pDataStruc->cntDataAttached)); ASSERT(pDataStruc->cntDataAttached == 0); return STATUS_UNSUCCESSFUL; } } void AVCStrmAbortStreamingWorkItemRoutine( #ifdef USE_WDM110 // Win2000 code base // Extra parameter if using WDM10 PDEVICE_OBJECT DeviceObject, #endif IN PAVC_STREAM_EXTENSION pAVCStrmExt ) /*++ Routine Description: This work item routine will stop streaming and cancel all IOs while running at PASSIVE level. --*/ { PAGED_CODE(); ENTER("AVCStrmAbortStreamingWorkItemRoutine"); TRACE(TL_STRM_WARNING,("CancelWorkItem: StreamState:%d; lCancel:%d\n", pAVCStrmExt->StreamState, pAVCStrmExt->lAbortToken)); ASSERT(pAVCStrmExt->lAbortToken == 1); #ifdef USE_WDM110 // Win2000 code base ASSERT(pAVCStrmExt->pIoWorkItem); #endif if(pAVCStrmExt->StreamState == KSSTATE_STOP) { ASSERT(pAVCStrmExt->StreamState == KSSTATE_STOP && "CancelWorkItem: Stream is already stopped!\n"); goto Done; } // Cancel all pending IOs AVCStrmCancelIO(pAVCStrmExt->pDevExt->physicalDevObj, pAVCStrmExt); Done: #ifdef USE_WDM110 // Win2000 code base // Release work item and release the cancel token IoFreeWorkItem(pAVCStrmExt->pIoWorkItem); pAVCStrmExt->pIoWorkItem = NULL; #endif // Release token and indicate abort has completed. InterlockedExchange(&pAVCStrmExt->lAbortToken, 0); KeSetEvent(&pAVCStrmExt->hAbortDoneEvent, 0, FALSE); } /***************************** * Property utility funcrtions *****************************/ NTSTATUS AVCStrmGetConnectionProperty( IN struct DEVICE_EXTENSION * pDevExt, IN PAVC_STREAM_EXTENSION pAVCStrmExt, PSTREAM_PROPERTY_DESCRIPTOR pSPD, PULONG pulActualBytesTransferred ) /*++ Routine Description: Handles KS_PROPERTY_CONNECTION* request. For now, only ALLOCATORFRAMING and CONNECTION_STATE are supported. --*/ { NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); ENTER("AVCStrmGetConnectionProperty"); TRACE(TL_STRM_TRACE,("GetConnectionProperty: entered ...\n")); switch (pSPD->Property->Id) { case KSPROPERTY_CONNECTION_ALLOCATORFRAMING: if (pDevExt != NULL && pDevExt->NumberOfStreams) { PKSALLOCATOR_FRAMING pFraming = (PKSALLOCATOR_FRAMING) pSPD->PropertyInfo; pFraming->RequirementsFlags = KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY | KSALLOCATOR_REQUIREMENTF_INPLACE_MODIFIER | KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY; pFraming->PoolType = NonPagedPool; pFraming->Frames = \ pAVCStrmExt->DataFlow == KSPIN_DATAFLOW_OUT ? \ pAVCStrmExt->pAVCStrmFormatInfo->NumOfRcvBuffers : \ pAVCStrmExt->pAVCStrmFormatInfo->NumOfXmtBuffers; // Note: we'll allocate the biggest frame. We need to make sure when we're // passing the frame back up we also set the number of bytes in the frame. pFraming->FrameSize = pAVCStrmExt->pAVCStrmDataStruc->FrameSize; pFraming->FileAlignment = 0; // FILE_LONG_ALIGNMENT; pFraming->Reserved = 0; *pulActualBytesTransferred = sizeof (KSALLOCATOR_FRAMING); TRACE(TL_STRM_TRACE,("*** AllocFraming: cntStrmOpen:%d; Frames %d; size:%d\n", \ pDevExt->NumberOfStreams, pFraming->Frames, pFraming->FrameSize)); } else { TRACE(TL_STRM_ERROR,("*** AllocFraming: pDevExt:%x; cntStrmOpen:%d\n", pDevExt, pDevExt->NumberOfStreams)); Status = STATUS_INVALID_PARAMETER; } break; default: *pulActualBytesTransferred = 0; Status = STATUS_NOT_SUPPORTED; ASSERT(pSPD->Property->Id == KSPROPERTY_CONNECTION_ALLOCATORFRAMING); break; } TRACE(TL_STRM_TRACE,("GetConnectionProperty: exit.\n")); return Status; } NTSTATUS AVCStrmGetDroppedFramesProperty( IN struct DEVICE_EXTENSION * pDevExt, IN PAVC_STREAM_EXTENSION pAVCStrmExt, PSTREAM_PROPERTY_DESCRIPTOR pSPD, PULONG pulBytesTransferred ) /*++ Routine Description: Return the dropped frame information while captureing. --*/ { NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); ENTER("AVCStrmGetDroppedFramesProperty"); switch (pSPD->Property->Id) { case KSPROPERTY_DROPPEDFRAMES_CURRENT: { PKSPROPERTY_DROPPEDFRAMES_CURRENT_S pDroppedFrames = (PKSPROPERTY_DROPPEDFRAMES_CURRENT_S) pSPD->PropertyInfo; pDroppedFrames->AverageFrameSize = pAVCStrmExt->pAVCStrmDataStruc->FrameSize; pDroppedFrames->PictureNumber = pAVCStrmExt->pAVCStrmDataStruc->PictureNumber; pDroppedFrames->DropCount = pAVCStrmExt->pAVCStrmDataStruc->FramesDropped; // For transmit, this value includes both dropped and repeated. TRACE(TL_STRM_TRACE,("*DroppedFP: Pic#(%d), Drp(%d)\n", (LONG) pDroppedFrames->PictureNumber, (LONG) pDroppedFrames->DropCount)); *pulBytesTransferred = sizeof (KSPROPERTY_DROPPEDFRAMES_CURRENT_S); Status = STATUS_SUCCESS; } break; default: *pulBytesTransferred = 0; Status = STATUS_NOT_SUPPORTED; ASSERT(pSPD->Property->Id == KSPROPERTY_DROPPEDFRAMES_CURRENT); break; } return Status; }