/*++ Copyright (c) 1997-2000 Microsoft Corporation All Rights Reserved Module Name: savedata.cpp Abstract: Implementation of MSVAD data saving class. To save the playback data to disk, this class maintains a circular data buffer, associated frame structures and worker items to save frames to disk. Each frame structure represents a portion of buffer. When that portion of frame is full, a workitem is scheduled to save it to disk. --*/ #include #include "savedata.h" #include // This is for using swprintf.. //============================================================================= // Defines //============================================================================= #define RIFF_TAG 0x46464952; #define WAVE_TAG 0x45564157; #define FMT__TAG 0x20746D66; #define DATA_TAG 0x61746164; #define DEFAULT_FRAME_COUNT 2 #define DEFAULT_FRAME_SIZE PAGE_SIZE * 4 #define DEFAULT_BUFFER_SIZE DEFAULT_FRAME_SIZE * DEFAULT_FRAME_COUNT #define DEFAULT_FILE_NAME L"\\DosDevices\\C:\\STREAM" #define MAX_WORKER_ITEM_COUNT 15 //============================================================================= // Statics //============================================================================= ULONG CSaveData::m_ulStreamId = 0; #pragma code_seg("PAGE") //============================================================================= // CSaveData //============================================================================= //============================================================================= CSaveData::CSaveData() : m_pDataBuffer(NULL), m_FileHandle(NULL), m_ulFrameCount(DEFAULT_FRAME_COUNT), m_ulBufferSize(DEFAULT_BUFFER_SIZE), m_ulFrameSize(DEFAULT_FRAME_SIZE), m_ulBufferPtr(0), m_ulFramePtr(0), m_fFrameUsed(NULL), m_pFilePtr(NULL), m_fWriteDisabled(FALSE) { PAGED_CODE(); m_FileHeader.dwRiff = RIFF_TAG; m_FileHeader.dwFileSize = 0; m_FileHeader.dwWave = WAVE_TAG; m_FileHeader.dwFormat = FMT__TAG; m_FileHeader.dwFormatLength = sizeof(WAVEFORMATEX); m_DataHeader.dwData = DATA_TAG; m_DataHeader.dwDataLength = 0; RtlZeroMemory(&m_objectAttributes, sizeof(m_objectAttributes)); m_ulStreamId++; } // CSaveData //============================================================================= CSaveData::~CSaveData() { PAGED_CODE(); LARGE_INTEGER offset; IO_STATUS_BLOCK ioStatusBlock; DPF_ENTER(("[CSaveData::~CSaveData]")); // Update the wave header in data file with real file size. // if (m_pFilePtr) { m_FileHeader.dwFileSize = (DWORD) m_pFilePtr->QuadPart - 2 * sizeof(DWORD); m_DataHeader.dwDataLength = (DWORD) m_pFilePtr->QuadPart - sizeof(m_FileHeader) - m_FileHeader.dwFormatLength - sizeof(m_DataHeader); if (NT_SUCCESS(FileOpen(FALSE))) { FileWriteHeader(); FileClose(); } } //frees the work items #ifndef USE_OBSOLETE_FUNCS for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++) { if (m_pWorkItems[i].WorkItem!=NULL) { IoFreeWorkItem(m_pWorkItems[i].WorkItem); m_pWorkItems[i].WorkItem = NULL; } } #endif if (m_waveFormat) { ExFreePool(m_waveFormat); } if (m_fFrameUsed) { ExFreePool(m_fFrameUsed); // NOTE : Do not release m_pFilePtr. } if (m_FileName.Buffer) { ExFreePool(m_FileName.Buffer); } if (m_pDataBuffer) { ExFreePool(m_pDataBuffer); } } // CSaveData //============================================================================= void CSaveData::DestroyWorkItems ( void ) { if (m_pWorkItems) { ExFreePool(m_pWorkItems); m_pWorkItems = NULL; } } // DestroyWorkItems //============================================================================= void CSaveData::Disable ( BOOL fDisable ) { m_fWriteDisabled = fDisable; } // Disable //============================================================================= NTSTATUS CSaveData::FileClose(void) { PAGED_CODE(); NTSTATUS ntStatus = STATUS_SUCCESS; if (m_FileHandle) { ntStatus = ZwClose(m_FileHandle); m_FileHandle = NULL; } return ntStatus; } // FileClose //============================================================================= NTSTATUS CSaveData::FileOpen ( IN BOOL fOverWrite ) { PAGED_CODE(); NTSTATUS ntStatus = STATUS_SUCCESS; IO_STATUS_BLOCK ioStatusBlock; if (!m_FileHandle) { ntStatus = ZwCreateFile ( &m_FileHandle, GENERIC_WRITE | SYNCHRONIZE, &m_objectAttributes, &ioStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, fOverWrite ? FILE_OVERWRITE_IF : FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); if (!NT_SUCCESS(ntStatus)) { DPF(D_TERSE, ("[CSaveData::FileOpen : Error opening data file]")); } } return ntStatus; } // FileOpen //============================================================================= NTSTATUS CSaveData::FileWrite ( IN PBYTE pData, IN ULONG ulDataSize ) { PAGED_CODE(); ASSERT(pData); ASSERT(m_pFilePtr); NTSTATUS ntStatus; if (m_FileHandle) { IO_STATUS_BLOCK ioStatusBlock; ntStatus = ZwWriteFile( m_FileHandle, NULL, NULL, NULL, &ioStatusBlock, pData, ulDataSize, m_pFilePtr, NULL); if (NT_SUCCESS(ntStatus)) { ASSERT(ioStatusBlock.Information == ulDataSize); m_pFilePtr->QuadPart += ulDataSize; } else { DPF(D_TERSE, ("[CSaveData::FileWrite : WriteFileError]")); } } else { DPF(D_TERSE, ("[CSaveData::FileWrite : File not open]")); ntStatus = STATUS_INVALID_HANDLE; } return ntStatus; } // FileWrite //============================================================================= NTSTATUS CSaveData::FileWriteHeader(void) { PAGED_CODE(); NTSTATUS ntStatus; if (m_FileHandle && m_waveFormat) { IO_STATUS_BLOCK ioStatusBlock; m_pFilePtr->QuadPart = 0; m_FileHeader.dwFormatLength = (m_waveFormat->wFormatTag == WAVE_FORMAT_PCM) ? sizeof( PCMWAVEFORMAT ) : sizeof( WAVEFORMATEX ) + m_waveFormat->cbSize; ntStatus = ZwWriteFile( m_FileHandle, NULL, NULL, NULL, &ioStatusBlock, &m_FileHeader, sizeof(m_FileHeader), m_pFilePtr, NULL); if (!NT_SUCCESS(ntStatus)) { DPF(D_TERSE, ("[CSaveData::FileWriteHeader : Write File Header Error]")); } m_pFilePtr->QuadPart += sizeof(m_FileHeader); ntStatus = ZwWriteFile( m_FileHandle, NULL, NULL, NULL, &ioStatusBlock, m_waveFormat, m_FileHeader.dwFormatLength, m_pFilePtr, NULL); if (!NT_SUCCESS(ntStatus)) { DPF(D_TERSE, ("[CSaveData::FileWriteHeader : Write Format Error]")); } m_pFilePtr->QuadPart += m_FileHeader.dwFormatLength; ntStatus = ZwWriteFile( m_FileHandle, NULL, NULL, NULL, &ioStatusBlock, &m_DataHeader, sizeof(m_DataHeader), m_pFilePtr, NULL); if (!NT_SUCCESS(ntStatus)) { DPF(D_TERSE, ("[CSaveData::FileWriteHeader : Write Data Header Error]")); } m_pFilePtr->QuadPart += sizeof(m_DataHeader); } else { DPF(D_TERSE, ("[CSaveData::FileWriteHeader : File not open]")); ntStatus = STATUS_INVALID_HANDLE; } return ntStatus; } // FileWriteHeader #pragma code_seg() //============================================================================= PSAVEWORKER_PARAM CSaveData::GetNewWorkItem ( void ) { LARGE_INTEGER timeOut = { 0 }; NTSTATUS ntStatus; for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++) { ntStatus = KeWaitForSingleObject ( &m_pWorkItems[i].EventDone, Executive, KernelMode, FALSE, &timeOut ); if (NT_SUCCESS(ntStatus)) { if (m_pWorkItems[i].WorkItem) return &(m_pWorkItems[i]); else return NULL; } } return NULL; } // GetNewWorkItem #pragma code_seg("PAGE") //============================================================================= NTSTATUS CSaveData::Initialize ( void ) { PAGED_CODE(); NTSTATUS ntStatus = STATUS_SUCCESS; WCHAR szTemp[MAX_PATH]; DPF_ENTER(("[CSaveData::Initialize]")); // Allocaet data file name. // swprintf(szTemp, L"%s_%d.wav", DEFAULT_FILE_NAME, m_ulStreamId); m_FileName.Length = 0; m_FileName.MaximumLength = (wcslen(szTemp) + 1) * sizeof(WCHAR); m_FileName.Buffer = (PWSTR) ExAllocatePool ( PagedPool, m_FileName.MaximumLength ); if (!m_FileName.Buffer) { DPF(D_TERSE, ("[Could not allocate memory for FileName]")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; } // Allocate memory for data buffer. // if (NT_SUCCESS(ntStatus)) { wcscpy(m_FileName.Buffer, szTemp); m_FileName.Length = wcslen(m_FileName.Buffer) * sizeof(WCHAR); DPF(D_BLAB, ("[New DataFile -- %s", m_FileName.Buffer)); m_pDataBuffer = (PBYTE) ExAllocatePoolWithTag ( NonPagedPool, m_ulBufferSize, MSVAD_POOLTAG ); if (!m_pDataBuffer) { DPF(D_TERSE, ("[Could not allocate memory for Saving Data]")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; } } // Allocate memory for frame usage flags and m_pFilePtr. // if (NT_SUCCESS(ntStatus)) { m_fFrameUsed = (PBOOL) ExAllocatePoolWithTag ( NonPagedPool, m_ulFrameCount * sizeof(BOOL) + sizeof(LARGE_INTEGER), MSVAD_POOLTAG ); if (!m_fFrameUsed) { DPF(D_TERSE, ("[Could not allocate memory for frame flags]")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; } } // Initialize the spinlock to synchronize access to the frames // KeInitializeSpinLock ( &m_FrameInUseSpinLock ) ; // Open the data file. // if (NT_SUCCESS(ntStatus)) { // m_fFrameUsed has additional memory to hold m_pFilePtr // m_pFilePtr = (PLARGE_INTEGER) (((PBYTE) m_fFrameUsed) + m_ulFrameCount * sizeof(BOOL)); RtlZeroMemory(m_fFrameUsed, m_ulFrameCount * sizeof(BOOL)); // Create data file. InitializeObjectAttributes ( &m_objectAttributes, &m_FileName, OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL ); // Write wave header information to data file. ntStatus = FileOpen(TRUE); if (NT_SUCCESS(ntStatus)) { ntStatus = FileWriteHeader(); FileClose(); } } return ntStatus; } // Initialize //============================================================================= NTSTATUS CSaveData::InitializeWorkItems ( IN PDEVICE_OBJECT DeviceObject ) { PAGED_CODE(); ASSERT(DeviceObject); NTSTATUS ntStatus = STATUS_SUCCESS; DPF_ENTER(("[CSaveData::InitializeWorkItems]")); m_pDeviceObject = DeviceObject; m_pWorkItems = (PSAVEWORKER_PARAM) ExAllocatePoolWithTag ( NonPagedPool, sizeof(SAVEWORKER_PARAM) * MAX_WORKER_ITEM_COUNT, MSVAD_POOLTAG ); if (m_pWorkItems) { for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++) { #ifdef USE_OBSOLETE_FUNCS ExInitializeWorkItem ( &m_pWorkItems[i].WorkItem, SaveFrameWorkerCallback, &m_pWorkItems[i] ); #else m_pWorkItems[i].WorkItem = IoAllocateWorkItem(DeviceObject); if(m_pWorkItems[i].WorkItem == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } #endif KeInitializeEvent ( &m_pWorkItems[i].EventDone, NotificationEvent, TRUE ); } } else { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } return ntStatus; } // InitializeWorkItems //============================================================================= void SaveFrameWorkerCallback #ifdef USE_OBSOLETE_FUNCS ( IN PVOID Context ) #else ( PDEVICE_OBJECT pDeviceObject, IN PVOID Context ) #endif { PAGED_CODE(); ASSERT(Context); PSAVEWORKER_PARAM pParam = (PSAVEWORKER_PARAM) Context; PCSaveData pSaveData; IO_STATUS_BLOCK ioStatusBlock; DPF(D_VERBOSE, ("[SaveFrameWorkerCallback], %d", pParam->ulFrameNo)); ASSERT(pParam->pSaveData); ASSERT(pParam->pSaveData->m_fFrameUsed); if (pParam->WorkItem) { pSaveData = pParam->pSaveData; if (NT_SUCCESS(pSaveData->FileOpen(FALSE))) { pSaveData->FileWrite(pParam->pData, pParam->ulDataSize); pSaveData->FileClose(); } InterlockedExchange( (LONG *)&(pSaveData->m_fFrameUsed[pParam->ulFrameNo]), FALSE ); } KeSetEvent(&pParam->EventDone, 0, FALSE); } // SaveFrameWorkerCallback //============================================================================= NTSTATUS CSaveData::SetDataFormat ( IN PKSDATAFORMAT pDataFormat ) { PAGED_CODE(); NTSTATUS ntStatus = STATUS_SUCCESS; DPF_ENTER(("[CSaveData::SetDataFormat]")); ASSERT(pDataFormat); PWAVEFORMATEX pwfx = NULL; if (IsEqualGUIDAligned(pDataFormat->Specifier, KSDATAFORMAT_SPECIFIER_DSOUND)) { pwfx = &(((PKSDATAFORMAT_DSOUND) pDataFormat)->BufferDesc.WaveFormatEx); } else if (IsEqualGUIDAligned(pDataFormat->Specifier, KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)) { pwfx = &((PKSDATAFORMAT_WAVEFORMATEX) pDataFormat)->WaveFormatEx; } if (pwfx) { // Free the previously allocated waveformat if (m_waveFormat) { ExFreePool(m_waveFormat); } m_waveFormat = (PWAVEFORMATEX) ExAllocatePoolWithTag ( NonPagedPool, (pwfx->wFormatTag == WAVE_FORMAT_PCM) ? sizeof( PCMWAVEFORMAT ) : sizeof( WAVEFORMATEX ) + pwfx->cbSize, MSVAD_POOLTAG ); if(m_waveFormat) { RtlCopyMemory( m_waveFormat, pwfx, (pwfx->wFormatTag == WAVE_FORMAT_PCM) ? sizeof( PCMWAVEFORMAT ) : sizeof( WAVEFORMATEX ) + pwfx->cbSize); } else { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } } return ntStatus; } // SetDataFormat //============================================================================= void CSaveData::ReadData ( IN PBYTE pBuffer, IN ULONG ulByteCount ) { // Not implemented yet. } // ReadData //============================================================================= #pragma code_seg() void CSaveData::SaveFrame ( IN ULONG ulFrameNo, IN ULONG ulDataSize ) { PSAVEWORKER_PARAM pParam = NULL; DPF_ENTER(("[CSaveData::SaveFrame]")); pParam = GetNewWorkItem(); if (pParam) { pParam->pSaveData = this; pParam->ulFrameNo = ulFrameNo; pParam->ulDataSize = ulDataSize; pParam->pData = m_pDataBuffer + ulFrameNo * m_ulFrameSize; KeResetEvent(&pParam->EventDone); #ifdef USE_OBSOLETE_FUNCS ExQueueWorkItem(&pParam->WorkItem, CriticalWorkQueue); #else IoQueueWorkItem(pParam->WorkItem, (PIO_WORKITEM_ROUTINE)SaveFrameWorkerCallback, CriticalWorkQueue, (PVOID)pParam); #endif } } // SaveFrame #pragma code_seg("PAGE") //============================================================================= void CSaveData::WaitAllWorkItems ( void ) { DPF_ENTER(("[CSaveData::WaitAllWorkItems]")); // Save the last partially-filled frame SaveFrame(m_ulFramePtr, m_ulBufferPtr - (m_ulFramePtr * m_ulFrameSize)); for (int i = 0; i < MAX_WORKER_ITEM_COUNT; i++) { DPF(D_VERBOSE, ("[Waiting for WorkItem] %d", i)); KeWaitForSingleObject ( &(m_pWorkItems[i].EventDone), Executive, KernelMode, FALSE, NULL ); } } // WaitAllWorkItems #pragma code_seg() //============================================================================= void CSaveData::WriteData ( IN PBYTE pBuffer, IN ULONG ulByteCount ) { ASSERT(pBuffer); ASSERT(ulByteCount); BOOL fSaveFrame = FALSE; ULONG ulSaveFramePtr; KIRQL OldIrql; LARGE_INTEGER timeOut = { 0 }; // If stream writing is disabled, then exit. // if (m_fWriteDisabled) { return; } DPF_ENTER(("[CSaveData::WriteData ulByteCount=%lu]", ulByteCount)); // Check to see if this frame is available. KeAcquireSpinLockAtDpcLevel( &m_FrameInUseSpinLock ); if (!m_fFrameUsed[m_ulFramePtr]) { KeReleaseSpinLockFromDpcLevel( &m_FrameInUseSpinLock ); ULONG ulWriteBytes = (ulByteCount + m_ulBufferPtr < m_ulBufferSize) ? ulByteCount : (m_ulBufferSize - m_ulBufferPtr); RtlCopyMemory(m_pDataBuffer + m_ulBufferPtr, pBuffer, ulWriteBytes); m_ulBufferPtr += ulWriteBytes; // Check to see if we need to save this frame if (m_ulBufferPtr >= ((m_ulFramePtr + 1) * m_ulFrameSize)) { fSaveFrame = TRUE; } // Loop the buffer, if we reached the end. if (m_ulBufferPtr == m_ulBufferSize) { fSaveFrame = TRUE; m_ulBufferPtr = 0; } if (fSaveFrame) { InterlockedExchange( (LONG *)&(m_fFrameUsed[m_ulFramePtr]), TRUE ); ulSaveFramePtr = m_ulFramePtr; m_ulFramePtr = (m_ulFramePtr + 1) % m_ulFrameCount; } // Write the left over if the next frame is available. if (ulWriteBytes != ulByteCount) { KeAcquireSpinLockAtDpcLevel( &m_FrameInUseSpinLock ); if (!m_fFrameUsed[m_ulFramePtr]) { KeReleaseSpinLockFromDpcLevel( &m_FrameInUseSpinLock ); RtlCopyMemory ( m_pDataBuffer + m_ulBufferPtr, pBuffer, ulWriteBytes ); } else { KeReleaseSpinLockFromDpcLevel( &m_FrameInUseSpinLock ); DPF(D_BLAB, ("[Frame overflow, next frame is in use]")); } } if (fSaveFrame) { SaveFrame(ulSaveFramePtr, m_ulFrameSize); } } else { KeReleaseSpinLockFromDpcLevel( &m_FrameInUseSpinLock ); DPF(D_BLAB, ("[Frame %d is in use]", m_ulFramePtr)); } } // WriteData