/*++ Copyright (C) Microsoft Corporation, 1999 - 2000 Module Name: MSDVLowr.c Abstract: Interface code with 61883 or 1394 class driver. Last changed by: Author: Yee J. Wu Environment: Kernel mode only Revision History: $Revision:: $ $Date:: $ --*/ #include "strmini.h" #include "ksmedia.h" #include "1394.h" #include "61883.h" #include "dbg.h" #include "msdvfmt.h" #include "msdvdef.h" #include "MSDVUtil.h" #include "MSDVGuts.h" #include "XPrtDefs.h" #include "EDevCtrl.h" extern DV_FORMAT_INFO DVFormatInfoTable[]; extern const GUID KSEVENTSETID_Connection_Local; // // Simple function prototype // VOID DVSRBRead( IN PKSSTREAM_HEADER pStrmHeader, IN ULONG ulFrameSize, IN PDVCR_EXTENSION pDevExt, IN PSTREAMEX pStrmExt, IN PHW_STREAM_REQUEST_BLOCK pSrb // needs Srb->Status ); NTSTATUS DVAttachWriteFrame( IN PSTREAMEX pStrmExt ); VOID DVFormatAttachFrame( IN KSPIN_DATAFLOW DataFlow, IN PSTREAMEX pStrmExt, IN PAV_61883_REQUEST pAVReq, IN PHW_STREAM_REQUEST_BLOCK pSrb, IN PSRB_DATA_PACKET pSrbDataPacket, IN ULONG ulSourceLength, // Packet length in bytes IN ULONG ulFrameSize, IN PVOID pFrameBuffer ); VOID DVAttachFrameThread( IN PSTREAMEX pStrmExt ); VOID DVTerminateAttachFrameThread( IN PSTREAMEX pStrmExt ); #if DBG ULONG cntInvSrcPkt = 0; #endif #if 0 // Enable later #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, DVSRBRead) #pragma alloc_text(PAGE, DVFormatAttachFrame) #pragma alloc_text(PAGE, DVAttachFrameThread) #pragma alloc_text(PAGE, DVTerminateAttachFrameThread) #pragma alloc_text(PAGE, DVAttachWriteFrame) #pragma alloc_text(PAGE, DVFormatAttachFrame) #endif #endif ULONG DVReadFrameValidate( IN PCIP_VALIDATE_INFO pInfo ) /*++ Routine Description: Used to detect the start of a DV frame. A DV frame is started with a header section. 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 ) { // // This can be used to detect dynamic format change if this function is called // to check for data packets always. This may require setting this flag: // CIP_VALIDATE_ALL_SOURCE instead of CIP_VALIDATE_FIRST_SOURCE // #if 0 // DBG PSRB_DATA_PACKET pSrbDataPacket = pInfo->Context; PSTREAMEX pStrmExt = pSrbDataPacket->pStrmExt; PDVCR_EXTENSION pDevExt = pStrmExt->pDevExt; if((pInfo->Packet[0] & DIF_HEADER_DSF) == 0) { // Indicate a 10 DIF sequences include in a video frame (525-60)/NTSC. if( pDevExt->VideoFormatIndex == FMT_IDX_SD_DVCR_PAL || pDevExt->VideoFormatIndex == FMT_IDX_SDL_DVCR_PAL ) { // Dynamic format changes!! TRACE(TL_STRM_ERROR|TL_CIP_WARNING,("Detect dynamic format change PAL -> NTSC!\n")); } } else { // Indicate a 12 DIF sequences include in a video frame (625-50)/PAL. if( pDevExt->VideoFormatIndex == FMT_IDX_SD_DVCR_NTSC || pDevExt->VideoFormatIndex == FMT_IDX_SDL_DVCR_NTSC ) { // Dynamic format changes!! TRACE(TL_STRM_ERROR|TL_CIP_WARNING,("Detect dynamic format change NTSC -> PAL!\n")); } } #endif // 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] & DIF_HEADER_TFn) || (pInfo->Packet[6] & DIF_HEADER_TFn) || (pInfo->Packet[7] & DIF_HEADER_TFn) ) { TRACE(TL_STRM_ERROR|TL_CIP_WARNING,("\'%d inv src pkts; [%x %x %d %x], [%x %x %x %x]\n", cntInvSrcPkt, 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; } #if DBG if(cntInvSrcPkt > 0) { TRACE(TL_CIP_TRACE,("\'%d inv src pkts; [%x %x %d %x] [%x %x %x %x]\n", cntInvSrcPkt, pInfo->Packet[0], pInfo->Packet[1], pInfo->Packet[2], pInfo->Packet[3], pInfo->Packet[4], pInfo->Packet[5], pInfo->Packet[6], pInfo->Packet[7] )); cntInvSrcPkt = 0; // Reset } #endif return 0; } else { #if DBG // // To detect invalid src pkt sequence; // If it exceeded the number of source packet per frame, we need to know about it. // PSRB_DATA_PACKET pSrbDataPacket = pInfo->Context; PSTREAMEX pStrmExt = pSrbDataPacket->pStrmExt; PDVCR_EXTENSION pDevExt = pStrmExt->pDevExt; if(++cntInvSrcPkt >= DVFormatInfoTable[pDevExt->VideoFormatIndex].ulSrcPackets) { TRACE(TL_CIP_TRACE,("(%d) Invalid SrcPkt >= max inv src pkt %d; ID0,1,2 = [%x %x %x]\n", cntInvSrcPkt, DVFormatInfoTable[pDevExt->VideoFormatIndex].ulSrcPackets, pInfo->Packet[0], pInfo->Packet[1], pInfo->Packet[2] )); if(DVTraceMask & TL_CIP_TRACE) { ASSERT(cntInvSrcPkt < DVFormatInfoTable[pDevExt->VideoFormatIndex].ulSrcPackets); } cntInvSrcPkt = 0; // Reset } else { TRACE(TL_CIP_INFO,("(%d) Invalid SrcPktSeq; ID0,1,2 = [%x,%x,%x]\n", cntInvSrcPkt, pInfo->Packet[0], pInfo->Packet[1], pInfo->Packet[2] )); } #endif return 1; } } else { TRACE(TL_CIP_WARNING, ("\'Validate: invalid SrcPktSeq; Packet %x\n", pInfo->Packet)); return 1; } } // DVReadFrameValidate #if DBG LONGLONG PreviousPictureNumber; LONGLONG PreviousTime; CYCLE_TIME PreviousTimestamp; #endif ULONG DVCompleteSrbRead( PCIP_NOTIFY_INFO pInfo ) /*++ Routine Description: 61883 has completed receiving data and callback to us to complete. --*/ { PSRB_DATA_PACKET pSrbDataPacket; PHW_STREAM_REQUEST_BLOCK pSrb; PKSSTREAM_HEADER pStrmHeader; PDVCR_EXTENSION pDevExt; PSTREAMEX pStrmExt; LONGLONG LastPictureNumber; PUCHAR pFrameBuffer; KIRQL oldIrql; PKS_FRAME_INFO pFrameInfo; // For VidOnly pin only #if DBG PXMT_FRAME_STAT pXmtStat; #endif // Callback and might be at the DISPATCH_LEVEL // The caller might have acquired spinlock as well! pSrbDataPacket = pInfo->Context; if(!pSrbDataPacket) { ASSERT(pSrbDataPacket && "Context is NULL!"); return 1; } pStrmExt = pSrbDataPacket->pStrmExt; KeAcquireSpinLock(pStrmExt->DataListLock, &oldIrql); #if DBG // Once it is completed by 61883, it becomes non-cancellable. if(!pStrmExt->bIsochIsActive) { TRACE(TL_CIP_WARNING,("CompleteSrbRead: bIsochActive:%d; pSrbDataPacket:%x\n", pStrmExt->bIsochIsActive, pSrbDataPacket)); } #endif pSrb = pSrbDataPacket->pSrb; ASSERT(pSrbDataPacket->pSrb); pDevExt = pStrmExt->pDevExt; pFrameBuffer = (PUCHAR) pSrbDataPacket->FrameBuffer; pStrmHeader = pSrb->CommandData.DataBufferArray; ASSERT(pStrmHeader->Size >= sizeof(KSSTREAM_HEADER)); // // Check CIP_STATUS_* from 61883 // // CIP_STATUS_CORRUPT_FRAME (0x00000001) // isoch header or cip header was incorrect if(pSrbDataPacket->Frame->Status & CIP_STATUS_CORRUPT_FRAME) { TRACE(TL_STRM_WARNING|TL_CIP_TRACE,("\'CIP_STATUS_CORRUPT_FRAME\n")); pStrmHeader->OptionsFlags = 0; pSrb->Status = STATUS_SUCCESS; // Success but no data ! pStrmHeader->DataUsed = 0; pStrmExt->PictureNumber++; pStrmExt->FramesProcessed++; } else // CIP_STATUS_SUCCESS (0x00000000) // 0 so cannot do bitwise operation!! // CIP_STATUS_FIRST_FRAME (0x00000002) // First attached frame to 61883 if(pSrbDataPacket->Frame->Status == CIP_STATUS_SUCCESS || (pSrbDataPacket->Frame->Status & CIP_STATUS_FIRST_FRAME)) { // Only increment FramesProcessed if it is a valid frame; pStrmExt->FramesProcessed++; pSrb->Status = STATUS_SUCCESS; pStrmHeader->OptionsFlags = KSSTREAM_HEADER_OPTIONSF_SPLICEPOINT; pStrmHeader->DataUsed = DVFormatInfoTable[pDevExt->VideoFormatIndex].ulFrameSize; // Put in Timestamp info depending on clock provider pStrmHeader->PresentationTime.Numerator = 1; pStrmHeader->PresentationTime.Denominator = 1; if(pStrmExt->hMasterClock || pStrmExt->hClock) { pStrmHeader->Duration = DVFormatInfoTable[pDevExt->VideoFormatIndex].ulAvgTimePerFrame; pStrmHeader->OptionsFlags |= (KSSTREAM_HEADER_OPTIONSF_TIMEVALID | // pStrmHeader->PresentationTime.Time is valid KSSTREAM_HEADER_OPTIONSF_DURATIONVALID); } // // 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. // // Other device (audio?) is the clock provider if(pStrmExt->hClock) { pStrmExt->TimeContext.HwDeviceExtension = (struct _HW_DEVICE_EXTENSION *) pDevExt; pStrmExt->TimeContext.HwStreamObject = pStrmExt->pStrmObject; pStrmExt->TimeContext.Function = TIME_GET_STREAM_TIME; pStrmExt->TimeContext.Time = 0; pStrmExt->TimeContext.SystemTime = 0; StreamClassQueryMasterClockSync( pStrmExt->hClock, &(pStrmExt->TimeContext) ); pStrmHeader->PresentationTime.Time = pStrmExt->CurrentStreamTime = pStrmExt->TimeContext.Time; // 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 = pStrmExt->PictureNumber; pStrmExt->PictureNumber = 1 + // Picture number start with 1. (pStrmHeader->PresentationTime.Time + TIME_PER_CYCLE) * (LONGLONG) GET_AVG_TIME_PER_FRAME_DENOM(pStrmExt->pDevExt->VideoFormatIndex) / (LONGLONG) GET_AVG_TIME_PER_FRAME_NUM(pStrmExt->pDevExt->VideoFormatIndex); // Detect discontinuity if(pStrmExt->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) pStrmExt->PictureNumber, (DWORD) pStrmHeader->PresentationTime.Time)); } // Detect if picture number did not progress. // This could be due to two frame being completely very close to each other. if(pStrmExt->PictureNumber <= LastPictureNumber) { TRACE(TL_CIP_WARNING,("\'hClock:Same pic #:(%d->%d); tmPres:(%d->%d); (%d:%d:%d) -> (%d:%d:%d); AQD[%d:%d:%d]\n", (DWORD) PreviousPictureNumber, (DWORD) pStrmExt->PictureNumber, (DWORD) PreviousTime, (DWORD) pStrmHeader->PresentationTime.Time, PreviousTimestamp.CL_SecondCount, PreviousTimestamp.CL_CycleCount, PreviousTimestamp.CL_CycleOffset, pSrbDataPacket->Frame->Timestamp.CL_SecondCount, pSrbDataPacket->Frame->Timestamp.CL_CycleCount, pSrbDataPacket->Frame->Timestamp.CL_CycleOffset, pStrmExt->cntDataAttached, pStrmExt->cntSRBQueued, pStrmExt->cntDataDetached )); pStrmExt->PictureNumber = LastPictureNumber + 1; // Picture number must progress !!!! } #if DBG PreviousPictureNumber = pStrmExt->PictureNumber; PreviousTime = pStrmHeader->PresentationTime.Time; PreviousTimestamp = pSrbDataPacket->Frame->Timestamp; #endif pStrmExt->FramesDropped = pStrmExt->PictureNumber - pStrmExt->FramesProcessed; // This subunit driver is a Master clock } else if (pStrmExt->hMasterClock) { #ifdef NT51_61883 ULONG ulDeltaCycleCounts; // No drop frame for PAUSE->RUN transition if(pStrmExt->b1stNewFrameFromPauseState) { pStrmHeader->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_DATADISCONTINUITY; pStrmExt->b1stNewFrameFromPauseState = FALSE; } else { ULONG ulCycleCount16bits; // Calculate skipped 1394 cycle from the returned CycleTime VALIDATE_CYCLE_COUNTS(pSrbDataPacket->Frame->Timestamp); ulCycleCount16bits = CALCULATE_CYCLE_COUNTS(pSrbDataPacket->Frame->Timestamp); ulDeltaCycleCounts = CALCULATE_DELTA_CYCLE_COUNT(pStrmExt->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; // Wrap around // // There are two cases for drop frames: // (1) Starve of buffer; or, // (2) no data (blank tape or tape is not playing) // // For case (1), 61883 returns CIP_STATUS_FIRST_FRAME. if(pSrbDataPacket->Frame->Status & CIP_STATUS_FIRST_FRAME) { // Use cycle count to calculate drop frame. We substract 1 from the MaxSrcPacket on purpose to avoid truncating. // The max range is MAX_CYCLE (8 * 8000 = 64000 cycles) // 64000 * 125 * 3 / 100100 = 239.76 // 64000 / 266 = 240 // 64000 / 267 = 239 if(ulDeltaCycleCounts >= (DVFormatInfoTable[pDevExt->VideoFormatIndex].ulMaxSrcPackets - 1)) { ULONG ulFrameElapsed = ulDeltaCycleCounts / (DVFormatInfoTable[pDevExt->VideoFormatIndex].ulMaxSrcPackets - 1); pStrmExt->FramesDropped += (ulFrameElapsed - 1); // There is a valid frame that is not dropped. } TRACE(TL_STRM_WARNING|TL_CIP_WARNING,("CIP_STATUS_FIRST_FRAME: Drop:%d; Processed:%d\n", (DWORD) pStrmExt->FramesDropped, pStrmExt->FramesProcessed )); pStrmHeader->OptionsFlags |= KSSTREAM_HEADER_OPTIONSF_DATADISCONTINUITY; } else { // Ignore all "drop frames" in the "no data" case // pStrmExt->FramesDropped += 0; } } // If we are the clock provider, the stream time is based on sample number * AvgTimePerFrame pStrmExt->PictureNumber = pStrmExt->FramesProcessed + pStrmExt->FramesDropped; pStrmHeader->PresentationTime.Time = pStrmExt->CurrentStreamTime = pStrmExt->PictureNumber * (LONGLONG) GET_AVG_TIME_PER_FRAME_NUM(pStrmExt->pDevExt->VideoFormatIndex) / (LONGLONG) GET_AVG_TIME_PER_FRAME_DENOM(pStrmExt->pDevExt->VideoFormatIndex); // Use to adjust the queried stream time pStrmExt->LastSystemTime = GetSystemTime(); // Cache current CycleCount pStrmExt->CycleCount16bits = CALCULATE_CYCLE_COUNTS(pSrbDataPacket->Frame->Timestamp); #if DBG // First frame or skipped frame if(pStrmExt->PictureNumber <= 1 || pStrmExt->PictureNumber <= PreviousPictureNumber || ulDeltaCycleCounts > DVFormatInfoTable[pDevExt->VideoFormatIndex].ulMaxSrcPackets ) TRACE(TL_CIP_WARNING,("\'hMasterClock: Same pic #:(%d->%d); tmPres:(%d->%d); (%d:%d:%d) -> (%d:%d:%d); AQD[%d:%d:%d]\n", (DWORD) PreviousPictureNumber, (DWORD) pStrmExt->PictureNumber, (DWORD) PreviousTime, (DWORD) pStrmHeader->PresentationTime.Time, PreviousTimestamp.CL_SecondCount, PreviousTimestamp.CL_CycleCount, PreviousTimestamp.CL_CycleOffset, pSrbDataPacket->Frame->Timestamp.CL_SecondCount, pSrbDataPacket->Frame->Timestamp.CL_CycleCount, pSrbDataPacket->Frame->Timestamp.CL_CycleOffset, pStrmExt->cntDataAttached, pStrmExt->cntSRBQueued, pStrmExt->cntDataDetached )); PreviousPictureNumber = pStrmExt->PictureNumber; PreviousTime = pStrmHeader->PresentationTime.Time; PreviousTimestamp = pSrbDataPacket->Frame->Timestamp; #endif #else // NT51_61883 // This is the old way when 61883 was not returning the correct CycleTime. pStrmHeader->PresentationTime.Time = pStrmExt->CurrentStreamTime; pStrmExt->LastSystemTime = GetSystemTime(); // Use to adjust the queried stream time pStrmExt->CurrentStreamTime += DVFormatInfoTable[pDevExt->VideoFormatIndex].ulAvgTimePerFrame; #endif // NT51_61883 // no Clock so "free flowing!" } else { pStrmHeader->PresentationTime.Time = 0; pStrmHeader->Duration = 0; // No clock so not valid. pStrmExt->PictureNumber++; TRACE(TL_CIP_TRACE,("\'No clock: PicNum:%d\n", (DWORD) pStrmExt->PictureNumber)); } } else { // 61883 has not defined this new status at this time! // Do not know what to do so we will complete it with 0 length for now. pStrmHeader->OptionsFlags = 0; pSrb->Status = STATUS_SUCCESS; pStrmHeader->DataUsed = 0; pStrmExt->PictureNumber++; pStrmExt->FramesProcessed++; TRACE(TL_STRM_WARNING|TL_CIP_ERROR,("pSrbDataPacket:%x; unexpected Frame->Status %x\n", pSrbDataPacket, pSrbDataPacket->Frame->Status)); ASSERT(FALSE && "Unknown pSrbDataPacket->Frame->Status"); } // For VidOnly which uses VideoInfoHeader and has // an extended frame information (KS_FRAME_INFO) appended to KSSTREAM_HEADER if( pDevExt->idxStreamNumber == 0 && (pStrmHeader->Size >= (sizeof(KSSTREAM_HEADER) + sizeof(PKS_FRAME_INFO))) ) { pFrameInfo = (PKS_FRAME_INFO) (pStrmHeader + 1); pFrameInfo->ExtendedHeaderSize = sizeof(KS_FRAME_INFO); pFrameInfo->PictureNumber = pStrmExt->PictureNumber; pFrameInfo->DropCount = pStrmExt->FramesDropped; pFrameInfo->dwFrameFlags = KS_VIDEO_FLAG_FRAME | // Complete frame KS_VIDEO_FLAG_I_FRAME; // Every DV frame is an I frame } #if DBG // Validate that the data is return in the right sequence if(pSrbDataPacket->FrameNumber != pStrmExt->FramesProcessed) { TRACE(TL_STRM_WARNING|TL_CIP_ERROR,("\'pSrbDataPacket:%x; Status:%x; Out of Sequence %d != %d; (Dropped:%x)\n", pSrbDataPacket, pSrbDataPacket->Frame->Status, (DWORD) pSrbDataPacket->FrameNumber, (DWORD) pStrmExt->FramesProcessed, (DWORD) pStrmExt->FramesDropped )); }; #endif #if DBG // Collect transmit buffer statistics if(pStrmExt->ulStatEntries < MAX_XMT_FRAMES_TRACED) { pXmtStat = pStrmExt->paXmtStat + pStrmExt->ulStatEntries; pXmtStat->StreamState = pStrmExt->StreamState; pXmtStat->cntSRBReceived = (LONG) pStrmExt->cntSRBReceived; pXmtStat->cntSRBPending = (LONG) pStrmExt->cntSRBPending; pXmtStat->cntSRBQueued = (LONG) pStrmExt->cntSRBQueued; pXmtStat->cntDataAttached= pStrmExt->cntDataAttached; pXmtStat->FrameSlot = (DWORD) pStrmExt->PictureNumber; pXmtStat->tmStreamTime = pStrmExt->CurrentStreamTime; pXmtStat->DropCount = (DWORD) pStrmExt->FramesDropped; pXmtStat->FrameNumber = (DWORD) pSrbDataPacket->FrameNumber; pXmtStat->OptionsFlags = pSrb->CommandData.DataBufferArray->OptionsFlags; pXmtStat->tmPresentation = pSrb->CommandData.DataBufferArray->PresentationTime.Time; pXmtStat->tsTransmitted= pSrbDataPacket->Frame->Timestamp; pStrmExt->ulStatEntries++; } #endif // // Mark completion is called. // pSrbDataPacket->State |= DE_IRP_CALLBACK_COMPLETED; // // Attached->Completed or Completed->Attached. // if(IsStateSet(pSrbDataPacket->State, DE_IRP_ATTACHED_COMPLETED)) { // // Recycle it back to the detach list // RemoveEntryList(&pSrbDataPacket->ListEntry); pStrmExt->cntDataAttached--; ASSERT(pStrmExt->cntDataAttached >= 0); InsertTailList(&pStrmExt->DataDetachedListHead, &pSrbDataPacket->ListEntry); pStrmExt->cntDataDetached++; #if DBG // Detect if 61883 is starve. This cause discontinuity. // This can happen for many valid reasons (slow system). // An assert is added to detect other unknown reason. if(pStrmExt->cntDataAttached == 0 && pStrmExt->StreamState == KSSTATE_RUN) { TRACE(TL_STRM_WARNING|TL_CIP_WARNING,("\n**** 61883 starved in RUN state (read); AQD[%d:%d:%d]\n\n", pStrmExt->cntDataAttached, pStrmExt->cntSRBQueued, pStrmExt->cntDataDetached )); // ASSERT(pStrmExt->cntDataAttached > 0 && "61883 is starve at RUN state!!"); } #endif // // Complete this Srb // StreamClassStreamNotification(StreamRequestComplete, pStrmExt->pStrmObject, pSrbDataPacket->pSrb ); pSrbDataPacket->State |= DE_IRP_SRB_COMPLETED; pSrbDataPacket->pSrb = NULL; #if DBG pStrmExt->cntSRBPending--; #endif } else { TRACE(TL_STRM_WARNING,("CompleteSrbRead: pSrbDataPacket:%x; Completed before attach.\n", pSrbDataPacket)); } KeReleaseSpinLock(pStrmExt->DataListLock, oldIrql); return 0; } // DVCompleteSrbRead NTSTATUS DVAttachFrameCR( IN PDEVICE_OBJECT DeviceObject, IN PIRP pIrp, IN PSRB_DATA_PACKET pSrbDataPacket ) /*++ Routine Description: Completion routine for attaching a frame for transmitting. Apply to attaching listen and talk frame. --*/ { PHW_STREAM_REQUEST_BLOCK pSrb; PSTREAMEX pStrmExt; PLONG plSrbUseCount; // When this count is 0, it can be completed. KIRQL oldIrql; pStrmExt = pSrbDataPacket->pStrmExt; KeAcquireSpinLock(pStrmExt->DataListLock, &oldIrql); pSrb = pSrbDataPacket->pSrb; // This entry is be already attached before IoCallDriver. // This is done this way because this buffer could be filled and // completed before the attach completion routine (here) is called. // If it is completed and callback is called, // pSrbDataPacket->pSrb has been set to NULL. // In the error case, pSrbDataPacket->pSrb should not be NULL. if(!NT_SUCCESS(pIrp->IoStatus.Status)) { if(pSrbDataPacket->pSrb == NULL) { // PBinder told me that this cannot happen. // A buffer is completed (pSRb set to NULL), and still return with an error! ASSERT(pSrbDataPacket->pSrb); KeReleaseSpinLock(pStrmExt->DataListLock, oldIrql); return STATUS_MORE_PROCESSING_REQUIRED; } pSrbDataPacket->State |= DE_IRP_ERROR; plSrbUseCount = (PLONG) (pSrb->SRBExtension); (*plSrbUseCount) --; // -- for being remove from queue ASSERT(*plSrbUseCount >= 0); TRACE(TL_CIP_ERROR,("DVAttachFrameCR: pSrb:%x; pSrb->Status:%x; failed pIrp->Status %x; UseCnt:%d\n", pSrb, pSrb->Status, pIrp->IoStatus.Status, *plSrbUseCount)); ASSERT(NT_SUCCESS(pIrp->IoStatus.Status) && "DVAttachFrameCR"); // Complete this SRB only if the count is 0. if(*plSrbUseCount == 0 && pSrb->Status != STATUS_CANCELLED) { pSrb->Status = pIrp->IoStatus.Status; pSrb->CommandData.DataBufferArray->DataUsed = 0; // Complete SRB StreamClassStreamNotification(StreamRequestComplete, pSrb->StreamObject, pSrbDataPacket->pSrb); pSrbDataPacket->State |= DE_IRP_SRB_COMPLETED; pSrbDataPacket->pSrb = NULL; #if DBG pStrmExt->cntSRBPending--; #endif } // Recycle list RemoveEntryList(&pSrbDataPacket->ListEntry); pStrmExt->cntDataAttached--; ASSERT(pStrmExt->cntDataAttached >= 0); InsertTailList(&pStrmExt->DataDetachedListHead, &pSrbDataPacket->ListEntry); pStrmExt->cntDataDetached++; #if DBG // Detect if 61883 is starve. This cause discontinuity. // This can happen for many valid reasons (slow system). // An assert is added to detect other unknown reason. if(!pStrmExt->bEOStream && pStrmExt->cntDataAttached == 0 && pStrmExt->StreamState == KSSTATE_RUN) { TRACE(TL_STRM_WARNING|TL_CIP_WARNING,("\n**** 61883 starve in RUN state (AttachCR); AQD[%d:%d:%d]\n\n", pStrmExt->cntDataAttached, pStrmExt->cntSRBQueued, pStrmExt->cntDataDetached )); if (pStrmExt->pStrmInfo->DataFlow == KSPIN_DATAFLOW_IN) { // ASSERT(pStrmExt->cntDataAttached > 0 && "61883 is starve at RUN state!!"); } } #endif KeReleaseSpinLock(pStrmExt->DataListLock, oldIrql); return STATUS_MORE_PROCESSING_REQUIRED; } // // Mark attached buffer completed. // pSrbDataPacket->State |= DE_IRP_ATTACHED_COMPLETED; // // Special case: Completed and then Attached. // if(IsStateSet(pSrbDataPacket->State, DE_IRP_CALLBACK_COMPLETED)) { // // Recycle it back to the detach list // RemoveEntryList(&pSrbDataPacket->ListEntry); pStrmExt->cntDataAttached--; ASSERT(pStrmExt->cntDataAttached >= 0); InsertTailList(&pStrmExt->DataDetachedListHead, &pSrbDataPacket->ListEntry); pStrmExt->cntDataDetached++; #if DBG // Detect if 61883 is starve. This cause discontinuity. // This can happen for many valid reasons (slow system). // An assert is added to detect other unknown reason. if(!pStrmExt->bEOStream && pStrmExt->cntDataAttached == 0 && pStrmExt->StreamState == KSSTATE_RUN) { TRACE(TL_STRM_WARNING|TL_CIP_WARNING,("\n**** 61883 starve in RUN state (AttachCR); AQD[%d:%d:%d]\n\n", pStrmExt->cntDataAttached, pStrmExt->cntSRBQueued, pStrmExt->cntDataDetached )); if (pStrmExt->pStrmInfo->DataFlow == KSPIN_DATAFLOW_IN) { // ASSERT(pStrmExt->cntDataAttached > 0 && "61883 is starve at RUN state!!"); } } #endif // // Complete this Srb // StreamClassStreamNotification(StreamRequestComplete, pStrmExt->pStrmObject, pSrbDataPacket->pSrb); pSrbDataPacket->State |= DE_IRP_SRB_COMPLETED; pSrbDataPacket->pSrb = NULL; #if DBG pStrmExt->cntSRBPending--; #endif TRACE(TL_STRM_WARNING,("AttachFrameCR: pSrbDataPacket:%x; completed before DttachFrameCR.\n", pSrbDataPacket)); } KeReleaseSpinLock(pStrmExt->DataListLock, oldIrql); TRACE(TL_CIP_INFO,("\'DVAttachFrameCR: pSrb:%x; AttachCnt:%d\n", pSrb, pStrmExt->cntDataAttached)); return STATUS_MORE_PROCESSING_REQUIRED; } VOID DVSRBRead( IN PKSSTREAM_HEADER pStrmHeader, IN ULONG ulFrameSize, IN PDVCR_EXTENSION pDevExt, IN PSTREAMEX pStrmExt, IN PHW_STREAM_REQUEST_BLOCK pSrb // needs Srb->Status ) /*++ Routine Description: Called when an Read Data Srb request is received --*/ { KIRQL oldIrql; NTSTATUS Status; PSRB_DATA_PACKET pSrbDataPacket; PAV_61883_REQUEST pAVReq; PLONG plSrbUseCount; PIO_STACK_LOCATION NextIrpStack; ULONG ulSrcPktLen; // Packet length in bytes PVOID pFrameBuffer; PAGED_CODE(); // // Some validation // if(pStrmHeader->FrameExtent < ulFrameSize) { TRACE(TL_CIP_WARNING,("\'SRBRead: FrmExt %d < FrmSz %d\n", pStrmHeader->FrameExtent, ulFrameSize)); #ifdef SUPPORT_NEW_AVC if(pStrmExt->bDV2DVConnect) { pSrb->Status = STATUS_SUCCESS; // Testing... } else { #endif ASSERT(pStrmHeader->FrameExtent >= ulFrameSize); pSrb->Status = STATUS_INVALID_PARAMETER; #ifdef SUPPORT_NEW_AVC } #endif goto ExitReadStreamError; } // // Make sure that there is enough entry // KeAcquireSpinLock(pStrmExt->DataListLock, &oldIrql); if(IsListEmpty(&pStrmExt->DataDetachedListHead)) { // // This can happen only if the upper layer send down more than what we preallocated. // In this case, we will expand the list. // if(!(pSrbDataPacket = ExAllocatePool(NonPagedPool, sizeof(SRB_DATA_PACKET)))) { KeReleaseSpinLock(pStrmExt->DataListLock, oldIrql); pSrb->Status = STATUS_INSUFFICIENT_RESOURCES; goto ExitReadStreamError; } RtlZeroMemory(pSrbDataPacket, sizeof(SRB_DATA_PACKET)); if(!(pSrbDataPacket->Frame = ExAllocatePool(NonPagedPool, sizeof(CIP_FRAME)))) { KeReleaseSpinLock(pStrmExt->DataListLock, oldIrql); ExFreePool(pSrbDataPacket); pSrb->Status = STATUS_INSUFFICIENT_RESOURCES; goto ExitReadStreamError; } if(!(pSrbDataPacket->pIrp = IoAllocateIrp(pDevExt->pBusDeviceObject->StackSize, FALSE))) { KeReleaseSpinLock(pStrmExt->DataListLock, oldIrql); ExFreePool(pSrbDataPacket->Frame); pSrbDataPacket->Frame = 0; ExFreePool(pSrbDataPacket); pSrbDataPacket = 0; pSrb->Status = STATUS_INSUFFICIENT_RESOURCES; goto ExitReadStreamError; } InsertTailList(&pStrmExt->DataDetachedListHead, &pSrbDataPacket->ListEntry); pStrmExt->cntDataDetached++; TRACE(TL_CIP_WARNING,("\'Add one node to DetachList\n")); } // Get a a nonpaged system-space virtual address for the buffer // This could fail it there is not enough system resource (MDL). #ifdef USE_WDM110 // Win2000 // // Driver verifier flag to use this but if this is used, this driver will not load for Millen!!! // pFrameBuffer = MmGetSystemAddressForMdlSafe(pSrb->Irp->MdlAddress, NormalPagePriority); #else // Win9x pFrameBuffer = MmGetSystemAddressForMdl (pSrb->Irp->MdlAddress); #endif if(pFrameBuffer == NULL) { KeReleaseSpinLock(pStrmExt->DataListLock, oldIrql); pSrb->Status = STATUS_INSUFFICIENT_RESOURCES; ASSERT(FALSE && "DVFormatAttachFrame() insufficient resource!"); goto ExitReadStreamError; } pSrbDataPacket = (PSRB_DATA_PACKET) RemoveHeadList(&pStrmExt->DataDetachedListHead); pStrmExt->cntDataDetached--; plSrbUseCount = (PLONG) (pSrb->SRBExtension); (*plSrbUseCount) = 0; // Not in a queue so 0. pAVReq = &pSrbDataPacket->AVReq; ulSrcPktLen = \ (DVFormatInfoTable[pDevExt->VideoFormatIndex].DataBlockSize << 2) * \ (1 << DVFormatInfoTable[pDevExt->VideoFormatIndex].FractionNumber); // // Format an attach frame request // DVFormatAttachFrame( pStrmExt->pStrmInfo->DataFlow, pStrmExt, pAVReq, pSrb, pSrbDataPacket, ulSrcPktLen, ulFrameSize, pFrameBuffer ); // Completion callback can be called before the attach frame completion routine; // Add this to the attached list now; if it ever failed, it will be removed in the completion routine. InsertTailList(&pStrmExt->DataAttachedListHead, &pSrbDataPacket->ListEntry); pStrmExt->cntDataAttached++; (*plSrbUseCount) ++; // ++ for being in queue ASSERT(*plSrbUseCount > 0); KeReleaseSpinLock(pStrmExt->DataListLock, oldIrql); NextIrpStack = IoGetNextIrpStackLocation(pSrbDataPacket->pIrp); NextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; NextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_61883_CLASS; NextIrpStack->Parameters.Others.Argument1 = &pSrbDataPacket->AVReq; IoSetCompletionRoutine( pSrbDataPacket->pIrp, DVAttachFrameCR, pSrbDataPacket, TRUE, TRUE, TRUE ); // Must set to _PENDING or MediaSample will return empty KSSTREAM_HEADER pSrb->Status = STATUS_PENDING; pSrbDataPacket->pIrp->IoStatus.Status = STATUS_SUCCESS; // Initialize it Status = IoCallDriver( pStrmExt->pDevExt->pBusDeviceObject, pSrbDataPacket->pIrp); ASSERT(Status == STATUS_PENDING || Status == STATUS_SUCCESS); return; ExitReadStreamError: StreamClassStreamNotification( StreamRequestComplete, pSrb->StreamObject, pSrb ); #if DBG pStrmExt->cntSRBPending--; #endif } ULONG DVCompleteSrbWrite( PCIP_NOTIFY_INFO pInfo ) /*++ Routine Description: This fucntion is called when 61883 has completed transmitting a frame. --*/ { PSRB_DATA_PACKET pSrbDataPacket ; PHW_STREAM_REQUEST_BLOCK pSrb; NTSTATUS Status = STATUS_SUCCESS; PDVCR_EXTENSION pDevExt; PSTREAMEX pStrmExt; PLONG plSrbUseCount; // When this count is 0, it can be completed. KIRQL oldIrql; #if DBG LONG lCycleCountElapsed; PXMT_FRAME_STAT pXmtStat; #endif // Callback and in DISPATCH_LEVEL // Caller might have acquired SpinLock as well! pSrbDataPacket = pInfo->Context; if(!pSrbDataPacket) { ASSERT(pSrbDataPacket); return 1; } pStrmExt = pSrbDataPacket->pStrmExt; ASSERT(pStrmExt); KeAcquireSpinLock(pStrmExt->DataListLock, &oldIrql); ASSERT(pSrbDataPacket->pSrb); pSrb = pSrbDataPacket->pSrb; pDevExt = pStrmExt->pDevExt; plSrbUseCount = (PLONG) pSrb->SRBExtension; // Check return Status if(!NT_SUCCESS(pSrbDataPacket->Frame->Status)) { TRACE(TL_CIP_ERROR,("\'DVCompleteSrbWrite: %d: Frame->Status %x\n", (DWORD) pSrbDataPacket->FrameNumber, pSrbDataPacket->Frame->Status)); ASSERT(NT_SUCCESS(pSrbDataPacket->Frame->Status)); pSrb->Status = STATUS_UNSUCCESSFUL; } else { pSrb->Status = STATUS_SUCCESS; } (*plSrbUseCount) --; // This count need to be 0 before the SRB is completed. ASSERT(*plSrbUseCount >= 0); #if DBG if(pSrbDataPacket->StreamState == KSSTATE_PAUSE) { pStrmExt->lPrevCycleCount = pSrbDataPacket->Frame->Timestamp.CL_CycleCount; pStrmExt->lTotalCycleCount = 0; pStrmExt->lFramesAccumulatedRun = 0; pStrmExt->lFramesAccumulatedPaused++; } else if(pSrbDataPacket->StreamState == KSSTATE_RUN) { if((LONG) pSrbDataPacket->Frame->Timestamp.CL_CycleCount > pStrmExt->lPrevCycleCount) lCycleCountElapsed = pSrbDataPacket->Frame->Timestamp.CL_CycleCount - pStrmExt->lPrevCycleCount; else lCycleCountElapsed = pSrbDataPacket->Frame->Timestamp.CL_CycleCount + 8000 - pStrmExt->lPrevCycleCount; if(lCycleCountElapsed <= (LONG) DVFormatInfoTable[pDevExt->VideoFormatIndex].ulSrcPackets) { TRACE(TL_CIP_WARNING, ("\'#### CycleCounts between frames %d <= expected %d + empty pkt?\n", lCycleCountElapsed, DVFormatInfoTable[pDevExt->VideoFormatIndex].ulSrcPackets )); } pStrmExt->lTotalCycleCount += lCycleCountElapsed; pStrmExt->lFramesAccumulatedRun++; TRACE(TL_CIP_TRACE,("\'%d) Attached:%d; pSrb:%x; FmSt:%x; CyTm:[SC:%d:CC:%d]; CyclElaps:%d; fps:%d/%d\n", (DWORD) pSrbDataPacket->FrameNumber, pStrmExt->cntDataAttached, pSrb, pSrbDataPacket->Frame->Status, pSrbDataPacket->Frame->Timestamp.CL_SecondCount, pSrbDataPacket->Frame->Timestamp.CL_CycleCount, lCycleCountElapsed, pStrmExt->lTotalCycleCount, (DWORD) pStrmExt->lFramesAccumulatedRun )); pStrmExt->lPrevCycleCount = pSrbDataPacket->Frame->Timestamp.CL_CycleCount; } else { TRACE(TL_CIP_ERROR,("\'This data was attached at %d state ?????\n", pSrbDataPacket->StreamState)); } #endif TRACE(TL_CIP_INFO,("\'%d) FmSt %x; Cnt %d; CyTm:[%d:%d:%d]; PrevCyclCnt:%d\n", (DWORD) pSrbDataPacket->FrameNumber, pSrbDataPacket->Frame->Status, *plSrbUseCount, pSrbDataPacket->Frame->Timestamp.CL_SecondCount, pSrbDataPacket->Frame->Timestamp.CL_CycleCount, pSrbDataPacket->Frame->Timestamp.CL_CycleOffset, pStrmExt->lPrevCycleCount )); TRACE(TL_CIP_INFO,("\'DVCompleteSrbWrite: Frm:%d; Attached:%d; cntUse:%d, Srb:%x; FrmSt:%x; CyclElaps:%d\n", (DWORD) pSrbDataPacket->FrameNumber, pStrmExt->cntDataAttached, *plSrbUseCount, pSrb, pSrbDataPacket->Frame->Status, lCycleCountElapsed )); // // Mark completion is called. // pSrbDataPacket->State |= DE_IRP_CALLBACK_COMPLETED; // // Attached->Completed or Completed->Attached. // if(IsStateSet(pSrbDataPacket->State, DE_IRP_ATTACHED_COMPLETED)) { // // Recycle it back to the detach list // RemoveEntryList(&pSrbDataPacket->ListEntry); pStrmExt->cntDataAttached--; ASSERT(pStrmExt->cntDataAttached >= 0); InsertTailList(&pStrmExt->DataDetachedListHead, &pSrbDataPacket->ListEntry); pStrmExt->cntDataDetached++; #if DBG // Detect if 61883 is starve. This cause discontinuity. // This can happen for many valid reasons (slow system). // An assert is added to detect other unknown reason. if(!pStrmExt->bEOStream && pStrmExt->cntDataAttached == 0 && pStrmExt->StreamState == KSSTATE_RUN) { TRACE(TL_STRM_WARNING|TL_CIP_WARNING,("\n**** 61883 starve in RUN state (write);AQD[%d:%d:%d]\n\n", pStrmExt->cntDataAttached, pStrmExt->cntSRBQueued, pStrmExt->cntDataDetached )); if (pStrmExt->pStrmInfo->DataFlow == KSPIN_DATAFLOW_IN) { // ASSERT(pStrmExt->cntDataAttached > 0 && "61883 is starve at RUN state!!"); } } #endif // Complete this SRB only if the count is 0. if(*plSrbUseCount == 0) { TRACE(TL_CIP_TRACE,("\'------------ Srb:%x completing..----------------\n", pSrb)); // Frame that possibly made it to the device pStrmExt->FramesProcessed++; pSrb->CommandData.DataBufferArray->DataUsed = DVFormatInfoTable[pDevExt->VideoFormatIndex].ulFrameSize; StreamClassStreamNotification(StreamRequestComplete, pStrmExt->pStrmObject, pSrbDataPacket->pSrb ); pSrbDataPacket->State |= DE_IRP_SRB_COMPLETED; pSrbDataPacket->pSrb = NULL; #if DBG pStrmExt->cntSRBPending--; #endif } } else { TRACE(TL_STRM_WARNING,("CompleteSrbWrite: pSrbDataPacket:%x; Completed before attach.\n", pSrbDataPacket)); } #if DBG // Collect transmit buffer statistics if((pStrmExt->lFramesAccumulatedPaused + pStrmExt->lFramesAccumulatedRun) <= MAX_XMT_FRAMES_TRACED) { pXmtStat = pStrmExt->paXmtStat + (pStrmExt->lFramesAccumulatedPaused + pStrmExt->lFramesAccumulatedRun - 1); pXmtStat->tsTransmitted = pSrbDataPacket->Frame->Timestamp; if(pSrbDataPacket->Frame->Timestamp.CL_CycleCount == 0) { TRACE(TL_CIP_WARNING,("\'PAUSE:%d; RUN:%d; %d:%d\n", pStrmExt->lFramesAccumulatedPaused, pStrmExt->lFramesAccumulatedRun, pSrbDataPacket->Frame->Timestamp.CL_SecondCount, pSrbDataPacket->Frame->Timestamp.CL_CycleCount)); } } #endif // Signal that all SRBs have been attached and transmitted. if(pStrmExt->bEOStream) { if(pStrmExt->cntDataAttached == 0 && pStrmExt->cntSRBQueued == 0) { // // Signal any pending clock events // DVSignalClockEvent(0, pStrmExt, 0, 0); // // No data request queued or pending; it is time to signal EOStream to // trigger EC_COMPLETE. // StreamClassStreamNotification( SignalMultipleStreamEvents, pStrmExt->pStrmObject, (GUID *)&KSEVENTSETID_Connection_Local, KSEVENT_CONNECTION_ENDOFSTREAM ); TRACE(TL_CIP_WARNING,("\'*-*-* EOStream Signalled: pSrb:%x completed; AQD [%d:%d:%d]; Took %d msec;\n", pSrb, pStrmExt->cntDataAttached, pStrmExt->cntSRBQueued, pStrmExt->cntDataDetached, (DWORD) ((GetSystemTime() - pStrmExt->tmStreamStart)/(ULONGLONG) 10000))); } else { TRACE(TL_CIP_TRACE,("\' *EOStream: pSrb:%x completed; cntAttached:%d; cntSRBQ:%d\n", pSrb, (DWORD) pStrmExt->cntDataAttached, (DWORD) pStrmExt->cntSRBQueued)); } } // // If we are not in the ending situtation (EOS pr Stop state) and number of // attach data request is below a threashold, we singal an event to the // code that does "throttle" to quickly attach another frame. // if(!pStrmExt->bEOStream || (pStrmExt->bEOStream && pStrmExt->cntSRBQueued > 0)) { if(pStrmExt->StreamState != KSSTATE_STOP && pStrmExt->cntDataAttached < NUM_BUF_ATTACHED_THEN_ISOCH) { KeSetEvent(&pStrmExt->hSrbArriveEvent, 0, FALSE); TRACE(TL_CIP_WARNING,("Threadshold:.AQD:[%d %d %d] < %d\n", pStrmExt->cntDataAttached, pStrmExt->cntSRBQueued, pStrmExt->cntDataDetached, NUM_BUF_ATTACHED_THEN_ISOCH )); } } KeReleaseSpinLock(pStrmExt->DataListLock, oldIrql); return 0; } // DVCompleteSrbWrite NTSTATUS DVAttachWriteFrame( IN PSTREAMEX pStrmExt ) /*++ Routine Description: Prepare and submit a frame to 61883 for transmit. --*/ { KIRQL oldIrql; PSRB_DATA_PACKET pSrbDataPacket; PSRB_ENTRY pSrbEntry; #if DBG ULONG SrbNumCache; // Cache the SRB number of tracking purpose PXMT_FRAME_STAT pXmtStat; #endif PHW_STREAM_REQUEST_BLOCK pSrb; PHW_STREAM_REQUEST_BLOCK pSrbNext; PVOID pFrameBuffer; PIO_STACK_LOCATION NextIrpStack; NTSTATUS Status; PLONG plSrbUseCount; // When this count is 0, it can be completed. ULONG ulSrcPktLen; LARGE_INTEGER Timeout; PAGED_CODE(); // Serialize setting state to STOP if(pStrmExt->StreamState != KSSTATE_PAUSE && pStrmExt->StreamState != KSSTATE_RUN) { TRACE(TL_CIP_WARNING,("\'DVAttachWriteFrame: StreamState:%d; no attach! Wait!\n", pStrmExt->StreamState)); Timeout.HighPart = -1; Timeout.LowPart = (ULONG)(-1 * DVFormatInfoTable[pStrmExt->pDevExt->VideoFormatIndex].ulAvgTimePerFrame); KeDelayExecutionThread(KernelMode, FALSE, &Timeout); return STATUS_SUCCESS; } KeAcquireSpinLock(pStrmExt->DataListLock, &oldIrql); if(IsListEmpty(&pStrmExt->SRBQueuedListHead) || IsListEmpty(&pStrmExt->DataDetachedListHead) ) { KeReleaseSpinLock(pStrmExt->DataListLock, oldIrql); #if DBG if(!pStrmExt->bEOStream) { TRACE(TL_CIP_WARNING,("\'StrmSt:%d; DetachList or SrbQ empty: EOStream:%d; AQD [%d:%d:%d]; Wait one frame time.\n", pStrmExt->StreamState, pStrmExt->bEOStream, pStrmExt->cntDataAttached, pStrmExt->cntSRBQueued, pStrmExt->cntDataDetached )); } #endif Timeout.HighPart = -1; Timeout.LowPart = (ULONG)(-1 * DVFormatInfoTable[pStrmExt->pDevExt->VideoFormatIndex].ulAvgTimePerFrame); KeDelayExecutionThread(KernelMode, FALSE, &Timeout); // SRB is queued so it is OK. We will process that later. // This is usually cause by receiving more than what we pre-allocate. return STATUS_SUCCESS; } // KSSTATE_PAUSE: "reuse" head of the SrbQ. // KSSTATE_RUN: "remove" a Srb from the queue. // Get NEXT(SrbQ) and determine if it needs to be removed. pSrbEntry = (PSRB_ENTRY) pStrmExt->SRBQueuedListHead.Flink; pSrb = pSrbEntry->pSrb; plSrbUseCount = (PLONG) pSrb->SRBExtension; ASSERT(*plSrbUseCount >= 0); #if DBG SrbNumCache = pSrbEntry->SrbNum; #endif // Get a a nonpaged system-space virtual address for the buffer // This could fail it there is not enough system resource (MDL). #ifdef USE_WDM110 // Win2000 // // Driver verifier flag to use this but if this is used, this driver will not load for Millen!!! // pFrameBuffer = MmGetSystemAddressForMdlSafe(pSrb->Irp->MdlAddress, NormalPagePriority); #else pFrameBuffer = MmGetSystemAddressForMdl (pSrb->Irp->MdlAddress); #endif if(pFrameBuffer == NULL) { KeReleaseSpinLock(pStrmExt->DataListLock, oldIrql); ASSERT(FALSE && "Insufficient MDL\n"); return STATUS_INSUFFICIENT_RESOURCES; } // Only in RUN state, the stream time in the Srb is considered and Srbs in the SrbQ will be dequeued. if(pStrmExt->StreamState == KSSTATE_RUN) { #define ALLOWABLE_TIMING_LATENCY TIME_PER_CYCLE // Presentation time is honor only if we are the master clock. if(pStrmExt->hMasterClock) { LONGLONG tmExpectedFrame; if( pStrmExt->pDevExt->VideoFormatIndex == FMT_IDX_SD_DVCR_PAL || pStrmExt->pDevExt->VideoFormatIndex == FMT_IDX_SDL_DVCR_PAL ) tmExpectedFrame = pStrmExt->PictureNumber * (LONGLONG) FRAME_TIME_PAL; else { tmExpectedFrame = (pStrmExt->PictureNumber * (LONGLONG) 1000 * (LONGLONG) 1001 ) / (LONGLONG) 3; // trouble NTSC! // Adjustment for rounding if((pStrmExt->PictureNumber % 3) == 1) tmExpectedFrame++; } // Use to adjust the querued stream time. pStrmExt->LastSystemTime = GetSystemTime(); // There are three situations about the NEXT(SrbQ) comparing with tmExpectedFrame: // 1. Early; 2. OnTime; 3.Late // // tmExpectedFrame // | // 3>------------2>-----------------1>--------------- // 3.Late | 2.On Time | 1.Early // | x | x | // where "x" is the allowable latency (for calculation rounding) // // Note: allow TIME_PER_CYCLE latency /*Early*/ /*N+1/++*/ if((tmExpectedFrame + ALLOWABLE_TIMING_LATENCY) <= pSrb->CommandData.DataBufferArray->PresentationTime.Time) { // FUTURE: if a frame arrive sooner than expected, do not remove SrbQ; // instead, repeat until passing its "scheduled departure". // Remove NEXT(SrbQ) only if bEOStream if(pStrmExt->bEOStream) { TRACE(TL_CIP_TRACE,("\'EOStream=== Srb:%x; (SrbNum:%d ?= PicNum:%d) cntSrbQ:%d; Attach:%d ===\n", pSrb, pSrbEntry->SrbNum, (DWORD) pStrmExt->PictureNumber, (DWORD) pStrmExt->cntSRBQueued, (DWORD) pStrmExt->cntDataAttached)); RemoveEntryList(&pSrbEntry->ListEntry); pStrmExt->cntSRBQueued--; (*plSrbUseCount)--; ExFreePool(pSrbEntry); pSrbEntry = NULL; // Removed so free it! } TRACE(TL_CIP_TRACE,("\'** Repeat: pSrb:%x; RefCnt:%d; cntSrbQ:%d; PicNum:%d; Drp:%d; PresTime:%d >= CurTime:%d\n", pSrb, *plSrbUseCount, pStrmExt->cntSRBQueued, (DWORD) pStrmExt->PictureNumber, (DWORD) pStrmExt->FramesDropped, (DWORD) (pSrb->CommandData.DataBufferArray->PresentationTime.Time/10000), (DWORD) tmExpectedFrame/10000)); /*OnTime*/ } else /* N */ if((tmExpectedFrame - ALLOWABLE_TIMING_LATENCY) <= pSrb->CommandData.DataBufferArray->PresentationTime.Time) { // ON-TIME: may exactly matching or due to integer calculation, within one frame time. // Dequeue if there are more than one Srb in the queue. #if DBG // Detect if a pSrb is used more than once if((*plSrbUseCount) > 1) { TRACE(TL_CIP_TRACE,("\'* Go: pSrb:%x; RefCnt:%d; cntSrbQ:%d; PicNum:%d; Drp:%d; PresTime:%d >= CurTime:%d\n", pSrb, *plSrbUseCount, pStrmExt->cntSRBQueued, (DWORD) pStrmExt->PictureNumber, (DWORD) pStrmExt->FramesDropped, (DWORD) (pSrb->CommandData.DataBufferArray->PresentationTime.Time/10000), (DWORD) tmExpectedFrame/10000)); } #endif if(pStrmExt->bEOStream) { // Remove NEXT(SrbQ) only if there are more than one SRB or bEOStream TRACE(TL_CIP_TRACE,("\'EOStream=== Srb:%x; (SrbNum:%d ?= PicNum:%d) cntSrbQ:%d; Attach:%d ===\n", pSrb, pSrbEntry->SrbNum, (DWORD) pStrmExt->PictureNumber, (DWORD) pStrmExt->cntSRBQueued, (DWORD) pStrmExt->cntDataAttached)); RemoveEntryList(&pSrbEntry->ListEntry); pStrmExt->cntSRBQueued--; (*plSrbUseCount)--; ExFreePool(pSrbEntry); pSrbEntry = NULL; // Removed so free it! // Remove SRB if more than one SRBs in Q and there is not a discontinuity, or end of stream. } else if(pStrmExt->cntSRBQueued > 1) { LONGLONG tmExpectedNextFrame = tmExpectedFrame + DVFormatInfoTable[pStrmExt->pDevExt->VideoFormatIndex].ulAvgTimePerFrame; pSrbNext = ((SRB_ENTRY *) (pSrbEntry->ListEntry.Flink))->pSrb; // Next SRB has the next presentation time // May add this check as well: (but check Presentation time is more reliable) // pSrb->CommandData.DataBufferArray->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_DATADISCONTINUITY /* N,N+1 */ if((tmExpectedNextFrame + ALLOWABLE_TIMING_LATENCY) > pSrbNext->CommandData.DataBufferArray->PresentationTime.Time) { TRACE(TL_CIP_TRACE,("\'=== Srb:%x; (SrbNum:%d ?= PicNum:%d) cntSrbQ:%d; Attach:%d ===\n", pSrb, pSrbEntry->SrbNum, (DWORD) pStrmExt->PictureNumber, (DWORD) pStrmExt->cntSRBQueued, (DWORD) pStrmExt->cntDataAttached)); RemoveEntryList(&pSrbEntry->ListEntry); pStrmExt->cntSRBQueued--; (*plSrbUseCount)--; ExFreePool(pSrbEntry); pSrbEntry = NULL; // Removed so free it! /* N, N+2/++ */ } else { TRACE(TL_CIP_TRACE,("\'=== GO(Stale=TRUE) Srb:%x; (SrbNum:%d ?= PicNum:%d) Attach:%d ==\n", pSrb, pSrbEntry->SrbNum, (DWORD) pStrmExt->PictureNumber, (DWORD) pStrmExt->cntDataAttached)); // Mark this stale and be remove as soon as another is attached. } } else { TRACE(TL_CIP_TRACE,("\'=== GO(Stale=TRUE) Srb:%x; (SrbNum:%d ?= PicNum:%d) Attach:%d ==\n", pSrb, pSrbEntry->SrbNum, (DWORD) pStrmExt->PictureNumber, (DWORD) pStrmExt->cntDataAttached)); // Mark this stale and be remove as soon as another is attached. pSrbEntry->bStale = TRUE; } // CLOCK: tick when a frame is transmitted. // LATE: this is dropped until there is only one Srb in the SrbQ. // WORKITEM: we may need to implement IQualityManagement to inform application to read ahead. /*Late*/ } /*N-1*/ else { if(pStrmExt->cntSRBQueued > 1) { pSrbNext = ((SRB_ENTRY *) (pSrbEntry->ListEntry.Flink))->pSrb; // Next SRB has the next presentation time; it can be: // Current time is N // Current frame is late (N-1 or N-2..) and we have more than one Srb in the queue; // check next frame: // (N?) // N-2, N-1, N late more than one frame; (Next frame is also late; dequeu and not transmit; "catch up" case.) // N-1, N late one frame; (Next frame is on time; dequeu this frame) <-- Normal case // N-1, N+1 late one frame, but next frame is not N+1; (Next frame is early; *current frame will be repeated*) // // May add this check this as well: (but check Presentation time is more reliable) // pSrb->CommandData.DataBufferArray->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_DATADISCONTINUITY // // ****************************************************************************************************** // If next frame is earlier than current stream time, "repeat" current stale frame; else we need to "catch up"! // ****************************************************************************************************** /* N-1++, N */ if((tmExpectedFrame + ALLOWABLE_TIMING_LATENCY) > pSrbNext->CommandData.DataBufferArray->PresentationTime.Time) { TRACE(TL_CIP_TRACE,("\'*** Stale(not Sent): pSrb:%x; RefCnt:%d; cntSrbQ:%d; cntAtt:%d; PicNum:%d; Drp:%d; PTm:%d < ExpTm:%d\n", pSrb, *plSrbUseCount, pStrmExt->cntSRBQueued, pStrmExt->cntDataAttached, (DWORD) pStrmExt->PictureNumber, (DWORD) pStrmExt->FramesDropped, (DWORD) (pSrb->CommandData.DataBufferArray->PresentationTime.Time/10000), (DWORD) (tmExpectedFrame/10000) )); // Never been attached; remove late entry RemoveEntryList(&pSrbEntry->ListEntry); pStrmExt->cntSRBQueued--; (*plSrbUseCount)--; ExFreePool(pSrbEntry); pSrbEntry = NULL; // Removed so free it! if(*plSrbUseCount == 0) { // If no reference to is, complete this. pSrb->Status = STATUS_SUCCESS; // It is not a failure but late; maybe other status to indicate "non-fatal" late status.. pSrb->CommandData.DataBufferArray->DataUsed = 0; StreamClassStreamNotification(StreamRequestComplete, pSrb->StreamObject, pSrb); #if DBG pStrmExt->cntSRBPending--; #endif } KeReleaseSpinLock(pStrmExt->DataListLock, oldIrql); // Since SrbQ is not empty and this is a stale frame, call recursively to get to next frame. // Only possible error is if there is not sufficient resource (esp MDL) // then, we bail out by self terminating this thread. if(STATUS_INSUFFICIENT_RESOURCES == DVAttachWriteFrame(pStrmExt)) { TRACE(TL_CIP_ERROR,("DVAttachWriteFrame: STATUS_INSUFFICIENT_RESOURCES\n")); return STATUS_INSUFFICIENT_RESOURCES; } else { return STATUS_SUCCESS; // SUCESS unless there is another status to indicate "non-fatal" late. } /*N-2++, N-1++*/ } else { pSrbEntry->bStale = TRUE; } } else { // EOStream and is a stale stream, it is the last element in SrbQ. // Remove it. if(pStrmExt->bEOStream) { TRACE(TL_CIP_TRACE,("\'*** Stale(bEOStream): pSrb:%x; RefCnt:%d; cntSrbQ:%d; cntAtt:%d; PicNum:%d; Drp:%d; PTm:%d < ExpTm:%d\n", pSrb, *plSrbUseCount, pStrmExt->cntSRBQueued, pStrmExt->cntDataAttached, (DWORD) pStrmExt->PictureNumber, (DWORD) pStrmExt->FramesDropped, (DWORD) (pSrb->CommandData.DataBufferArray->PresentationTime.Time/10000), (DWORD) (tmExpectedFrame/10000) )); RemoveEntryList(&pSrbEntry->ListEntry); pStrmExt->cntSRBQueued--; (*plSrbUseCount)--; ExFreePool(pSrbEntry); pSrbEntry = NULL; // Removed so free it! if(*plSrbUseCount == 0) { // If no reference to is, complete this. pSrb->Status = STATUS_SUCCESS; // It is not a failure but late; maybe other status to indicate "non-fatal" late status.. pSrb->CommandData.DataBufferArray->DataUsed = 0; StreamClassStreamNotification(StreamRequestComplete, pSrb->StreamObject, pSrb); #if DBG pStrmExt->cntSRBPending--; #endif } KeReleaseSpinLock(pStrmExt->DataListLock, oldIrql); // Update current stream time pStrmExt->CurrentStreamTime = tmExpectedFrame; return STATUS_SUCCESS; // SUCESS unless there is another status to indicate "non-fatal" late. } TRACE(TL_CIP_TRACE,("\'*** Stale(Sent): pSrb:%x; RefCnt:%d; cntSrbQ:%d; cntAtt:%d; PicNum:%d; Drp:%d; PTm:%d < ExpTm:%d\n", pSrb, *plSrbUseCount, pStrmExt->cntSRBQueued, pStrmExt->cntDataAttached, (DWORD) pStrmExt->PictureNumber, (DWORD) pStrmExt->FramesDropped, (DWORD) (pSrb->CommandData.DataBufferArray->PresentationTime.Time/10000), (DWORD) (tmExpectedFrame/10000) )); // If this is stale and this is the only frame in SrbQ, Xmt it } // If late, this frame is always drop. pStrmExt->FramesDropped++; } // Update current stream time pStrmExt->CurrentStreamTime = tmExpectedFrame; } // if(pStrmExt->hMasterClock) else { // Not the master clock, no "pacing" so always dequeu (SrbQ) and transmit // as long as there is one Srb in the queue. if(pStrmExt->cntSRBQueued > 1 || pStrmExt->bEOStream) { RemoveEntryList(&pSrbEntry->ListEntry); pStrmExt->cntSRBQueued--; (*plSrbUseCount)--; ExFreePool(pSrbEntry); pSrbEntry = NULL; // Removed so free it! } TRACE(TL_CIP_TRACE,("\'* GO: (NoClock) pSrb:%x; RefCnt:%d; cntSrbQ:%d; PicNum:%d;\n", pSrb, *plSrbUseCount, pStrmExt->cntSRBQueued, (DWORD) pStrmExt->PictureNumber)); } // if(pStrmExt->hMasterClock) // pStrmExt->FramesProcessed is updated when a frame has been transmitted in the notify routine. // **** THIS IS THE CLOCK TICK **** pStrmExt->PictureNumber++; // After tmExpectedFrame is calculated; Another frame to be attached if(pStrmExt->hMasterClock) { #ifdef SUPPORT_QUALITY_CONTROL // +: late; -: early pStrmExt->KSQuality.DeltaTime = pStrmExt->CurrentStreamTime - pSrb->CommandData.DataBufferArray->PresentationTime.Time; // Percentage * 10 of frame transmitted pStrmExt->KSQuality.Proportion = (ULONG) ((pStrmExt->PictureNumber - pStrmExt->FramesDropped) * 1000 / pStrmExt->PictureNumber); pStrmExt->KSQuality.Context = /* NOT USED */ 0; #define MIN_ATTACH_BUFFER 3 // This is where we may want to signal that we are near Famine!! if (pStrmExt->KSQuality.DeltaTime > (DV_NUM_OF_XMT_BUFFERS - MIN_ATTACH_BUFFER) * DVFormatInfoTable[pStrmExt->pDevExt->VideoFormatIndex].ulAvgTimePerFrame) { TRACE(TL_CIP_TRACE,("\'QualityControl: pic#%d; drop:%d; Prop:%d; DeltaTime:%d (Srb.tmPres:%d, tmStream:%d)\n", (DWORD) pStrmExt->PictureNumber, (DWORD) pStrmExt->FramesDropped, pStrmExt->KSQuality.Proportion, (DWORD) pStrmExt->KSQuality.DeltaTime/10000, (DWORD) pSrb->CommandData.DataBufferArray->PresentationTime.Time/10000, (DWORD) pStrmExt->CurrentStreamTime/10000 )); } #endif } } // KSSTATE_RUN #if DBG // Collect transmit buffer statistics if(pStrmExt->ulStatEntries < MAX_XMT_FRAMES_TRACED) { pXmtStat = pStrmExt->paXmtStat + pStrmExt->ulStatEntries; pXmtStat->StreamState = pStrmExt->StreamState; pXmtStat->cntSRBReceived = (LONG) pStrmExt->cntSRBReceived; pXmtStat->cntSRBPending = (LONG) pStrmExt->cntSRBPending; pXmtStat->cntSRBQueued = (LONG) pStrmExt->cntSRBQueued; pXmtStat->cntDataAttached= pStrmExt->cntDataAttached; pXmtStat->FrameSlot = (DWORD) pStrmExt->PictureNumber; pXmtStat->tmStreamTime = pStrmExt->CurrentStreamTime; pXmtStat->DropCount = (DWORD) pStrmExt->FramesDropped; pXmtStat->FrameNumber = SrbNumCache; pXmtStat->OptionsFlags = pSrb->CommandData.DataBufferArray->OptionsFlags; pXmtStat->tmPresentation = pSrb->CommandData.DataBufferArray->PresentationTime.Time; // get the actual CyclTime when the frame is transmitted in the completion routine. pStrmExt->ulStatEntries++; } #endif #ifdef MSDV_SUPPORT_MUTE_AUDIO // pSrbEntry could have been freed; if it has not and useCnt>1, then it could be a repeat frame. if(pSrbEntry && (*plSrbUseCount) > 1) { // Set it only once if(!pSrbEntry->bAudioMute) pSrbEntry->bAudioMute = DVMuteDVFrame(pStrmExt->pDevExt, pFrameBuffer, TRUE); } #endif // Get a data packet node as the context and list node pSrbDataPacket = (PSRB_DATA_PACKET) RemoveHeadList(&pStrmExt->DataDetachedListHead); pStrmExt->cntDataDetached--; ulSrcPktLen = \ (DVFormatInfoTable[pStrmExt->pDevExt->VideoFormatIndex].DataBlockSize << 2) * \ (1 << DVFormatInfoTable[pStrmExt->pDevExt->VideoFormatIndex].FractionNumber); // Format an attach frame request DVFormatAttachFrame( pStrmExt->pStrmInfo->DataFlow, pStrmExt, &pSrbDataPacket->AVReq, pSrb, pSrbDataPacket, ulSrcPktLen, DVFormatInfoTable[pStrmExt->pDevExt->VideoFormatIndex].ulFrameSize, pFrameBuffer ); TRACE(TL_CIP_INFO,("\'------ New>> UseCnt:%d; pAVReq:%x; Srb:%x; DtaPkt:%x; AQD [%d:%d:%d]\n", *plSrbUseCount, &pSrbDataPacket->AVReq, pSrb, pSrbDataPacket, pStrmExt->cntDataAttached, pStrmExt->cntSRBQueued, pStrmExt->cntDataDetached )); // Add this to the attached list InsertTailList(&pStrmExt->DataAttachedListHead, &pSrbDataPacket->ListEntry); pStrmExt->cntDataAttached++; (*plSrbUseCount) ++; // ++ for being in queue ASSERT(*plSrbUseCount > 0); KeReleaseSpinLock(pStrmExt->DataListLock, oldIrql); NextIrpStack = IoGetNextIrpStackLocation(pSrbDataPacket->pIrp); NextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; NextIrpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_61883_CLASS; NextIrpStack->Parameters.Others.Argument1 = &pSrbDataPacket->AVReq; IoSetCompletionRoutine( pSrbDataPacket->pIrp, DVAttachFrameCR, pSrbDataPacket, TRUE, TRUE, TRUE ); pSrbDataPacket->pIrp->IoStatus.Status = STATUS_SUCCESS; // Initialize it to something Status = IoCallDriver( pStrmExt->pDevExt->pBusDeviceObject, pSrbDataPacket->pIrp); ASSERT(Status == STATUS_PENDING || Status == STATUS_SUCCESS); if(!NT_SUCCESS(Status)) { // put the resource back! TRACE(TL_CIP_ERROR,("DVAttachWriteFrame: Failed to attach; St:%x\n", Status)); ASSERT(FALSE && "Failed to attach a Xmt frame."); } // // This is our throttle that regulate data attach to DV: // // This function is called by the attach thread which is running in an infinite loop. // This function need to utilize the buffer that it receive and its repeat mechanism to // regulate the incoming buffer from client and outgoing buffer attach to 1394 stadck for transmit. // One way is to wait while there is certain number of frame already attach. KeAcquireSpinLock(pStrmExt->DataListLock, &oldIrql); if(!pStrmExt->bEOStream && // Need to keep NUM_BUF_ATTACHED_THEN_ISOCH buffer attached at all time to keep 61883 isoch xmt going. (pStrmExt->StreamState == KSSTATE_RUN && pStrmExt->cntDataAttached > NUM_BUF_ATTACHED_THEN_ISOCH || pStrmExt->StreamState == KSSTATE_PAUSE && pStrmExt->cntDataAttached >= NUM_BUF_ATTACHED_THEN_ISOCH ) ) { NTSTATUS StatusDelay = STATUS_SUCCESS; #if DBG ULONGLONG tmStart = GetSystemTime(); TRACE(TL_CIP_TRACE,("\'[Pic# %d]; SrbNum:%d; Dropped:%d; pSrb:%x; StrmSt:%d; EOS:%d; AQD:[%d;%d;%d]; ", (DWORD) pStrmExt->PictureNumber, SrbNumCache, (DWORD) pStrmExt->FramesDropped, pSrb, pStrmExt->StreamState, pStrmExt->bEOStream, pStrmExt->cntDataAttached, pStrmExt->cntSRBQueued, pStrmExt->cntDataDetached)); #endif Timeout.HighPart = -1; Timeout.LowPart = (ULONG)(-1 * DVFormatInfoTable[pStrmExt->pDevExt->VideoFormatIndex].ulAvgTimePerFrame * \ (pStrmExt->StreamState == KSSTATE_PAUSE ? 1 : (pStrmExt->cntDataAttached - NUM_BUF_ATTACHED_THEN_ISOCH))) ; // Wait the full time until we are very low in SrbQ or Attached buffers. if(pStrmExt->cntSRBQueued <= 1 && pStrmExt->cntDataAttached <= NUM_BUF_ATTACHED_THEN_ISOCH) { KeReleaseSpinLock(pStrmExt->DataListLock, oldIrql); // Guard against pStrmExt->cntSRBQueued // Down to one frame so we will wait for an event and will be signalled // when a new frame has arrived, or // when number of attach buffer is below the minimum. StatusDelay = KeWaitForSingleObject( &pStrmExt->hSrbArriveEvent, Executive, KernelMode, FALSE, &Timeout ); // Important: If signalled, reset it else (timeout), // we are still behind so next time we will not wait! if(StatusDelay == STATUS_SUCCESS) KeClearEvent(&pStrmExt->hSrbArriveEvent); } else { KeReleaseSpinLock(pStrmExt->DataListLock, oldIrql); // Wait for frame(s) to be delivered; this is our throttle.. // The timeout period can be one or up to // (pStrmExt->cntDataAttached - NUM_BUF_ATTACHED_THEN_ISOCH) frames KeDelayExecutionThread(KernelMode, FALSE, &Timeout); } #if DBG TRACE(TL_CIP_TRACE,("\'Wait(ST:%x) %d nsec!\n", StatusDelay, (DWORD) ((GetSystemTime() - tmStart)/10))); #endif } else { KeReleaseSpinLock(pStrmExt->DataListLock, oldIrql); } return Status; } VOID DVFormatAttachFrame( IN KSPIN_DATAFLOW DataFlow, IN PSTREAMEX pStrmExt, IN PAV_61883_REQUEST pAVReq, IN PHW_STREAM_REQUEST_BLOCK pSrb, IN PSRB_DATA_PACKET pSrbDataPacket, IN ULONG ulSourceLength, // Packet length in bytes IN ULONG ulFrameSize, // Buffer size; may contain one or multiple source packets IN PVOID pFrameBuffer ) /*++ Routine Description: Format an attach frame request. --*/ { ASSERT(pSrb); // // Setup PSRB_DATA_PACKET, except its Frame structure (PCIP_APP_FRAME) // InitializeListHead(&pSrbDataPacket->ListEntry); pSrbDataPacket->State = DE_PREPARED; // Initial state of a resued DataEntry (start over!) pSrbDataPacket->pSrb = pSrb; pSrbDataPacket->StreamState = pStrmExt->StreamState; // StreamState when this buffer is attached. pSrbDataPacket->pStrmExt = pStrmExt; pSrbDataPacket->FrameBuffer = pFrameBuffer; ASSERT(pSrbDataPacket->FrameBuffer != NULL); pSrbDataPacket->Frame->pNext = NULL; pSrbDataPacket->Frame->Status = 0; pSrbDataPacket->Frame->Packet = (PUCHAR) pFrameBuffer; if(DataFlow == KSPIN_DATAFLOW_OUT) { pSrbDataPacket->FrameNumber = pStrmExt->cntSRBReceived; #ifdef NT51_61883 // This is needed since we have an old 61883.h in Lab06 (according to include path, anyway). // Remove this when 61883.h is updated. #ifndef CIP_RESET_FRAME_ON_DISCONTINUITY #define CIP_RESET_FRAME_ON_DISCONTINUITY 0x00000040 #endif // // 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) // pSrbDataPacket->Frame->Flags = CIP_VALIDATE_FIRST_SOURCE // Verify the start of a DV frame | CIP_RESET_FRAME_ON_DISCONTINUITY; // No partial frame #else pSrbDataPacket->Frame->Flags = 0; #endif pSrbDataPacket->Frame->pfnValidate = DVReadFrameValidate; // use to validate the 1st source packet pSrbDataPacket->Frame->ValidateContext = pSrbDataPacket; pSrbDataPacket->Frame->pfnNotify = DVCompleteSrbRead; } else { pSrbDataPacket->FrameNumber = pStrmExt->FramesProcessed; pSrbDataPacket->Frame->Flags = CIP_DV_STYLE_SYT; pSrbDataPacket->Frame->pfnValidate = NULL; pSrbDataPacket->Frame->ValidateContext = NULL; pSrbDataPacket->Frame->pfnNotify = DVCompleteSrbWrite; } pSrbDataPacket->Frame->NotifyContext = pSrbDataPacket; // // Av61883_AttachFrames // RtlZeroMemory(pAVReq, sizeof(AV_61883_REQUEST)); INIT_61883_HEADER(pAVReq, Av61883_AttachFrame); pAVReq->AttachFrame.hConnect = pStrmExt->hConnect; pAVReq->AttachFrame.FrameLength = ulFrameSize; pAVReq->AttachFrame.SourceLength = ulSourceLength; pAVReq->AttachFrame.Frame = pSrbDataPacket->Frame; ASSERT(pStrmExt->hConnect); ASSERT(pSrbDataPacket->Frame); } VOID DVAttachFrameThread( IN PSTREAMEX pStrmExt ) /*++ Routine Description: This is a system thread to attach frame for transmit. --*/ { NTSTATUS Status; PDVCR_EXTENSION pDevExt; KIRQL OldIrql; PAGED_CODE(); pDevExt = pStrmExt->pDevExt; // // Pump up the priority since we are dealing with real time data // KeSetPriorityThread(KeGetCurrentThread(), #if 1 LOW_REALTIME_PRIORITY #else HIGH_PRIORITY #endif ); while (!pStrmExt->bTerminateThread) { // // Halt this thread operation if other critical service // is requested. // if( !pStrmExt->bTerminateThread && (pStrmExt->lNeedService > 0) ) { // not fullly awake/powered. NTSTATUS StatusWait; TRACE(TL_CIP_WARNING,("\'Request stop thread for other service: lNeedService:%d; pStrmExt:%x; AQD[%d,%d,%d]\n", pStrmExt->lNeedService, pStrmExt, pStrmExt->cntDataAttached, pStrmExt->cntSRBQueued, pStrmExt->cntDataDetached )); InterlockedDecrement(&pStrmExt->lNeedService); // One reuqest serviced. // // Indicate that thread is about to stop. Signal it so other operation can begin. // KeSetEvent(&pStrmExt->hStopThreadEvent, 0 ,FALSE); TRACE(TL_CIP_WARNING,("\'>>>> Enter WFSO(hRunThreadEvent)\n")); StatusWait = KeWaitForSingleObject( &pStrmExt->hRunThreadEvent, Executive, KernelMode, FALSE, 0 ); TRACE(TL_CIP_WARNING,("\'<<<< Exit WFSO(hRunThreadEvent); lNeedService:%d\n", pStrmExt->lNeedService)); ASSERT(pStrmExt->lNeedService == 0); } // Halt attach operation if // not in either PAUSE or RUN state ( i.e. in STOP state) // Device is removed // in the process of being cancelled. if( !pStrmExt->bTerminateThread && pStrmExt->StreamState != KSSTATE_PAUSE && pStrmExt->StreamState != KSSTATE_RUN || pStrmExt->pDevExt->bDevRemoved || pStrmExt->lCancelStateWorkItem > 0 ) { NTSTATUS StatusWait; TRACE(TL_CIP_WARNING,("\'Enter WFSO(hSrbArriveEvent): StrmState:%d; bDevRemoved:%d\n", pStrmExt->StreamState, pStrmExt->pDevExt->bDevRemoved)); StatusWait = // Can only return STATUS_SUCCESS (signal) or STATUS_TIMEOUT KeWaitForSingleObject( &pStrmExt->hSrbArriveEvent, // Signal with arrival of the first frame Executive, KernelMode, // Cannot return STATUS_USER_APC FALSE, // Cannot be alerted STATUS_ALERTED 0 // INFINITE!! ); // Reset notification event (or it will stay signalled). KeClearEvent(&pStrmExt->hSrbArriveEvent); TRACE(TL_CIP_WARNING,("\'Exit WFSO(hSrbArriveEvent): StrmState:%d; bDevRemoved:%d\n", pStrmExt->StreamState, pStrmExt->pDevExt->bDevRemoved)); #ifdef SUPPORT_PREROLL_AT_RUN_STATE // Simulate preroll at the RUN state // We do this only when we are the clock provider to avoid dropping frame // This timed WFSO is necessary if application send us only one frame while in RUN state. #define PREROLL_WAITTIME 2000000 if( !pStrmExt->bTerminateThread && pStrmExt->hMasterClock ) { LARGE_INTEGER DueTime; DueTime = RtlConvertLongToLargeInteger(-((LONG) PREROLL_WAITTIME)); TRACE(TL_CIP_WARNING,("\'Enter WFSO(hPreRollEvent)\n")); StatusWait = // Can only return STATUS_SUCCESS (signal) or STATUS_TIMEOUT KeWaitForSingleObject( &pStrmExt->hPreRollEvent, Executive, KernelMode, // Cannot return STATUS_USER_APC FALSE, // Cannot be alerted STATUS_ALERTED &DueTime ); // hPreRollEvent is a ont shot event; no need to reset it; let it stay signalled. TRACE(TL_CIP_WARNING,("\'Exit WFSO(hPreRollEvent); waited %d msec; waitStatus:%x\n", (DWORD) ((GetSystemTime() - pStrmExt->tmStreamStart)/10000), StatusWait )); } #endif } #if DBG if( !pStrmExt->bTerminateThread && !pStrmExt->bEOStream && pStrmExt->FramesProcessed > 0 && pStrmExt->cntDataAttached < NUM_BUF_ATTACHED_THEN_ISOCH ) { TRACE(TL_CIP_TRACE,("\'AttachBuf is low!! SrbRcv:%d;Pic#:%d;Prc:%d;;Drop:%d;Cncl:%d; AQD [%d:%d:%d]\n", (DWORD) pStrmExt->cntSRBReceived, (DWORD) pStrmExt->PictureNumber, (DWORD) pStrmExt->FramesProcessed, (DWORD) pStrmExt->FramesDropped, (DWORD) pStrmExt->cntSRBCancelled, pStrmExt->cntDataAttached, pStrmExt->cntSRBQueued, pStrmExt->cntDataDetached )); } #endif // Attach another frame for transmit // Only possible error is if there is not sufficient resource (esp MDL) // Then, we bail out by self terminating this thread. if( !pStrmExt->pDevExt->bDevRemoved && !pStrmExt->bTerminateThread ) { if(STATUS_INSUFFICIENT_RESOURCES == \ DVAttachWriteFrame(pStrmExt)) { TRACE(TL_CIP_ERROR,("STATUS_INSUFFICIENT_RESOURCES while attaching write frame.\n")); pStrmExt->bTerminateThread = TRUE; } } // // Start Isoch_Talk once we have enough buffers attached. // It is possible that streaming state is set to RUN before we have enough attach buffer // start streaming. So we need to kick start here. // if( !pStrmExt->bTerminateThread && !pStrmExt->pDevExt->bDevRemoved && pStrmExt->pDevExt->PowerState == PowerDeviceD0 // Need to be PoweredON && pStrmExt->lCancelStateWorkItem == 0 // No pending cancel work item && !pStrmExt->bIsochIsActive && (pStrmExt->StreamState == KSSTATE_PAUSE || pStrmExt->StreamState == KSSTATE_RUN) && pStrmExt->cntDataAttached >= NUM_BUF_ATTACHED_THEN_ISOCH ) { Status = DVStreamingStart( pStrmExt->pStrmInfo->DataFlow, pStrmExt, pStrmExt->pDevExt ); } } TRACE(TL_STRM_WARNING,("\'*** ThreadTerminating... AQD [%d:%d:%d]\n", pStrmExt->cntDataAttached, pStrmExt->cntSRBQueued, pStrmExt->cntDataDetached )); KeAcquireSpinLock(&pStrmExt->pDevExt->AVCCmdLock, &OldIrql); if(pStrmExt->lNeedService) { TRACE(TL_STRM_WARNING,("Thread is exiting but lNeedService:%x != 0!\n", pStrmExt->lNeedService)); KeSetEvent(&pStrmExt->hStopThreadEvent, 0 ,FALSE); InterlockedDecrement(&pStrmExt->lNeedService); ASSERT(pStrmExt->lNeedService == 0); } KeReleaseSpinLock(&pStrmExt->pDevExt->AVCCmdLock, OldIrql); KeSetEvent(&pStrmExt->hThreadEndEvent, 0, FALSE); // Signal it Status = PsTerminateSystemThread(STATUS_SUCCESS); // Must be called at PASSIVE_LEVEL // End of this thread! } VOID DVTerminateAttachFrameThread( IN PSTREAMEX pStrmExt ) /*++ Routine Description: To terminate the system thread. It waits for an event that is triggered right before PsTerminateSystemThread() is called. --*/ { PAGED_CODE(); TRACE(TL_CIP_WARNING,("\'DVTerminateAttachFrameThread enter\n")); // // Wake up the DataReady thread and terminate it if not already done so. // ASSERT(pStrmExt->bIsochIsActive == FALSE && "Terminate therad while IsochActive!"); // // This function can be called from either CloseStrean or SurpriseRemoval; // When a DV is surprise removal, this function may get called from both functions. // Assuming StreamClass is serializing these two functions, no need to serialize it locally. // if(pStrmExt->bTerminateThread) { TRACE(TL_CIP_ERROR,("DVTerminateAttachFrameThread: Thread already terminated. Was surprise removed?\n")); return; } // ****** Terminate thread ****** pStrmExt->bTerminateThread = TRUE; // ****** // With bTerminate thread set, let the thread run and terminate. KeSetEvent(&pStrmExt->hRunThreadEvent, 0 ,FALSE); // WFSO while in STOP state; signal so it can be terminated. KeSetEvent(&pStrmExt->hSrbArriveEvent, 0, FALSE); #ifdef SUPPORT_PREROLL_AT_RUN_STATE // Signal it in case that it is still in WFSO. KeSetEvent(&pStrmExt->hPreRollEvent, 0, FALSE); #endif KeWaitForSingleObject( &pStrmExt->hThreadEndEvent, Executive, KernelMode, FALSE, NULL ); TRACE(TL_CIP_WARNING,("\'Thread terminated!\n")); ObDereferenceObject( &pStrmExt->pAttachFrameThreadObject ); TRACE(TL_CIP_WARNING,("\'ObDereferenceObject done!\n")); }