[host] initial service framework implemented

This commit is contained in:
Geoffrey McRae 2017-10-31 23:21:05 +11:00
parent 941f0f1c16
commit 3dd205bafc
10 changed files with 274 additions and 8 deletions

View File

@ -1,3 +1,4 @@
#pragma once
/* /*
KVMGFX Client - A KVM Client for VGA Passthrough KVMGFX Client - A KVM Client for VGA Passthrough
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com> Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
@ -16,6 +17,8 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#include <stdint.h>
#define KVMGFX_HEADER_MAGIC "[[KVMGFXHeader]]" #define KVMGFX_HEADER_MAGIC "[[KVMGFXHeader]]"
typedef enum FrameType typedef enum FrameType

15
host/CaptureFactory.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#define W32_LEAN_AND_MEAN
#include <Windows.h>
#include "ICapture.h"
static class CaptureFactory
{
public:
static ICapture * GetCaptureDevice()
{
return NULL;
}
};

14
host/ICapture.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#include <common/KVMGFXHeader.h>
__interface ICapture
{
public:
bool Initialize();
bool DeInitialize();
enum FrameType GetFrameType();
enum FrameComp GetFrameCompression();
size_t GetMaxFrameSize();
bool GrabFrame(void * buffer, size_t bufferSize, size_t * outLen);
};

165
host/Service.cpp Normal file
View File

@ -0,0 +1,165 @@
#include "service.h"
#include "ivshmem.h"
#include <common\debug.h>
#include <common\KVMGFXHeader.h>
#include "CaptureFactory.h"
Service * Service::m_instance = NULL;
Service::Service() :
m_initialized(false),
m_readyEvent(INVALID_HANDLE_VALUE),
m_capture(NULL),
m_memory(NULL)
{
m_ivshmem = IVSHMEM::Get();
}
Service::~Service()
{
}
bool Service::Initialize()
{
if (m_initialized)
DeInitialize();
if (!m_ivshmem->Initialize())
{
DEBUG_ERROR("IVSHMEM failed to initalize");
DeInitialize();
return false;
}
if (m_ivshmem->GetSize() < sizeof(KVMGFXHeader))
{
DEBUG_ERROR("Shared memory is not large enough for the KVMGFXHeader");
DeInitialize();
return false;
}
m_memory = m_ivshmem->GetMemory();
if (!m_memory)
{
DEBUG_ERROR("Failed to get IVSHMEM memory");
DeInitialize();
return false;
}
m_readyEvent = m_ivshmem->CreateVectorEvent(0);
if (m_readyEvent == INVALID_HANDLE_VALUE)
{
DEBUG_ERROR("Failed to get event for vector 0");
DeInitialize();
return false;
}
m_capture = CaptureFactory::GetCaptureDevice();
if (!m_capture || !m_capture->Initialize())
{
DEBUG_ERROR("Failed to initialize capture interface");
DeInitialize();
return false;
}
KVMGFXHeader * header = static_cast<KVMGFXHeader*>(m_memory);
ZeroMemory(header, sizeof(KVMGFXHeader));
memcpy(header->magic, KVMGFX_HEADER_MAGIC, sizeof(KVMGFX_HEADER_MAGIC));
header->version = 2;
header->guestID = m_ivshmem->GetPeerID();
m_initialized = true;
return true;
}
void Service::DeInitialize()
{
if (m_capture)
{
m_capture->DeInitialize();
m_capture = NULL;
}
if (m_readyEvent != INVALID_HANDLE_VALUE)
CloseHandle(m_readyEvent);
m_memory = NULL;
m_ivshmem->DeInitialize();
m_initialized = false;
}
bool Service::Process(HANDLE stopEvent)
{
if (!m_initialized)
return false;
KVMGFXHeader * header = static_cast<KVMGFXHeader *>(m_memory );
void * data = static_cast<void *>(header + 1);
const size_t available = m_ivshmem->GetSize() - sizeof(KVMGFXHeader);
if (m_capture->GetMaxFrameSize() > available)
{
DEBUG_ERROR("Frame could exceed buffer size!");
return false;
}
// setup the header
header->frameType = m_capture->GetFrameType();
header->compType = m_capture->GetFrameCompression();
// capture a frame of data
if (!m_capture->GrabFrame(
data,
m_ivshmem->GetSize() - sizeof(KVMGFXHeader),
&header->dataLen))
{
DEBUG_ERROR("Capture failed");
return false;
}
// tell the host where the cursor is
POINT cursorPos;
GetCursorPos(&cursorPos);
header->mouseX = cursorPos.x;
header->mouseY = cursorPos.y;
// wait for the host to notify that is it is ready to proceed
ResetEvent(m_readyEvent);
while(
stopEvent == INVALID_HANDLE_VALUE ||
(WaitForSingleObject(stopEvent, 0) == WAIT_OBJECT_0)
)
{
if (!m_ivshmem->RingDoorbell(header->hostID, 0))
{
DEBUG_ERROR("Failed to ring doorbell");
return false;
}
switch (WaitForSingleObject(m_readyEvent, 1000))
{
case WAIT_ABANDONED:
DEBUG_ERROR("Wait abandoned");
return false;
case WAIT_OBJECT_0:
return true;
// if we timed out we just continue to ring until we get an answer or we are stopped
case WAIT_TIMEOUT:
break;
case WAIT_FAILED:
DEBUG_ERROR("Wait failed");
return false;
default:
DEBUG_ERROR("Unknown error");
return false;
}
}
return true;
}

35
host/Service.h Normal file
View File

@ -0,0 +1,35 @@
#pragma once
#define W32_LEAN_AND_MEAN
#include <Windows.h>
#include <stdbool.h>
#include "ivshmem.h"
#include "ICapture.h"
class Service
{
public:
static Service * Get()
{
if (!m_instance)
m_instance = new Service();
return m_instance;
}
bool Initialize();
void DeInitialize();
bool Process(HANDLE stopEvent);
private:
static Service * m_instance;
Service();
~Service();
bool m_initialized;
IVSHMEM * m_ivshmem;
HANDLE m_readyEvent;
ICapture * m_capture;
void * m_memory;
};

View File

@ -216,4 +216,22 @@ HANDLE IVSHMEM::CreateVectorEvent(UINT16 vector)
} }
return event; return event;
}
bool IVSHMEM::RingDoorbell(UINT16 peerID, UINT16 door)
{
if (!m_initialized)
return false;
IVSHMEM_RING msg;
msg.peerID = peerID;
msg.vector = door;
if (!DeviceIoControl(m_handle, IOCTL_IVSHMEM_RING_DOORBELL, &msg, sizeof(IVSHMEM_RING), NULL, 0, NULL, NULL))
{
DEBUG_ERROR("DeviceIoControl Failed: %d", GetLastError());
return false;
}
return true;
} }

View File

@ -23,6 +23,7 @@ public:
UINT16 GetVectors(); UINT16 GetVectors();
void * GetMemory(); void * GetMemory();
HANDLE CreateVectorEvent(UINT16 vector); HANDLE CreateVectorEvent(UINT16 vector);
bool RingDoorbell(UINT16 peerID, UINT16 door);
protected: protected:

View File

@ -154,9 +154,13 @@
<ItemGroup> <ItemGroup>
<ClCompile Include="ivshmem.cpp" /> <ClCompile Include="ivshmem.cpp" />
<ClCompile Include="main.cpp" /> <ClCompile Include="main.cpp" />
<ClCompile Include="Service.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="CaptureFactory.h" />
<ClInclude Include="ICapture.h" />
<ClInclude Include="ivshmem.h" /> <ClInclude Include="ivshmem.h" />
<ClInclude Include="Service.h" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">

View File

@ -21,10 +21,22 @@
<ClCompile Include="main.cpp"> <ClCompile Include="main.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Service.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="ivshmem.h"> <ClInclude Include="ivshmem.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="ICapture.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Service.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CaptureFactory.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -2,7 +2,7 @@
#include <tchar.h> #include <tchar.h>
#include <common\debug.h> #include <common\debug.h>
#include "ivshmem.h" #include "Service.h"
#define SERVICE_NAME "kvm-ivshmem-host" #define SERVICE_NAME "kvm-ivshmem-host"
@ -146,19 +146,18 @@ VOID WINAPI ServiceCtrlHandler(DWORD CtrlCode)
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam) DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
{ {
IVSHMEM * ivshmem = IVSHMEM::Get(); Service *svc = svc->Get();
if (!ivshmem->Initialize()) if (!svc->Initialize())
{ {
DEBUG_ERROR("Failed to initialize IVSHMEM"); DEBUG_ERROR("Failed to initialize service");
return ERROR; return ERROR;
} }
while (WaitForSingleObject(app.serviceStopEvent, 0) != WAIT_OBJECT_0) while (WaitForSingleObject(app.serviceStopEvent, 0) != WAIT_OBJECT_0)
{ if (!svc->Process(app.serviceStopEvent))
Sleep(1000); break;
}
ivshmem->DeInitialize(); svc->DeInitialize();
return ERROR_SUCCESS; return ERROR_SUCCESS;
} }