Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

820 lines
21 KiB

/*++
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 <msvad.h>
#include "savedata.h"
#include <stdio.h> // 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