/*++ Copyright (C) Microsoft Corporation, 1999 - 2000 Module Name: MSDVUtil.c Abstract: Provide utility functions for MSDV. 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 "avc.h" #include "dbg.h" #include "msdvfmt.h" #include "msdvdef.h" #include "MsdvAvc.h" #include "MsdvUtil.h" #include "XPrtDefs.h" #if 0 // Enable later #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, DVDelayExecutionThread) #pragma alloc_text(PAGE, DVGetUnitCapabilities) // Local variables might paged out but the called might use it in DISPATCH level! // #pragma alloc_text(PAGE, DVGetDevModeOfOperation) // #pragma alloc_text(PAGE, DVGetDevIsItDVCPro) // #pragma alloc_text(PAGE, DVGetDevSignalFormat) #pragma alloc_text(PAGE, DvAllocatePCResource) #pragma alloc_text(PAGE, DvFreePCResource) #pragma alloc_text(PAGE, DVGetPlugState) #pragma alloc_text(PAGE, DVConnect) #pragma alloc_text(PAGE, DVDisconnect) #endif #endif extern DV_FORMAT_INFO DVFormatInfoTable[]; VOID DVDelayExecutionThread( ULONG ulDelayMSec ) /* Device might need a "wait" in between AV/C commands. */ { PAGED_CODE(); if (ulDelayMSec) { LARGE_INTEGER tmDelay; TRACE(TL_PNP_TRACE,("\'DelayExeThrd: %d MSec\n", ulDelayMSec)); tmDelay.LowPart = (ULONG) (-1 * ulDelayMSec * 10000); tmDelay.HighPart = -1; KeDelayExecutionThread(KernelMode, FALSE, &tmDelay); } } NTSTATUS DVIrpSynchCR( IN PDEVICE_OBJECT DeviceObject, IN PIRP pIrp, IN PKEVENT Event ) { KeSetEvent(Event, 0, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; } // DVIrpSynchCR NTSTATUS DVSubmitIrpSynch( IN PDVCR_EXTENSION pDevExt, IN PIRP pIrp, IN PAV_61883_REQUEST pAVReq ) { NTSTATUS Status; KEVENT Event; PIO_STACK_LOCATION NextIrpStack; 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, DVIrpSynchCR, &Event, TRUE, TRUE, TRUE ); Status = IoCallDriver( pDevExt->pBusDeviceObject, pIrp ); if (Status == STATUS_PENDING) { TRACE(TL_PNP_TRACE,("\'Irp is pending...\n")); if(KeGetCurrentIrql() < DISPATCH_LEVEL) { KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL ); TRACE(TL_PNP_TRACE,("\'Irp has returned; IoStatus==Status %x\n", pIrp->IoStatus.Status)); Status = pIrp->IoStatus.Status; // Final status } else { ASSERT(FALSE && "Pending but in DISPATCH_LEVEL!"); return Status; } } return Status; } // DVSubmitIrpSynchAV BOOL DVGetDevModeOfOperation( IN PDVCR_EXTENSION pDevExt ) { NTSTATUS Status; BYTE bAvcBuf[MAX_FCP_PAYLOAD_SIZE]; PAGED_CODE(); // // Use ConnectAV STATUS cmd to determine mode of operation, // except for some Canon DVs that it requires its vendor specific command // Status = DVIssueAVCCommand( pDevExt, AVC_CTYPE_STATUS, DV_CONNECT_AV_MODE, (PVOID) bAvcBuf ); TRACE(TL_FCP_TRACE,("\'DV_CONNECT_AV_MODE: St:%x, %x %x %x %x : %x %x %x %x\n", Status, bAvcBuf[0], bAvcBuf[1], bAvcBuf[2], bAvcBuf[3], bAvcBuf[4], bAvcBuf[5], bAvcBuf[6], bAvcBuf[7])); if(Status == STATUS_SUCCESS) { if(bAvcBuf[0] == 0x0c) { if(bAvcBuf[1] == 0x00 && bAvcBuf[2] == 0x38 && bAvcBuf[3] == 0x38) { pDevExt->ulDevType = ED_DEVTYPE_CAMERA; } else { pDevExt->ulDevType = ED_DEVTYPE_VCR; } } } else if(pDevExt->ulVendorID == VENDORID_CANON) { // Try a vendor dependent command if it is a Canon AV device. Status = DVIssueAVCCommand( pDevExt, AVC_CTYPE_STATUS, DV_VEN_DEP_CANON_MODE, (PVOID) bAvcBuf ); TRACE(TL_FCP_WARNING,("\'DV_VEN_DEP_CANON_MODE: Status %x, %x %x %x %x : %x %x %x %x %x %x\n", Status, bAvcBuf[0], bAvcBuf[1], bAvcBuf[2], bAvcBuf[3], bAvcBuf[4], bAvcBuf[5], bAvcBuf[6], bAvcBuf[7], bAvcBuf[8], bAvcBuf[9])); if(Status == STATUS_SUCCESS) { if(bAvcBuf[0] == 0x0c) { if(bAvcBuf[7] == 0x38) { pDevExt->ulDevType = ED_DEVTYPE_CAMERA; } else if(bAvcBuf[7] == 0x20) { pDevExt->ulDevType = ED_DEVTYPE_VCR; } } } } if(Status != STATUS_SUCCESS) { pDevExt->ulDevType = ED_DEVTYPE_UNKNOWN; TRACE(TL_FCP_ERROR,("\'DV_CONNECT_AV_MODE: Status %x, DevType %x, %x %x %x %x : %x %x %x %x : %x %x\n", Status, pDevExt->ulDevType, bAvcBuf[0], bAvcBuf[1], bAvcBuf[2], bAvcBuf[3], bAvcBuf[4], bAvcBuf[5], bAvcBuf[6], bAvcBuf[7], bAvcBuf[8], bAvcBuf[9])); } TRACE(TL_FCP_WARNING,("\'%s; NumOPlg:%d; NumIPlg:%d\n", pDevExt->ulDevType == ED_DEVTYPE_CAMERA ? "Camera" : pDevExt->ulDevType == ED_DEVTYPE_VCR ? "VTR" : "Unknown", pDevExt->NumOutputPlugs, pDevExt->NumInputPlugs)); return TRUE; } BOOL DVGetDevIsItDVCPro( IN PDVCR_EXTENSION pDevExt ) { NTSTATUS Status; BYTE bAvcBuf[MAX_FCP_PAYLOAD_SIZE]; PAGED_CODE(); // // Use Panasnoic's vendor dependent command to determine if the system support DVCPro // Status = DVIssueAVCCommand( pDevExt, AVC_CTYPE_STATUS, DV_VEN_DEP_DVCPRO, (PVOID) bAvcBuf ); pDevExt->bDVCPro = Status == STATUS_SUCCESS; TRACE(TL_FCP_WARNING,("\'DVGetDevIsItDVCPro? %s; Status %x, %x %x %x %x : %x %x %x %x\n", pDevExt->bDVCPro ? "Yes":"No", Status, bAvcBuf[0], bAvcBuf[1], bAvcBuf[2], bAvcBuf[3], bAvcBuf[4], bAvcBuf[5], bAvcBuf[6], bAvcBuf[7])); return pDevExt->bDVCPro; } // The retries might be redundant since AVC.sys and 1394.sys retries. // For device that TIMEOUT an AVC command, we will only try it once. #define GET_MEDIA_FMT_MAX_RETRIES 10 BOOL DVGetDevSignalFormat( IN PDVCR_EXTENSION pDevExt, IN KSPIN_DATAFLOW DataFlow, IN PSTREAMEX pStrmExt ) { NTSTATUS Status; BYTE bAvcBuf[MAX_FCP_PAYLOAD_SIZE]; LONG lRetries = GET_MEDIA_FMT_MAX_RETRIES; PAGED_CODE(); // // Respone of Input/output signal mode is used to determine plug signal format: // // FMT: // DVCR 10:00 0000 = 0x80; Canon returns 00:100000 (0x20) // 50/60: 0:NTSC/60; 1:PAL/50 // STYPE: // SD: 00000 (DVCPRO:11110) // HD: 00010 // SDL:00001 // 00: // SYT: // MPEG 10:10 0000 = 0xa0 // TSF:0:NotTimeShifted; 1:Time shifted // 000 0000 0000 0000 0000 0000 // // If this command failed, we can use Input/Output Signal Mode subunit command // to determine signal format. // do { RtlZeroMemory(bAvcBuf, sizeof(bAvcBuf)); Status = DVIssueAVCCommand( pDevExt, AVC_CTYPE_STATUS, pStrmExt == NULL ? DV_OUT_PLUG_SIGNAL_FMT : (DataFlow == KSPIN_DATAFLOW_OUT ? DV_OUT_PLUG_SIGNAL_FMT : DV_IN_PLUG_SIGNAL_FMT), (PVOID) bAvcBuf ); // // Camcorders that has problem with this command: // // Panasonic's DVCPRO: if power on while connected to PC, it will // reject this command with (STATUS_REQUEST_NOT_ACCEPTED) // so we will retry up to 10 time with .5 second wait between tries. // // JVC: returns STATUS_NOT_SUPPORTED. // // SONY DV Decoder Box: return STATUS_TIMEOUT or STATUS_REQUEST_ABORTED // if(STATUS_REQUEST_ABORTED == Status) return FALSE; else if(STATUS_SUCCESS == Status) break; // Normal case. else if(STATUS_NOT_SUPPORTED == Status || STATUS_TIMEOUT == Status) { TRACE(TL_FCP_WARNING | TL_PNP_WARNING,("SignalFormat: Encountered a known failed status:%x; no more retry\n", Status)); break; // No need to retry } else { if(Status == STATUS_REQUEST_NOT_ACCEPTED) { // If device is not accepting command and return this status, retry. if(lRetries > 0) { TRACE(TL_FCP_WARNING | TL_PNP_WARNING,("\'ST:%x; Retry getting signal mode; wait...\n", Status)); DVDelayExecutionThread(DV_AVC_CMD_DELAY_DVCPRO); } } } } while (--lRetries >= 0); if(NT_SUCCESS(Status)) { switch(bAvcBuf[0]) { case FMT_DVCR: case FMT_DVCR_CANON: // Workaround for buggy Canon Camcorders switch(bAvcBuf[1] & FDF0_STYPE_MASK) { case FDF0_STYPE_SD_DVCR: case FDF0_STYPE_SD_DVCPRO: pDevExt->VideoFormatIndex = ((bAvcBuf[1] & FDF0_50_60_MASK) ? FMT_IDX_SD_DVCR_PAL : FMT_IDX_SD_DVCR_NTSC); if(pStrmExt) RtlCopyMemory(&pStrmExt->cipQuad2[0], &bAvcBuf[0], 4); break; #ifdef MSDV_SUPPORT_HD_DVCR case FDF0_STYPE_HD_DVCR: pDevExt->VideoFormatIndex = ((bAvcBuf[1] & FDF0_50_60_MASK) ? FMT_IDX_HD_DVCR_PAL : FMT_IDX_HD_DVCR_NTSC); if(pStrmExt) RtlCopyMemory(&pStrmExt->cipQuad2[0], &bAvcBuf[0], 4); break; #endif #ifdef MSDV_SUPPORT_SDL_DVCR case FDF0_STYPE_SDL_DVCR: pDevExt->VideoFormatIndex = ((bAvcBuf[1] & FDF0_50_60_MASK) ? FMT_IDX_SDL_DVCR_PAL : FMT_IDX_SDL_DVCR_NTSC); if(pStrmExt) RtlCopyMemory(&pStrmExt->cipQuad2[0], &bAvcBuf[0], 4); break; #endif default: // Unknown format Status = STATUS_UNSUCCESSFUL; break; } break; #ifdef MSDV_SUPPORT_MPEG2TS case FMT_MPEG: pDevExt->VideoFormatIndex = FMT_IDX_MPEG2TS; if(pStrmExt) RtlCopyMemory(&pStrmExt->cipQuad2[0], &bAvcBuf[0], 4); break; #endif default: Status = STATUS_UNSUCCESSFUL; } if(NT_SUCCESS(Status)) { TRACE(TL_FCP_WARNING,("\'ST:%x; PlugSignal:FMT[%x %x %x %x]; VideoFormatIndex;%d\n", Status, bAvcBuf[0], bAvcBuf[1], bAvcBuf[2] , bAvcBuf[3], pDevExt->VideoFormatIndex)); return TRUE; // Success } } TRACE(TL_FCP_WARNING,("\'ST:%x; PlugSignal:FMT[%x %x %x %x]\n", Status, bAvcBuf[0], bAvcBuf[1], bAvcBuf[2] , bAvcBuf[3], pDevExt->VideoFormatIndex)); // // If "recommended" unit input/output plug signal status command fails, // try "manadatory" input/output signal mode status command. // This command may failed some device if its tape is not playing for // output signal mode command. // Status = DVIssueAVCCommand( pDevExt, AVC_CTYPE_STATUS, DataFlow == KSPIN_DATAFLOW_OUT ? VCR_OUTPUT_SIGNAL_MODE : VCR_INPUT_SIGNAL_MODE, (PVOID) bAvcBuf ); if(STATUS_SUCCESS == Status) { PKSPROPERTY_EXTXPORT_S pXPrtProperty; pXPrtProperty = (PKSPROPERTY_EXTXPORT_S) bAvcBuf; TRACE(TL_FCP_WARNING,("\'** MediaFormat: Retry %d mSec; ST:%x; SignalMode:%dL\n", (GET_MEDIA_FMT_MAX_RETRIES - lRetries) * DV_AVC_CMD_DELAY_DVCPRO, Status, pXPrtProperty->u.SignalMode - ED_BASE)); switch(pXPrtProperty->u.SignalMode) { case ED_TRANSBASIC_SIGNAL_525_60_SD: pDevExt->VideoFormatIndex = FMT_IDX_SD_DVCR_NTSC; if(pStrmExt) { pStrmExt->cipQuad2[0] = FMT_DVCR; // 0x80 if(pDevExt->bDVCPro) pStrmExt->cipQuad2[1] = FDF0_50_60_NTSC | FDF0_STYPE_SD_DVCPRO; // 0x78 = NTSC(0):STYPE(11110):RSV(00) else pStrmExt->cipQuad2[1] = FDF0_50_60_NTSC | FDF0_STYPE_SD_DVCR; // 0x00 = NTSC(0):STYPE(00000):RSV(00) } break; case ED_TRANSBASIC_SIGNAL_625_50_SD: pDevExt->VideoFormatIndex = FMT_IDX_SD_DVCR_PAL; if(pStrmExt) { pStrmExt->cipQuad2[0] = FMT_DVCR; // 0x80 if(pDevExt->bDVCPro) pStrmExt->cipQuad2[1] = FDF0_50_60_PAL | FDF0_STYPE_SD_DVCPRO; // 0xf8 = PAL(1):STYPE(11110):RSV(00) else pStrmExt->cipQuad2[1] = FDF0_50_60_PAL | FDF0_STYPE_SD_DVCR; // 0x80 = PAL(1):STYPE(00000):RSV(00) } break; #ifdef MSDV_SUPPORT_SDL_DVCR case ED_TRANSBASIC_SIGNAL_525_60_SDL: pDevExt->VideoFormatIndex = FMT_IDX_SDL_DVCR_NTSC; if(pStrmExt) { pStrmExt->cipQuad2[0] = FMT_DVCR; // 0x80 pStrmExt->cipQuad2[1] = FDF0_50_60_NTSC | FDF0_STYPE_SDL_DVCR; } break; case ED_TRANSBASIC_SIGNAL_625_50_SDL: pDevExt->VideoFormatIndex = FMT_IDX_SDL_DVCR_PAL; if(pStrmExt) { pStrmExt->cipQuad2[0] = FMT_DVCR; // 0x80 pStrmExt->cipQuad2[1] = FDF0_50_60_PAL | FDF0_STYPE_SDL_DVCR; } break; #endif default: TRACE(TL_FCP_ERROR,("\'Unsupported SignalMode:%dL", pXPrtProperty->u.SignalMode - ED_BASE)); ASSERT(FALSE && "Unsupported IoSignal! Refuse to load."); return FALSE; break; } } // WORKITEM Sony HW CODEC does not response to any AVC command. // We are making an exception here to load it. if(Status == STATUS_TIMEOUT) { Status = STATUS_SUCCESS; } // We must know the signal format!! If this failed, the driver will either: // fail to load, or fail to open an stream ASSERT(Status == STATUS_SUCCESS && "Failed to get media signal format!\n"); #if DBG if(pStrmExt) { // Note: bAvcBuf[0] is operand[1] == 10:fmt TRACE(TL_FCP_WARNING,("\'** MediaFormat: St:%x; idx:%d; CIP:[FMT:%.2x(%s); FDF:[%.2x(%s,%s):SYT]\n", Status, pDevExt->VideoFormatIndex, pStrmExt->cipQuad2[0], pStrmExt->cipQuad2[0] == FMT_DVCR ? "DVCR" : pStrmExt->cipQuad2[0] == FMT_MPEG ? "MPEG" : "Fmt:???", pStrmExt->cipQuad2[1], (pStrmExt->cipQuad2[1] & FDF0_50_60_MASK) == FDF0_50_60_PAL ? "PAL" : "NTSC", (pStrmExt->cipQuad2[1] & FDF0_STYPE_MASK) == FDF0_STYPE_SD_DVCR ? "SD" : \ (pStrmExt->cipQuad2[1] & FDF0_STYPE_MASK) == FDF0_STYPE_SDL_DVCR ? "SDL" : \ (pStrmExt->cipQuad2[1] & FDF0_STYPE_MASK) == FDF0_STYPE_HD_DVCR ? "HD" : \ (pStrmExt->cipQuad2[1] & FDF0_STYPE_MASK) == FDF0_STYPE_SD_DVCPRO ? "DVCPRO" : "DV:????" )); } else TRACE(TL_FCP_WARNING|TL_CIP_WARNING,("\'** MediaFormat: St:%x; use idx:%d\n", Status, pDevExt->VideoFormatIndex)); #endif return STATUS_SUCCESS == Status; } BOOL DVCmpGUIDsAndFormatSize( IN PKSDATARANGE pDataRange1, IN PKSDATARANGE pDataRange2, IN BOOL fCompareSubformat, IN BOOL fCompareFormatSize ) /*++ Routine Description: Checks for a match on the three GUIDs and FormatSize Arguments: IN pDataRange1 IN pDataRange2 Return Value: TRUE if all elements match FALSE if any are different --*/ { return ( IsEqualGUID ( &pDataRange1->MajorFormat, &pDataRange2->MajorFormat) && (fCompareSubformat ? IsEqualGUID ( &pDataRange1->SubFormat, &pDataRange2->SubFormat) : TRUE) && IsEqualGUID ( &pDataRange1->Specifier, &pDataRange2->Specifier) && (fCompareFormatSize ? (pDataRange1->FormatSize == pDataRange2->FormatSize) : TRUE )); } NTSTATUS DvAllocatePCResource( IN KSPIN_DATAFLOW DataFlow, IN PSTREAMEX pStrmExt // Note that pStrmExt can be NULL! ) { PSRB_DATA_PACKET pSrbDataPacket; PDVCR_EXTENSION pDevExt; ULONG i, j; PAGED_CODE(); // // Pre-allcoate PC resource // pDevExt = pStrmExt->pDevExt; for(i=0; i < (DataFlow == KSPIN_DATAFLOW_OUT ? \ DVFormatInfoTable[pDevExt->VideoFormatIndex].ulNumOfRcvBuffers : \ DVFormatInfoTable[pDevExt->VideoFormatIndex].ulNumOfXmtBuffers); i++) { if(!(pSrbDataPacket = ExAllocatePool(NonPagedPool, sizeof(SRB_DATA_PACKET)))) { for(j = 0; j < i; j++) { pSrbDataPacket = (PSRB_DATA_PACKET) \ RemoveHeadList(&pStrmExt->DataDetachedListHead); pStrmExt->cntDataDetached--; ExFreePool(pSrbDataPacket->Frame); pSrbDataPacket->Frame = NULL; IoFreeIrp(pSrbDataPacket->pIrp); pSrbDataPacket->pIrp = NULL; ExFreePool(pSrbDataPacket); pSrbDataPacket = NULL; ASSERT(pStrmExt->cntDataDetached >= 0); } return STATUS_NO_MEMORY; } RtlZeroMemory(pSrbDataPacket, sizeof(SRB_DATA_PACKET)); pSrbDataPacket->State = DE_IRP_SRB_COMPLETED; // Initial state. if(!(pSrbDataPacket->Frame = ExAllocatePool(NonPagedPool, sizeof(CIP_FRAME)))) { ExFreePool(pSrbDataPacket); pSrbDataPacket = NULL; for(j = 0; j < i; j++) { pSrbDataPacket = (PSRB_DATA_PACKET) \ RemoveHeadList(&pStrmExt->DataDetachedListHead); pStrmExt->cntDataDetached--; ExFreePool(pSrbDataPacket->Frame); pSrbDataPacket->Frame = NULL; IoFreeIrp(pSrbDataPacket->pIrp); pSrbDataPacket->pIrp = NULL; ExFreePool(pSrbDataPacket); pSrbDataPacket = NULL; } return STATUS_NO_MEMORY; } if(!(pSrbDataPacket->pIrp = IoAllocateIrp(pDevExt->pBusDeviceObject->StackSize, FALSE))) { ExFreePool(pSrbDataPacket->Frame); pSrbDataPacket->Frame = NULL; ExFreePool(pSrbDataPacket); pSrbDataPacket = NULL; for(j = 0; j < i; j++) { pSrbDataPacket = (PSRB_DATA_PACKET) \ RemoveHeadList(&pStrmExt->DataDetachedListHead); pStrmExt->cntDataDetached--; ExFreePool(pSrbDataPacket->Frame); pSrbDataPacket->Frame = NULL; IoFreeIrp(pSrbDataPacket->pIrp); pSrbDataPacket->pIrp = NULL; ExFreePool(pSrbDataPacket); pSrbDataPacket = NULL; } return STATUS_INSUFFICIENT_RESOURCES; } InsertTailList(&pStrmExt->DataDetachedListHead, &pSrbDataPacket->ListEntry); pStrmExt->cntDataDetached++; } return STATUS_SUCCESS; } NTSTATUS DvFreePCResource( IN PSTREAMEX pStrmExt ) { PSRB_DATA_PACKET pSrbDataPacket; KIRQL oldIrql; PAGED_CODE(); KeAcquireSpinLock(pStrmExt->DataListLock, &oldIrql); while(!IsListEmpty(&pStrmExt->DataDetachedListHead)) { pSrbDataPacket = (PSRB_DATA_PACKET) RemoveHeadList( &pStrmExt->DataDetachedListHead ); ExFreePool(pSrbDataPacket->Frame); pSrbDataPacket->Frame = NULL; IoFreeIrp(pSrbDataPacket->pIrp); pSrbDataPacket->pIrp = NULL; ExFreePool(pSrbDataPacket); pStrmExt->cntDataDetached--; ASSERT(pStrmExt->cntDataDetached >= 0); } ASSERT(pStrmExt->cntDataDetached == 0); KeReleaseSpinLock(pStrmExt->DataListLock, oldIrql); return STATUS_SUCCESS; } NTSTATUS DVGetUnitCapabilities( IN PDVCR_EXTENSION pDevExt ) /*++ Routine Description: Get the targe device's unit capabilities Arguments: Return Value: STATUS_SUCCESS STATUS_INSUFFICIENT_RESOURCES status return from 61883. --*/ { PIRP pIrp; PAV_61883_REQUEST pAVReq; NTSTATUS Status = STATUS_SUCCESS; GET_UNIT_IDS * pUnitIds; GET_UNIT_CAPABILITIES * pUnitCaps; PAGED_CODE(); if(!(pIrp = IoAllocateIrp(pDevExt->pBusDeviceObject->StackSize, FALSE))) return STATUS_INSUFFICIENT_RESOURCES; if(!(pAVReq = (AV_61883_REQUEST *) ExAllocatePool(NonPagedPool, sizeof(AV_61883_REQUEST)))) { IoFreeIrp(pIrp); pIrp = NULL; return STATUS_INSUFFICIENT_RESOURCES; } // // Query device's capability // if(!(pUnitIds = (GET_UNIT_IDS *) ExAllocatePool(NonPagedPool, sizeof(GET_UNIT_IDS)))) { IoFreeIrp(pIrp); pIrp = NULL; ExFreePool(pAVReq); pAVReq = NULL; return STATUS_INSUFFICIENT_RESOURCES; } // // Query device's capability // if(!(pUnitCaps = (GET_UNIT_CAPABILITIES *) ExAllocatePool(NonPagedPool, sizeof(GET_UNIT_CAPABILITIES)))) { IoFreeIrp(pIrp); pIrp = NULL; ExFreePool(pAVReq); pAVReq = NULL; ExFreePool(pUnitIds); pUnitIds = NULL; return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(pAVReq, sizeof(AV_61883_REQUEST)); INIT_61883_HEADER(pAVReq, Av61883_GetUnitInfo); pAVReq->GetUnitInfo.nLevel = GET_UNIT_INFO_IDS; RtlZeroMemory(pUnitIds, sizeof(GET_UNIT_IDS)); pAVReq->GetUnitInfo.Information = (PVOID) pUnitIds; Status = DVSubmitIrpSynch( pDevExt, pIrp, pAVReq ); if(!NT_SUCCESS(Status)) { TRACE(TL_61883_ERROR,("\'Av61883_GetUnitInfo (IDS) Failed = 0x%x\n", Status)); pDevExt->UniqueID.QuadPart = 0; pDevExt->ulVendorID = 0; pDevExt->ulModelID = 0; } else { pDevExt->UniqueID = pUnitIds->UniqueID; pDevExt->ulVendorID = pUnitIds->VendorID; pDevExt->ulModelID = pUnitIds->ModelID; TRACE(TL_61883_WARNING,("\'UniqueId:(%x:%x); VID:%x; MID:%x\n", pDevExt->UniqueID.LowPart, pDevExt->UniqueID.HighPart, pUnitIds->VendorID, pUnitIds->ModelID )); } RtlZeroMemory(pAVReq, sizeof(AV_61883_REQUEST)); INIT_61883_HEADER(pAVReq, Av61883_GetUnitInfo); pAVReq->GetUnitInfo.nLevel = GET_UNIT_INFO_CAPABILITIES; RtlZeroMemory(pUnitCaps, sizeof(GET_UNIT_CAPABILITIES)); pAVReq->GetUnitInfo.Information = (PVOID) pUnitCaps; Status = DVSubmitIrpSynch( pDevExt, pIrp, pAVReq ); if(!NT_SUCCESS(Status)) { TRACE(TL_61883_ERROR,("Av61883_GetUnitInfo (CAPABILITIES) Failed = 0x%x\n", Status)); pDevExt->MaxDataRate = 0; pDevExt->NumOutputPlugs = 0; pDevExt->NumInputPlugs = 0; pDevExt->HardwareFlags = 0; } else { pDevExt->MaxDataRate = pUnitCaps->MaxDataRate; pDevExt->NumOutputPlugs = pUnitCaps->NumOutputPlugs; pDevExt->NumInputPlugs = pUnitCaps->NumInputPlugs; pDevExt->HardwareFlags = pUnitCaps->HardwareFlags; } #if DBG if( pDevExt->NumOutputPlugs == 0 || pDevExt->NumInputPlugs == 0) { TRACE(TL_PNP_WARNING|TL_61883_WARNING,("\'Watch out! NumOPlug:%d; NumIPlug:%d\n", pDevExt->NumOutputPlugs, pDevExt->NumInputPlugs)); } #endif TRACE(TL_61883_WARNING,("\'UnitCaps:%s OutP:%d; InP:%d; MDRt:%s; HWFlg:%x; CtsF:%x; HwF:%x\n", (pUnitCaps->HardwareFlags & AV_HOST_DMA_DOUBLE_BUFFERING_ENABLED) ? "*PAE*;":"", pUnitCaps->NumOutputPlugs, pUnitCaps->NumInputPlugs, pUnitCaps->MaxDataRate == 0 ? "S100": pUnitCaps->MaxDataRate == 1? "S200" : "S400 or +", pUnitCaps->HardwareFlags, pUnitCaps->CTSFlags, pUnitCaps->HardwareFlags )); ExFreePool(pUnitIds); pUnitIds = NULL; ExFreePool(pUnitCaps); pUnitCaps = NULL; IoFreeIrp(pIrp); pIrp = NULL; ExFreePool(pAVReq); pAVReq = NULL; return Status; } NTSTATUS DVGetDVPlug( IN PDVCR_EXTENSION pDevExt, IN CMP_PLUG_TYPE PlugType, IN ULONG PlugNum, OUT HANDLE *pPlugHandle ) /*++ Routine Description: Get the targe device's plug handle Arguments: Return Value: STATUS_SUCCESS STATUS_INSUFFICIENT_RESOURCES status return from 61883. --*/ { PIRP pIrp; PAV_61883_REQUEST pAVReq; NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); if(!(pIrp = IoAllocateIrp(pDevExt->pBusDeviceObject->StackSize, FALSE))) return STATUS_INSUFFICIENT_RESOURCES; if(!(pAVReq = (AV_61883_REQUEST *) ExAllocatePool(NonPagedPool, sizeof(AV_61883_REQUEST)))) { IoFreeIrp(pIrp); pIrp = NULL; return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(pAVReq, sizeof(AV_61883_REQUEST)); INIT_61883_HEADER(pAVReq, Av61883_GetPlugHandle); pAVReq->GetPlugHandle.PlugNum = PlugNum; pAVReq->GetPlugHandle.hPlug = 0; pAVReq->GetPlugHandle.Type = PlugType; if(NT_SUCCESS( Status = DVSubmitIrpSynch( pDevExt, pIrp, pAVReq ))) { *pPlugHandle = pAVReq->GetPlugHandle.hPlug; TRACE(TL_61883_WARNING,("\'Created h%sPlugDV[%d]=%x\n", PlugType == CMP_PlugIn ? "I" : "O", PlugNum, *pPlugHandle)); } else { TRACE(TL_61883_ERROR,("\'Created h%sPlugDV[%d] failed; Status:%x\n", PlugType == CMP_PlugIn ? "I" : "O", PlugNum, Status)); Status = STATUS_INSUFFICIENT_RESOURCES; } IoFreeIrp(pIrp); pIrp = NULL; ExFreePool(pAVReq); pAVReq = NULL; return Status; } #ifdef NT51_61883 NTSTATUS DVSetAddressRangeExclusive( IN PDVCR_EXTENSION pDevExt ) /*++ Routine Description: Set this mode so that our local plug will be created in address exclusive mode. Arguments: Return Value: STATUS_SUCCESS STATUS_INSUFFICIENT_RESOURCES --*/ { PIRP pIrp; PAV_61883_REQUEST pAVReq; NTSTATUS Status = STATUS_SUCCESS; SET_CMP_ADDRESS_TYPE SetCmpAddress; PAGED_CODE(); if(!(pIrp = IoAllocateIrp(pDevExt->pBusDeviceObject->StackSize, FALSE))) return STATUS_INSUFFICIENT_RESOURCES; if(!(pAVReq = (AV_61883_REQUEST *) ExAllocatePool(NonPagedPool, sizeof(AV_61883_REQUEST)))) { IoFreeIrp(pIrp); pIrp = NULL; return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(pAVReq, sizeof(AV_61883_REQUEST)); INIT_61883_HEADER(pAVReq, Av61883_SetUnitInfo); pAVReq->SetUnitInfo.nLevel = SET_CMP_ADDRESS_RANGE_TYPE; SetCmpAddress.Type = CMP_ADDRESS_TYPE_EXCLUSIVE; pAVReq->SetUnitInfo.Information = (PVOID) &SetCmpAddress; if(!NT_SUCCESS( Status = DVSubmitIrpSynch( pDevExt, pIrp, pAVReq ))) { TRACE(TL_61883_ERROR,("\'SET_CMP_ADDRESS_RANGE_TYPE Failed:%x\n", Status)); } else { TRACE(TL_61883_TRACE,("\'SET_CMP_ADDRESS_RANGE_TYPE suceeded.\n")); } IoFreeIrp(pIrp); pIrp = NULL; ExFreePool(pAVReq); pAVReq = NULL; return Status; } NTSTATUS DVGetUnitIsochParam( IN PDVCR_EXTENSION pDevExt, OUT UNIT_ISOCH_PARAMS * pUnitIoschParams ) /*++ Routine Description: Create an enumated local PC PCR Arguments: Return Value: STATUS_SUCCESS STATUS_INSUFFICIENT_RESOURCES --*/ { PIRP pIrp; PAV_61883_REQUEST pAVReq; NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); if(!(pIrp = IoAllocateIrp(pDevExt->pBusDeviceObject->StackSize, FALSE))) return STATUS_INSUFFICIENT_RESOURCES; if(!(pAVReq = (AV_61883_REQUEST *) ExAllocatePool(NonPagedPool, sizeof(AV_61883_REQUEST)))) { IoFreeIrp(pIrp); pIrp = NULL; return STATUS_INSUFFICIENT_RESOURCES; } // // Get Unit isoch parameters // RtlZeroMemory(pAVReq, sizeof(AV_61883_REQUEST)); INIT_61883_HEADER(pAVReq, Av61883_GetUnitInfo); pAVReq->GetUnitInfo.nLevel = GET_UNIT_INFO_ISOCH_PARAMS; RtlZeroMemory(pUnitIoschParams, sizeof(UNIT_ISOCH_PARAMS)); pAVReq->GetUnitInfo.Information = (PVOID) pUnitIoschParams; Status = DVSubmitIrpSynch( pDevExt, pIrp, pAVReq ); if(!NT_SUCCESS(Status)) { TRACE(TL_61883_ERROR,("Av61883_GetUnitInfo Failed:%x\n", Status)); Status = STATUS_UNSUCCESSFUL; // Cannot stream without this! } TRACE(TL_61883_WARNING,("\'IsochParam: RxPkt:%d, RxDesc:%d; XmPkt:%d, XmDesc:%d\n", pUnitIoschParams->RX_NumPackets, pUnitIoschParams->RX_NumDescriptors, pUnitIoschParams->TX_NumPackets, pUnitIoschParams->TX_NumDescriptors )); IoFreeIrp(pIrp); pIrp = NULL; ExFreePool(pAVReq); pAVReq = NULL; return Status; } NTSTATUS DVSetUnitIsochParams( IN PDVCR_EXTENSION pDevExt, IN UNIT_ISOCH_PARAMS *pUnitIoschParams ) /*++ Routine Description: Set AV unit's isoch parameters via 61883. Arguments: Return Value: STATUS_SUCCESS STATUS_INSUFFICIENT_RESOURCES --*/ { PIRP pIrp; PAV_61883_REQUEST pAVReq; NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); if(!(pIrp = IoAllocateIrp(pDevExt->pBusDeviceObject->StackSize, FALSE))) return STATUS_INSUFFICIENT_RESOURCES; if(!(pAVReq = (AV_61883_REQUEST *) ExAllocatePool(NonPagedPool, sizeof(AV_61883_REQUEST)))) { IoFreeIrp(pIrp); pIrp = NULL; return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(pAVReq, sizeof(AV_61883_REQUEST)); INIT_61883_HEADER(pAVReq, Av61883_SetUnitInfo); pAVReq->SetUnitInfo.nLevel = SET_UNIT_INFO_ISOCH_PARAMS; pAVReq->SetUnitInfo.Information = (PVOID) pUnitIoschParams; if(!NT_SUCCESS( Status = DVSubmitIrpSynch( pDevExt, pIrp, pAVReq ))) { TRACE(TL_61883_ERROR,("DVSetUnitIsochParams: Av61883_SetUnitInfo Failed:%x\n", Status)); } TRACE(TL_61883_WARNING,("\'UnitIsochParams: Set: RxPkt:%d, RxDesc:%d; XmPkt:%d, XmDesc:%d\n", pUnitIoschParams->RX_NumPackets, pUnitIoschParams->RX_NumDescriptors, pUnitIoschParams->TX_NumPackets, pUnitIoschParams->TX_NumDescriptors )); IoFreeIrp(pIrp); pIrp = NULL; ExFreePool(pAVReq); pAVReq = NULL; return Status; } NTSTATUS DVMakeP2PConnection( IN PDVCR_EXTENSION pDevExt, IN KSPIN_DATAFLOW DataFlow, IN PSTREAMEX pStrmExt ) /*++ Routine Description: Make a point to point connection . Arguments: Return Value: STATUS_SUCCESS STATUS_INSUFFICIENT_RESOURCES --*/ { PIRP pIrp; PAV_61883_REQUEST pAVReq; NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); if(!(pIrp = IoAllocateIrp(pDevExt->pBusDeviceObject->StackSize, FALSE))) return STATUS_INSUFFICIENT_RESOURCES; if(!(pAVReq = (AV_61883_REQUEST *) ExAllocatePool(NonPagedPool, sizeof(AV_61883_REQUEST)))) { IoFreeIrp(pIrp); pIrp = NULL; return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(pAVReq, sizeof(AV_61883_REQUEST)); INIT_61883_HEADER(pAVReq, Av61883_Connect); pAVReq->Connect.Type = CMP_PointToPoint; // !! pAVReq->Connect.hOutputPlug = pStrmExt->hOutputPcr; pAVReq->Connect.hInputPlug = pStrmExt->hInputPcr; // see which way we the data will flow... if(DataFlow == KSPIN_DATAFLOW_OUT) { // Other parameters !! } else { pAVReq->Connect.Format.FMT = pStrmExt->cipQuad2[0]; // From AV/C in/outpug plug signal format status cmd // 00 for NTSC, 80 for PAL; set the 50/60 bit pAVReq->Connect.Format.FDF_hi = pStrmExt->cipQuad2[1]; // From AV/C in/outpug plug signal format status cmd // // 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) DVFormatInfoTable[pDevExt->VideoFormatIndex].SrcPktHeader; pAVReq->Connect.Format.Padding = (UCHAR) DVFormatInfoTable[pDevExt->VideoFormatIndex].QuadPadCount; pAVReq->Connect.Format.BlockSize = (UCHAR) DVFormatInfoTable[pDevExt->VideoFormatIndex].DataBlockSize; pAVReq->Connect.Format.Fraction = (UCHAR) DVFormatInfoTable[pDevExt->VideoFormatIndex].FractionNumber; } // Set this so that 61883 can know it is NTSC or PAL; // For read: It is needed so 61883 can preallocate just-enough packets // so that data can return in a much regular interval. if( pDevExt->VideoFormatIndex == FMT_IDX_SD_DVCR_NTSC || pDevExt->VideoFormatIndex == FMT_IDX_SDL_DVCR_NTSC) pAVReq->Connect.Format.BlockPeriod = 133466800; // nano-sec else pAVReq->Connect.Format.BlockPeriod = 133333333; // nano-sec TRACE(TL_61883_WARNING,("\'cipQuad2[0]:%x, cipQuad2[1]:%x, cipQuad2[2]:%x, cipQuad2[3]:%x\n", pStrmExt->cipQuad2[0], pStrmExt->cipQuad2[1], pStrmExt->cipQuad2[2], pStrmExt->cipQuad2[3] )); TRACE(TL_61883_WARNING,("\'Connect:oPcr:%x->iPcr:%x; cipQuad2[%.2x:%.2x:%.2x:%.2x]\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 )); TRACE(TL_61883_WARNING,("\' BlkSz %d; SrcPkt %d; AvgTm %d, BlkPrd %d\n", pAVReq->Connect.Format.BlockSize, DVFormatInfoTable[pDevExt->VideoFormatIndex].ulSrcPackets, DVFormatInfoTable[pDevExt->VideoFormatIndex].ulAvgTimePerFrame, (DWORD) pAVReq->Connect.Format.BlockPeriod )); if(NT_SUCCESS( Status = DVSubmitIrpSynch( pDevExt, pIrp, pAVReq ))) { TRACE(TL_61883_WARNING,("\'hConnect:%x\n", pAVReq->Connect.hConnect)); ASSERT(pAVReq->Connect.hConnect != NULL); pStrmExt->hConnect = pAVReq->Connect.hConnect; } else { TRACE(TL_61883_ERROR,("Av61883_Connect Failed; Status:%x\n", Status)); ASSERT(!NT_SUCCESS(Status) && "DisConnect failed"); pStrmExt->hConnect = NULL; } IoFreeIrp(pIrp); pIrp = NULL; ExFreePool(pAVReq); pAVReq = NULL; return Status; } NTSTATUS DVCreateLocalPlug( IN PDVCR_EXTENSION pDevExt, IN CMP_PLUG_TYPE PlugType, IN ULONG PlugNum, OUT HANDLE *pPlugHandle ) /*++ Routine Description: Create an enumated local PC PCR Arguments: Return Value: STATUS_SUCCESS STATUS_INSUFFICIENT_RESOURCES --*/ { PIRP pIrp; PAV_61883_REQUEST pAVReq; NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); if(!(pIrp = IoAllocateIrp(pDevExt->pBusDeviceObject->StackSize, FALSE))) return STATUS_INSUFFICIENT_RESOURCES; if(!(pAVReq = (AV_61883_REQUEST *) ExAllocatePool(NonPagedPool, sizeof(AV_61883_REQUEST)))) { IoFreeIrp(pIrp); pIrp = NULL; return STATUS_INSUFFICIENT_RESOURCES; } // Need to correctly update Overhead_ID and payload fields of PC's own oPCR RtlZeroMemory(pAVReq, sizeof(AV_61883_REQUEST)); INIT_61883_HEADER(pAVReq, Av61883_CreatePlug); pAVReq->CreatePlug.PlugNum = PlugNum; pAVReq->CreatePlug.hPlug = NULL; pAVReq->CreatePlug.Context = NULL; pAVReq->CreatePlug.pfnNotify = NULL; pAVReq->CreatePlug.PlugType = PlugType; // // Initialize oPCR values to default values using SDDV signal mode // with speed of 100Mbps data rate // pAVReq->CreatePlug.Pcr.oPCR.OnLine = 0; // We are not online so we cannot be programmed. pAVReq->CreatePlug.Pcr.oPCR.BCCCounter = 0; pAVReq->CreatePlug.Pcr.oPCR.PPCCounter = 0; pAVReq->CreatePlug.Pcr.oPCR.Channel = 0; pAVReq->CreatePlug.Pcr.oPCR.DataRate = CMP_SPEED_S100; pAVReq->CreatePlug.Pcr.oPCR.OverheadID = PCR_OVERHEAD_ID_SDDV; pAVReq->CreatePlug.Pcr.oPCR.Payload = PCR_PAYLOAD_SDDV; if(NT_SUCCESS( Status = DVSubmitIrpSynch( pDevExt, pIrp, pAVReq ))) { *pPlugHandle = pAVReq->CreatePlug.hPlug; TRACE(TL_61883_WARNING,("\'Created h%sPlugPC[%d]=%x\n", PlugType == CMP_PlugIn ? "I" : "O", PlugNum, *pPlugHandle)); } else { TRACE(TL_61883_ERROR,("\'Created h%sPlugPC[%d] failed; Status:%x\n", pAVReq->CreatePlug.PlugType == CMP_PlugIn ? "I" : "O", PlugNum, Status)); Status = STATUS_INSUFFICIENT_RESOURCES; // No plug! } IoFreeIrp(pIrp); pIrp = NULL; ExFreePool(pAVReq); pAVReq = NULL; return Status; } NTSTATUS DVDeleteLocalPlug( IN PDVCR_EXTENSION pDevExt, IN HANDLE PlugHandle ) /*++ Routine Description: Delete an enumated local PC PCR Arguments: Return Value: Nothing --*/ { PIRP pIrp; PAV_61883_REQUEST pAVReq; NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); if(!(pIrp = IoAllocateIrp(pDevExt->pBusDeviceObject->StackSize, FALSE))) return STATUS_INSUFFICIENT_RESOURCES; if(!(pAVReq = (AV_61883_REQUEST *) ExAllocatePool(NonPagedPool, sizeof(AV_61883_REQUEST)))) { IoFreeIrp(pIrp); pIrp = NULL; return STATUS_INSUFFICIENT_RESOURCES; } // Delete our local oPCR RtlZeroMemory(pAVReq, sizeof(AV_61883_REQUEST)); INIT_61883_HEADER(pAVReq, Av61883_DeletePlug); pAVReq->DeletePlug.hPlug = PlugHandle; Status = DVSubmitIrpSynch( pDevExt, pIrp, pAVReq ); if(!NT_SUCCESS(Status)) { TRACE(TL_61883_ERROR,("Av61883_DeletePlug Failed = 0x%x\n", Status)); // Do not care if this result in error. } else { TRACE(TL_61883_WARNING,("\'Av61883_DeletePlug: Deleted!\n", pDevExt->hOPcrPC)); } IoFreeIrp(pIrp); pIrp = NULL; ExFreePool(pAVReq); pAVReq = NULL; return Status; } #endif NTSTATUS DVGetPlugState( IN PDVCR_EXTENSION pDevExt, IN PSTREAMEX pStrmExt, IN PAV_61883_REQUEST pAVReq ) /*++ Routine Description: Ask 61883.sys for the plug state. Arguments: Return Value: Nothing --*/ { PIRP pIrp; NTSTATUS Status = STATUS_SUCCESS; PAGED_CODE(); if(!(pIrp = IoAllocateIrp(pDevExt->pBusDeviceObject->StackSize, FALSE))) return STATUS_INSUFFICIENT_RESOURCES; // // Query oPCR plug state // if(pStrmExt->hOutputPcr) { RtlZeroMemory(pAVReq, sizeof(AV_61883_REQUEST)); INIT_61883_HEADER(pAVReq, Av61883_GetPlugState); pAVReq->GetPlugState.hPlug = pStrmExt->hOutputPcr; Status = DVSubmitIrpSynch( pDevExt, pIrp, pAVReq ); if(!NT_SUCCESS(Status)) { TRACE(TL_61883_ERROR,("Av61883_GetPlugState Failed %x\n", Status)); goto ExitGetState; } else { TRACE(TL_61883_WARNING,("\'PlgState:(oPCR:%x): State %x; DRate %d; Payld %d; BCCnt %d; PPCnt %d\n", pAVReq->GetPlugState.hPlug, pAVReq->GetPlugState.State, pAVReq->GetPlugState.DataRate, pAVReq->GetPlugState.Payload, pAVReq->GetPlugState.BC_Connections, pAVReq->GetPlugState.PP_Connections )); } } // // Query iPCR plug state // if(pStrmExt->hInputPcr) { RtlZeroMemory(pAVReq, sizeof(AV_61883_REQUEST)); INIT_61883_HEADER(pAVReq, Av61883_GetPlugState); pAVReq->GetPlugState.hPlug = pStrmExt->hInputPcr; Status = DVSubmitIrpSynch( pDevExt, pIrp, pAVReq ); if(!NT_SUCCESS(Status)) { TRACE(TL_61883_ERROR,("Av61883_GetPlugState Failed %x\n", Status)); goto ExitGetState; } else { TRACE(TL_61883_WARNING,("\'PlugState(iPCR:%x): State %x; DRate %d; Payld %d; BCCnt %d; PPCnt %d\n", pAVReq->GetPlugState.hPlug, pAVReq->GetPlugState.State, pAVReq->GetPlugState.DataRate, pAVReq->GetPlugState.Payload, pAVReq->GetPlugState.BC_Connections, pAVReq->GetPlugState.PP_Connections )); } } ExitGetState: IoFreeIrp(pIrp); return Status; } NTSTATUS DVCreateAttachFrameThread( PSTREAMEX pStrmExt ) /*++ Routine Description: Create a system thread for attaching data (for transmiut to DV only). Arguments: Return Value: STATUS_SUCCESS or return status from PsCreateSystemThread --*/ { NTSTATUS Status = STATUS_SUCCESS; HANDLE hAttachFrameThread; Status = PsCreateSystemThread( &hAttachFrameThread, (ACCESS_MASK) 0, NULL, (HANDLE) 0, NULL, DVAttachFrameThread, pStrmExt ); if(!NT_SUCCESS(Status)) { pStrmExt->bTerminateThread = TRUE; TRACE(TL_CIP_ERROR|TL_FCP_ERROR,("\'PsCreateSystemThread() failed %x\n", Status)); ASSERT(NT_SUCCESS(Status)); } else { pStrmExt->bTerminateThread = FALSE; // Just started! Status = ObReferenceObjectByHandle( hAttachFrameThread, THREAD_ALL_ACCESS, NULL, KernelMode, &pStrmExt->pAttachFrameThreadObject, NULL ); TRACE(TL_CIP_WARNING|TL_PNP_WARNING,("\'ObReferenceObjectByHandle() St %x; Obj %x\n", Status, pStrmExt->pAttachFrameThreadObject)); ZwClose(hAttachFrameThread); // To signl end of an event KeInitializeEvent(&pStrmExt->hThreadEndEvent, NotificationEvent, FALSE); // Non-signal } return Status; } NTSTATUS DVConnect( IN KSPIN_DATAFLOW ulDataFlow, IN PDVCR_EXTENSION pDevExt, IN PSTREAMEX pStrmExt, IN PAV_61883_REQUEST pAVReq ) /*++ Routine Description: Ask 61883.sys to allocate isoch bandwidth and program PCR. Arguments: Return Value: STATUS_SUCCESS other Status from calling other routine. --*/ { NTSTATUS Status; PAGED_CODE(); ASSERT(pStrmExt->hConnect == NULL); // // Do not reconnect. 61883 should handle all the necessary CMP reconnect. // if(pStrmExt->hConnect) { return STATUS_SUCCESS; } #ifdef SUPPORT_NEW_AVC // // For Device to device connection, we only connect if we are the data producer (oPCR) // TRACE(TL_61883_WARNING,("\'[pStrmExt:%x]: %s PC (oPCR:%x, iPCR:%x); DV (oPCR:%x; iPCR:%x)\n", pStrmExt, ulDataFlow == KSPIN_DATAFLOW_OUT ? "OutPin" : "InPin", pDevExt->hOPcrPC, 0, pDevExt->hOPcrDV, pDevExt->hIPcrDV )); if( pStrmExt->bDV2DVConnect && (pStrmExt->hOutputPcr != pDevExt->hOPcrDV)) { TRACE(TL_61883_WARNING,("\'** pStrmExt:%x not data producer!\n\n", pStrmExt)); return STATUS_SUCCESS; } #endif #ifdef NT51_61883 // // Set Unit isoch parameters: // The number of packets is depending on two factors: // For a PAE system, number of packets cannnot be bigger than 64k/480 = 133 // For capture, number of packets should not be bigger than max packets to construct a DV buffer. // This is needed to avoid completing two buffers in the same descriptor and can cause glitched // in the "real time" playback of the data, esp the audio. // if(pDevExt->HardwareFlags & AV_HOST_DMA_DOUBLE_BUFFERING_ENABLED) { // PAE system pDevExt->UnitIoschParams.RX_NumPackets = // pDevExt->UnitIoschParams.TX_NumPackets = // Use the default set by 61883 ((pDevExt->VideoFormatIndex == FMT_IDX_SD_DVCR_NTSC || pDevExt->VideoFormatIndex == FMT_IDX_SDL_DVCR_NTSC) ? MAX_SRC_PACKETS_PER_NTSC_FRAME_PAE : MAX_SRC_PACKETS_PER_PAL_FRAME_PAE); } else { pDevExt->UnitIoschParams.RX_NumPackets = // pDevExt->UnitIoschParams.TX_NumPackets = // Use the default set by 61883 ((pDevExt->VideoFormatIndex == FMT_IDX_SD_DVCR_NTSC || pDevExt->VideoFormatIndex == FMT_IDX_SDL_DVCR_NTSC) ? MAX_SRC_PACKETS_PER_NTSC_FRAME : MAX_SRC_PACKETS_PER_PAL_FRAME); } if(!NT_SUCCESS( Status = DVSetUnitIsochParams( pDevExt, &pDevExt->UnitIoschParams ))) { return Status; } #endif // NT51_61883 // // Make a point to point connection // Status = DVMakeP2PConnection( pDevExt, ulDataFlow, pStrmExt ); return Status; } NTSTATUS DVDisconnect( IN KSPIN_DATAFLOW ulDataFlow, IN PDVCR_EXTENSION pDevExt, IN PSTREAMEX pStrmExt ) /*++ Routine Description: Ask 61883.sys to free isoch bandwidth and program PCR. Arguments: Return Value: Nothing --*/ { PIRP pIrp; NTSTATUS Status = STATUS_SUCCESS; PAV_61883_REQUEST pAVReq; PAGED_CODE(); // // Use the hPlug to disconnect // if(pStrmExt->hConnect) { if(!(pAVReq = (AV_61883_REQUEST *) ExAllocatePool(NonPagedPool, sizeof(AV_61883_REQUEST)))) return STATUS_INSUFFICIENT_RESOURCES; if(!(pIrp = IoAllocateIrp(pDevExt->pBusDeviceObject->StackSize, FALSE))) { ExFreePool(pAVReq); pAVReq = NULL; return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(pAVReq, sizeof(AV_61883_REQUEST)); INIT_61883_HEADER(pAVReq, Av61883_Disconnect); pAVReq->Disconnect.hConnect = pStrmExt->hConnect; ASSERT(pStrmExt->hConnect); if(!NT_SUCCESS( Status = DVSubmitIrpSynch( pDevExt, pIrp, pAVReq ))) { // This could be caused that the connection was not P2P, and // it tried to disconnect. TRACE(TL_61883_ERROR,("\'Disconnect hConnect:%x failed; ST %x; AvReq->ST %x\n", pStrmExt->hConnect, Status, pAVReq->Flags )); // ASSERT(NT_SUCCESS(Status) && "DisConnect failed"); } else { TRACE(TL_61883_TRACE,("\'Disconnect suceeded; ST %x; AvReq->ST %x\n", Status, pAVReq->Flags )); } IoFreeIrp(pIrp); pIrp = NULL; ExFreePool(pAVReq); pAVReq = NULL; TRACE(TL_61883_WARNING,("\'DisConn %s St:%x; Stat: SRBCnt:%d; [Pic# =? Prcs:Drp:Cncl:Rpt] [%d ?=%d+%d+%d+%d]\n", ulDataFlow == KSPIN_DATAFLOW_OUT ? "[OutPin]" : "[InPin]", Status, (DWORD) pStrmExt->cntSRBReceived, (DWORD) pStrmExt->PictureNumber, (DWORD) pStrmExt->FramesProcessed, (DWORD) pStrmExt->FramesDropped, (DWORD) pStrmExt->cntSRBCancelled, // number of SRB_READ/WRITE_DATA cancelled (DWORD) (pStrmExt->PictureNumber - pStrmExt->FramesProcessed - pStrmExt->FramesDropped - pStrmExt->cntSRBCancelled) )); #if DBG if(DVFormatInfoTable[pDevExt->VideoFormatIndex].SrcPktHeader) { ULONG ulElapsed = (DWORD) ((GetSystemTime() - pStrmExt->tmStreamStart)/(ULONGLONG) 10000); TRACE(TL_61883_WARNING,("\'****-* TotalSrcPkt:%d; DisCont:%d; Elapse:%d msec; DataRate:%d bps *-****\n", \ pStrmExt->lTotalCycleCount, pStrmExt->lDiscontinuityCount, ulElapsed, pStrmExt->lTotalCycleCount * 188 * 1000 / ulElapsed * 8 )); } #endif // We will not have another chance to reconnect it so we assume it is disconnected. pStrmExt->hConnect = NULL; } return Status; } // // GetSystemTime in 100 nS units // ULONGLONG GetSystemTime() { LARGE_INTEGER rate, ticks; ticks = KeQueryPerformanceCounter(&rate); return (KSCONVERT_PERFORMANCE_TIME(rate.QuadPart, ticks)); } #define DIFBLK_SIZE 12000 #define PACK_NO_INFO 0xff // Subcode header identifier #define SC_HDR_TIMECODE 0x13 #define SC_HDR_BINARYGROUP 0x14 // header identifier #define AAUX_HDR_SOURCE 0x50 #define AAUX_HDR_SOURCE_CONTROL 0x51 #define AAUX_HDR_REC_DATE 0x52 #define AAUX_HDR_REC_TIME 0x53 #define AAUX_HDR_BINARY_GROUP 0x54 #define AAUX_HDR_CC 0x55 #define AAUX_HDR_TR 0x56 #define VAUX_HDR_SOURCE 0x60 #define VAUX_HDR_SOURCE_CONTROL 0x61 #define VAUX_HDR_REC_DATE 0x62 #define VAUX_HDR_REC_TIME 0x63 #define VAUX_HDR_BINARY_GROUP 0x64 #define VAUX_HDR_CC 0x65 #define VAUX_HDR_TR 0x66 // Determine section type (MS 3 bits); Fig.66; Table 36. #define ID0_SCT_MASK 0xe0 #define ID0_SCT_HEADER 0x00 #define ID0_SCT_SUBCODE 0x20 #define ID0_SCT_VAUX 0x40 #define ID0_SCT_AUDIO 0x60 #define ID0_SCT_VIDEO 0x80 // A pack is consisted of one byte of header identifier and 4 bytes of data; Part2, annex D. typedef struct _DV_PACK { UCHAR Header; UCHAR Data[4]; } DV_PACK, *PDV_PACK; typedef struct _DV_H0 { UCHAR ID0; UCHAR ID1; UCHAR ID2; UCHAR DSF; UCHAR DFTIA; UCHAR TF1; UCHAR TF2; UCHAR TF3; UCHAR Reserved[72]; } DV_H0, *PDV_H0; typedef struct _DV_SC { UCHAR ID0; UCHAR ID1; UCHAR ID2; struct { UCHAR SID0; UCHAR SID1; UCHAR Reserved; DV_PACK Pack; } SSyb0; struct { UCHAR SID0; UCHAR SID1; UCHAR Reserved; DV_PACK Pack; } SSyb1; struct { UCHAR SID0; UCHAR SID1; UCHAR Reserved; DV_PACK Pack; } SSyb2; struct { UCHAR SID0; UCHAR SID1; UCHAR Reserved; DV_PACK Pack; } SSyb3; struct { UCHAR SID0; UCHAR SID1; UCHAR Reserved; DV_PACK Pack; } SSyb4; struct { UCHAR SID0; UCHAR SID1; UCHAR Reserved; DV_PACK Pack; } SSyb5; UCHAR Reserved[29]; } DV_SC, *PDV_SC; #define MAX_VAUX_PACK 15 typedef struct _DV_VAUX { UCHAR ID0; UCHAR ID1; UCHAR ID2; DV_PACK Pack[MAX_VAUX_PACK]; UCHAR Reserved[2]; } DV_VAUX, *PDV_VAUX; typedef struct _DV_A { UCHAR ID0; UCHAR ID1; UCHAR ID2; DV_PACK Pack; UCHAR Data[72]; } DV_A, *PDV_A; typedef struct _DV_V { UCHAR ID0; UCHAR ID1; UCHAR ID2; UCHAR Data[77]; // 3..79 } DV_V, *PDV_V; // Two source packets #define V_BLOCKS 15 typedef struct _DV_AV { DV_A A; DV_V V[V_BLOCKS]; } DV_AV, *PDV_AV; #define SC_SECTIONS 2 #define VAUX_SECTIONS 3 #define AV_SECTIONS 9 typedef struct _DV_DIF_SEQ { DV_H0 H0; DV_SC SC[SC_SECTIONS]; DV_VAUX VAux[VAUX_SECTIONS]; DV_AV AV[AV_SECTIONS]; } DV_DIF_SEQ, *PDV_DIF_SEQ; typedef struct _DV_FRAME_NTSC { DV_DIF_SEQ DifSeq[10]; } DV_FRAME_NTSC, *PDV_FRAME_NTSC; typedef struct _DV_FRAME_PAL { DV_DIF_SEQ DifSeq[12]; } DV_FRAME_PAL, *PDV_FRAME_PAL; // By setting REC MODE to 111b (invalid recording) can // cause DV to mute the audio #define AAUX_REC_MODE_INVALID_MASK 0x38 // xx11:1xxx #define AAUX_REC_MODE_ORIGINAL 0x08 // xx00:1xxx #ifdef MSDV_SUPPORT_MUTE_AUDIO // #define SHOW_ONE_FIELD_TWICE BOOL DVMuteDVFrame( IN PDVCR_EXTENSION pDevExt, IN OUT PUCHAR pFrameBuffer, IN BOOL bMuteAudio ) { PDV_DIF_SEQ pDifSeq; #ifdef SHOW_ONE_FIELD_TWICE PDV_VAUX pVAux; ULONG k; #endif ULONG i, j; #ifdef SHOW_ONE_FIELD_TWICE BOOL bFound1 = FALSE; #endif BOOL bFound2 = FALSE; pDifSeq = (PDV_DIF_SEQ) pFrameBuffer; // find the VVAX Source pack for (i=0; i < DVFormatInfoTable[pDevExt->VideoFormatIndex].ulNumOfDIFSequences; i++) { #ifdef SHOW_ONE_FIELD_TWICE // Advise by Adobe that we may want to show bothj field but mute audio // Make the field2 output twice, FrameChange to 0 (same as previous frame) for (j=0; j < VAUX_SECTIONS; j++) { pVAux = &pDifSeq->VAux[j]; if((pVAux->ID0 & ID0_SCT_MASK) != ID0_SCT_VAUX) { TRACE(TL_CIP_WARNING,("\'Invalid ID0:%.2x for pVAUX:%x (Dif:%d;V%d;S%d)\n", pVAux->ID0, pVAux, i, j, k)); continue; } for (k=0; k< MAX_VAUX_PACK; k++) { if(pVAux->Pack[k].Header == VAUX_HDR_SOURCE_CONTROL) { if(bMuteAudio) { TRACE(TL_CIP_WARNING,("\'Mute Audio; pDifSeq:%x; pVAux:%x; (Dif:%d,V%d,S%d); %.2x,[%.2x,%.2x,%.2x,%.2x]; pack[2]->%.2x\n", \ pDifSeq, pVAux, i, j, k, \ pVAux->Pack[k].Header, pVAux->Pack[k].Data[0], pVAux->Pack[k].Data[1], pVAux->Pack[k].Data[2], pVAux->Pack[k].Data[3], \ (pVAux->Pack[k].Data[2] & 0x1F) )); pVAux->Pack[k].Data[2] &= 0x1f; // 0x1F; // set FF, FS and FC to 0 TRACE(TL_CIP_TRACE,("\'pVAux->Pack[k].Data[2] = %.2x\n", pVAux->Pack[k].Data[2])); } else { TRACE(TL_CIP_TRACE,("\'un-Mute Audio; pack[2]: %.2x ->%.2x\n", pVAux->Pack[k].Data[2], (pVAux->Pack[k].Data[2] | 0xc0) )); pVAux->Pack[k].Data[2] |= 0xe0; // set FF, FS and FCto 1; Show both fields in field 1,2 order } bFound1 = TRUE; break; // Set only the 1st occurrence } } } #endif for (j=0; j < AV_SECTIONS; j++) { if(pDifSeq->AV[j].A.Pack.Header == AAUX_HDR_SOURCE_CONTROL) { TRACE(TL_CIP_TRACE,("\'A0Aux %.2x,[%.2x,%.2x,%.2x,%.2x] %.2x->%.2x\n", \ pDifSeq->AV[j].A.Pack.Header, pDifSeq->AV[j].A.Pack.Data[0], \ pDifSeq->AV[j].A.Pack.Data[1], pDifSeq->AV[j].A.Pack.Data[2], pDifSeq->AV[j].A.Pack.Data[3], \ pDifSeq->AV[j].A.Pack.Data[1], pDifSeq->AV[j].A.Pack.Data[1] | AAUX_REC_MODE_INVALID_MASK )); if(bMuteAudio) pDifSeq->AV[j].A.Pack.Data[1] |= AAUX_REC_MODE_INVALID_MASK; // Cause DV to mute this. else pDifSeq->AV[j].A.Pack.Data[1] = \ (pDifSeq->AV[j].A.Pack.Data[1] & ~AAUX_REC_MODE_INVALID_MASK) | AAUX_REC_MODE_ORIGINAL; bFound2 = TRUE; break; // Set only the 1st occurrence } } // Must do the 1st occurance of all Dif sequences; pDifSeq++; // Next DIF sequence } #ifdef SHOW_ONE_FIELD_TWICE return (bFound1 && bFound2); #else return bFound2; #endif } #endif #ifdef MSDV_SUPPORT_EXTRACT_SUBCODE_DATA VOID DVCRExtractTimecodeFromFrame( IN PDVCR_EXTENSION pDevExt, IN PSTREAMEX pStrmExt, IN PUCHAR pFrameBuffer ) { PUCHAR pDIFBlk; PUCHAR pS0, pS1, pSID0; ULONG i, j; BYTE LastTimecode[4], Timecode[4]; // hh:mm:ss,ff DWORD LastAbsTrackNumber, AbsTrackNumber; PUCHAR pSID1; BYTE Timecode2[4]; // hh:mm:ss,ff DWORD AbsTrackNumber2; BOOL bGetAbsT = TRUE, bGetTimecode = TRUE; // Can be called at DISPATCH_LEVEL pDIFBlk = (PUCHAR) pFrameBuffer; // Save the last timecode so we will now if it has LastTimecode[0] = pStrmExt->Timecode[0]; LastTimecode[1] = pStrmExt->Timecode[1]; LastTimecode[2] = pStrmExt->Timecode[2]; LastTimecode[3] = pStrmExt->Timecode[3]; LastAbsTrackNumber = pStrmExt->AbsTrackNumber; // // Traverse thru every DIF BLOCK looking for VA0,1 and 2 for(i=0; i < DVFormatInfoTable[pDevExt->VideoFormatIndex].ulNumOfDIFSequences; i++) { pS0 = pDIFBlk + 80; pS1 = pS0 + 80; // // Is this Subcode source packet? See Table 36 (P.111) of the Blue Book // if ((pS0[0] & 0xe0) == 0x20 && (pS1[0] & 0xe0) == 0x20) { if(bGetAbsT) { // // See Figure 42 (p. 94) of the Blue book // SID0(Low nibble),1 (high nibble) of every three subcode sync block can form the ATN // pSID0 = &pS0[3]; AbsTrackNumber = 0; for (j = 0 ; j < 3; j++) { AbsTrackNumber = (( ( (pSID0[0] & 0x0f) << 4) | (pSID0[1] >> 4) ) << (j * 8)) | AbsTrackNumber; pSID0 += 8; bGetAbsT = FALSE; } pSID1 = &pS1[3]; AbsTrackNumber2 = 0; for (j = 0 ; j < 3; j++) { AbsTrackNumber2 = (( ( (pSID1[0] & 0x0f) << 4) | (pSID1[1] >> 4) ) << (j * 8)) | AbsTrackNumber2; pSID1 += 8; } // Verify that the track number is the same! if(AbsTrackNumber == AbsTrackNumber2) { bGetAbsT = FALSE; } else { bGetAbsT = TRUE; TRACE(TL_CIP_TRACE,("\'%d Sequence; AbsT (%d,%d) != AbsT2 (%d,%d)\n", i, AbsTrackNumber / 2, AbsTrackNumber & 0x01, AbsTrackNumber2 / 2, AbsTrackNumber2 & 0x01 )); } } if(bGetTimecode) { // See Figure 68 (p. 114) of the Blue Book // Subcode sync block number 3, 4 and 5 for(j = 3; j <= 5; j++) { // 3 bytes of IDs and follow by sequence of 8 bytes SyncBlock (3:5); // 0x13 == TIMECODE if(pS0[3+3+j*8] == 0x13 && pS0[3+3+j*8+4] != 0xff && pS0[3+3+j*8+3] != 0xff && pS0[3+3+j*8+2] != 0xff && pS0[3+3+j*8+1] != 0xff) { Timecode[0] = pS0[3+3+j*8+4]&0x3f; // hh Timecode[1] = pS0[3+3+j*8+3]&0x7f; // mm Timecode[2] = pS0[3+3+j*8+2]&0x7f; // ss Timecode[3] = pS0[3+3+j*8+1]&0x3f; // ff bGetTimecode = FALSE; break; } } // Subcode sync block number 9, 10 and 11 for(j = 3; j <= 5; j++) { // 3 bytes of IDs and follow by sequence of 8 bytes SyncBlock (3:5); // 0x13 == TIMECODE if(pS1[3+3+j*8] == 0x13 && pS1[3+3+j*8+4] != 0xff && pS1[3+3+j*8+3] != 0xff && pS1[3+3+j*8+2] != 0xff && pS1[3+3+j*8+1] != 0xff) { Timecode2[0] = pS1[3+3+j*8+4]&0x3f; // hh Timecode2[1] = pS1[3+3+j*8+3]&0x7f; // mm Timecode2[2] = pS1[3+3+j*8+2]&0x7f; // ss Timecode2[3] = pS1[3+3+j*8+1]&0x3f; // ff bGetTimecode = FALSE; break; } } // // Verify // if(!bGetTimecode) { if( Timecode[0] == Timecode2[0] && Timecode[1] == Timecode2[1] && Timecode[2] == Timecode2[2] && Timecode[3] == Timecode2[3]) { } else { bGetTimecode = TRUE; TRACE(TL_CIP_TRACE,("\'%d Sequence; %.2x:%.2x:%.2x,%.2x != %.2x:%.2x:%.2x,%.2x\n", i, Timecode[0], Timecode[1], Timecode[2], Timecode[3], Timecode2[0], Timecode2[1], Timecode2[2], Timecode2[3] )); } } } } if(!bGetAbsT && !bGetTimecode) break; pDIFBlk += DIFBLK_SIZE; // Get to next block } if(!bGetAbsT && pStrmExt->AbsTrackNumber != AbsTrackNumber) { pStrmExt->AbsTrackNumber = AbsTrackNumber; // BF is the LSB pStrmExt->bATNUpdated = TRUE; TRACE(TL_CIP_TRACE,("\'Extracted TrackNum:%d; DicontBit:%d\n", AbsTrackNumber / 2, AbsTrackNumber & 0x01)); } if(!bGetTimecode && ( Timecode[0] != LastTimecode[0] || Timecode[1] != LastTimecode[1] || Timecode[2] != LastTimecode[2] || Timecode[3] != LastTimecode[3] ) ) { pStrmExt->Timecode[0] = Timecode[0]; // hh pStrmExt->Timecode[1] = Timecode[1]; // mm pStrmExt->Timecode[2] = Timecode[2]; // mm pStrmExt->Timecode[3] = Timecode[3]; // ff pStrmExt->bTimecodeUpdated = TRUE; TRACE(TL_CIP_TRACE,("\'Extracted Timecode %.2x:%.2x:%.2x,%.2x\n", Timecode[0], Timecode[1], Timecode[2], Timecode[3])); } } #endif // MSDV_SUPPORT_EXTRACT_SUBCODE_DATA #ifdef MSDV_SUPPORT_EXTRACT_DV_DATE_TIME VOID DVCRExtractRecDateAndTimeFromFrame( IN PDVCR_EXTENSION pDevExt, IN PSTREAMEX pStrmExt, IN PUCHAR pFrameBuffer ) { PUCHAR pDIFBlk; PUCHAR pS0, pS1; ULONG i, j; BOOL bGetRecDate = TRUE, bGetRecTime = TRUE; // Can be called at DISPATCH_LEVEL pDIFBlk = (PUCHAR) pFrameBuffer + DIFBLK_SIZE * DVFormatInfoTable[pDevExt->VideoFormatIndex].ulNumOfDIFSequences/2; // // REC Data (VRD) and Time (VRT) on in the 2nd half oa a video frame // for(i=DVFormatInfoTable[pDevExt->VideoFormatIndex].ulNumOfDIFSequences/2; i < DVFormatInfoTable[pDevExt->VideoFormatIndex].ulNumOfDIFSequences; i++) { pS0 = pDIFBlk + 80; pS1 = pS0 + 80; // // Find SC0 and SC1. See Table 36 (P.111) of the Blue Book // // SC0/1: ID(0,1,2), Data (3,50), Reserved(51-79) // SC0:Data: SSYB0(3..10), SSYB1(11..18), SSYB2(19..26), SSYB3(27..34), SSYB4(35..42), SSYB5(43..50) // SC1:Data: SSYB6(3..10), SSYB7(11..18), SSYB8(19..26), SSYB9(27..34), SSYB10(35..42), SSYB11(43..50) // SSYBx(SubCodeId0, SubcodeID1, Reserved, Pack(3,4,5,6,7)) // // TTC are in the 1st half: SSYB0..11 (every) // TTC are in the 2nd half: SSYB0,3,6,9 // VRD are in the 2nd half of a video frame, SSYB1,4,7,10 // VRT are in the 2nd half of a video frame, SSYB2,5,8,11 // // Subcode data ? if ((pS0[0] & 0xe0) == 0x20 && (pS1[0] & 0xe0) == 0x20) { // // RecDate: VRD // if(bGetRecDate) { // go thru 6 sync blocks (8 bytes per block) per Subcode; idx 1(SSYB1),4(SSYB4) for SC0 for(j=0; j <= 5 ; j++) { if(j == 1 || j == 4) { // 0x62== RecDate if(pS0[3+3+j*8] == 0x62) { pStrmExt->RecDate[0] = pS0[3+3+j*8+4]; // Year pStrmExt->RecDate[1] = pS0[3+3+j*8+3]&0x1f; // Month pStrmExt->RecDate[2] = pS0[3+3+j*8+2]&0x3f; // Day pStrmExt->RecDate[3] = pS0[3+3+j*8+1]&0x3f; // TimeZone bGetRecDate = FALSE; break; } } } } if(bGetRecDate) { // go thru 6 sync blocks (8 bytes per block) per Subcode; idx 1 (SSYB7),4(SSYB10) for SC1 for(j=0; j <= 5; j++) { if(j == 1 || j == 4) { // 0x62== RecDate if(pS1[3+3+j*8] == 0x62) { pStrmExt->RecDate[0] = pS1[3+3+j*8+4]; // Year pStrmExt->RecDate[1] = pS1[3+3+j*8+3]&0x1f; // Month pStrmExt->RecDate[2] = pS1[3+3+j*8+2]&0x3f; // Day pStrmExt->RecDate[3] = pS1[3+3+j*8+1]&0x3f; // TimeZone bGetRecDate = FALSE; break; } } } } // // RecTime: VRT // if(bGetRecTime) { // go thru 6 sync blocks (8 bytes per block) per Subcode; idx 2(SSYB2),5(SSYB5) for SC0 for(j=0; j <= 5 ; j++) { if(j == 2 || j == 5) { // 0x63== RecTime if(pS0[3+3+j*8] == 0x63) { pStrmExt->RecTime[0] = pS0[3+3+j*8+4]&0x3f; pStrmExt->RecTime[1] = pS0[3+3+j*8+3]&0x7f; pStrmExt->RecTime[2] = pS0[3+3+j*8+2]&0x7f; pStrmExt->RecTime[3] = pS0[3+3+j*8+1]&0x3f; bGetRecTime = FALSE; break; } } } } if(bGetRecTime) { // go thru 6 sync blocks (8 bytes per block) per Subcode; idx 2 (SSYB8),5(SSYB11) for SC1 for(j=0; j <= 5; j++) { if(j == 2 || j == 5) { // 0x63== RecTime if(pS1[3+3+j*8] == 0x63) { pStrmExt->RecTime[0] = pS1[3+3+j*8+4]&0x3f; pStrmExt->RecTime[1] = pS1[3+3+j*8+3]&0x7f; pStrmExt->RecTime[2] = pS1[3+3+j*8+2]&0x7f; pStrmExt->RecTime[3] = pS1[3+3+j*8+1]&0x3f; bGetRecTime = FALSE; break; } } } } } if(!bGetRecDate && !bGetRecTime) break; pDIFBlk += DIFBLK_SIZE; // Next sequence } TRACE(TL_CIP_TRACE,("\'Frame# %.5d, Date %s %x-%.2x-%.2x, Time %s %.2x:%.2x:%.2x,%.2x\n", (ULONG) pStrmExt->FramesProcessed, bGetRecDate ? "NF:" : "Found:", pStrmExt->RecDate[0], pStrmExt->RecDate[1] & 0x1f, pStrmExt->RecDate[2] & 0x3f, bGetRecTime ? "NF:" : "Found:",pStrmExt->RecTime[0], pStrmExt->RecTime[1], pStrmExt->RecTime[2], pStrmExt->RecTime[3] )); } #endif // MSDV_SUPPORT_EXTRACT_DV_DATE_TIME #ifdef READ_CUTOMIZE_REG_VALUES NTSTATUS CreateRegistryKeySingle( IN HANDLE hKey, IN ACCESS_MASK desiredAccess, PWCHAR pwszSection, OUT PHANDLE phKeySection ) { NTSTATUS status; UNICODE_STRING ustr; OBJECT_ATTRIBUTES objectAttributes; RtlInitUnicodeString(&ustr, pwszSection); InitializeObjectAttributes( &objectAttributes, &ustr, OBJ_CASE_INSENSITIVE, hKey, NULL ); status = ZwCreateKey( phKeySection, desiredAccess, &objectAttributes, 0, NULL, /* optional*/ REG_OPTION_NON_VOLATILE, NULL ); return status; } NTSTATUS CreateRegistrySubKey( IN HANDLE hKey, IN ACCESS_MASK desiredAccess, PWCHAR pwszSection, OUT PHANDLE phKeySection ) { UNICODE_STRING ustr; USHORT usPos = 1; // Skip first backslash static WCHAR wSep = '\\'; NTSTATUS status = STATUS_SUCCESS; RtlInitUnicodeString(&ustr, pwszSection); while(usPos < ustr.Length) { if(ustr.Buffer[usPos] == wSep) { // NULL terminate our partial string ustr.Buffer[usPos] = UNICODE_NULL; status = CreateRegistryKeySingle( hKey, desiredAccess, ustr.Buffer, phKeySection ); ustr.Buffer[usPos] = wSep; if(NT_SUCCESS(status)) { ZwClose(*phKeySection); } else { break; } } usPos++; } // Create the full key if(NT_SUCCESS(status)) { status = CreateRegistryKeySingle( hKey, desiredAccess, ustr.Buffer, phKeySection ); } return status; } NTSTATUS GetRegistryKeyValue ( IN HANDLE Handle, IN PWCHAR KeyNameString, IN ULONG KeyNameStringLength, IN PVOID Data, IN PULONG DataLength ) /*++ Routine Description: This routine gets the specified value out of the registry Arguments: Handle - Handle to location in registry KeyNameString - registry key we're looking for KeyNameStringLength - length of registry key we're looking for Data - where to return the data DataLength - how big the data is Return Value: status is returned from ZwQueryValueKey --*/ { NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES; UNICODE_STRING keyName; ULONG length; PKEY_VALUE_FULL_INFORMATION fullInfo; RtlInitUnicodeString(&keyName, KeyNameString); length = sizeof(KEY_VALUE_FULL_INFORMATION) + KeyNameStringLength + *DataLength; fullInfo = ExAllocatePool(PagedPool, length); if (fullInfo) { status = ZwQueryValueKey( Handle, &keyName, KeyValueFullInformation, fullInfo, length, &length ); if (NT_SUCCESS(status)){ ASSERT(fullInfo->DataLength <= *DataLength); RtlCopyMemory( Data, ((PUCHAR) fullInfo) + fullInfo->DataOffset, fullInfo->DataLength ); } *DataLength = fullInfo->DataLength; ExFreePool(fullInfo); } return (status); } #if 0 // Not used NTSTATUS SetRegistryKeyValue( HANDLE hKey, PWCHAR pwszEntry, LONG nValue ) { NTSTATUS status; UNICODE_STRING ustr; RtlInitUnicodeString(&ustr, pwszEntry); status = ZwSetValueKey( hKey, &ustr, 0, /* optional */ REG_DWORD, &nValue, sizeof(nValue) ); return status; } #endif // Not used // // Registry subky and values wide character strings. // WCHAR wszSettings[] = L"Settings"; WCHAR wszATNSearch[] = L"bSupportATNSearch"; WCHAR wszSyncRecording[] = L"bSyncRecording"; WCHAR wszMaxDataSync[] = L"tmMaxDataSync"; WCHAR wszPlayPs2RecPs[] = L"fmPlayPause2RecPause"; WCHAR wszStop2RecPs[] = L"fmStop2RecPause"; WCHAR wszRecPs2Rec[] = L"tmRecPause2Rec"; WCHAR wszXprtStateChangeWait[] = L"tmXprtStateChangeWait"; BOOL DVGetPropertyValuesFromRegistry( IN PDVCR_EXTENSION pDevExt ) { NTSTATUS Status; HANDLE hPDOKey, hKeySettings; ULONG ulLength; // // Registry key: // Windows 2000: // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\ // {6BDD1FC6-810F-11D0-BEC7-08002BE2092F\000x // // Win98: // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Class\Image\000x // Status = IoOpenDeviceRegistryKey( pDevExt->pPhysicalDeviceObject, PLUGPLAY_REGKEY_DRIVER, STANDARD_RIGHTS_READ, &hPDOKey ); // PDO might be deleted when it was removed. if(! pDevExt->bDevRemoved) { ASSERT(Status == STATUS_SUCCESS); } // // loop through our table of strings, // reading the registry for each. // if(NT_SUCCESS(Status)) { // Create or open the settings key Status = CreateRegistrySubKey( hPDOKey, KEY_ALL_ACCESS, wszSettings, &hKeySettings ); if(NT_SUCCESS(Status)) { // Note: we can be more selective by checking // pDevExt->ulDevType #if 0 // Not supported yet! // ATNSearch ulLength = sizeof(LONG); Status = GetRegistryKeyValue( hKeySettings, wszATNSearch, sizeof(wszATNSearch), (PVOID) &pDevExt->bATNSearch, &ulLength); TRACE(TL_PNP_WARNING,("\'GetRegVal: St:%x, Len:%d, bATNSearch:%d (1:Yes)\n", Status, ulLength, pDevExt->bATNSearch)); if(!NT_SUCCESS(Status)) pDevExt->bATNSearch = FALSE; // bSyncRecording ulLength = sizeof(LONG); Status = GetRegistryKeyValue( hKeySettings, wszSyncRecording, sizeof(wszSyncRecording), (PVOID) &pDevExt->bSyncRecording, &ulLength); TRACE(TL_PNP_WARNING,("\'GetRegVal: St:%x, Len:%d, bSyncRecording:%d (1:Yes)\n", Status, ulLength, pDevExt->bSyncRecording)); if(!NT_SUCCESS(Status)) pDevExt->bSyncRecording = FALSE; // tmMaxDataSync ulLength = sizeof(LONG); Status = GetRegistryKeyValue( hKeySettings, wszMaxDataSync, sizeof(wszMaxDataSync), (PVOID) &pDevExt->tmMaxDataSync, &ulLength); TRACE(TL_PNP_WARNING,("\'GetRegVal: St:%x, Len:%d, tmMaxDataSync:%d (msec)\n", Status, ulLength, pDevExt->tmMaxDataSync)); // fmPlayPs2RecPs ulLength = sizeof(LONG); Status = GetRegistryKeyValue( hKeySettings, wszPlayPs2RecPs, sizeof(wszPlayPs2RecPs), (PVOID) &pDevExt->fmPlayPs2RecPs, &ulLength); TRACE(TL_PNP_WARNING,("\'GetRegVal: St:%x, Len:%d, fmPlayPs2RecPs:%d (frames)\n", Status, ulLength, pDevExt->fmPlayPs2RecPs)); // fmStop2RecPs ulLength = sizeof(LONG); Status = GetRegistryKeyValue( hKeySettings, wszStop2RecPs, sizeof(wszStop2RecPs), (PVOID) &pDevExt->fmStop2RecPs, &ulLength); TRACE(TL_PNP_WARNING,("\'GetRegVal: St:%x, Len:%d, fmStop2RecPs:%d (frames)\n", Status, ulLength, pDevExt->fmStop2RecPs)); // tmRecPs2Rec ulLength = sizeof(LONG); Status = GetRegistryKeyValue( hKeySettings, wszRecPs2Rec, sizeof(wszRecPs2Rec), (PVOID) &pDevExt->tmRecPs2Rec, &ulLength); TRACE(TL_PNP_WARNING,("\'GetRegVal: St:%x, Len:%d, tmRecPs2Rec:%d (msec)\n", Status, ulLength, pDevExt->tmRecPs2Rec)); #endif ulLength = sizeof(LONG); Status = GetRegistryKeyValue( hKeySettings, wszXprtStateChangeWait, sizeof(wszXprtStateChangeWait), (PVOID) &pDevExt->XprtStateChangeWait, // in msec &ulLength); TRACE(TL_PNP_WARNING,("\'GetRegVal: St:%x, Len:%d, XprtStateChangeWait:%d msec\n", Status, ulLength, pDevExt->XprtStateChangeWait)); if(!NT_SUCCESS(Status)) pDevExt->XprtStateChangeWait = 0; ZwClose(hKeySettings); ZwClose(hPDOKey); return TRUE; } else { TRACE(TL_PNP_ERROR,("\'GetPropertyValuesFromRegistry: CreateRegistrySubKey failed with Status=%x\n", Status)); } ZwClose(hPDOKey); } else { TRACE(TL_PNP_ERROR,("\'GetPropertyValuesFromRegistry: IoOpenDeviceRegistryKey failed with Status=%x\n", Status)); } // Not implemented so always return FALSE to use the defaults. return FALSE; } #if 0 // Not used BOOL DVSetPropertyValuesToRegistry( PDVCR_EXTENSION pDevExt ) { // Set the default to : // HLM\Software\DeviceExtension->pchVendorName\1394DCam NTSTATUS Status; HANDLE hPDOKey, hKeySettings; TRACE(TL_PNP_TRACE,("\'SetPropertyValuesToRegistry: pDevExt=%x; pDevExt->pBusDeviceObject=%x\n", pDevExt, pDevExt->pBusDeviceObject)); // // Registry key: // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\ // {6BDD1FC6-810F-11D0-BEC7-08002BE2092F\000x // Status = IoOpenDeviceRegistryKey( pDevExt->pPhysicalDeviceObject, PLUGPLAY_REGKEY_DRIVER, STANDARD_RIGHTS_WRITE, &hPDOKey); // PDO might be deleted when it was removed. if(! pDevExt->bDevRemoved) { ASSERT(Status == STATUS_SUCCESS); } // // loop through our table of strings, // reading the registry for each. // if(NT_SUCCESS(Status)) { // Create or open the settings key Status = CreateRegistrySubKey( hPDOKey, KEY_ALL_ACCESS, wszSettings, &hKeySettings ); if(NT_SUCCESS(Status)) { #if 0 // Note used, just an example: // Brightness Status = SetRegistryKeyValue( hKeySettings, wszBrightness, pDevExt->XXXX); TRACE(TL_PNP_TRACE,("\'SetPropertyValuesToRegistry: Status %x, Brightness %d\n", Status, pDevExt->Brightness)); #endif ZwClose(hKeySettings); ZwClose(hPDOKey); return TRUE; } else { TRACE(TL_PNP_ERROR,("\'GetPropertyValuesToRegistry: CreateRegistrySubKey failed with Status=%x\n", Status)); } ZwClose(hPDOKey); } else { TRACE(TL_PNP_TRACE,("\'GetPropertyValuesToRegistry: IoOpenDeviceRegistryKey failed with Status=%x\n", Status)); } return FALSE; } #endif // Not used #endif // READ_CUTOMIZE_REG_VALUES