421 lines · 14.1 KB · C++
RawDownload
//-----------------------------------------------------------------------------// Author - Marco Martinelli - https://github.com/martinellimarco//----------------------------------------------------------------------------- #pragma warning (disable : 4127) #include "scream.h"#include "ivshmemsavedata.h" //=============================================================================// Defines//============================================================================= #define MAGIC_NUM           0x11112014; #pragma code_seg("PAGE")//=============================================================================// CIVSHMEMSaveData//============================================================================= //=============================================================================BOOLEAN CIVSHMEMSaveData::RequestMMAP() {	if (!m_ivshmem.devObj) {		return FALSE;	}     IO_STATUS_BLOCK ioStatus = { 0 };    KEVENT event;    KeInitializeEvent(&event, NotificationEvent, FALSE);    PIRP irp = IoBuildDeviceIoControlRequest(        IOCTL_IVSHMEM_REQUEST_MMAP,        m_ivshmem.devObj,        NULL, 0,        &m_ivshmem.mmap, sizeof(IVSHMEM_MMAP),        FALSE,        &event,        &ioStatus    );    if (irp) {        if (IoCallDriver(m_ivshmem.devObj, irp) == STATUS_PENDING) {            KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);        }        if (ioStatus.Status == 0) {            return TRUE;        }        else {            DPF_ENTER(("IVSHMEM: IRP for REQUEST_MMAP failed with status %u\n", ioStatus.Status));        }    }    else {        DPF_ENTER(("IVSHMEM: Failed to get the IRP for REQUEST_MMAP\n"));    }    return FALSE;} //=============================================================================void CIVSHMEMSaveData::ReleaseMMAP() {	if (!m_ivshmem.devObj) {		return;	}     IO_STATUS_BLOCK ioStatus = { 0 };    KEVENT event;    KeInitializeEvent(&event, NotificationEvent, FALSE);    PIRP irp = IoBuildDeviceIoControlRequest(        IOCTL_IVSHMEM_RELEASE_MMAP,        m_ivshmem.devObj,        NULL, 0,        NULL, 0,        FALSE,        &event,        &ioStatus    );    if (irp) {        if (IoCallDriver(m_ivshmem.devObj, irp) == STATUS_PENDING) {            KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);        }        if (ioStatus.Status != 0) {            DPF_ENTER(("IVSHMEM: IRP for RELEASE_MMAP failed with status %u\n", ioStatus.Status));        }    }    else {        DPF_ENTER(("IVSHMEM: Failed to get the IRP for RELEASE_MMAP\n"));    }} //=============================================================================CIVSHMEMSaveData::CIVSHMEMSaveData() : m_pBuffer(NULL), m_ulOffset(0), m_ulSendOffset(0), m_fWriteDisabled(FALSE) {    PAGED_CODE();     DPF_ENTER(("[CIVSHMEMSaveData::CIVSHMEMSaveData]"));        if (g_UseIVSHMEM) {        // allocate work item for this stream        m_pWorkItem = (PIVSHMEM_SAVEWORKER_PARAM)ExAllocatePoolWithTag(NonPagedPool, sizeof(IVSHMEM_SAVEWORKER_PARAM), MSVAD_POOLTAG);        if (m_pWorkItem) {            m_pWorkItem->WorkItem = IoAllocateWorkItem(GetDeviceObject());            KeInitializeEvent(&(m_pWorkItem->EventDone), NotificationEvent, TRUE);        }         // Enumerate IVSHMEM devices        GUID interfaceClassGuid = { 0xdf576976,0x569d,0x4672,0x95,0xa0,0xf5,0x7e,0x4e,0xa0,0xb2,0x10 };        PWSTR symbolicLinkList;        NTSTATUS ntStatus;        ntStatus = IoGetDeviceInterfaces(            &interfaceClassGuid,            NULL,            0,            &symbolicLinkList        );         if (NT_SUCCESS(ntStatus) && NULL != symbolicLinkList) {            PFILE_OBJECT fileObj;            PDEVICE_OBJECT devObj;            UNICODE_STRING objName;             KEVENT event;            IO_STATUS_BLOCK ioStatus = { 0 };             PIRP irp;            UINT64 ivshmemSize = 0;                        for (PWSTR symbolicLink = symbolicLinkList;                symbolicLink[0] != NULL && symbolicLink[1] != NULL;                symbolicLink += wcslen(symbolicLink) + 1) {                RtlInitUnicodeString(&objName, symbolicLink),                 ntStatus = IoGetDeviceObjectPointer(                    &objName,                    FILE_ALL_ACCESS,                    &fileObj,                    &devObj                );                if (NT_SUCCESS(ntStatus)) {                    ObDereferenceObject(fileObj);                    ntStatus = ObReferenceObjectByPointer(devObj, FILE_ALL_ACCESS, NULL, KernelMode);                    if (NT_SUCCESS(ntStatus)) {                        ivshmemSize = 0;                        KeInitializeEvent(&event, NotificationEvent, FALSE);                        irp = IoBuildDeviceIoControlRequest(                            IOCTL_IVSHMEM_REQUEST_SIZE,                            devObj,                            NULL, 0,                            &ivshmemSize, sizeof(ivshmemSize),                            FALSE,                            &event,                            &ioStatus                        );                        if (irp) {                            ntStatus = IoCallDriver(devObj, irp);                            if (ntStatus == STATUS_PENDING) {                                KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);                            }                            if (ioStatus.Status == 0 && ivshmemSize == g_UseIVSHMEM*1024*1024) { //we suppose that we are the only g_UseIVSHMEM*1MiB IVSHMEM device around                                DPF_ENTER(("IVSHMEM Device Detected!\n"));                                                                RtlZeroMemory(&m_ivshmem, sizeof(IVSHMEM_OBJECT));                                m_ivshmem.devObj = devObj;                                // we have found a suitable device, we skip the others, and we DON'T dereference devObj, it will be dereferenced in the destructor                                break;                            }                        }                        else {                            DPF_ENTER(("IVSHMEM: Failed to get the IRP for REQUEST_SIZE\n"));                        }                    }                     ObDereferenceObject(devObj);                }            }        }         ExFreePool(symbolicLinkList);    }} // CIVSHMEMSaveData //=============================================================================CIVSHMEMSaveData::~CIVSHMEMSaveData() {    PAGED_CODE();     DPF_ENTER(("[CIVSHMEMSaveData::~CIVSHMEMSaveData]"));    if (g_UseIVSHMEM) {        // frees the work item        if (m_pWorkItem->WorkItem != NULL) {            IoFreeWorkItem(m_pWorkItem->WorkItem);            m_pWorkItem->WorkItem = NULL;        }         //release mmap        if (m_ivshmem.initialized) {            m_ivshmem.initialized = false;            if (RequestMMAP()) {                //set to 0 the header so the receiver know we terminated                RtlZeroMemory(m_ivshmem.mmap.ptr, sizeof(IVSHMEM_SCREAM_HEADER));            }            ReleaseMMAP();        }        if (m_ivshmem.devObj) {            ObDereferenceObject(m_ivshmem.devObj);        }         if (m_pBuffer) {            ExFreePoolWithTag(m_pBuffer, MSVAD_POOLTAG);        }    }} // CIVSHMEMSaveData //=============================================================================void CIVSHMEMSaveData::DestroyWorkItems(void) {    PAGED_CODE();        DPF_ENTER(("[CIVSHMEMSaveData::DestroyWorkItems]"));     if (m_pWorkItem) {        ExFreePoolWithTag(m_pWorkItem, MSVAD_POOLTAG);        m_pWorkItem = NULL;    } } // DestroyWorkItems //=============================================================================void CIVSHMEMSaveData::Disable(BOOL fDisable) {    PAGED_CODE();     m_fWriteDisabled = fDisable;} // Disable //=============================================================================NTSTATUS CIVSHMEMSaveData::SetDeviceObject(IN PDEVICE_OBJECT DeviceObject) {    PAGED_CODE();     ASSERT(DeviceObject);     NTSTATUS ntStatus = STATUS_SUCCESS;        m_pDeviceObject = DeviceObject;    return ntStatus;} //=============================================================================PDEVICE_OBJECT CIVSHMEMSaveData::GetDeviceObject(void) {    PAGED_CODE();     return m_pDeviceObject;} #pragma code_seg("PAGE")//=============================================================================NTSTATUS CIVSHMEMSaveData::Initialize(DWORD nSamplesPerSec, WORD wBitsPerSample, WORD nChannels, DWORD dwChannelMask) {    PAGED_CODE();     NTSTATUS          ntStatus = STATUS_SUCCESS;     DPF_ENTER(("[CIVSHMEMSaveData::Initialize]"));     m_ivshmem.chunkSize = (UINT32)((wBitsPerSample>>3)*nChannels*nSamplesPerSec/50);    if (RequestMMAP()) {        PIVSHMEM_SCREAM_HEADER hdr = (PIVSHMEM_SCREAM_HEADER)m_ivshmem.mmap.ptr;         RtlZeroMemory(hdr, sizeof(IVSHMEM_SCREAM_HEADER));        m_ivshmem.writeIdx = 0;        hdr->offset = m_ivshmem.offset = (UINT8)sizeof(IVSHMEM_SCREAM_HEADER);        hdr->chunkSize = m_ivshmem.chunkSize;        hdr->maxChunks = m_ivshmem.maxChunks = (UINT16)(m_ivshmem.mmap.size / m_ivshmem.chunkSize);        m_ivshmem.bufferSize = m_ivshmem.chunkSize*m_ivshmem.maxChunks;         // Only multiples of 44100 and 48000 are supported        hdr->sampleRate = (UINT8)((nSamplesPerSec % 44100) ? (0 + (nSamplesPerSec / 48000)) : (128 + (nSamplesPerSec / 44100)));        hdr->sampleSize = (UINT8)(wBitsPerSample);        hdr->channels = (UINT8)(nChannels);        hdr->channelMap = (UINT16)(dwChannelMask);         hdr->magic = MAGIC_NUM;        m_ivshmem.initialized = true;    }    else {        m_ivshmem.initialized = false;    }    ReleaseMMAP();     // Allocate memory for data buffer.    if (NT_SUCCESS(ntStatus)) {        m_pBuffer = (PBYTE)ExAllocatePoolWithTag(NonPagedPool, m_ivshmem.bufferSize, MSVAD_POOLTAG);        if (!m_pBuffer) {            DPF(D_TERSE, ("[Could not allocate memory for sending data]"));            ntStatus = STATUS_INSUFFICIENT_RESOURCES;        }    }     return ntStatus;} // Initialize //=============================================================================IO_WORKITEM_ROUTINE IVSHMEMSendDataWorkerCallback; VOID IVSHMEMSendDataWorkerCallback(PDEVICE_OBJECT pDeviceObject, IN  PVOID  Context) {    UNREFERENCED_PARAMETER(pDeviceObject);     PAGED_CODE();     ASSERT(Context);        PIVSHMEM_SAVEWORKER_PARAM pParam = (PIVSHMEM_SAVEWORKER_PARAM) Context;    PCIVSHMEMSaveData        pSaveData;     ASSERT(pParam->pSaveData);     if (pParam->WorkItem) {        pSaveData = pParam->pSaveData;        pSaveData->IVSHMEMSendData();    }     KeSetEvent(&(pParam->EventDone), 0, FALSE);} // SendDataWorkerCallback //=============================================================================void CIVSHMEMSaveData::IVSHMEMSendData() {    ULONG storeOffset;    PBYTE mmap = NULL;    if (RequestMMAP()) {        mmap = (PBYTE)m_ivshmem.mmap.ptr + m_ivshmem.offset;    }     PIVSHMEM_SCREAM_HEADER hdr = (PIVSHMEM_SCREAM_HEADER)m_ivshmem.mmap.ptr;        while (1) {        // Read latest storeOffset. There might be new data.        storeOffset = m_ulOffset;         // Abort if there's nothing to send. Note: When storeOffset < sendOffset, we can always send a chunk.        if ((storeOffset >= m_ulSendOffset) && ((storeOffset - m_ulSendOffset) < m_ivshmem.chunkSize))            break;         // Send a chunk        if (mmap) {            if (++m_ivshmem.writeIdx >= m_ivshmem.maxChunks) {                m_ivshmem.writeIdx = 0;            }            RtlCopyMemory(&mmap[m_ivshmem.writeIdx*m_ivshmem.chunkSize], &m_pBuffer[m_ulSendOffset], m_ivshmem.chunkSize);            hdr->writeIdx = m_ivshmem.writeIdx;        }         m_ulSendOffset += m_ivshmem.chunkSize; if (m_ulSendOffset >= m_ivshmem.bufferSize) m_ulSendOffset = 0;    }    ReleaseMMAP();} #pragma code_seg("PAGE")//=============================================================================void CIVSHMEMSaveData::WaitAllWorkItems(void) {    PAGED_CODE();     DPF_ENTER(("[CIVSHMEMSaveData::WaitAllWorkItems]"));     DPF(D_VERBOSE, ("[Waiting for WorkItem]"));    KeWaitForSingleObject(&(m_pWorkItem->EventDone), Executive, KernelMode, FALSE, NULL);    } // WaitAllWorkItems #pragma code_seg()//=============================================================================void CIVSHMEMSaveData::WriteData(IN PBYTE pBuffer, IN ULONG ulByteCount) {    ASSERT(pBuffer);     if (!m_ivshmem.initialized) {        return;    }     LARGE_INTEGER timeOut = { 0 };    NTSTATUS ntStatus;    ULONG offset;    ULONG toWrite;    ULONG w;     if (m_fWriteDisabled) {        return;    }     //DPF_ENTER(("[CIVSHMEMSaveData::WriteData ulByteCount=%lu]", ulByteCount));     // Undersized (paranoia)    if (0 == ulByteCount) {        return;    }     // Oversized (paranoia)    if (ulByteCount > (m_ivshmem.bufferSize / 2)) {        return;    }     // Append to ring buffer. Don't write intermediate states to m_ulOffset,    // but update it once at the end.    offset = m_ulOffset;    toWrite = ulByteCount;    while (toWrite > 0) {        w = offset % m_ivshmem.chunkSize;        if (w > 0) {            // Fill up last chunk            w = (m_ivshmem.chunkSize - w);            w = (toWrite < w) ? toWrite : w;            RtlCopyMemory(&(m_pBuffer[offset]), &(pBuffer[ulByteCount - toWrite]), w);        }        else {            // Start a new chunk            w = ((m_ivshmem.bufferSize - offset) < toWrite) ? (m_ivshmem.bufferSize - offset) : toWrite;            w = (w > m_ivshmem.chunkSize) ? m_ivshmem.chunkSize : w;            RtlCopyMemory(&(m_pBuffer[offset]), &(pBuffer[ulByteCount - toWrite]), w);        }        toWrite -= w;        offset += w;  if (offset >= m_ivshmem.bufferSize) offset = 0;    }    m_ulOffset = offset;     // If I/O worker was done, relaunch it    ntStatus = KeWaitForSingleObject(&(m_pWorkItem->EventDone), Executive, KernelMode, FALSE, &timeOut);    if (STATUS_SUCCESS == ntStatus) {        m_pWorkItem->pSaveData = this;        KeResetEvent(&(m_pWorkItem->EventDone));        IoQueueWorkItem(m_pWorkItem->WorkItem, IVSHMEMSendDataWorkerCallback, CriticalWorkQueue, (PVOID)m_pWorkItem);    }} // WriteData

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t